load custom .g1a, add file system + many syscalls

This commit is contained in:
kishimisu 2023-11-18 10:13:34 +01:00
parent 553e0e6745
commit daa10b58e8
23 changed files with 1148 additions and 601 deletions

View File

@ -1,7 +1,6 @@
# Casio fx-9860 SH4 emulator
This is a work-in-progress emulator for the Casio fx-9860 SH4 models
It will display the content of the VRAM in the console at the end of the execution.
Broswser demo : https://sh4.vercel.app/
@ -12,29 +11,37 @@ All non-DSP instructions are implemented, except the following ones:
fx9860-emulator/ contains the source for the emulator
scripts/ contains the code for auto-generating the instructions code from the doc
scripts/ contains the code for auto-generating the instructions set from the doc
### Browser version
To build the browser version of the emulator, make sure you have emscripten downloaded and installed: https://emscripten.org/docs/getting_started/downloads.html
First, make sur `#define EMSCRIPT` is enabled in main.h (not as a comment).
First, make sure `#define USE_EMSCRIPT` is enabled in main.h (not as a comment).
Then:
cd fx9860-emulator
mkdir build
cd build
emcmake make ..
emcmake cmake ..
emmake make
Then, until I find a nicer way to integrate this into the Makefile, run the following command:
emcc -O2 .\CMakeFiles\fx9860-emulator.dir\main.o .\CMakeFiles\fx9860-emulator.dir\utils.o .\CMakeFiles\fx9860-emulator.dir\memory.o .\CMakeFiles\fx9860-emulator.dir\instructions\instructions.o .\CMakeFiles\fx9860-emulator.dir\instructions\syscalls.o --embed-file ..\..\U+0020.png@U+0020.png --embed-file ..\..\PrintMini.bmp@PrintMini.bmp --embed-file ..\..\JJSH3.G1A@JJSH3.G1A -sASYNCIFY -sEXIT_RUNTIME -o index.js -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE='$getBoundingClientRect' -sEXPORTED_RUNTIME_METHODS='writeArrayToMemory, setValue, getValue'
emcc -O2 ".\CMakeFiles\fx9860-emulator.dir\main.o" ".\CMakeFiles\fx9860-emulator.dir\utils.o" ".\CMakeFiles\fx9860-emulator.dir\memory.o" ".\CMakeFiles\fx9860-emulator.dir\instructions\instructions.o" ".\CMakeFiles\fx9860-emulator.dir\syscalls\syscalls.o" ".\CMakeFiles\fx9860-emulator.dir\syscalls\filesystem.o" ".\CMakeFiles\fx9860-emulator.dir\syscalls\malloc.o" ".\CMakeFiles\fx9860-emulator.dir\syscalls\misc.o" ".\CMakeFiles\fx9860-emulator.dir\syscalls\print.o" \
--embed-file "..\..\U+0020.png@U+0020.png" \
--embed-file "..\..\PrintMini.bmp@PrintMini.bmp" \
--embed-file "..\..\JJSH3.G1A@JJSH3.G1A" \
-sEXPORTED_FUNCTIONS="_main,_get_next_instruction,_reset_execution,_load_new_g1a" \
-sASYNCIFY -sEXIT_RUNTIME -o index.js \
-sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE='$getBoundingClientRect' \
-sEXPORTED_RUNTIME_METHODS='writeArrayToMemory, setValue, getValue'
After that you can server the content of the build/ folder through a local server and open index.html
After that you can serve the content of the build/ folder through a local server and open index.html
### Console version
To build the console version of the emulator (no display, only VRAM dump after 10M executions):
To build the console version of the emulator (no display, only VRAM dump after 10M iterations):
First, remove (comment) `#define EMSCRIPT` in main.h
First, remove (comment) `#define USE_EMSCRIPT` in main.h
cd fx9860-emulator
mkdir build

View File

@ -1,12 +1,27 @@
# CMakeList.txt : CMake project for fx9860-emulator, include source and define
# project specific logic here.
#
# Add source to this project's executable.
add_executable (fx9860-emulator "main.c" "headers/main.h" "headers/instructions/instructions.h" "instructions/instructions.c" "headers/utils.h" "utils.c" "memory.c" "headers/memory.h" "instructions/syscalls.c" "headers/instructions/syscalls.h" "headers/stb_image.h" "headers/display.h" )
# Add source to this project's executable.
add_executable (fx9860-emulator
"main.c"
"headers/main.h"
"headers/instructions/instructions.h"
"instructions/instructions.c"
"headers/utils.h"
"utils.c"
"memory.c"
"headers/memory.h"
"syscalls/syscalls.c"
"syscalls/filesystem.c"
"syscalls/malloc.c"
"syscalls/misc.c"
"syscalls/print.c"
"headers/syscalls/syscalls.h"
"headers/syscalls/filesystem.h"
"headers/syscalls/malloc.h"
"headers/syscalls/misc.h"
"headers/syscalls/print.h"
"headers/stb_image.h"
"headers/display.h"
)
if (CMAKE_VERSION VERSION_GREATER 3.12)
set_property(TARGET fx9860-emulator PROPERTY CXX_STANDARD 20)
endif()
# TODO: Add tests and install targets if needed.
endif()

View File

