diff --git a/.gitignore b/.gitignore index a5c4883..6489b8c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ .vs -out/build/x64-debug/* -!out/build/x64-debug/fx9860-emulator -out/build/x64-debug/fx9860-emulator/* -!out/build/x64-debug/fx9860-emulator/SAMPLE_ADDIN.G1A -!out/build/x64-debug/fx9860-emulator/U+0020.png +.vscode +tmp/ +fx9860-emulator/build \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 25f4f35..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -# CMakeList.txt : Top-level CMake project file, do global configuration -# and include sub-projects here. -# -cmake_minimum_required (VERSION 3.8) - -# Enable Hot Reload for MSVC compilers if supported. -if (POLICY CMP0141) - cmake_policy(SET CMP0141 NEW) - set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") -endif() - -project ("fx9860-emulator") - -# Include sub-projects. -add_subdirectory ("fx9860-emulator") diff --git a/CMakePresets.json b/CMakePresets.json deleted file mode 100644 index abf4065..0000000 --- a/CMakePresets.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "version": 3, - "configurePresets": [ - { - "name": "windows-base", - "hidden": true, - "generator": "Ninja", - "binaryDir": "${sourceDir}/out/build/${presetName}", - "installDir": "${sourceDir}/out/install/${presetName}", - "cacheVariables": { - "CMAKE_C_COMPILER": "cl.exe", - "CMAKE_CXX_COMPILER": "cl.exe" - }, - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Windows" - } - }, - { - "name": "x64-debug", - "displayName": "x64 Debug", - "inherits": "windows-base", - "architecture": { - "value": "x64", - "strategy": "external" - }, - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug" - } - }, - { - "name": "x64-release", - "displayName": "x64 Release", - "inherits": "x64-debug", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Release" - } - }, - { - "name": "x86-debug", - "displayName": "x86 Debug", - "inherits": "windows-base", - "architecture": { - "value": "x86", - "strategy": "external" - }, - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug" - } - }, - { - "name": "x86-release", - "displayName": "x86 Release", - "inherits": "x86-debug", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Release" - } - } - ] -} diff --git a/JJSH3.g1a b/JJSH3.g1a new file mode 100644 index 0000000..c597819 Binary files /dev/null and b/JJSH3.g1a differ diff --git a/PrintMini.bmp b/PrintMini.bmp new file mode 100644 index 0000000..a634a1b Binary files /dev/null and b/PrintMini.bmp differ diff --git a/README.md b/README.md index ff18bd9..85ae94c 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ 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/ + All non-DSP instructions are implemented, except the following ones: - 'MOVUAL', 'MOVUALP', 'ICBI', 'OCBI', - 'OCBP', 'OCBWB', 'PREFI', 'SLEEP', @@ -10,4 +12,32 @@ 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 \ No newline at end of file +scripts/ contains the code for auto-generating the instructions code 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). +Then: + + cd fx9860-emulator + mkdir build + cd build + emcmake 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' + +After that you can server 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): + +First, remove (comment) `#define EMSCRIPT` in main.h + + cd fx9860-emulator + mkdir build + cd build + cmake .. + make \ No newline at end of file diff --git a/out/build/x64-debug/fx9860-emulator/U+0020.png b/U+0020.png similarity index 100% rename from out/build/x64-debug/fx9860-emulator/U+0020.png rename to U+0020.png diff --git a/fx9860-emulator/headers/display.h b/fx9860-emulator/headers/display.h index ea1f655..3a6b6a7 100644 --- a/fx9860-emulator/headers/display.h +++ b/fx9860-emulator/headers/display.h @@ -26,16 +26,15 @@ typedef struct { int col; // starts at 1 int row; // starts at 1 - //int flashstyle; // -1 if not flashing, else flashmode - //int graphic_mode; } cursor_t; struct display_t { // Pointer to virtual memory in RAM uint8_t vram[VRAM_SIZE]; - //uint8_t* vram; - uint8_t lcd_registers; // Unused, but still emulates access to the LCD screen + // LCD screen emulation + uint8_t lcd[VRAM_SIZE]; + uint8_t lcd_registers; // Use for both selector & data registers // Current cursor position cursor_t cursor; diff --git a/fx9860-emulator/headers/instructions/instructions.h b/fx9860-emulator/headers/instructions/instructions.h index 2986f5a..eef9e42 100644 --- a/fx9860-emulator/headers/instructions/instructions.h +++ b/fx9860-emulator/headers/instructions/instructions.h @@ -4,6 +4,8 @@ void (*get_instruction_impl(uint16_t instruction))(cpu_t*, uint16_t); +#define R0 cpu->r[0] + void ADD(cpu_t* cpu, uint16_t instruction); void ADDI(cpu_t* cpu, uint16_t instruction); void ADDC(cpu_t* cpu, uint16_t instruction); diff --git a/fx9860-emulator/headers/instructions/syscalls.h b/fx9860-emulator/headers/instructions/syscalls.h index 4211b22..78ff512 100644 --- a/fx9860-emulator/headers/instructions/syscalls.h +++ b/fx9860-emulator/headers/instructions/syscalls.h @@ -9,9 +9,17 @@ 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 type); +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_Malloc(cpu_t* cpu, uint32_t size); \ No newline at end of file +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); \ No newline at end of file diff --git a/fx9860-emulator/headers/main.h b/fx9860-emulator/headers/main.h index ca27b45..6a5740f 100644 --- a/fx9860-emulator/headers/main.h +++ b/fx9860-emulator/headers/main.h @@ -5,6 +5,12 @@ typedef struct memory_t memory_t; typedef struct cpu_t cpu_t; typedef struct display_t display_t; +#define USE_EMSCRIPT + +#ifdef USE_EMSCRIPT +#include +#endif + #include #include #include @@ -13,6 +19,7 @@ typedef struct display_t display_t; #include "display.h" #include "utils.h" #include "instructions/instructions.h" +#include "instructions/syscalls.h" // Initialization #define PC_PROGRAM_START 0x00300200 // Execution starting address @@ -21,9 +28,6 @@ typedef struct display_t display_t; #define SYSCALL_ADDRESS 0x80010070 // The calling convention to access the system calls, is to jump to 0x80010070 with the syscall number in r0 -// Status Register bits -#define SR_BIT_T 0 - // CPU struct cpu_t { // General registers R0 - R15 @@ -31,7 +35,12 @@ struct cpu_t { // Control registers uint32_t gbr; // Global base register - uint32_t sr; // Status register (0: T Bit) + 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 @@ -39,23 +48,24 @@ struct cpu_t { 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; + uint32_t instruction_per_frame; // External Components - memory_t* mem; // Memory + memory_t* mem; // Memory display_t* disp; // Display + + // (Temporary) Character sets + uint8_t* asciiTexture; + uint8_t* printMiniTexture; }; -void run_next_instruction(cpu_t* cpu); \ No newline at end of file +void run_next_instruction(cpu_t* cpu); + +void main_loop_html(void* arg); +void init_loop_html(cpu_t* cpu); +void init_loop_c(cpu_t* cpu); \ No newline at end of file diff --git a/fx9860-emulator/headers/memory.h b/fx9860-emulator/headers/memory.h index a11c04b..7aa574c 100644 --- a/fx9860-emulator/headers/memory.h +++ b/fx9860-emulator/headers/memory.h @@ -2,6 +2,7 @@ #include "main.h" +// Memory start addresses #define ROM_START 0x00300000 #define RAM_START_MMU_ALIAS 0x08100000 #define RAM_START 0x8801c000 // 0x88048000 on 35+E II @@ -9,11 +10,21 @@ #define YRAM_START 0xe5017000 #define ILRAM_START 0xe5200000 -#define ROM_SIZE (2048 * 1024) -#define RAM_SIZE (512 * 1024) -#define XRAM_SIZE (8 * 1024) -#define YRAM_SIZE (8 * 1024) -#define ILRAM_SIZE (4 * 1024) +// Memory sizes +#define ROM_SIZE (2048 * 1024) +#define RAM_SIZE (512 * 1024) +#define XRAM_SIZE (8 * 1024) +#define YRAM_SIZE (8 * 1024) +#define ILRAM_SIZE (4 * 1024) + +// Keyboard register addresses (SH3-specific) +#define KB_PORTB_CTRL 0xA4000102 +#define KB_PORTM_CTRL 0xA4000118 +#define KB_PORTA 0xA4000120 +#define KB_PORTB 0xA4000122 +#define KB_PORTM 0xA4000138 +#define KB_SIZE (0xA4000138 - 0xA4000102 + 1) // Max - min address + 1 (insclusive) +#define KB_START KB_PORTB_CTRL typedef struct{ uint32_t size; @@ -27,10 +38,13 @@ struct memory_t { uint8_t ram[RAM_SIZE]; // Random-access memory + uint8_t keyboard[10]; // Keyboard rows state + uint8_t keyboard_registers[KB_SIZE]; // Keyboard registers + + uint32_t* tmp; + // uint8_t xram[XRAM_SIZE]; - // uint8_t yram[YRAM_SIZE]; - // uint8_t ilram[ILRAM_SIZE]; // Malloc @@ -41,5 +55,7 @@ struct memory_t { uint8_t* get_memory_for_address(cpu_t* cpu, uint32_t address); uint32_t mem_read(cpu_t* cpu, uint32_t address, uint8_t bytes); +void mem_write(cpu_t* cpu, uint32_t address, uint32_t data, uint8_t bytes); -void mem_write(cpu_t* cpu, uint32_t address, uint32_t data, uint8_t bytes); \ No newline at end of file +uint32_t emulate_keyboard_register_read(cpu_t* cpu); +void emulate_lcd_register_write(cpu_t* cpu, uint32_t address, uint32_t data); \ No newline at end of file diff --git a/fx9860-emulator/headers/utils.h b/fx9860-emulator/headers/utils.h index c2dc952..d8ff343 100644 --- a/fx9860-emulator/headers/utils.h +++ b/fx9860-emulator/headers/utils.h @@ -10,6 +10,8 @@ void critical_error(const char* format, ...); // Prints the binary representation of a number void print_binary(uint32_t x, int n); -void loadAsciiTexture(cpu_t* cpu); +// Load a character set from an image +uint8_t* load_character_set(const char* path); -void cpu_debug(cpu_t* cpu); \ No newline at end of file +void cpu_debug(cpu_t* cpu); +void vram_debug(cpu_t* cpu); \ No newline at end of file diff --git a/fx9860-emulator/instructions/instructions.c b/fx9860-emulator/instructions/instructions.c index 21b79e0..beeb666 100644 --- a/fx9860-emulator/instructions/instructions.c +++ b/fx9860-emulator/instructions/instructions.c @@ -245,13 +245,12 @@ struct SR0 { #define PC cpu->pc #define PR cpu->pr #define R cpu->r -#define R0 cpu->r[0] #define MACH cpu->mach #define MACL cpu->macl -#define Read_Byte(addr) mem_read_nolog(cpu, addr, 1) -#define Read_Word(addr) mem_read_nolog(cpu, addr, 2) -#define Read_Long(addr) mem_read_nolog(cpu, addr, 4) +#define Read_Byte(addr) mem_read(cpu, addr, 1) +#define Read_Word(addr) mem_read(cpu, addr, 2) +#define Read_Long(addr) mem_read(cpu, addr, 4) #define Write_Byte(addr, data) mem_write(cpu, addr, data, 1) #define Write_Word(addr, data) mem_write(cpu, addr, data, 2) diff --git a/fx9860-emulator/instructions/syscalls.c b/fx9860-emulator/instructions/syscalls.c index fe6a330..64accfe 100644 --- a/fx9860-emulator/instructions/syscalls.c +++ b/fx9860-emulator/instructions/syscalls.c @@ -2,9 +2,9 @@ // Syscall entry point void run_syscall(cpu_t* cpu) { - uint32_t syscall_code = cpu->r[0]; + uint32_t syscall_code = R0; - printf("run_syscall: 0x%03X 0x%X 0x%X 0x%X 0x%X (Return: 0x%08X)\n", syscall_code, cpu->r[4], cpu->r[5], cpu->r[6], cpu->r[7], cpu->pr); + // 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]; @@ -15,6 +15,7 @@ void run_syscall(cpu_t* cpu) { 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 @@ -23,60 +24,113 @@ void run_syscall(cpu_t* cpu) { 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 + 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 == 0xACD) { // malloc - syscall_Malloc(cpu, arg1); + 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 == 0x462) { // GetAppName - syscall_GetAppName(cpu, arg1); + 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("Syscall not implemented, skipping: GetKey\n"); - cpu->isExecutionFinished = 1; + 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("Syscall not implemented, skipping: Keyboard_IsSpecialKeyDown\n"); - cpu->isExecutionFinished = 1; + 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 == 0x43B) { // Bfile_FindFirst - printf("Syscall not implemented, skipping: Bfile_FindFirst\n"); - } else if (syscall_code == 0x434) { // Bfile_CreateEntry_OS - printf("Syscall not implemented, skipping: Bfile_CreateEntry_OS\n"); + 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 - printf("Syscall not implemented, skipping: Bfile_OpenFile_OS\n"); - } + 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("Syscall not implemented, skipping: MCSPutVar2\n"); + printf("Skipped syscall: MCSPutVar2\n"); } else if (syscall_code == 0x840) { // MCSGetDlen2 - printf("Syscall not implemented, skipping: MCSGetDlen2\n"); + 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("Syscall not implemented, skipping: OS_inner_Sleep\n"); - } - else if (syscall_code == 0x014) { // GlibGetAddinLibInf - printf("Syscall not implemented, skipping: GlibGetAddinLibInf\n"); - } - else if (syscall_code == 0x015) { // GlibGetOSVersionInfo - printf("Syscall not implemented, skipping: GlibGetOSVersionInfo\n"); + // printf("Skipped syscall: OS_inner_Sleep\n"); } else if (syscall_code == 0x494) { // void SetQuitHandler( void (*callback)(void) ); - printf("Syscall not implemented, skipping: SetQuitHandler\n"); + 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"); } - // Can be skipped - else if (syscall_code == 0x013) {} // GlibAddinAplExecutionCheck - else if (syscall_code == 0x3FA) {} // Hmem_SetMMU + // 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; @@ -107,15 +161,63 @@ void syscall_Bdisp_AllClr_DDVRAM(cpu_t* cpu) { void syscall_Locate(cpu_t* cpu, int x, int y) { cpu->disp->cursor.col = x; cpu->disp->cursor.row = y; - printf("syscall_locate: %d %d\n", x, 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) { +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 (16x6) + // 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) @@ -128,9 +230,8 @@ void draw_character(cpu_t* cpu, const char c, int pixel_start_x, int pixel_start int ascii_x = char_x + x; int ascii_y = char_y + y; int ascii_id = ascii_x + ascii_y * 112; // Pixel index - int ascii_bit = cpu->asciiTexture[ascii_id]; // Pixel value - if (ascii_id < 0 || ascii_id > 112 * 54) critical_error("ASCII Texture Access out of bounds: %d %d (%d)", ascii_x, ascii_y, ascii_id); + 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; @@ -138,10 +239,13 @@ void draw_character(cpu_t* cpu, const char c, int pixel_start_x, int pixel_start 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) critical_error("VRAM Access out of bounds: %d %d (%d)", vram_x, vram_y, vram_id); + 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 - cpu->disp->vram[vram_id] = (cpu->disp->vram[vram_id] & ~(1 << vram_bit)) | (ascii_bit << vram_bit); + 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); } } } @@ -163,34 +267,32 @@ void syscall_Print(cpu_t* cpu, const unsigned char* str) { screen_x = screen_x * CHAR_WIDTH + 1; screen_y = screen_y * CHAR_HEIGHT; - draw_character(cpu, c, screen_x, screen_y); + 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 type) { - printf("syscall_PrintXY: %d %d %s\n", x, y, str); - +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); + 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 * 1042) +#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) { +void syscall_Malloc(cpu_t* cpu, uint32_t size, uint8_t clear_data) { memory_t* mem = cpu->mem; // Initialize with the lowest address @@ -206,35 +308,146 @@ void syscall_Malloc(cpu_t* cpu, uint32_t size) { 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}; - printf("syscall_malloc %d : 0x%08X\n", mem->mallocCount, addr); - // Return the address of the allocated memory - cpu->r[0] = addr; + 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) { - // The app names is stored in 9 bytes - dest = malloc(9 * sizeof(char)); - // Copies the app name to the new buffer - memcpy(dest, &cpu->mem->rom[0x20], 9); + for (int i = 0; i < 9; i++) { + mem_write(cpu, (uint32_t)dest + i, cpu->mem->rom[0x20 + i], 1); + } - // Return the buffer - cpu->r[0] = dest; + printf("Run Syscall: GetAppName (%s)\n", (const char*)get_memory_for_address(cpu, (uint32_t)dest)); - printf("syscall_GetAppName %s\n", dest); + R0 = (int32_t)dest; // Return the buffer } void syscall_GetVRAMAddress(cpu_t* cpu) { - cpu->r[0] = VRAM_ADDRESS; // &cpu->mem->ram[VRAM_ADDRESS]; - //printf("syscall_GetVRAMAddress 0x%08X\n", cpu->r[0]); -} \ No newline at end of file + 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; +// } \ No newline at end of file diff --git a/fx9860-emulator/main.c b/fx9860-emulator/main.c index 0e6e379..92b9689 100644 --- a/fx9860-emulator/main.c +++ b/fx9860-emulator/main.c @@ -38,6 +38,13 @@ memory_t* init_memory(uint8_t* g1a_content, uint32_t g1a_size) { mem->rom = g1a_content; mem->rom_size = g1a_size; + + mem->mallocs = calloc(1, sizeof(uint8_t)); + mem->tmp = calloc(1024, sizeof(uint8_t)); + + // Initialize keyboard rows (1 = key not pressed) + for (int i = 0; i < 10; i++) + mem->keyboard[i] = 0xFF; return mem; } @@ -48,7 +55,6 @@ display_t* init_display(memory_t* memory) { if(disp == NULL) critical_error("Could not allocate memory for Display"); - //disp->vram = &memory->ram[VRAM_ADDRESS]; disp->cursor.row = 1; disp->cursor.col = 1; @@ -95,10 +101,7 @@ void run_next_instruction(cpu_t* cpu) { } // Extract 16bits instruction code - uint16_t instruction = (mem_read_nolog(cpu, address, 1) << 8) | mem_read_nolog(cpu, address + 1, 1); - - if (cpu->instruction_count % 1000000 == 0) - cpu_debug(cpu); + uint16_t instruction = (mem_read(cpu, address, 1) << 8) | mem_read(cpu, address + 1, 1); // Get a pointer to the function implementing the current instruction get_instruction_impl(instruction) @@ -108,44 +111,84 @@ void run_next_instruction(cpu_t* cpu) { int main() { // Load G1A uint32_t g1a_size; - uint8_t* g1a_content = load_g1a("SAMPLE_ADDIN.G1A", &g1a_size); + uint8_t* g1a_content = load_g1a("JJSH3.G1A", &g1a_size); // Init CPU, Memory and Display memory_t* mem = init_memory(g1a_content, g1a_size); display_t* disp = init_display(mem); cpu_t* cpu = init_cpu(mem, disp); - loadAsciiTexture(cpu); // (Temporary) load the ASCII font texture + // (Temporary) load the ASCII font texture + cpu->asciiTexture = load_character_set("U+0020.png"); + cpu->printMiniTexture = load_character_set("PrintMini.bmp"); +#ifdef USE_EMSCRIPT + init_loop_html(cpu); +#else + init_loop_c(cpu); +#endif - /// [Main Loop] - /// Execute add-in (up to 20M instructions for now) - for (int i = 0; i < 2e7; i++) { + return 0; +} + +// Init the HTML version of the emulator +void init_loop_html(cpu_t* cpu) { +#ifdef USE_EMSCRIPT + cpu->instruction_per_frame = 10000; + + // Pass useful pointers to javascript + EM_ASM({ + window.lcdImage = $0; + window.keyboardPointer = $1; + window.instructionPerFrame = $2; + window.instructionCount = $3; + window.cpuPointer = $4; + window.initGUI(); + }, + &cpu->disp->vram, + &cpu->mem->keyboard, + &cpu->instruction_per_frame, + &cpu->instruction_count, + cpu + ); + + // Setup the main animation frame loop + emscripten_set_main_loop_arg(main_loop_html, cpu, -1, 0); +#endif +} + +// Main loop for the HTML version of the emulator +void main_loop_html(void* arg) { + cpu_t* cpu = (cpu_t*)arg; + + if (cpu->isExecutionFinished) return; + + for (int i = 0; i < cpu->instruction_per_frame; i++) { + run_next_instruction(cpu); + cpu->instruction_count++; + + if (cpu->isExecutionFinished) break; + } +} + +// Run the console version of the emulator +// (no display - VRAM dump at the end) +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; } + vram_debug(cpu); - // (Temporary) Display VRAM - for (int y = 0; y < 64; y++) { - for (int x = 0; x < 16; x++) { - for (int b = 7; b >= 0; b--) { - int id = x + y * 16; - if (id < 0 || id >= VRAM_SIZE) critical_error("error %d", id); - int bit = (disp->vram[id] >> b) & 1; - printf(bit || y == 0 || y == 63 || (x==0 && b==7) || (x==15 && b==0) ? "X" : " "); - } - } - printf("\n"); - } - - printf("\n"); - - free(g1a_content); - free(mem); + free(cpu->mem->mallocs); + free(cpu->mem->tmp); + free(cpu->mem->rom); + free(cpu->mem); + free(cpu->asciiTexture); + free(cpu->printMiniTexture); free(cpu); - - return 0; } \ No newline at end of file diff --git a/fx9860-emulator/memory.c b/fx9860-emulator/memory.c index 7d7bce5..23a4a41 100644 --- a/fx9860-emulator/memory.c +++ b/fx9860-emulator/memory.c @@ -3,36 +3,42 @@ // Returns a pointer to the buffer (memory) // corresponding to the `address` parameter 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 + if (address >= ROM_START && address < ROM_START + cpu->mem->rom_size) { // ROM - (MMU) return &cpu->mem->rom[address - ROM_START]; } - else if (address >= RAM_START_MMU_ALIAS && address < RAM_START_MMU_ALIAS + RAM_SIZE) { - // (RAM) - MMU + else if (address >= RAM_START_MMU_ALIAS && address < RAM_START_MMU_ALIAS + RAM_SIZE) { // RAM - (MMU) return &cpu->mem->ram[address - RAM_START_MMU_ALIAS]; } - else if (address >= RAM_START && address < RAM_START + RAM_SIZE) { - // (RAM) - Physical + else if (address >= RAM_START && address < RAM_START + RAM_SIZE) { // RAM - (Physical) return &cpu->mem->ram[address - RAM_START]; } - else if (address >= VRAM_ADDRESS && address < VRAM_ADDRESS + VRAM_SIZE) { - // (VRAM) + else if (address >= VRAM_ADDRESS && address < VRAM_ADDRESS + VRAM_SIZE) { // VRAM return &cpu->disp->vram[address - VRAM_ADDRESS]; } - else if (address == LCD_SELECT_REGISTER || address == LCD_DATA_REGISTER) { - // LCD emulation + 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 + 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); } } // Write to the memory void mem_write(cpu_t* cpu, uint32_t address, uint32_t data, uint8_t bytes) { - uint8_t* mem = get_memory_for_address(cpu, address); + // LCD input registers + if (address == LCD_SELECT_REGISTER || address == LCD_DATA_REGISTER) { + emulate_lcd_register_write(cpu, address, data); + return; + } - //printf("Memory write [0x%08X]: 0x%08X\n", address, data); + uint8_t* mem = get_memory_for_address(cpu, address); if (bytes == 1) { *mem = data & 0xFF; @@ -50,24 +56,99 @@ void mem_write(cpu_t* cpu, uint32_t address, uint32_t data, uint8_t bytes) { } // Read from the memory -uint32_t mem_read_(cpu_t* cpu, uint32_t address, uint8_t bytes) { +uint32_t mem_read(cpu_t* cpu, uint32_t address, uint8_t bytes) { + // Keyboard output register + if (address == KB_PORTA) { + return emulate_keyboard_register_read(cpu); + } + // Address that stores the model of the calculator (0 for emulator) + else if (address == 0x80000300) { + return 0; + } + uint8_t* mem = get_memory_for_address(cpu, address); + uint32_t data; if (bytes == 1) { - return *mem; + data = *mem; } else if (bytes == 2) { - return (*mem << 8) | (*(mem + 1)); + data = (*mem << 8) | (*(mem + 1)); } else { - return (*mem << 24) | (*(mem + 1) << 16) | (*(mem + 2) << 8) | (*(mem + 3)); + data = (*mem << 24) | (*(mem + 1) << 16) | (*(mem + 2) << 8) | (*(mem + 3)); + } + + // printf("Memory read [0x%08X]: : 0x%X\n", address, data); + + return data; +} + +// Emulate keyboard register A output by reading values +// from port B and M control registers. +// Returns the state of the keyboard for the current row +// https://www.planet-casio.com/Fr/forums/topic17509-2-emulateur-fx-9860-sh4.html#194390 +uint32_t emulate_keyboard_register_read(cpu_t* cpu) { + uint16_t PORTB_CTRL = mem_read(cpu, KB_PORTB_CTRL, 2); + uint8_t row = 0; + + if (PORTB_CTRL != 0xAAAA) { // Rows 0-7 + uint16_t smask = PORTB_CTRL ^ 0xAAAA; + + while (smask >> (row*2) > 0b11) row++; + } + else { // Rows 8-9 + uint16_t PORTM_CTRL = mem_read(cpu, KB_PORTM_CTRL, 2); // row 8: xxxx0001 row 9: xxx0010 + + row = 8 + ((PORTM_CTRL & 0b11) >> 1); + } + + if (row > 9) { + printf("[Warning] Request for keyboard row %d which does not exist!\n", row); + return 0xffffffff; + } + else { + return cpu->mem->keyboard[row]; } } -uint32_t mem_read_nolog(cpu_t* cpu, uint32_t address, uint8_t bytes) { - return mem_read_(cpu, address, bytes); -} -uint32_t mem_read(cpu_t* cpu, uint32_t address, uint8_t bytes) { - uint32_t value = mem_read_(cpu, address, bytes); - printf("Memory read [0x%08X]: : 0x%X\n", address, value); - return value; + +// Keep track of current row and column set in the LCD selector register +// and write data to the LCD buffer. +// Refresh the HTML canvas when the last row is written. +// Works well for MonochromeLib's DisplayVRAM() function, but might +// not work for more specific register accesses. +void emulate_lcd_register_write(cpu_t* cpu, uint32_t address, uint32_t data) { + static int row = -1; + static int col = 0; + + // Setup LCD row + if (address == LCD_SELECT_REGISTER) { + if (data == 7) { // New row + row++; + + if (row >= 64) { + row = 0; + col = 0; + } + } + // printf("LCD REGISTER Write: 0x%08X, set row: %d\n", data, row); + } + // Write data to LCD row + else if (address == LCD_DATA_REGISTER) { + if (row >= 0) { + cpu->disp->lcd[row * 16 + col++] = data; // Update LCD buffer + +#ifdef EMSCRIPTEN + if (row == 63 && col == 16) { // Refresh HTML canvas + EM_ASM({ + window.refreshScreen(); + }); + } +#endif + if (col >= 16) { + col = 0; + } + } + // printf("LCD DATA Write: 0x%08X, row: %d, col: %d\n", data, row, col); + } } \ No newline at end of file diff --git a/fx9860-emulator/utils.c b/fx9860-emulator/utils.c index ae938e2..bbfbf96 100644 --- a/fx9860-emulator/utils.c +++ b/fx9860-emulator/utils.c @@ -8,8 +8,14 @@ void critical_error(const char* format, ...) { va_list args; va_start(args, format); + printf("[Error] "); vprintf(format, args); va_end(args); + +#ifdef USE_EMSCRIPT + emscripten_cancel_main_loop(); +#endif + exit(EXIT_FAILURE); } @@ -21,65 +27,79 @@ void print_binary(uint32_t x, int n) { } } -void loadAsciiTexture(cpu_t* cpu) { - const char* file_path = "U+0020.png"; - +// Load a character set from an image +uint8_t* load_character_set(const char* path) { // Load the PNG image using stb_image int width, height, channels; - unsigned char* image_data = stbi_load(file_path, &width, &height, &channels, STBI_rgb); + unsigned char* image_data = stbi_load(path, &width, &height, &channels, STBI_rgb); - // Check if the image loading was successful if (image_data == NULL) { - printf("Error loading image: %s\n", stbi_failure_reason()); - return -1; + critical_error("Could not load character set %s: %s\n", path, stbi_failure_reason()); } - // Check if the image dimensions match the requirements - /*if (width != 112 || height != 54 || channels != 3) { - printf("Image dimensions or bit depth do not match requirements.\n"); - stbi_image_free(image_data); - return -1; - }*/ - - uint8_t* asciiTexture = malloc(width * height * sizeof(uint8_t)); + uint8_t* packed_data = malloc(width * height * sizeof(uint8_t)); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int index = (y * width + x); int indexRGB = (y * width + x) * channels; unsigned char red = image_data[indexRGB]; - asciiTexture[index] = red ? 0 : 1; - //printf(asciiTexture[index] ? "X" : " "); + if (index >= width * height) critical_error("error %d", index); + packed_data[index] = red ? 0 : 1; + //printf(packed_data[index] ? "X" : " "); } //printf("\n"); } - cpu->asciiTexture = asciiTexture; - // Free the allocated memory for the image data stbi_image_free(image_data); + + printf("Loaded character set %s: %dx%dx%d\n", path, width, height, channels); + + return packed_data; } +// Prints the current state of the CPU void cpu_debug(cpu_t* cpu) { - uint16_t instruction = (mem_read_nolog(cpu, cpu->pc, 1) << 8) | mem_read_nolog(cpu, cpu->pc + 1, 1); - uint8_t high = (instruction >> 12) & 0x0F; - uint8_t high8 = instruction >> 8; - uint8_t low = instruction & 0x0F; - uint8_t low8 = instruction & 0xFF; + if (cpu->pc != 0x80010070) { + uint16_t instruction = (mem_read(cpu, cpu->pc, 1) << 8) | mem_read(cpu, cpu->pc + 1, 1); + uint8_t high = (instruction >> 12) & 0x0F; + uint8_t high8 = instruction >> 8; + uint8_t low = instruction & 0x0F; + uint8_t low8 = instruction & 0xFF; - printf("[%d][0x%08X] instruction: 0x%04X -", cpu->instruction_count, cpu->pc, instruction); - print_binary(instruction, 16); - print_binary(high, 4); - print_binary(low, 4); - printf("\n"); - - /* - for (int i = 0; i < 16; i++) { - int r = cpu->r[i]; - if (r != 0) - printf("r%d: 0x%08X, ", i, r); + printf("[%d][0x%08X] instruction: 0x%04X -", cpu->instruction_count, cpu->pc, instruction); + print_binary(instruction, 16); + print_binary(high, 4); + print_binary(low, 4); + printf("\n"); } - printf("pr: 0x%08X, cursor: %d-%d, T: %d\n", cpu->pr, cpu->disp->cursor.col, cpu->disp->cursor.row, get_status_register_bit(cpu, SR_BIT_T)); - */ - //printf("mem: 0x%02X 0x%02X 0x%02X 0x%02X\n", mem_read_nolog(cpu, 0x00300670, 1), mem_read_nolog(cpu, 0x00300670 + 1, 1), mem_read_nolog(cpu, 0x00300670 + 2, 1), mem_read_nolog(cpu, 0x00300670 + 3, 1)); + 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); + } + + 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, 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)); +} + +// Prints the current state of the VRAM +void vram_debug(cpu_t* cpu) { + for (int y = 0; y < 64; y++) { + for (int x = 0; x < 16; x++) { + for (int b = 7; b >= 0; b--) { + int id = x + y * 16; + if (id < 0 || id >= VRAM_SIZE) critical_error("error %d", id); + int bit = ((cpu->disp->vram[id] >> b) & 1) || y == 0 || y == 63 || (x == 0 && b == 7) || (x == 15 && b == 0); + + printf(bit ? "_" : "X"); + } + } + printf("\n"); + } + printf("\n"); } \ No newline at end of file diff --git a/out/build/x64-debug/fx9860-emulator/SAMPLE_ADDIN.G1A b/out/build/x64-debug/fx9860-emulator/SAMPLE_ADDIN.G1A deleted file mode 100644 index af65d60..0000000 Binary files a/out/build/x64-debug/fx9860-emulator/SAMPLE_ADDIN.G1A and /dev/null differ