@ -14,14 +14,6 @@
#define LCD_SELECT_REGISTER 0xB4000000
#define LCD_DATA_REGISTER 0xB4010000
// Size of characters in ASCII texture
#define ASCII_CHAR_WIDTH 7
#define ASCII_CHAR_HEIGHT 9
// Size of characters on screen
#define CHAR_WIDTH 6
#define CHAR_HEIGHT 8
// https://bible.planet-casio.com/simlo/chm/v20/fx_legacy_Cursor.HTM
typedef struct {
int col; // starts at 1
@ -33,7 +25,6 @@ struct display_t {
uint8_t vram[VRAM_SIZE];
// LCD screen emulation
uint8_t lcd[VRAM_SIZE];
uint8_t lcd_registers; // Use for both selector & data registers
// Current cursor position

View File

@ -3,6 +3,7 @@
#include "../main.h"
void (*get_instruction_impl(uint16_t instruction))(cpu_t*, uint16_t);
const char* get_instruction_name(uint16_t instruction);
#define R0 cpu->r[0]
@ -162,5 +163,4 @@ void XORM(cpu_t* cpu, uint16_t instruction);
void XTRCT(cpu_t* cpu, uint16_t instruction);
void BSR(cpu_t* cpu, uint16_t instruction);
void BSRF(cpu_t* cpu, uint16_t instruction);
void JSR(cpu_t* cpu, uint16_t instruction);
void JSR(cpu_t* cpu, uint16_t instruction);

View File

@ -1,25 +0,0 @@
#pragma once
#include "../main.h"
void run_syscall(cpu_t* cpu);
void syscall_GetVRAMAddress(cpu_t* cpu);
void syscall_Bdisp_AllClr_DD(cpu_t* cpu);
void syscall_Bdisp_AllClr_VRAM(cpu_t* cpu);
void syscall_Bdisp_AllClr_DDVRAM(cpu_t* cpu);
void syscall_GetKey(cpu_t* cpu, unsigned int keycode_address);
void syscall_Locate(cpu_t* cpu, int x, int y);
void syscall_Print(cpu_t* cpu, const unsigned char* str);
void syscall_PrintXY(cpu_t* cpu, int x, int y, const unsigned char* str, int mode);
void syscall_PrintMiniSd(cpu_t* cpu, int x, int y, const unsigned char* str, int mode);
void syscall_GetAppName(cpu_t* cpu, char* dest);
void syscall_GlibGetAddinLibInf(cpu_t* cpu, uint32_t a_ptr, uint32_t b_ptr, uint32_t c_ptr);
void syscall_GlibGetOSVersionInfo(cpu_t* cpu, uint32_t a_ptr, uint32_t b_ptr, uint32_t c_ptr, uint32_t d_ptr);
void syscall_Malloc(cpu_t* cpu, uint32_t size, uint8_t clear_data);
void syscall_Bfile_OpenFile_OS(cpu_t* cpu, const char* filename, int mode, int mode2);
void syscall_Bfile_CreateEntry_OS(cpu_t* cpu, const char* filename, int mode, int size_ptr);

View File

@ -4,6 +4,8 @@
typedef struct memory_t memory_t;
typedef struct cpu_t cpu_t;
typedef struct display_t display_t;
typedef struct fs_t fs_t;
typedef struct malloc_manager_t malloc_manager_t;
#define USE_EMSCRIPT
@ -19,7 +21,7 @@ typedef struct display_t display_t;
#include "display.h"
#include "utils.h"
#include "instructions/instructions.h"
#include "instructions/syscalls.h"
#include "syscalls/syscalls.h"
// Initialization
#define PC_PROGRAM_START 0x00300200 // Execution starting address
@ -34,30 +36,36 @@ struct cpu_t {
int32_t r[16];
// Control registers
uint32_t gbr; // Global base register
uint32_t sr; // Status register
uint32_t ssr; // Saved status register
uint32_t spc; // Saved program counter
uint32_t vbr; // Vector base register
uint32_t sgr; // Saved general register
uint32_t dbr; // Debug base register
uint32_t gbr; // Global base register
uint32_t sr; // Status register
uint32_t ssr; // Saved status register
uint32_t spc; // Saved program counter
uint32_t vbr; // Vector base register
uint32_t sgr; // Saved general register
uint32_t dbr; // Debug base register
// System registers
uint32_t pc; // Program counter
uint32_t pr; // Procedure register
uint32_t mach; // Multiply-accumulate high
uint32_t macl; // Multiply-accumulate low
// Custom variables
uint8_t isExecutionFinished; // Set to true when PR == PC_PROGRAM_START
uint32_t pc; // Program counter
uint32_t pr; // Procedure register
uint32_t mach; // Multiply-accumulate high
uint32_t macl; // Multiply-accumulate low
// Debug
uint32_t instruction_count;
uint32_t instruction_per_frame;
uint32_t instruction_count; // Number of instructions executed
uint32_t instruction_per_frame; // Number of instructions to execute each frame
uint32_t execution_paused; // Whether the execution is paused
uint32_t execution_finished; // Whether the program has reached the end
uint32_t execute_one_step; // Whether to execute only one step
uint32_t next_breakpoint; // The execution will pause when reaching this address
// External Components
memory_t* mem; // Memory
display_t* disp; // Display
memory_t* mem; // Memory
display_t* disp; // Display
fs_t* fs; // Custom file system
malloc_manager_t* malloc_manager; // malloc/realloc/free manager
int32_t RTC_Ticks; // 1/128 second ticks (updated from javascript)
// (Temporary) Character sets
uint8_t* asciiTexture;

View File

@ -41,9 +41,9 @@ struct memory_t {
uint8_t keyboard[10]; // Keyboard rows state
uint8_t keyboard_registers[KB_SIZE]; // Keyboard registers
uint32_t* tmp;
uint8_t* tmp;
// uint8_t xram[XRAM_SIZE];
// uint8_t xram[XRAM_SIZE]; // TODO: add (gint support)
// uint8_t yram[YRAM_SIZE];
// uint8_t ilram[ILRAM_SIZE];

View File

@ -0,0 +1,30 @@
#pragma once
#include "syscalls.h"
typedef struct {
char name[40];
FILE* file_handle;
int32_t handle;
int32_t seek_pos;
int32_t size;
} file_t;
struct fs_t {
file_t files[4]; // Only 4 files can be opened at the same time on real hardware
uint8_t open_files_count;
};
void syscall_Bfile_CreateEntry(cpu_t* cpu, uint32_t filename_ptr, int mode, uint32_t size_ptr);
void syscall_Bfile_DeleteEntry(cpu_t* cpu, uint32_t filename_ptr, int mode);
void syscall_Bfile_OpenFile(cpu_t* cpu, uint32_t filename_ptr, int mode, int mode2);
void syscall_Bfile_CloseFile(cpu_t* cpu, int handle);
void syscall_Bfile_WriteFile(cpu_t* cpu, int handle, uint32_t buf_ptr, int size);
void syscall_Bfile_ReadFile(cpu_t* cpu, int handle, uint32_t buf_ptr, int size, int readpos);
void syscall_Bfile_SeekFile(cpu_t* cpu, int handle, int pos);
// Not implemented
void syscall_MCSGetDlen2(cpu_t* cpu, int32_t dir_ptr, int32_t item_ptr, int32_t len_ptr);
void syscall_MCSGetData1(cpu_t* cpu, int32_t offset, int32_t len_to_copy, int32_t buffer_ptr);
void syscall_MCSPutVar2(cpu_t* cpu, int32_t dir_ptr, int32_t item_ptr, int32_t data_len, int32_t buffer_ptr);
void syscall_MCSOvwDat2(cpu_t* cpu, int32_t dir_ptr, int32_t item_ptr, int32_t bytes_to_write, int32_t buffer_ptr, int32_t write_offset);

View File

@ -0,0 +1,17 @@
#pragma once
#include "syscalls.h"
#define MALLOC_START 0x22220000
#define MALLOC_SIZE (128 * 1024)
// Ugly malloc manager that works for testing
struct malloc_manager_t {
uint8_t* mallocs;
uint32_t mallocCount;
uint32_t currentSize;
};
void syscall_Malloc(cpu_t* cpu, uint32_t size, uint8_t clear_data);
void syscall_Realloc(cpu_t* cpu, uint32_t ptr, uint32_t size);
void syscall_Free(cpu_t* cpu, uint32_t ptr);

View File

@ -0,0 +1,12 @@
#pragma once
#include "syscalls.h"
void syscall_GetVRAMAddress(cpu_t* cpu);
void syscall_GetAppName(cpu_t* cpu, uint32_t dest_ptr);
void syscall_GlibGetAddinLibInf(cpu_t* cpu, uint32_t a_ptr, uint32_t b_ptr, uint32_t c_ptr);
void syscall_GlibGetOSVersionInfo(cpu_t* cpu, uint32_t a_ptr, uint32_t b_ptr, uint32_t c_ptr, uint32_t d_ptr);
void syscall_RTC_GetTicks(cpu_t* cpu);
void syscall_OS_inner_Sleep(cpu_t* cpu, uint32_t ms);
void syscall_GetKey(cpu_t* cpu, unsigned int keycode_address);

View File

@ -0,0 +1,22 @@
#pragma once
#include "syscalls.h"
// Size of characters in ASCII texture
#define ASCII_CHAR_WIDTH 7
#define ASCII_CHAR_HEIGHT 9
// Size of characters on screen
#define CHAR_WIDTH 6
#define CHAR_HEIGHT 8
void syscall_Bdisp_AllClr_DD(cpu_t* cpu);
void syscall_Bdisp_AllClr_VRAM(cpu_t* cpu);
void syscall_Bdisp_AllClr_DDVRAM(cpu_t* cpu);
void syscall_Bdisp_PutDisp_DD(cpu_t* cpu);
void syscall_Bdisp_SetPoint_VRAM(cpu_t* cpu, uint32_t x, uint32_t y, uint32_t point);
void syscall_Locate(cpu_t* cpu, int x, int y);
void syscall_Print(cpu_t* cpu, uint32_t str_ptr);
void syscall_PrintXY(cpu_t* cpu, int x, int y, uint32_t str_ptr, int mode);
void syscall_PrintMiniSd(cpu_t* cpu, int x, int y, uint32_t str_ptr, int mode);

View File

@ -0,0 +1,9 @@
#pragma once
#include "../main.h"
#include "filesystem.h"
#include "print.h"
#include "malloc.h"
#include "misc.h"
void run_syscall(cpu_t* cpu);

View File

@ -14,4 +14,5 @@ void print_binary(uint32_t x, int n);
uint8_t* load_character_set(const char* path);
void cpu_debug(cpu_t* cpu);
void vram_debug(cpu_t* cpu);
void vram_debug(cpu_t* cpu);
char* get_next_instruction(cpu_t* cpu);

View File

@ -181,53 +181,205 @@ void (*get_instruction_impl(uint16_t instruction))(cpu_t*, uint16_t) {
print_binary(instruction, 16);
critical_error(" => Unknown instruction: 0x%04X\n", instruction);
return 0;
}
// #include <stdint.h>
const char* get_instruction_name(uint16_t instruction) {
uint8_t high = (instruction >> 12) & 0x0F;
uint8_t high8 = instruction >> 8;
uint8_t low = instruction & 0x0F;
uint8_t low8 = instruction & 0xFF;
// typedef struct cpu_t cpu_t;
if (instruction == 0b0000000000101000) return "CLRMAC";
if (instruction == 0b0000000001001000) return "CLRS";
if (instruction == 0b0000000000001000) return "CLRT";
if (instruction == 0b0000000000011001) return "DIV0U";
if (instruction == 0b0000000000001001) return "NOP";
if (instruction == 0b0000000000101011) return "RTE";
if (instruction == 0b0000000000001011) return "RTS";
if (instruction == 0b0000000001011000) return "SETS";
if (instruction == 0b0000000000011000) return "SETT";
if (high8 == 0b11001001) return "ANDI";
if (high8 == 0b11001101) return "ANDM";
if (high8 == 0b10001011) return "BF";
if (high8 == 0b10001111) return "BFS";
if (high8 == 0b10001001) return "BT";
if (high8 == 0b10001101) return "BTS";
if (high8 == 0b10001000) return "CMPIM";
if (high8 == 0b11000100) return "MOVBLG";
if (high8 == 0b11000101) return "MOVWLG";
if (high8 == 0b11000110) return "MOVLLG";
if (high8 == 0b11000000) return "MOVBSG";
if (high8 == 0b11000001) return "MOVWSG";
if (high8 == 0b11000010) return "MOVLSG";
if (high8 == 0b10000000) return "MOVBS4";
if (high8 == 0b10000001) return "MOVWS4";
if (high8 == 0b10000100) return "MOVBL4";
if (high8 == 0b10000101) return "MOVWL4";
if (high8 == 0b11000111) return "MOVA";
if (high8 == 0b11001011) return "ORI";
if (high8 == 0b11001111) return "ORM";
if (high8 == 0b11001000) return "TSTI";
if (high8 == 0b11001100) return "TSTM";
if (high8 == 0b11001010) return "XORI";
if (high8 == 0b11001110) return "XORM";
if (high == 0b0111) return "ADDI";
if (high == 0b1010) return "BRA";
if (high == 0b1110) return "MOVI";
if (high == 0b1001) return "MOVWI";
if (high == 0b1101) return "MOVLI";
if (high == 0b0001) return "MOVLS4";
if (high == 0b0101) return "MOVLL4";
if (high == 0b1011) return "BSR";
if (high == 0b0100) {
if (low8 == 0b00010101) return "CMPPL";
if (low8 == 0b00010001) return "CMPPZ";
if (low8 == 0b00010000) return "DT";
if (low8 == 0b00101011) return "JMP";
if (low8 == 0b00011110) return "LDCGBR";
if (low8 == 0b00101110) return "LDCVBR";
if (low8 == 0b00111010) return "LDCSGR";
if (low8 == 0b00111110) return "LDCSSR";
if (low8 == 0b01001110) return "LDCSPC";
if (low8 == 0b11111010) return "LDCDBR";
if (low8 == 0b00010111) return "LDCMGBR";
if (low8 == 0b00100111) return "LDCMVBR";
if (low8 == 0b00110110) return "LDCMSGR";
if (low8 == 0b00110111) return "LDCMSSR";
if (low8 == 0b01000111) return "LDCMSPC";
if (low8 == 0b11110110) return "LDCMDBR";
if (low8 == 0b00001010) return "LDSMACH";
if (low8 == 0b00011010) return "LDSMACL";
if (low8 == 0b00101010) return "LDSPR";
if (low8 == 0b00000110) return "LDSMMACH";
if (low8 == 0b00010110) return "LDSMMACL";
if (low8 == 0b00100110) return "LDSMPR";
if (low8 == 0b00100100) return "ROTCL";
if (low8 == 0b00100101) return "ROTCR";
if (low8 == 0b00000100) return "ROTL";
if (low8 == 0b00000101) return "ROTR";
if (low8 == 0b00100000) return "SHAL";
if (low8 == 0b00100001) return "SHAR";
if (low8 == 0b00000000) return "SHLL";
if (low8 == 0b00001000) return "SHLL2";
if (low8 == 0b00011000) return "SHLL8";
if (low8 == 0b00101000) return "SHLL16";
if (low8 == 0b00000001) return "SHLR";
if (low8 == 0b00001001) return "SHLR2";
if (low8 == 0b00011001) return "SHLR8";
if (low8 == 0b00101001) return "SHLR16";
if (low8 == 0b00010011) return "STCMGBR";
if (low8 == 0b00100011) return "STCMVBR";
if (low8 == 0b00110011) return "STCMSSR";
if (low8 == 0b01000011) return "STCMSPC";
if (low8 == 0b00110010) return "STCMSGR";
if (low8 == 0b11110010) return "STCMDBR";
if (low8 == 0b00000010) return "STSMMACH";
if (low8 == 0b00010010) return "STSMMACL";
if (low8 == 0b00100010) return "STSMPR";
if (low8 == 0b00011011) return "TAS";
if (low8 == 0b00001011) return "JSR";
}
if (high == 0b0000) {
if (low8 == 0b00100011) return "BRAF";
if (low8 == 0b00101001) return "MOVT";
if (low8 == 0b10000011) return "PREF";
if (low8 == 0b00010010) return "STCGBR";
if (low8 == 0b00100010) return "STCVBR";
if (low8 == 0b00110010) return "STCSSR";
if (low8 == 0b01000010) return "STCSPC";
if (low8 == 0b00111010) return "STCSGR";
if (low8 == 0b11111010) return "STCDBR";
if (low8 == 0b00001010) return "STSMACH";
if (low8 == 0b00011010) return "STSMACL";
if (low8 == 0b00101010) return "STSPR";
if (low8 == 0b00000011) return "BSRF";
}
if (high == 0b0110) {
if (low == 0b1110) return "EXTSB";
if (low == 0b1111) return "EXTSW";
if (low == 0b1100) return "EXTUB";
if (low == 0b1101) return "EXTUW";
if (low == 0b0011) return "MOV";
if (low == 0b0000) return "MOVBL";
if (low == 0b0001) return "MOVWL";
if (low == 0b0010) return "MOVLL";
if (low == 0b0100) return "MOVBP";
if (low == 0b0101) return "MOVWP";
if (low == 0b0110) return "MOVLP";
if (low == 0b1011) return "NEG";
if (low == 0b1010) return "NEGC";
if (low == 0b0111) return "NOT";
if (low == 0b1000) return "SWAPB";
if (low == 0b1001) return "SWAPW";
}
if (high == 0b0010) {
if (low == 0b1001) return "AND";
if (low == 0b1100) return "CMPSTR";
if (low == 0b0111) return "DIV0S";
if (low == 0b0000) return "MOVBS";
if (low == 0b0001) return "MOVWS";
if (low == 0b0010) return "MOVLS";
if (low == 0b0100) return "MOVBM";
if (low == 0b0101) return "MOVWM";
if (low == 0b0110) return "MOVLM";
if (low == 0b1111) return "MULS";
if (low == 0b1110) return "MULU";
if (low == 0b1011) return "OR";
if (low == 0b1000) return "TST";
if (low == 0b1010) return "XOR";
if (low == 0b1101) return "XTRCT";
}
if (high == 0b0011) {
if (low == 0b1100) return "ADD";
if (low == 0b1110) return "ADDC";
if (low == 0b1111) return "ADDV";
if (low == 0b0000) return "CMPEQ";
if (low == 0b0011) return "CMPGE";
if (low == 0b0111) return "CMPGT";
if (low == 0b0110) return "CMPHI";
if (low == 0b0010) return "CMPHS";
if (low == 0b0100) return "DIV1";
if (low == 0b1101) return "DMULS";
if (low == 0b0101) return "DMULU";
if (low == 0b1000) return "SUB";
if (low == 0b1010) return "SUBC";
if (low == 0b1011) return "SUBV";
}
if (high == 0b0000) {
if (low == 0b1111) return "MACL_";
if (low == 0b0100) return "MOVBS0";
if (low == 0b0101) return "MOVWS0";
if (low == 0b0110) return "MOVLS0";
if (low == 0b1100) return "MOVBL0";
if (low == 0b1101) return "MOVWL0";
if (low == 0b1110) return "MOVLL0";
if (low == 0b0111) return "MULL";
}
if (high == 0b0100) {
if (low == 0b1111) return "MACW";
if (low == 0b1100) return "SHAD";
if (low == 0b1101) return "SHLD";
}
// struct cpu_t {
// // General registers R0 - R15
// int32_t r[16];
// // Control registers
// uint32_t gbr; // Global base register
// uint32_t sr; // Status register (0: T Bit)
// // System registers
// uint32_t pc; // Program counter
// uint32_t pr; // Procedure register
// uint32_t mach; // Multiply-accumulate high
// uint32_t macl; // Multiply-accumulate low
// // To sort
// uint32_t vbr; // Vector base register
// uint32_t ssr; // Saved status register
// uint32_t sgr; // Saved general register
// uint32_t spc; // Saved program counter
// uint32_t dbr; // Debug base register
// // Custom variables
// uint8_t isExecutionFinished; // Set to true when PR == PC_PROGRAM_START
// // Debug
// uint32_t instruction_count;
// uint8_t* asciiTexture;
// // External Components
// //memory_t* mem; // Memory
// //display_t* disp; // Display
// };
print_binary(instruction, 16);
critical_error(" => Unknown instruction: 0x%04X\n", instruction);
return "";
}
struct SR0 {
unsigned long dummy0 : 22;
unsigned long M0 : 1;
unsigned long Q0 : 1;
unsigned long I0 : 4;
unsigned long dummy1 : 2;
unsigned long S0 : 1;
unsigned long T0 : 1;
unsigned long S0 : 1;
unsigned long dummy1 : 2;
unsigned long I0 : 4;
unsigned long Q0 : 1;
unsigned long M0 : 1;
unsigned long dummy0 : 22;
};
#define M ((*(struct SR0 *)(&SR)).M0)
#define Q ((*(struct SR0 *)(&SR)).Q0)

View File

@ -1,453 +0,0 @@
#include "../headers/instructions/syscalls.h"
// Syscall entry point
void run_syscall(cpu_t* cpu) {
uint32_t syscall_code = R0;
// printf("run_syscall 0x%03X, args: 0x%X 0x%X 0x%X 0x%X\n", syscall_code, cpu->r[4], cpu->r[5], cpu->r[6], cpu->r[7]);
int arg1 = cpu->r[4];
int arg2 = cpu->r[5];
int arg3 = cpu->r[6];
int arg4 = cpu->r[7];
if (syscall_code == 0x135) { // GetVRAMAddress
syscall_GetVRAMAddress(cpu);
}
else if (syscall_code == 0x144) { // Bdisp_AllClr_DDVRAM
printf("Run Syscall: Bdisp_AllClr_DDVRAM\n");
syscall_Bdisp_AllClr_DDVRAM(cpu);
}
else if (syscall_code == 0x807) { // locate
syscall_Locate(cpu, arg1, arg2);
}
else if (syscall_code == 0x808) { // Print
uint8_t* mem = get_memory_for_address(cpu, arg1);
const unsigned char* str = (const unsigned char*)mem;
// printf("Run Syscall: syscall_Print (%s)\n", str);
syscall_Print(cpu, str);
}
else if (syscall_code == 0x9AD) { // PrintXY
uint8_t* mem = get_memory_for_address(cpu, arg3);
const unsigned char* str = (const unsigned char*)mem;
// printf("Run Syscall: syscall_PrintXY (%d %d %s %d)\n", arg1, arg2, str, arg4);
syscall_PrintXY(cpu, arg1, arg2, str, arg4);
}
else if (syscall_code == 0xC4F) { // PrintMiniSd
uint8_t* mem = get_memory_for_address(cpu, arg3);
const unsigned char* str = (const unsigned char*)mem;
// printf("Run Syscall: PrintMiniSd (%d %d %s %d)\n", arg1, arg2, str, arg4);
syscall_PrintMiniSd(cpu, arg1, arg2, str, arg4);
}
else if (syscall_code == 0xACD) { // malloc
syscall_Malloc(cpu, arg1, 0);
}
else if (syscall_code == 0xE6B) { // calloc
syscall_Malloc(cpu, arg1, 1);
}
else if (syscall_code == 0xE6D) { // realloc
printf("Skipped syscall: realloc\n");
}
else if (syscall_code == 0xACC) { // free
printf("Skipped syscall: free\n");
}
else if (syscall_code == 0x462) { // GetAppName
syscall_GetAppName(cpu, (char*)arg1);
}
else if (syscall_code == 0x014) { // GlibGetAddinLibInf
syscall_GlibGetAddinLibInf(cpu, arg1, arg2, arg3);
}
else if (syscall_code == 0x015) { // GlibGetOSVersionInfo
syscall_GlibGetOSVersionInfo(cpu, arg1, arg2, arg3, arg4);
}
else if (syscall_code == 0x90F) { // GetKey
printf("Skipped syscall: GetKey\n");
// syscall_GetKey(cpu, arg1);
// printf("Run Syscall: GetKey (0x%08X: %d)\n", arg1, R0);
// cpu->isExecutionFinished = 1;
}
else if (syscall_code == 0x24C) { // Keyboard_IsSpecialKeyDown
printf("Skipped syscall: Keyboard_IsSpecialKeyDown\n");
}
else if (syscall_code == 0x03B) { // RTC_GetTicks
R0 = cpu->instruction_count / 7812;
// printf("Run Syscall: RTC_GetTicks %d\n", R0);
}
else if (syscall_code == 0x434) { // Bfile_CreateEntry_OS
uint8_t* mem = get_memory_for_address(cpu, arg1);
const char* str = (const char*)mem;
// syscall_Bfile_CreateEntry_OS(cpu, str, arg2, arg3);
printf("Skipped syscall: Bfile_CreateEntry_OS\n");
}
else if (syscall_code == 0x42C) { // Bfile_OpenFile_OS
uint8_t* mem = get_memory_for_address(cpu, arg1);
const char* str = (const char*)mem;
// syscall_Bfile_OpenFile_OS(cpu, str, arg2, arg3);
printf("Skipped syscall: Bfile_OpenFile_OS\n");
}
else if (syscall_code == 0x432) { // Bfile_ReadFile_OS
printf("Skipped syscall: Bfile_ReadFile_OS\n");
}
else if (syscall_code == 0x435) { // Bfile_WriteFile_OS
printf("Skipped syscall: Bfile_WriteFile_OS\n");
}
else if (syscall_code == 0x42D) { // Bfile_CloseFile_OS
printf("Skipped syscall: Bfile_CloseFile_OS\n");
}
else if (syscall_code == 0x43B) { // Bfile_FindFirst
printf("Skipped syscall: Bfile_FindFirst\n");
}
else if (syscall_code == 0x439) { // Bfile_DeleteEntry
printf("Skipped syscall: Bfile_DeleteEntry\n");
}
else if (syscall_code == 0x82B) { // MCSPutVar2
printf("Skipped syscall: MCSPutVar2\n");
}
else if (syscall_code == 0x840) { // MCSGetDlen2
printf("Skipped syscall: MCSGetDlen2\n");
}
else if (syscall_code == 0x420) { // OS_inner_Sleep
// https://bible.planet-casio.com/simlo/chm/v20/fx_legacy_Sleep.HTM
// printf("Skipped syscall: OS_inner_Sleep\n");
}
else if (syscall_code == 0x494) { // void SetQuitHandler( void (*callback)(void) );
printf("Skipped syscall: SetQuitHandler\n");
}
else if (syscall_code == 0x3ED) { // Interrupt_SetOrClrStatusFlags
// https://bible.planet-casio.com/simlo/chm/v20/fx_legacy_INTERRUPT.HTM
printf("Skipped syscall: Interrupt_SetOrClrStatusFlags\n");
}
// Can be ignored
else if (syscall_code == 0x013) { // GlibAddinAplExecutionCheck
printf("Ignored syscall: GlibAddinAplExecutionCheck\n");
}
else if (syscall_code == 0x3FA) { // Hmem_SetMMU
printf("Ignored syscall: Hmem_SetMMU\n");
}
// else if (syscall_code == 0x1032) { // return a pointer to a matrixcode/keycode mapping table
// // https://bible.planet-casio.com/simlo/chm/v20/fx_legacy_keyboard.htm
// printf("Skipped syscall: 0x1032\n");
// }
else {
printf("Syscall not implemented: 0x%03X\n", syscall_code);
cpu->isExecutionFinished = 1;
}
cpu->pc = cpu->pr;
}
// These functions clear the specified area of VRAM and/or DD (Display Driver).
void syscall_Bdisp_AllClr_DD(cpu_t* cpu) {
// TODO
}
void syscall_Bdisp_AllClr_VRAM(cpu_t* cpu) {
for (int i = 0; i < VRAM_SIZE; i++) {
cpu->disp->vram[i] = 0;
}
}
void syscall_Bdisp_AllClr_DDVRAM(cpu_t* cpu) {
syscall_Bdisp_AllClr_DD(cpu);
syscall_Bdisp_AllClr_VRAM(cpu);
}
// Sets the position of the display cursor
// x: [1-21], y: [1-8]
void syscall_Locate(cpu_t* cpu, int x, int y) {
cpu->disp->cursor.col = x;
cpu->disp->cursor.row = y;
printf("Run Syscall: locate (%d %d)\n", x, y);
}
void draw_character_mini(cpu_t* cpu, const char c, int pixel_start_x, int pixel_start_y, int mode) {
// Character x,y in ASCII texture (16x16)
int char_x = c / 16;
int char_y = c % 16;
if (char_x < 0 || char_x >= 16 || char_y < 0) return;
// Pixel start x, y in ASCII texture (128x128)
char_x = char_x * 7;
char_y = char_y * 7;
for (int y = 0; y < 7; y++) {
for (int x = 0; x < 7; x++) {
// Current pixel position in ASCII texture
int ascii_x = char_x + x;
int ascii_y = char_y + y;
int ascii_id = ascii_x + ascii_y * 128; // Pixel index
if (ascii_id < 0 || ascii_id >= 128 * 128) break;
// Current pixel position on-screen
int vram_x = pixel_start_x + x;
int vram_y = pixel_start_y + y;
int vram_id = (vram_x + vram_y * SCREEN_WIDTH) / 8; // index in VRAM
if (vram_id < 0 || vram_id >= VRAM_SIZE) break;
int ascii_bit = cpu->printMiniTexture[ascii_id] ^ mode; // Pixel value
int vram_bit = 7 - vram_x % 8; // Current bit
// Change one single bit in the VRAM
if (ascii_bit)
cpu->disp->vram[vram_id] = (cpu->disp->vram[vram_id] & ~(1 << vram_bit)) | (1 << vram_bit);
}
}
}
void syscall_PrintMiniSd(cpu_t* cpu, int x, int y, const unsigned char* str, int mode) {
int i = 0;
while (x < SCREEN_WIDTH) {
const char c = str[i++]; // Current character
if (c == 0x00) break; // Line terminator
draw_character_mini(cpu, c, x, y, mode);
x += 4; // Move the cursor the the right
}
}
// Draw a character on screen
// pixel_start_x and y represent the top left pixel of the character
void draw_character(cpu_t* cpu, const char c, int pixel_start_x, int pixel_start_y, int mode) {
int offset = c - 0x0020;
// Character x,y in ASCII texture (16x16)
int char_x = offset % 16;
int char_y = offset / 16;
// Pixel start x, y in ASCII texture (112x54)
char_x = char_x * ASCII_CHAR_WIDTH;
char_y = char_y * ASCII_CHAR_HEIGHT;
for (int y = 0; y < ASCII_CHAR_HEIGHT; y++) {
for (int x = 0; x < ASCII_CHAR_WIDTH; x++) {
// Current pixel position in ASCII texture
int ascii_x = char_x + x;
int ascii_y = char_y + y;
int ascii_id = ascii_x + ascii_y * 112; // Pixel index
if (ascii_id < 0 || ascii_id >= 112 * 54) break; // critical_error("ASCII Texture Access out of bounds: %d %d (%d)", ascii_x, ascii_y, ascii_id);
// Current pixel position on-screen
int vram_x = pixel_start_x + x;
int vram_y = pixel_start_y + y;
int vram_id = (vram_x + vram_y * SCREEN_WIDTH) / 8; // index in VRAM
int vram_bit = 7 - vram_x % 8; // Current bit
if (vram_id < 0 || vram_id >= VRAM_SIZE) break; // critical_error("VRAM Access out of bounds: %d %d (%d)", vram_x, vram_y, vram_id);
// Change one single bit in the VRAM
int ascii_bit = cpu->asciiTexture[ascii_id] ^ mode; // Pixel value
if (ascii_bit)
cpu->disp->vram[vram_id] = (cpu->disp->vram[vram_id] & ~(1 << vram_bit)) | (ascii_bit << vram_bit);
}
}
}
// Display a string at the current position of the display cursor
void syscall_Print(cpu_t* cpu, const unsigned char* str) {
printf("syscall_print: %s\n", str);
int i = 0;
while(cpu->disp->cursor.col < 21) {
const char c = str[i++]; // Current character
if (c == 0x00) break; // Line terminator
// Character x,y on screen (21x7)
int screen_x = cpu->disp->cursor.col;
int screen_y = cpu->disp->cursor.row;
// Pixel start x,y on screen (128x64)
screen_x = screen_x * CHAR_WIDTH + 1;
screen_y = screen_y * CHAR_HEIGHT;
draw_character(cpu, c, screen_x, screen_y, 0);
// Move the cursor the the right
cpu->disp->cursor.col++;
}
}
void syscall_PrintXY(cpu_t* cpu, int x, int y, const unsigned char* str, int mode) {
int i = 0;
while (x < SCREEN_WIDTH) {
const char c = str[i++]; // Current character
if (c == 0x00) break; // Line terminator
draw_character(cpu, c, x, y, mode);
x += CHAR_WIDTH; // Move the cursor the the right
}
}
#define MALLOC_MEM_LOW RAM_START_MMU_ALIAS + (32 * 1024)
#define MALLOC_MEM_HIGH RAM_START_MMU_ALIAS + RAM_SIZE - (16 * 1024)
#define MALLOC_MARGIN 0x200
// Allocate memory and return the address of the allocated memory in r0
void syscall_Malloc(cpu_t* cpu, uint32_t size, uint8_t clear_data) {
memory_t* mem = cpu->mem;
// Initialize with the lowest address
int addr = MALLOC_MEM_LOW;
// If there are mallocs, start at the end of the last malloc
if (mem->mallocCount > 0) {
addr = mem->mallocs[mem->mallocCount - 1].addr +
mem->mallocs[mem->mallocCount - 1].size + MALLOC_MARGIN;
}
// Update malloc manager
mem->mallocCount++;
mem->mallocs = realloc(mem->mallocs, mem->mallocCount * sizeof(malloc_info_t));
if (clear_data) {
for (int i = 0; i < size; i++) {
mem_write(cpu, addr + i, 0, 4);
}
}
if (mem->mallocs == 0) {
critical_error("syscall_malloc(): Could not allocate memory");
}
if (clear_data)
printf("Run Syscall: Calloc #%d (size: %d)\n", mem->mallocCount - 1, size);
else
printf("Run Syscall: Malloc #%d (size: %d)\n", mem->mallocCount - 1, size);
mem->mallocs[mem->mallocCount - 1] = (malloc_info_t){size, addr};
// Return the address of the allocated memory
R0 = addr;
}
// https://bible.planet-casio.com/simlo/chm/v20/fx_legacy_AppName.HTM
// Copies the registered name for the running application into the character array dest.
// dest must be able to hold 9 bytes. dest is returned.
void syscall_GetAppName(cpu_t* cpu, char* dest) {
// Copies the app name to the new buffer
for (int i = 0; i < 9; i++) {
mem_write(cpu, (uint32_t)dest + i, cpu->mem->rom[0x20 + i], 1);
}
printf("Run Syscall: GetAppName (%s)\n", (const char*)get_memory_for_address(cpu, (uint32_t)dest));
R0 = (int32_t)dest; // Return the buffer
}
void syscall_GetVRAMAddress(cpu_t* cpu) {
R0 = VRAM_ADDRESS;
// printf("Run Syscall: GetVRAMAddress (-> 0x%08X)\n", R0);
}
void syscall_GlibGetAddinLibInf(cpu_t* cpu, uint32_t a_ptr, uint32_t b_ptr, uint32_t c_ptr) {
// Mimic Casio SDK Emulator
mem_write(cpu, a_ptr, 0x0, 4);
mem_write(cpu, b_ptr, 0x1, 4);
mem_write(cpu, c_ptr, 0x1, 4);
cpu->r[0] = 0x1;// 0x0;
cpu->r[2] = 0xA0151F28;
cpu->r[3] = 0x0;
cpu->r[4] = 0x1;
printf("Run Syscall: GlibGetAddinLibInf\n");
}
void syscall_GlibGetOSVersionInfo(cpu_t* cpu, uint32_t a_ptr, uint32_t b_ptr, uint32_t c_ptr, uint32_t d_ptr) {
// Mimic Casio SDK Emulator
mem_write(cpu, a_ptr, 0x1, 1);
mem_write(cpu, b_ptr, 0x3, 1);
mem_write(cpu, c_ptr, 0x0, 2);
mem_write(cpu, d_ptr, 0x0, 2);
cpu->r[0] = 0x1;
cpu->r[2] = 0x3;
cpu->r[3] = 0x1;
cpu->r[4] = 0x0;
// printf("Run Syscall: GlibGetOSVersionInfo\n");
}
const char* convertFileName(const char* filename) {
char name[40];
char ch;
int i;
// Convert filename from { 0x00, 0xXX, 0x00, 0xXX, ... } to { 0xXX, 0xXX, ... }
for (i = 0; i < 40; i++) {
uint16_t ch = *(uint16_t*) &filename[i * 2 + 1];
name[i] = ch;
if (ch == 0x00) break;
}
// Remove '\\fls0\'
i -= 6;
char* final_name = malloc(sizeof(char) * i);
memcpy(final_name, name + 7, i);
return (const char*)final_name;
}
// mode: 1 = read, 2 = write
void syscall_Bfile_OpenFile_OS(cpu_t* cpu, const char* filename, int mode, int mode2) {
const char* name = convertFileName(filename);
FILE* handle = fopen(name, mode == 1 ? "rb" : "wb");
printf("Run Syscall: Bfile_OpenFile_OS (%s, 0x%X, 0x%X) handle: 0x%X\n", name, mode, mode2, (uint32_t)handle);
R0 = handle == NULL ? -1 : (int32_t)handle;
}
void syscall_Bfile_CreateEntry_OS(cpu_t* cpu, const char* filename, int mode, int size_ptr) {
const char* name = convertFileName(filename);
FILE* handle;
if (mode == 0x1) { // Create file
handle = fopen(name, "wb");
fclose(handle);
}
// else if (mode == 0x5) { } // Create folder
else printf("[Warning] CreateEntry mode not implemented\n");
printf("Run Syscall: Bfile_CreateEntry_OS (%s, 0x%X) size: %d, handle: 0x%X\n", name, mode, mem_read(cpu, size_ptr, 4), (uint32_t)handle);
R0 = handle == NULL ? -1 : (int32_t)handle;
}
// #ifdef USE_EMSCRIPT
// EM_ASYNC_JS(int, async_browser_getkey, (), {
// return await new Promise((resolve, reject) => {
// const onClick = (event) => {
// const res = window.keyboardEvent(event);
// if (res != null) {
// document.getElementById("keyboard").removeEventListener("click", onClick);
// resolve(res);
// }
// };
// document.getElementById("keyboard").addEventListener("click", onClick);
// });
// })
// #else
// int async_browser_getkey() {
// return 0;
// }
// #endif
// void syscall_GetKey(cpu_t* cpu, unsigned int keycode_address) {
// int key = async_browser_getkey();
// mem_write(cpu, keycode_address, key, 4);
// R0 = key;
// }

View File

@ -74,6 +74,10 @@ cpu_t* init_cpu(memory_t* mem, display_t* disp) {
cpu->mem = mem;
cpu->disp = disp;
cpu->fs = calloc(1, sizeof(fs_t));
cpu->malloc_manager = calloc(1, sizeof(malloc_manager_t));
cpu->malloc_manager->mallocs = malloc(MALLOC_SIZE * sizeof(int8_t));
return cpu;
}
@ -83,6 +87,7 @@ cpu_t* init_cpu(memory_t* mem, display_t* disp) {
void run_next_instruction(cpu_t* cpu) {
// Current instruction address
uint32_t address = cpu->pc;
cpu->instruction_count++;
// Handle system calls
if (address == SYSCALL_ADDRESS) {
@ -92,13 +97,13 @@ void run_next_instruction(cpu_t* cpu) {
// Handle program execution finish
if (address == PR_INIT_VALUE) {
printf("Program execution finished!\n");
cpu->isExecutionFinished = 1;
cpu->execution_finished = 1;
return;
}
if (address % 2 != 0) {
critical_error("Address is not a multiple of 2 bytes! (0x%08X)", address);
}
// if (address % 2 != 0) {
// critical_error("Address is not a multiple of 2 bytes! (0x%08X)", address);
// }
// Extract 16bits instruction code
uint16_t instruction = (mem_read(cpu, address, 1) << 8) | mem_read(cpu, address + 1, 1);
@ -134,21 +139,23 @@ int main() {
// Init the HTML version of the emulator
void init_loop_html(cpu_t* cpu) {
#ifdef USE_EMSCRIPT
cpu->instruction_per_frame = 10000;
cpu->instruction_per_frame = 50000;
// Pass useful pointers to javascript
EM_ASM({
window.lcdImage = $0;
window.vramPointer = $0;
window.keyboardPointer = $1;
window.instructionPerFrame = $2;
window.instructionCount = $3;
window.cpuPointer = $4;
window.rtcTicksPointer = $4;
window.cpuPointer = $5;
window.initGUI();
},
},
&cpu->disp->vram,
&cpu->mem->keyboard,
&cpu->instruction_per_frame,
&cpu->instruction_count,
&cpu->RTC_Ticks,
cpu
);
@ -161,14 +168,15 @@ void init_loop_html(cpu_t* cpu) {
void main_loop_html(void* arg) {
cpu_t* cpu = (cpu_t*)arg;
if (cpu->isExecutionFinished) return;
if ((cpu->execution_paused && !cpu->execute_one_step) || cpu->execution_finished) return;
for (int i = 0; i < cpu->instruction_per_frame; i++) {
run_next_instruction(cpu);
cpu->instruction_count++;
if (cpu->isExecutionFinished) break;
if (cpu->execution_paused || cpu->execution_finished) break;
}
if (cpu->execute_one_step) cpu->execute_one_step = 0;
}
// Run the console version of the emulator
@ -177,9 +185,8 @@ void init_loop_c(cpu_t* cpu) {
// Run up to 10M instructions
for (int i = 0; i < 1e7; i++) {
run_next_instruction(cpu);
cpu->instruction_count++;
if (cpu->isExecutionFinished) break;
if (cpu->execution_finished) break;
}
vram_debug(cpu);
@ -191,4 +198,36 @@ void init_loop_c(cpu_t* cpu) {
free(cpu->asciiTexture);
free(cpu->printMiniTexture);
free(cpu);
}
// Functions called from javascript
void reset_execution(cpu_t* cpu) {
cpu->pc = PC_PROGRAM_START;
cpu->pr = PR_INIT_VALUE;
cpu->sr = SR_INIT_VALUE;
cpu->r[15] = RAM_START + RAM_SIZE;
cpu->instruction_count = 0;
cpu->execution_finished = 0;
for (int i = 0; i < VRAM_SIZE; i++) {
cpu->disp->vram[i] = 0;
}
free(cpu->fs);
cpu->fs = calloc(1, sizeof(fs_t));
cpu->malloc_manager->currentSize = 0;
cpu->malloc_manager->mallocCount = 0;
}
void load_new_g1a(cpu_t* cpu, uint8_t* g1a_content, uint32_t g1a_size) {
reset_execution(cpu);
printf("Loading new G1A file %d\n", g1a_size);
free(cpu->mem->rom);
cpu->mem->rom = g1a_content;
cpu->mem->rom_size = g1a_size;
}

View File

@ -2,7 +2,7 @@
// Returns a pointer to the buffer (memory)
// corresponding to the `address` parameter
uint8_t* get_memory_for_address(cpu_t* cpu, uint32_t address) {
uint8_t* get_memory_for_address(cpu_t* cpu, uint32_t address) {
if (address >= ROM_START && address < ROM_START + cpu->mem->rom_size) { // ROM - (MMU)
return &cpu->mem->rom[address - ROM_START];
}
@ -15,18 +15,26 @@ uint8_t* get_memory_for_address(cpu_t* cpu, uint32_t address) {
else if (address >= VRAM_ADDRESS && address < VRAM_ADDRESS + VRAM_SIZE) { // VRAM
return &cpu->disp->vram[address - VRAM_ADDRESS];
}
else if (address == VRAM_ADDRESS + VRAM_SIZE) {
// printf("Prevent OOB Ram access\n");
// TODO: Check why Gravity Duck doesnt crash when exceeding this margin
return &cpu->mem->tmp[0];
}
else if (address == LCD_SELECT_REGISTER || address == LCD_DATA_REGISTER) { // LCD registers
return &cpu->disp->lcd_registers;
}
else if (address >= KB_START && address < KB_START + KB_SIZE) { // Keyboard registers
return &cpu->mem->keyboard_registers[address - KB_START];
}
else if (address >= 0xFFFFFEE0 && address < 0xFFFFFFFF) { // Temporary memory
else if (address >= MALLOC_START && address < MALLOC_START + MALLOC_SIZE) { // Malloc
return &cpu->malloc_manager->mallocs[address - MALLOC_START];
}
else if (address >= 0xFFFFFEE0 && address < 0xFFFFFFFF) { // Interrupt registers
return &cpu->mem->tmp[address - 0xFFFFFEE0];
}
else {
// cpu_debug(cpu);
critical_error("Request for memory at address 0x%08X is out of bounds\n", address);
printf("[Error] Request for memory at address 0x%08X is out of bounds\n", address);
return &cpu->mem->tmp[0];
}
}
@ -37,9 +45,11 @@ void mem_write(cpu_t* cpu, uint32_t address, uint32_t data, uint8_t bytes) {
emulate_lcd_register_write(cpu, address, data);
return;
}
uint8_t* mem = get_memory_for_address(cpu, address);
// printf("Memory write [0x%08X]: : 0x%X\n", address, data);
if (bytes == 1) {
*mem = data & 0xFF;
}
@ -79,6 +89,7 @@ uint32_t mem_read(cpu_t* cpu, uint32_t address, uint8_t bytes) {
data = (*mem << 24) | (*(mem + 1) << 16) | (*(mem + 2) << 8) | (*(mem + 3));
}
// if (address != cpu->pc && address != cpu->pc + 1)
// printf("Memory read [0x%08X]: : 0x%X\n", address, data);
return data;
@ -108,6 +119,7 @@ uint32_t emulate_keyboard_register_read(cpu_t* cpu) {
return 0xffffffff;
}
else {
// printf("Keyboard row %d: 0x%08X\n", row, cpu->mem->keyboard[row]);
return cpu->mem->keyboard[row];
}
}
@ -136,15 +148,12 @@ void emulate_lcd_register_write(cpu_t* cpu, uint32_t address, uint32_t data) {
// Write data to LCD row
else if (address == LCD_DATA_REGISTER) {
if (row >= 0) {
cpu->disp->lcd[row * 16 + col++] = data; // Update LCD buffer
col++; // cpu->disp->lcd[row * 16 + col++] = data; // Update LCD buffer
#ifdef EMSCRIPTEN
if (row == 63 && col == 16) { // Refresh HTML canvas
EM_ASM({
window.refreshScreen();
});
syscall_Bdisp_PutDisp_DD(cpu);
}
#endif
if (col >= 16) {
col = 0;
}

View File

@ -0,0 +1,285 @@
#include "../headers/syscalls/filesystem.h"
const char* convertFileName(const char* filename) {
char name[40];
char ch;
int i;
// Convert filename from { 0x00, 0xXX, 0x00, 0xXX, ... } to { 0xXX, 0xXX, ... }
for (i = 0; i < 40; i++) {
uint16_t ch = *(uint16_t*)&filename[i * 2 + 1];
name[i] = ch;
if (ch == 0x00) break;
}
// Remove '\\fls0\'
i -= 6;
char* final_name = malloc(sizeof(char) * i);
memcpy(final_name, name + 7, i);
return (const char*)final_name;
}
/**
* Creates a file/directory
* @param filename This is the pointer to a null-terminated string that names the file/directory to be created.
* @param size This is the size of the file to be created. This parameter is only used when you create a file in Storage Memory.
* @return If the function succeeds, the return value is 0.
* If the function fails, the return value is an error code. It is a negative value.
* @remarks <p>This function creates a file in Storage Memory or the SD card.
*/
void syscall_Bfile_CreateEntry(cpu_t* cpu, uint32_t filename_ptr, int mode, uint32_t size_ptr) {
const char* filename = (const char*)get_memory_for_address(cpu, filename_ptr);
const char* name = convertFileName(filename);
if (mode == 0x1) {
// Create file with fixed size, initialize with 0
uint32_t size = mem_read(cpu, size_ptr, 4);
uint8_t* data = calloc(size, sizeof(uint8_t));
FILE* file = fopen(name, "wb");
fwrite(data, 1, size, file);
fclose(file);
R0 = 0; // Success
printf("Run syscall: Bfile_CreateEntry %s (0x%X, size: %d)\n", name, mode, mem_read(cpu, size_ptr, 4));
}
// else if (mode == 0x5) { } // Create folder
else {
R0 = -1; // Error
printf("[Warning] In syscall Bfile_CreateEntry: CreateEntry mode %d not implemented\n", mode);
}
}
/**
* Deletes an existing file
* @param filename This is the pointer to a null-terminated string that specifies the file to delete.
* @param mode always should be used and set to 0.
* @return If the function succeeds, the return value is 0.
* If the function fails, the return value is an error code. It is a negative value.
* @remarks <p>This function deletes a file in Storage Memory or the SD card.
*/
void syscall_Bfile_DeleteEntry(cpu_t* cpu, uint32_t filename_ptr, int mode) {
const char* filename = (const char*)get_memory_for_address(cpu, filename_ptr);
const char* name = convertFileName(filename);
if (remove(name) == 0) {
R0 = 0; // Success
printf("Run syscall: Bfile_DeleteEntry %s\n", name);
}
else {
R0 = -1; // Error
printf("[Warning] In syscall Bfile_DeleteEntry: Could not delete file %s\n", name);
}
}
/**
* Opens an existing file.
* @param filename This is the pointer to a null-terminated string that names the file to be opened.
* @param mode The mode parameter specifies the action to open (read/write/read_write...)
* @return If the function succeeds, the return value specifies a file handle. It is greater than or equal to 0.
* If the function fails, the return value is an error code. It is a negative value.
* @remarks This function opens a file in Storage Memory or the SD card.
* mode2 always should be used and set to 0.
*/
void syscall_Bfile_OpenFile(cpu_t* cpu, uint32_t filename_ptr, int mode, int mode2) {
const char* filename = (const char*)get_memory_for_address(cpu, filename_ptr);
const char* name = convertFileName(filename);
fs_t* fs = cpu->fs;
// Only four files can be opened at the same time
if (fs->open_files_count >= 4) {
printf("[Warning] In syscall Bfile_OpenFile: Too many files opened! (%s, mode: %d)\n", name, mode);
R0 = -8;
return;
}
// Check if file exists
FILE* file = fopen(name, "rb");
if (file == NULL) {
printf("[Warning] In syscall Bfile_OpenFile: File not found: %s\n", name);
R0 = -1;
return;
}
// Get file size
fseek(file, 0, SEEK_END);
int32_t file_size = ftell(file);
fclose(file);
// Open file for reading/writing
file = fopen(name, mode == 1 ? "rb" : "wb");
// Handle is 0x1000000 + file index
int32_t handle = 0x01000000 + fs->open_files_count;
// Create file entry
strcpy(fs->files[fs->open_files_count].name, name);
cpu->fs->files[fs->open_files_count].handle = handle;
cpu->fs->files[fs->open_files_count].file_handle = file;
cpu->fs->files[fs->open_files_count].seek_pos = 0;
cpu->fs->files[fs->open_files_count].size = file_size;
fs->open_files_count++;
printf("Run syscall: Bfile_OpenFile: %s (mode: %d), handle: 0x%X\n", name, mode, handle);
R0 = handle;
}
/**
* Closes an open file handle.
* @param HANDLE This is the handle of the file to close. HANDLE should be the handle opened by the Bfile_OpenFile or
* Bfile_OpenMainMemory function.
* @return If the function succeeds, this function returns 0.
* If the function fails, the return value is an error code. It is a negative value.
* @remarks Only four files can be opened at the same time.
*/
void syscall_Bfile_CloseFile(cpu_t* cpu, int handle) {
R0 = -1;
// Check if the handle is valid
if (handle - 0x01000000 < 0 || handle - 0x01000000 >= 4) {
printf("[Warning] Bfile_CloseFile: Invalid handle: 0x%X\n", handle);
return;
}
// Loop through each open file
for (int i = 0; i < cpu->fs->open_files_count; i++) {
// Find the corresponding file and close it
if (cpu->fs->files[i].handle == handle) {
fclose(cpu->fs->files[i].file_handle);
cpu->fs->open_files_count--;
R0 = 0;
printf("Run syscall: Bfile_CloseFile %s (0x%X)\n", cpu->fs->files[i].name, handle);
}
// Shift the following files to the left
if (R0 == 0 && i < 3 && i < cpu->fs->open_files_count) {
strcpy(cpu->fs->files[i].name, cpu->fs->files[i + 1].name);
cpu->fs->files[i].handle = cpu->fs->files[i + 1].handle;
cpu->fs->files[i].file_handle = cpu->fs->files[i + 1].file_handle;
cpu->fs->files[i].seek_pos = cpu->fs->files[i + 1].seek_pos;
cpu->fs->files[i].size = cpu->fs->files[i + 1].size;
// printf("Bfile_CloseFile: Moved file %s from %d to %d\n", cpu->fs->files[i].name, i + 1, i);
}
}
if (R0 == -1)
printf("[Warning] In syscall Bfile_CloseFile: File not found for handle: 0x%X\n", handle);
}
/**
* Writes data to a file. The function starts writing data to the file at the position
* indicated by the file pointer. After the write operation has been completed, the file pointer is adjusted by the
* number of bytes actually written (TODO)
* @param HANDLE This is the handle of the file to write. HANDLE should be the handle opened by the Bfile_OpenFile or
* Bfile_OpenMainMemory function.
* @param buf This is the pointer to the buffer containing the data to be written to the file.
* @param size This is the number of bytes to write to the file.
* @return If the function succeeds, this function returns the position indicated by the file pointer.
* It is greater than or equal to 0.
* If the function fails, the return value is an error code. It is a negative value.
* @remarks If you use a file that is written by this function on the PC, you should save the multi byte data in the Little Endian
* format.
*/
void syscall_Bfile_WriteFile(cpu_t* cpu, int handle, uint32_t buf_ptr, int size) {
// Check if the handle is valid
if (handle - 0x01000000 < 0 || handle - 0x01000000 >= 4) {
printf("[Warning] In syscall Bfile_WriteFile: Invalid handle: 0x%X (0x%X, %d)\n", handle, buf_ptr, size);
R0 = -1;
return;
}
file_t* file = &cpu->fs->files[handle & 3];
printf("Run syscall: Bfile_WriteFile %s (0x%X, 0x%X, %d)\n", file->name, handle, buf_ptr, size);
// Advance the file pointer
fseek(file->file_handle, file->seek_pos, SEEK_SET);
file->seek_pos += size;
// Write the data to the file
for (int i = 0; i < size; i++) {
uint8_t byte = mem_read(cpu, buf_ptr + i, 1);
fwrite(&byte, 1, 1, file->file_handle);
}
R0 = file->seek_pos;
}
/**
* Reads data from a file, starting at the position indicated by the file pointer. After the
* read operation has been completed, the file pointer is adjusted by the number of bytes actually read.
* @param HANDLE This is the handle of the file to read. HANDLE should be the handle opened by the Bfile_OpenFile or
* Bfile_OpenMainMemory function.
* @param buf This is the pointer to the buffer that receives the data read from the file.
* @param size This is the number of bytes to be read from the file.
* @param readpos This is the starting position to read. If the readpos parameter is -1, this function reads data from the position
* indicated by the file pointer. If the readpos parameter greater than or equal to 0, this function reads data from
* the position indicated by the readpos parameter.
* @return If the function succeeds, this function returns the number of bytes actually read. It is greater than or equal to 0.
* If the function fails, the return value is an error code. It is a negative value.
* @remarks If you read the Windows file, the multi byte data is stored in Little Endian format. To use this data, you should
* convert the multi byte data into the Big Endian format.
*/
void syscall_Bfile_ReadFile(cpu_t* cpu, int handle, uint32_t buf_ptr, int size, int readpos) {
// Check if the handle is valid
if (handle - 0x01000000 < 0 || handle - 0x01000000 >= 4) {
printf("[Warning] In stscall Bfile_ReadFile: Invalid handle: 0x%X (0x%X, %d, %d)\n", handle, buf_ptr, size, readpos);
return;
}
file_t* file = &cpu->fs->files[handle & 3];
printf("Run syscall: Bfile_ReadFile %s (0x%X, 0x%X, %d, %d)\n", file->name, handle, buf_ptr, size, readpos);
// Advance the file pointer
if (readpos == -1) {
fseek(file->file_handle, file->seek_pos, SEEK_SET);
file->seek_pos += size;
}
else {
fseek(file->file_handle, readpos, SEEK_SET);
file->seek_pos = readpos + size;
}
// Read the data from the file
for (int i = 0; i < size; i++) {
uint8_t byte;
fread(&byte, 1, 1, file->file_handle);
mem_write(cpu, buf_ptr + i, byte, 1);
}
R0 = size;
}
/**
* Moves the file pointer of an open file.
* @param HANDLE This is the file handle whose file pointer will be moved
* @param pos This is the number of bytes to move file pointer.
* @return If the function succeeds, this function returns the number of bytes that can continuously be read.
* It is greater than or equal to 0.
* If the function fails, the return value is an error code. It is a negative value.
* @remarks The starting point for the file pointer move is the beginning of the file. The file pointer is at the head of the file
* immediately after the file was opened.
*/
void syscall_Bfile_SeekFile(cpu_t* cpu, int handle, int pos) {
// Check if the handle is valid
if (handle - 0x01000000 < 0 || handle - 0x01000000 >= 4) {
printf("[Warning] In syscall Bfile_SeekFile: Invalid handle: 0x%X (%d)\n", handle, pos);
return;
}
file_t* file = &cpu->fs->files[handle & 3];
// Advance the file pointer
file->seek_pos = pos;
// Return the number of bytes left from current pos
R0 = file->size - pos;
printf("Run syscall: Bfile_SeekFile %s (0x%X, %d) ret: %d\n", file->name, handle, pos, R0);
}

View File

@ -0,0 +1,47 @@
#include "../headers/syscalls/malloc.h"
// Allocate memory and return the address of the allocated memory
// The data is cleared (from what I observed), so syscall Calloc
// redirects to this function too and `clear_data` is ignored
void syscall_Malloc(cpu_t* cpu, uint32_t size, uint8_t clear_data) {
malloc_manager_t* m = cpu->malloc_manager;
// Use the first 4 bytes to store the size of the malloc
*(uint32_t*)(&m->mallocs[m->currentSize]) = size;
R0 = MALLOC_START + m->currentSize + 4;
// Clear the data
for (int i = 0; i < size; i++) {
m->mallocs[m->currentSize + i + 4] = 0;
}
m->currentSize += size + 4;
m->mallocCount++;
printf("Run syscall: %salloc #%d (size: %d, addr: 0x%X) Total allocated: %d\n", clear_data ? clear_data == 1 ? "C" : "Re" : "M", m->mallocCount, size, R0, m->currentSize - m->mallocCount);
}
void syscall_Realloc(cpu_t* cpu, uint32_t ptr, uint32_t size) {
uint8_t* mallocs = cpu->malloc_manager->mallocs;
syscall_Malloc(cpu, size, 2);
if (ptr == 0x0) return; // No pointer to reallocate, act as a malloc only
// Get the size of the previous malloc
uint32_t prev_size = *(uint32_t*)(&mallocs[ptr - MALLOC_START - 4]);
// Transfer data to the new malloc
for (int i = 0; i < prev_size; i++) {
mallocs[R0 - MALLOC_START + i] = mallocs[ptr - MALLOC_START + i];
}
syscall_Free(cpu, ptr);
}
void syscall_Free(cpu_t* cpu, uint32_t ptr) {
cpu->malloc_manager->mallocCount--;
// printf("Run syscall: Free #%d (addr: 0x%X) Total allocated: %d\n", cpu->malloc_manager->mallocCount, ptr, cpu->malloc_manager->currentSize - cpu->malloc_manager->mallocCount);
}

View File

@ -0,0 +1,91 @@
#include "../headers/syscalls/misc.h"
// https://bible.planet-casio.com/simlo/chm/v20/fx_legacy_AppName.HTM
// Copies the registered name for the running application into the character array dest.
// dest must be able to hold 9 bytes. dest is returned.
void syscall_GetAppName(cpu_t* cpu, uint32_t dest_ptr) {
for (int i = 0; i < 9; i++) {
mem_write(cpu, dest_ptr + i, cpu->mem->rom[0x20 + i], 1);
}
printf("Run syscall: GetAppName (%s)\n", (const char*)get_memory_for_address(cpu, dest_ptr));
R0 = dest_ptr; // Return the buffer
}
void syscall_GetVRAMAddress(cpu_t* cpu) {
R0 = VRAM_ADDRESS;
// printf("Run syscall: GetVRAMAddress (-> 0x%08X)\n", R0);
}
void syscall_GlibGetAddinLibInf(cpu_t* cpu, uint32_t a_ptr, uint32_t b_ptr, uint32_t c_ptr) {
// Mimic Casio SDK Emulator
mem_write(cpu, a_ptr, 0x0, 4);
mem_write(cpu, b_ptr, 0x1, 4);
mem_write(cpu, c_ptr, 0x1, 4);
cpu->r[0] = 0x1;// 0x0;
cpu->r[2] = 0xA0151F28;
cpu->r[3] = 0x0;
cpu->r[4] = 0x1;
printf("Run syscall: GlibGetAddinLibInf\n");
}
void syscall_GlibGetOSVersionInfo(cpu_t* cpu, uint32_t a_ptr, uint32_t b_ptr, uint32_t c_ptr, uint32_t d_ptr) {
// Mimic Casio SDK Emulator
mem_write(cpu, a_ptr, 0x1, 1);
mem_write(cpu, b_ptr, 0x3, 1);
mem_write(cpu, c_ptr, 0x0, 2);
mem_write(cpu, d_ptr, 0x0, 2);
cpu->r[0] = 0x1;
cpu->r[2] = 0x3;
cpu->r[3] = 0x1;
cpu->r[4] = 0x0;
// printf("Run syscall: GlibGetOSVersionInfo\n");
}
// Returns number of 1/128 seconds since an arbitrary date.
void syscall_RTC_GetTicks(cpu_t* cpu) {
R0 = cpu->RTC_Ticks;
// printf("Run syscall: RTC_GetTicks %d\n", R0);
}
// https://bible.planet-casio.com/simlo/chm/v20/fx_legacy_Sleep.HTM
void syscall_OS_inner_Sleep(cpu_t* cpu, uint32_t ms) {
#ifdef USE_EMSCRIPT
emscripten_sleep(ms * .75);
#endif
// printf("Run syscall: OS_inner_Sleep %d\n", ms);
}
#ifdef USE_EMSCRIPT
EM_ASYNC_JS(int, async_browser_getkey, (), {
return await new Promise((resolve, reject) => {
const onClick = (event) => {
const res = window.keyboardEvent(event, 'getkey');
if (res != null) {
document.getElementById("keyboard").removeEventListener("click", onClick);
resolve(res);
}
};
document.getElementById("keyboard").addEventListener("click", onClick);
});
})
#else
int async_browser_getkey() {
return 0;
}
#endif
void syscall_GetKey(cpu_t* cpu, unsigned int keycode_address) {
printf("Run syscall: GetKey\n");
int key = async_browser_getkey();
mem_write(cpu, keycode_address, key, 4);
R0 = key;
}

View File

@ -0,0 +1,199 @@
#include "../headers/syscalls/print.h"
// These functions clear the specified area of VRAM and/or DD (Display Driver).
void syscall_Bdisp_AllClr_DD(cpu_t* cpu) {
// Does nothing for now
}
void syscall_Bdisp_AllClr_VRAM(cpu_t* cpu) {
for (int i = 0; i < VRAM_SIZE; i++) {
cpu->disp->vram[i] = 0;
}
}
void syscall_Bdisp_AllClr_DDVRAM(cpu_t* cpu) {
printf("Run syscall: Bdisp_AllClr_DDVRAM\n");
syscall_Bdisp_AllClr_DD(cpu);
syscall_Bdisp_AllClr_VRAM(cpu);
}
void syscall_Bdisp_PutDisp_DD(cpu_t* cpu) {
// printf("Run syscall: Bdisp_PutDisp_DD\n");
#ifdef USE_EMSCRIPT
EM_ASM({
window.refreshScreen();
});
#endif
}
/**
* Set or erase a dot at the specified position of VRAM and/or DD (Display Driver).
* @param x (0~127) This is the x coordinate of the specified position.
* @param y (0~63) This is the y coordinate of the specified position.
* @param point If you set point to 1 then the dot is made black. If you set point to 0 then the dot is cleared.
*/
void syscall_Bdisp_SetPoint_VRAM(cpu_t* cpu, uint32_t x, uint32_t y, uint32_t point) {
uint32_t vramID = x/8 + y * 16;
uint8_t bit = 7 - x % 8; // Current bit
cpu->disp->vram[vramID] = (cpu->disp->vram[vramID] & ~(1 << bit)) | (point << bit);
// printf("Run syscall: Bdisp_SetPoint_VRAM (%d, %d, %d)\n", x, y, point);
}
// Sets the position of the display cursor
// x: [1-21], y: [1-8]
void syscall_Locate(cpu_t* cpu, int x, int y) {
cpu->disp->cursor.col = x;
cpu->disp->cursor.row = y;
printf("Run syscall: locate (%d %d)\n", x, y);
}
#define MINI_OVER 0
#define MINI_OR 1
#define MINI_REV 2
#define MINI_REVOR 3
void draw_character_mini(cpu_t* cpu, const char c, int pixel_start_x, int pixel_start_y, int mode) {
// Character x,y in ASCII texture (16x16)
int char_x = c / 16;
int char_y = c % 16;
if (char_x < 0 || char_x >= 16 || char_y < 0) return;
// Pixel start x, y in ASCII texture (128x128)
char_x = char_x * 7;
char_y = char_y * 7;
for (int y = 0; y < 6; y++) {
for (int x = 0; x < 6; x++) {
// Current pixel position in ASCII texture
int ascii_x = char_x + x;
int ascii_y = char_y + y;
int ascii_id = ascii_x + ascii_y * 128; // Pixel index
if (ascii_id < 0 || ascii_id >= 128 * 128) break;
// Current pixel position on-screen
int vram_x = pixel_start_x + x;
int vram_y = pixel_start_y + y;
int vram_id = (vram_x + vram_y * SCREEN_WIDTH) / 8; // index in VRAM
if (vram_id < 0 || vram_id >= VRAM_SIZE) break;
int ascii_pixel = cpu->printMiniTexture[ascii_id]; // Pixel value
int vram_bit = 7 - vram_x % 8; // Current bit
int vram_pixel = (cpu->disp->vram[vram_id] >> (vram_bit)) & 1;
// Change one single bit in the VRAM
if (mode == MINI_OVER)
cpu->disp->vram[vram_id] = (cpu->disp->vram[vram_id] & ~(1 << vram_bit)) | (ascii_pixel << vram_bit);
else if (mode == MINI_OR)
cpu->disp->vram[vram_id] = (cpu->disp->vram[vram_id] & ~(1 << vram_bit)) | ((ascii_pixel | vram_pixel) << vram_bit);
else if (mode == MINI_REV)
cpu->disp->vram[vram_id] = (cpu->disp->vram[vram_id] & ~(1 << vram_bit)) | ((1 - ascii_pixel) << vram_bit);
else
// cpu->disp->vram[vram_id] = (cpu->disp->vram[vram_id] & ~(1 << vram_bit)) | (ascii_pixel|vram_pixel) << vram_bit; // MINI OR
printf("[Warning] PrintMiniSd: mode not implemented: %d\n", mode);
}
}
}
void syscall_PrintMiniSd(cpu_t* cpu, int x, int y, uint32_t str_ptr, int mode) {
const char* str = (const char*)get_memory_for_address(cpu, str_ptr);
// printf("Run syscall: PrintMiniSd (%d %d %s %d)\n", x, y, str, mode);
mode = mode & 3; // Only keep the first 2 bits
int i = 0;
while (x < SCREEN_WIDTH) {
const char c = str[i++]; // Current character
if (c == 0x00) break; // Line terminator
draw_character_mini(cpu, c, x, y, mode);
// Move the cursor the the right
if (c == 'i') x += 2;
else if (c == 'n' || c == 'r' || c == 'K') x += 5;
else if (c == 'm' || c == 'w' || c == 'M' || c == 'N' || c == 'W') x += 6;
else x += 4;
}
}
// Draw a character on screen
// pixel_start_x and y represent the top left pixel of the character
void draw_character(cpu_t* cpu, const char c, int pixel_start_x, int pixel_start_y, int mode) {
int offset = c - 0x0020;
// Character x,y in ASCII texture (16x16)
int char_x = offset % 16;
int char_y = offset / 16;
// Pixel start x, y in ASCII texture (112x54)
char_x = char_x * ASCII_CHAR_WIDTH;
char_y = char_y * ASCII_CHAR_HEIGHT;
for (int y = 0; y < ASCII_CHAR_HEIGHT; y++) {
for (int x = 0; x < ASCII_CHAR_WIDTH; x++) {
// Current pixel position in ASCII texture
int ascii_x = char_x + x;
int ascii_y = char_y + y;
int ascii_id = ascii_x + ascii_y * 112; // Pixel index
if (ascii_id < 0 || ascii_id >= 112 * 54) break; // critical_error("ASCII Texture Access out of bounds: %d %d (%d)", ascii_x, ascii_y, ascii_id);
// Current pixel position on-screen
int vram_x = pixel_start_x + x;
int vram_y = pixel_start_y + y;
int vram_id = (vram_x + vram_y * SCREEN_WIDTH) / 8; // index in VRAM
int vram_bit = 7 - vram_x % 8; // Current bit
if (vram_id < 0 || vram_id >= VRAM_SIZE) break; // critical_error("VRAM Access out of bounds: %d %d (%d)", vram_x, vram_y, vram_id);
// Change one single bit in the VRAM
int ascii_bit = cpu->asciiTexture[ascii_id] ^ mode; // Pixel value
// TODO: Add modes
if (ascii_bit)
cpu->disp->vram[vram_id] = (cpu->disp->vram[vram_id] & ~(1 << vram_bit)) | (ascii_bit << vram_bit);
}
}
}
// Display a string at the current position of the display cursor
void syscall_Print(cpu_t* cpu, uint32_t str_ptr) {
const char* str = (const char*)get_memory_for_address(cpu, str_ptr);
// printf("Run syscall: syscall_Print (%s)\n", str);
int i = 0;
while(cpu->disp->cursor.col < 21) {
const char c = str[i++]; // Current character
if (c == 0x00) break; // Line terminator
// Character x,y on screen (21x7)
int screen_x = cpu->disp->cursor.col;
int screen_y = cpu->disp->cursor.row;
// Pixel start x,y on screen (128x64)
screen_x = screen_x * CHAR_WIDTH + 1;
screen_y = screen_y * CHAR_HEIGHT;
draw_character(cpu, c, screen_x, screen_y, 0);
// Move the cursor the the right
cpu->disp->cursor.col++;
}
}
void syscall_PrintXY(cpu_t* cpu, int x, int y, uint32_t str_ptr, int mode) {
const char* str = (const char*)get_memory_for_address(cpu, str_ptr);
// printf("Run syscall: syscall_PrintXY (%d %d %s %d)\n", x, y, str, mode);
int i = 0;
while (x < SCREEN_WIDTH) {
const char c = str[i++]; // Current character
if (c == 0x00) break; // Line terminator
draw_character(cpu, c, x, y, mode);
x += CHAR_WIDTH; // Move the cursor the the right
}
}

View File

@ -0,0 +1,70 @@
#include "../headers/syscalls/syscalls.h"
// Syscall entry point
void run_syscall(cpu_t* cpu) {
uint32_t syscall_code = R0;
int arg1 = cpu->r[4];
int arg2 = cpu->r[5];
int arg3 = cpu->r[6];
int arg4 = cpu->r[7];
if (syscall_code == 0x135) syscall_GetVRAMAddress(cpu);
else if (syscall_code == 0x144) syscall_Bdisp_AllClr_DDVRAM(cpu);
else if (syscall_code == 0x028) syscall_Bdisp_PutDisp_DD(cpu);
else if (syscall_code == 0x146) syscall_Bdisp_SetPoint_VRAM(cpu, arg1, arg2, arg3);
else if (syscall_code == 0x807) syscall_Locate(cpu, arg1, arg2);
else if (syscall_code == 0x808) syscall_Print(cpu, arg1);
else if (syscall_code == 0x9AD) syscall_PrintXY(cpu, arg1, arg2, arg3, arg4);
else if (syscall_code == 0xC4F) syscall_PrintMiniSd(cpu, arg1, arg2, arg3, arg4);
else if (syscall_code == 0xACD) syscall_Malloc(cpu, arg1, 0);
else if (syscall_code == 0xE6B) syscall_Malloc(cpu, arg1, 1); // Calloc
else if (syscall_code == 0xE6D) syscall_Realloc(cpu, arg1, arg2);
else if (syscall_code == 0xACC) syscall_Free(cpu, arg1);
else if (syscall_code == 0x462) syscall_GetAppName(cpu, arg1);
else if (syscall_code == 0x014) syscall_GlibGetAddinLibInf(cpu, arg1, arg2, arg3);
else if (syscall_code == 0x015) syscall_GlibGetOSVersionInfo(cpu, arg1, arg2, arg3, arg4);
else if (syscall_code == 0x90F) syscall_GetKey(cpu, arg1);
else if (syscall_code == 0x03B) syscall_RTC_GetTicks(cpu);
else if (syscall_code == 0x420) syscall_OS_inner_Sleep(cpu, arg1);
else if (syscall_code == 0x434) syscall_Bfile_CreateEntry(cpu, arg1, arg2, arg3);
else if (syscall_code == 0x439) syscall_Bfile_DeleteEntry(cpu, arg1, arg2);
else if (syscall_code == 0x42C) syscall_Bfile_OpenFile(cpu, arg1, arg2, arg3);
else if (syscall_code == 0x42D) syscall_Bfile_CloseFile(cpu, arg1);
else if (syscall_code == 0x435) syscall_Bfile_WriteFile(cpu, arg1, arg2, arg3);
else if (syscall_code == 0x432) syscall_Bfile_ReadFile(cpu, arg1, arg2, arg3, arg4);
else if (syscall_code == 0x431) syscall_Bfile_SeekFile(cpu, arg1, arg2);
else if (syscall_code == 0x43B) { // Bfile_FindFirst
printf("Skipped syscall: Bfile_FindFirst\n");
}
// else if (syscall_code == 0x840) syscall_MCSGetDlen2(cpu, arg1, arg2, arg3);
// else if (syscall_code == 0x841) syscall_MCSGetData1(cpu, arg1, arg2, arg3);
// else if (syscall_code == 0x82B) syscall_MCSPutVar2(cpu, arg1, arg2, arg3, arg4);
// else if (syscall_code == 0x830) syscall_MCSOvwDat2(cpu, arg1, arg2, arg3, arg4, cpu->r[15]);
else if (syscall_code == 0x24C) { // Keyboard_IsSpecialKeyDown
printf("Skipped syscall: Keyboard_IsSpecialKeyDown\n");
}
else if (syscall_code == 0x3ED) { // Interrupt_SetOrClrStatusFlags
// https://bible.planet-casio.com/simlo/chm/v20/fx_legacy_INTERRUPT.HTM
printf("[Error] Blocking syscall not implemented: Interrupt_SetOrClrStatusFlags 0x%X\n", arg2);
}
// Can be ignored
else if (syscall_code == 0x494) { // void SetQuitHandler( void (*callback)(void) );
printf("Ignored syscall: SetQuitHandler\n");
}
else if (syscall_code == 0x013) { // GlibAddinAplExecutionCheck
printf("Ignored syscall: GlibAddinAplExecutionCheck\n");
}
else if (syscall_code == 0x3FA) { // Hmem_SetMMU
printf("Ignored syscall: Hmem_SetMMU\n");
}
else {
printf("[Warning] Syscall not implemented, skipping: 0x%03X\n", syscall_code);
}
cpu->pc = cpu->pr;
}

View File

@ -68,21 +68,24 @@ void cpu_debug(cpu_t* cpu) {
uint8_t low = instruction & 0x0F;
uint8_t low8 = instruction & 0xFF;
printf("[%d][0x%08X] instruction: 0x%04X -", cpu->instruction_count, cpu->pc, instruction);
printf("[%d][0x%08X] instruction: %s 0x%04X -", cpu->instruction_count, cpu->pc, get_instruction_name(instruction), instruction);
print_binary(instruction, 16);
print_binary(high, 4);
print_binary(low, 4);
// print_binary(high, 4);
// print_binary(low, 4);
printf("\n");
}
else printf("[%d][0x%08X] instruction: syscall\n", cpu->instruction_count, cpu->pc);
for (int i = 0; i < 16; i++) {
int r = cpu->r[i];
if (r != 0)
printf("r%d: 0x%08X, ", i, r);
}
// for (int i = 0; i < 16; i++) {
// int r = cpu->r[i];
// if (r != 0)
// printf("r%d: 0x%08X, ", i, r);
// }
printf("pr: 0x%08X, cursor: %d-%d, T: %d\n", cpu->pr, cpu->disp->cursor.col, cpu->disp->cursor.row, (cpu->sr >> 31) & 1);
// printf("pr: 0x%08X, cursor: %d-%d, T: %d\n", cpu->pr, cpu->disp->cursor.col, cpu->disp->cursor.row, (cpu->sr >> 31) & 1);
// printf("pr: 0x%08X\n", cpu->pr);
// print_binary(cpu->sr, 32);
// printf("\n");
// printf("pr: 0x%08X, macl: 0x%08X, gbr: 0x%08X, T: %d\n", cpu->pr, cpu->macl, cpu->gbr, cpu->disp->cursor.col, cpu->disp->cursor.row, (cpu->sr >> 31) & 1);
// printf("mem: 0x%02X 0x%02X 0x%02X 0x%02X\n", mem_read(cpu, 0x00300670, 1), mem_read(cpu, 0x00300670 + 1, 1), mem_read(cpu, 0x00300670 + 2, 1), mem_read(cpu, 0x00300670 + 3, 1));
}
@ -102,4 +105,22 @@ void vram_debug(cpu_t* cpu) {
printf("\n");
}
printf("\n");
}
// Debug function called from javascript
char* get_next_instruction(cpu_t* cpu) {
if (cpu->pc == cpu->pr) return "<EOF>";
if (cpu->pc == SYSCALL_ADDRESS) return "<syscall>";
uint16_t instruction = (mem_read(cpu, cpu->pc, 1) << 8) | mem_read(cpu, cpu->pc + 1, 1);
char* instruction_name = calloc(64, sizeof(char));
strcpy(instruction_name, get_instruction_name(instruction));
strcat(instruction_name, " 0x");
char* instruction_hex = calloc(5, sizeof(char));
sprintf(instruction_hex, "%04X", instruction);
strcat(instruction_name, instruction_hex);
free(instruction_hex);
return instruction_name;
}