From 7f7312d98bb4e9f01af0c8f7f762da41ec3fbdc3 Mon Sep 17 00:00:00 2001 From: "Rustem Gimadutdinov (rgimad)" Date: Sat, 22 May 2021 21:55:47 +0000 Subject: [PATCH] add chip8 emulator + to iso autobuild git-svn-id: svn://kolibrios.org@8725 a494cfbc-eb01-0410-851d-a64ba20cac60 --- data/Tupfile.lua | 3 + programs/emulator/chip8/Tupfile.lua | 6 + programs/emulator/chip8/build.sh | 1 + programs/emulator/chip8/chip8.asm | 114 +++ programs/emulator/chip8/constants.inc | 29 + programs/emulator/chip8/data.inc | 57 ++ programs/emulator/chip8/emu.inc | 779 +++++++++++++++++++ programs/emulator/chip8/gui.inc | 65 ++ programs/emulator/chip8/readme.txt | 12 + programs/emulator/chip8/roms/chip8.ch8 | Bin 0 -> 164 bytes programs/emulator/chip8/roms/clock.ch8 | Bin 0 -> 280 bytes programs/emulator/chip8/roms/ibm.ch8 | Bin 0 -> 132 bytes programs/emulator/chip8/roms/invaders1.ch8 | Bin 0 -> 1301 bytes programs/emulator/chip8/roms/keytest.ch8 | Bin 0 -> 114 bytes programs/emulator/chip8/roms/life.ch8 | Bin 0 -> 256 bytes programs/emulator/chip8/roms/maze_alt.ch8 | Bin 0 -> 38 bytes programs/emulator/chip8/roms/pong1.ch8 | Bin 0 -> 246 bytes programs/emulator/chip8/roms/pong2.ch8 | Bin 0 -> 246 bytes programs/emulator/chip8/roms/pong3.ch8 | Bin 0 -> 264 bytes programs/emulator/chip8/roms/rnd.ch8 | Bin 0 -> 34 bytes programs/emulator/chip8/roms/sqrt.ch8 | Bin 0 -> 386 bytes programs/emulator/chip8/roms/test_opcode.ch8 | Bin 0 -> 478 bytes programs/emulator/chip8/roms/tetris1.ch8 | Bin 0 -> 494 bytes programs/emulator/chip8/utils.inc | 112 +++ 24 files changed, 1178 insertions(+) create mode 100644 programs/emulator/chip8/Tupfile.lua create mode 100755 programs/emulator/chip8/build.sh create mode 100644 programs/emulator/chip8/chip8.asm create mode 100644 programs/emulator/chip8/constants.inc create mode 100644 programs/emulator/chip8/data.inc create mode 100644 programs/emulator/chip8/emu.inc create mode 100644 programs/emulator/chip8/gui.inc create mode 100644 programs/emulator/chip8/readme.txt create mode 100644 programs/emulator/chip8/roms/chip8.ch8 create mode 100644 programs/emulator/chip8/roms/clock.ch8 create mode 100644 programs/emulator/chip8/roms/ibm.ch8 create mode 100644 programs/emulator/chip8/roms/invaders1.ch8 create mode 100644 programs/emulator/chip8/roms/keytest.ch8 create mode 100644 programs/emulator/chip8/roms/life.ch8 create mode 100644 programs/emulator/chip8/roms/maze_alt.ch8 create mode 100644 programs/emulator/chip8/roms/pong1.ch8 create mode 100644 programs/emulator/chip8/roms/pong2.ch8 create mode 100644 programs/emulator/chip8/roms/pong3.ch8 create mode 100644 programs/emulator/chip8/roms/rnd.ch8 create mode 100644 programs/emulator/chip8/roms/sqrt.ch8 create mode 100644 programs/emulator/chip8/roms/test_opcode.ch8 create mode 100644 programs/emulator/chip8/roms/tetris1.ch8 create mode 100644 programs/emulator/chip8/utils.inc diff --git a/data/Tupfile.lua b/data/Tupfile.lua index 6b2419b6e3..55e1d37884 100644 --- a/data/Tupfile.lua +++ b/data/Tupfile.lua @@ -201,6 +201,9 @@ extra_files = { {"kolibrios/emul/e80/keyboard.png", PROGS .. "/emulator/e80/trunk/keyboard.png"}, {"kolibrios/emul/fceu/fceu", PROGS .. "/emulator/fceu/fceu"}, {"kolibrios/emul/fceu/FCEU ReadMe.txt", PROGS .. "/emulator/fceu/FCEU ReadMe.txt"}, + {"kolibrios/emul/chip8/chip8", PROGS .. "/emulator/chip8/chip8"}, + {"kolibrios/emul/chip8/readme.txt", PROGS .. "/emulator/chip8/readme.txt"}, + {"kolibrios/emul/chip8/roms/", PROGS .. "/emulator/chip8/roms/*"}, {"kolibrios/emul/kwine/kwine", PROGS .. "/emulator/kwine/bin/kwine"}, {"kolibrios/emul/kwine/lib/", PROGS .. "/emulator/kwine/bin/lib/*"}, {"kolibrios/emul/uarm/", "common/emul/uarm/*"}, diff --git a/programs/emulator/chip8/Tupfile.lua b/programs/emulator/chip8/Tupfile.lua new file mode 100644 index 0000000000..3f985bdfde --- /dev/null +++ b/programs/emulator/chip8/Tupfile.lua @@ -0,0 +1,6 @@ +if tup.getconfig("NO_FASM") ~= "" then return end +HELPERDIR = (tup.getconfig("HELPERDIR") == "") and "../../.." or tup.getconfig("HELPERDIR") +tup.include(HELPERDIR .. "/use_fasm.lua") + +tup.rule("chip8.asm", FASM .. " %f %o " .. tup.getconfig("KPACK_CMD"), "chip8") + \ No newline at end of file diff --git a/programs/emulator/chip8/build.sh b/programs/emulator/chip8/build.sh new file mode 100755 index 0000000000..495ecf3168 --- /dev/null +++ b/programs/emulator/chip8/build.sh @@ -0,0 +1 @@ +fasm chip8.asm chip8 diff --git a/programs/emulator/chip8/chip8.asm b/programs/emulator/chip8/chip8.asm new file mode 100644 index 0000000000..ca997e46b9 --- /dev/null +++ b/programs/emulator/chip8/chip8.asm @@ -0,0 +1,114 @@ +; Author: rgimad (2021) +; License: GNU GPL v2 + +format binary as "" +use32 +org 0 +db 'MENUET01' ; signature +dd 1 ; header version +dd start ; entry point +dd _i_end ; end of image +dd _mem ; required memory size +dd _stacktop ; address of stack top +dd cmdline ; buffer for command line arguments +dd 0 ; buffer for path + +include 'constants.inc' + +; application's entry point +align 4 +start: + + stdcall chip8_init ; initialize + + DEBUGF DBG_INFO, "app started, args = %s\n", cmdline + DEBUGF DBG_INFO, "MAX_GAME_SIZE = %x = %u\n", MAX_GAME_SIZE, MAX_GAME_SIZE + +; xor ecx, ecx +; @@: +; mov al, byte [chip8_fontset + ecx] +; DEBUGF DBG_INFO, "%x ", al +; cmp ecx, 79 +; je @f +; inc ecx +; jmp @b +; @@: + + mov dword [fread_struct.filename], cmdline + stdcall chip8_loadgame, fread_struct + jz .file_not_found + + DEBUGF DBG_INFO, "file was read. bytes: %x %x %x..\n", [memory + 0x200], [memory + 0x200 + 4], [memory + 0x200 + 8] + + ; mov byte [gfx + 5], 1 + ; mov byte [gfx + 64], 1 + ; mov byte [gfx + 65], 1 + ; mov byte [gfx + 64*2 + 3], 1 + +.event_loop: + mcall 23, CLOCK_RATE ; wait for event with CLOCK_RATE timeout + ;DEBUGF DBG_INFO, "evenp loop iter i\n" + + cmp eax, 1 + je .event_redraw + + cmp eax, 2 + je .event_key + + cmp eax, 3 + je .event_button + + jmp .event_default + + .event_redraw: + stdcall draw_main_window + jmp .event_default + + .event_key: + mcall 2 + stdcall keyboard_update, eax + DEBUGF DBG_INFO, "key scancode %x\n", eax:4 + jmp .event_default + + .event_button: + mcall 17 + cmp ah, 1 + jne .event_default + mcall -1 + + .event_default: + stdcall chip8_emulatecycle + cmp byte [chip8_draw_flag], 0 + jz @f + + ; mov byte [gfx + 64*2 + 3], 1 ; DBG + + stdcall draw_screen + mov byte [chip8_draw_flag], 0 + @@: + stdcall chip8_tick + jmp .event_loop + +.file_not_found: + DEBUGF DBG_ERR, "Unable to open game file! eax = %u\n", eax + jmp .exit + +.exit: + mov eax, -1 + int 0x40 + +;;;;;;;;;;;;;;;;;;;;;;; + + +include 'gui.inc' + +include 'emu.inc' + +include 'utils.inc' + +include 'data.inc' + rb 4096 ; reserve for main thread stack +align 16 +_stacktop: + +_mem: diff --git a/programs/emulator/chip8/constants.inc b/programs/emulator/chip8/constants.inc new file mode 100644 index 0000000000..7245a375bd --- /dev/null +++ b/programs/emulator/chip8/constants.inc @@ -0,0 +1,29 @@ +; constants for formatted debug +__DEBUG__ = 1 ; 0 - disable debug output / 1 - enable debug output +__DEBUG_LEVEL__ = DBG_ERR ; set the debug level + +DBG_ALL = 0 ; all messages +DBG_INFO = 1 ; info and errors +DBG_ERR = 2 ; only errors + +; emulator constants +MAX_GAME_SIZE = 0x1000 - 0x200 +FONTSET_ADDRESS = 0x00 +FONTSET_BYTES_PER_CHAR = 5 +MEM_SIZE = 4096 +STACK_SIZE = 16 +KEY_SIZE = 16 +GFX_ROWS = 32 +GFX_COLS = 64 +GFX_SIZE = GFX_ROWS * GFX_COLS +GFX_PIX_SIZE = 10 +CLOCK_RATE = 1 ; in 10^-2 seconds + + +include '../../macros.inc' +purge mov, add, sub + +include '../../debug-fdo.inc' +include '../../proc32.inc' + +;========================================= \ No newline at end of file diff --git a/programs/emulator/chip8/data.inc b/programs/emulator/chip8/data.inc new file mode 100644 index 0000000000..c0df58a45c --- /dev/null +++ b/programs/emulator/chip8/data.inc @@ -0,0 +1,57 @@ +;========================================= +; initialized data + include_debug_strings ; for debug-fdo + + chip8_fontset db \ + 0xF0, 0x90, 0x90, 0x90, 0xF0, \ ; 0 + 0x20, 0x60, 0x20, 0x20, 0x70, \ ; 1 + 0xF0, 0x10, 0xF0, 0x80, 0xF0, \ ; 2 + 0xF0, 0x10, 0xF0, 0x10, 0xF0, \ ; 3 + 0x90, 0x90, 0xF0, 0x10, 0x10, \ ; 4 + 0xF0, 0x80, 0xF0, 0x10, 0xF0, \ ; 5 + 0xF0, 0x80, 0xF0, 0x90, 0xF0, \ ; 6 + 0xF0, 0x10, 0x20, 0x40, 0x40, \ ; 7 + 0xF0, 0x90, 0xF0, 0x90, 0xF0, \ ; 8 + 0xF0, 0x90, 0xF0, 0x10, 0xF0, \ ; 9 + 0xF0, 0x90, 0xF0, 0x90, 0x90, \ ; A + 0xE0, 0x90, 0xE0, 0x90, 0xE0, \ ; B + 0xF0, 0x80, 0x80, 0x80, 0xF0, \ ; C + 0xE0, 0x90, 0x90, 0x90, 0xE0, \ ; D + 0xF0, 0x80, 0xF0, 0x80, 0xF0, \ ; E + 0xF0, 0x80, 0xF0, 0x80, 0x80 ; F + + opcode dw 0 ; operation code + V db 16 dup(0) ; 16 8-bit registers + I dw 0 ; additional register (usually used for storing addresses) + P_C dw 0 ; program counter + S_P dw 0 ; stack pointer + delay_timer db 0 + sound_timer db 0 + stackmem dw STACK_SIZE dup(0) ; stack memory + key db KEY_SIZE dup (0) ; keyboard + chip8_draw_flag db 0 + next_rand dd 1 + + align 4 + fread_struct: + .subfunction dd 0 ; + 0 + .offset_low dd 0 ; + 4 + .offset_high dd 0 ; + 8 + .size dd MAX_GAME_SIZE ; + 12 + .buffer dd memory + 0x200 ; + 16 + db 0 ; + 20 + .filename: dd 0 ; + 24 + + sys_colors system_colors + main_window_title db 'CHIP-8 Emulator',0 + +;========================================= +align 16 +_i_end: +; uninitialized data + cmdline rb 1024 ; reserve for command line arguments + + memory rb MEM_SIZE + gfx rb GFX_SIZE + + ;tmp_buf rb 128 diff --git a/programs/emulator/chip8/emu.inc b/programs/emulator/chip8/emu.inc new file mode 100644 index 0000000000..b7703b10f7 --- /dev/null +++ b/programs/emulator/chip8/emu.inc @@ -0,0 +1,779 @@ +; initalize the emulator +align 4 +proc chip8_init stdcall +; destroys: nothing, mb flags + push eax ecx + mov word [P_C], 0x200 + mov word [opcode], 0 + mov word [I], 0 + mov word [S_P], 0 + + ;DEBUGF DBG_INFO, "ESP = %x\n", esp + stdcall _memset, memory, 0, MEM_SIZE + stdcall _memset, V, 0, 16 + stdcall _memset, gfx, 0, GFX_SIZE + stdcall _memset, stackmem, 0, 2*STACK_SIZE ; 2 = sizeof(dw) + stdcall _memset, key, 0, KEY_SIZE + ;DEBUGF DBG_INFO, "ESP = %x\n", esp + + mcall 66, 1 ; set scancode keyboard mode + + xor ecx, ecx +@@: + cmp ecx, 80 + jge @f + mov al, byte [chip8_fontset + ecx] + mov byte [memory + FONTSET_ADDRESS + ecx], al + inc ecx + jmp @b +@@: + mov byte [chip8_draw_flag], 1 + mov byte [delay_timer], 0 + mov byte [sound_timer], 0 + stdcall _getseed + stdcall _srand, eax + + stdcall _rand + DEBUGF DBG_INFO, "rand() = %u\n", eax + ;stdcall _rand + ;DEBUGF DBG_INFO, "rand() = %u\n", eax + ;stdcall _rand + ;DEBUGF DBG_INFO, "rand() = %u\n", eax + ;stdcall _rand + ;DEBUGF DBG_INFO, "rand() = %u\n", eax + ;stdcall _rand + ;DEBUGF DBG_INFO, "rand() = %u\n", eax + + ;mov word [opcode], 0xBFAF + ;movzx eax, word [opcode] + ;mov eax, 0xABCDEF92 + ;stdcall unknown_opcode, eax + ;DEBUGF DBG_INFO, "testprint\n" + pop ecx eax + ret +endp + + +; load game from file to memory +align 4 +proc chip8_loadgame stdcall, struct_ptr: dword +; in: struct_ptr - pointer to structure for sysfn70 +; out: ZF = 1 file loaded successfully +; ZF = 0 error +; destroys: only flags + push eax ebx + + mov eax, 70 + mov ebx, [struct_ptr] + int 0x40 + + cmp eax, 0 + je @f + cmp eax, 6 + je @f + jmp .load_fail +@@: + mov eax, 1 + jmp .ret + +.load_fail: + xor eax, eax +.ret: + test eax, eax + pop ebx eax + ret +endp + + +; emulate one cycle +align 4 +proc chip8_emulatecycle stdcall +; destroys: ? + locals + x db ? + y db ? + n db ? + kk db ? + nnn dw ? + endl + ; fetch: + movzx ecx, word [P_C] + movzx ax, byte [memory + ecx] + shl ax, 8 + movzx bx, byte [memory + 1 + ecx] + or ax, bx + mov word [opcode], ax + ; DEBUGF DBG_INFO, "opcode = 0x%x, ax = 0x%x\n", [opcode]:4, ax + + shr ax, 8 + and ax, 0x000F + mov byte [x], al + + mov ax, word [opcode] + shr ax, 4 + and ax, 0x000F + mov byte [y], al + + mov ax, word [opcode] + and ax, 0x000F + mov byte [n], al + + mov ax, word [opcode] + and ax, 0x00FF + mov byte [kk], al + + mov ax, word [opcode] + and ax, 0x0FFF + mov word [nnn], ax + + ; DEBUGF DBG_INFO, "P_C: 0x%x Op: 0x%x\n", [P_C], [opcode]:4 ; was word word + ; TODO test this and watch values of x, y, n, kk, nnn + + ; decode & execute + ; sw1 + mov ax, word [opcode] + and ax, 0xF000 + + cmp ax, 0x0000 + je .sw1_case_0000 + + cmp ax, 0x1000 + je .sw1_case_1000 + + cmp ax, 0x2000 + je .sw1_case_2000 + + cmp ax, 0x3000 + je .sw1_case_3000 + + cmp ax, 0x4000 + je .sw1_case_4000 + + cmp ax, 0x5000 + je .sw1_case_5000 + + cmp ax, 0x6000 + je .sw1_case_6000 + + cmp ax, 0x7000 + je .sw1_case_7000 + + cmp ax, 0x8000 + je .sw1_case_8000 + + cmp ax, 0x9000 + je .sw1_case_9000 + + cmp ax, 0xA000 + je .sw1_case_A000 + + cmp ax, 0xB000 + je .sw1_case_B000 + + cmp ax, 0xC000 + je .sw1_case_C000 + + cmp ax, 0xD000 + je .sw1_case_D000 + + cmp ax, 0xE000 + je .sw1_case_E000 + + cmp ax, 0xF000 + je .sw1_case_F000 + + jmp .sw1_default + +.sw1_case_0000: + ; sw2 + cmp byte [kk], 0xE0 + je .sw2_case_E0 + + cmp byte [kk], 0xEE + je .sw2_case_EE + + jmp .sw2_default + + .sw2_case_E0: ; clear the screen + stdcall _memset, gfx, 0, GFX_SIZE + mov byte [chip8_draw_flag], 1 + add word [P_C], 2 + jmp .sw2_end + + .sw2_case_EE: ; TODO check!!; ret + dec word [S_P] + movzx ecx, word [S_P] + mov ax, word [stackmem + ecx*2] + mov word [P_C], ax + jmp .sw2_end + + .sw2_default: + movzx eax, word [opcode] + stdcall unknown_opcode, eax + .sw2_end: + jmp .sw1_end + +.sw1_case_1000: ; TODO check; 1nnn: jump to address nnn + mov ax, word [nnn] + mov word [P_C], ax + jmp .sw1_end + +.sw1_case_2000: ; TODO check; 2nnn: call address nnn + mov ax, word [P_C] + add ax, 2 + movzx ecx, word [S_P] + mov word [stackmem + ecx*2], ax + inc word [S_P] + mov ax, word [nnn] + mov word [P_C], ax + jmp .sw1_end + +.sw1_case_3000: ; 3xkk: skip next instr if V[x] = kk + movzx ecx, byte [x] + mov al, byte [V + ecx] + mov bl, byte [kk] + mov cx, 2 + cmp al, bl + jne @f + mov cx, 4 +@@: + add word [P_C], cx + jmp .sw1_end + +.sw1_case_4000: ; 4xkk: skip next instr if V[x] != kk + movzx ecx, byte [x] + mov al, byte [V + ecx] + mov bl, byte [kk] + mov cx, 2 + cmp al, bl + je @f + mov cx, 4 +@@: + add word [P_C], cx + jmp .sw1_end + +.sw1_case_5000: ; 5xy0: skip next instr if V[x] == V[y] + movzx ecx, byte [x] + mov al, byte [V + ecx] + movzx ecx, byte [y] + mov bl, byte [V + ecx] + mov cx, 2 + cmp al, bl + jne @f + mov cx, 4 +@@: + add word [P_C], cx + jmp .sw1_end + +.sw1_case_6000: ; 6xkk: set V[x] = kk + movzx ecx, byte [x] + mov bl, byte [kk] + mov byte [V + ecx], bl + add word [P_C], 2 + jmp .sw1_end + +.sw1_case_7000: ; 7xkk: set V[x] = V[x] + kk + movzx ecx, byte [x] + mov bl, byte [kk] + add byte [V + ecx], bl + add word [P_C], 2 + jmp .sw1_end + +.sw1_case_8000: ; 8xyn: Arithmetic stuff + ; sw3 + cmp byte [n], 0x0 + je .sw3_case_0 + + cmp byte [n], 0x1 + je .sw3_case_1 + + cmp byte [n], 0x2 + je .sw3_case_2 + + cmp byte [n], 0x3 + je .sw3_case_3 + + cmp byte [n], 0x4 + je .sw3_case_4 + + cmp byte [n], 0x5 + je .sw3_case_5 + + cmp byte [n], 0x6 + je .sw3_case_6 + + cmp byte [n], 0x7 + je .sw3_case_7 + + cmp byte [n], 0xE + je .sw3_case_E + + jmp .sw3_default + + .sw3_case_0: ; V[x] = V[y] + movzx ecx, byte [x] + movzx edx, byte [y] + mov al, byte [V + edx] + mov byte [V + ecx], al + jmp .sw3_end + + .sw3_case_1: ; V[x] = V[x] | V[y] + movzx ecx, byte [x] + movzx edx, byte [y] + mov al, byte [V + ecx] + or al, byte [V + edx] + mov byte [V + ecx], al + jmp .sw3_end + + .sw3_case_2: ; V[x] = V[x] & V[y] + movzx ecx, byte [x] + movzx edx, byte [y] + mov al, byte [V + ecx] + and al, byte [V + edx] + mov byte [V + ecx], al + jmp .sw3_end + + .sw3_case_3: ; V[x] = V[x] ^ V[y] + movzx ecx, byte [x] + movzx edx, byte [y] + mov al, byte [V + ecx] + xor al, byte [V + edx] + mov byte [V + ecx], al + jmp .sw3_end + + .sw3_case_4: ; V[x] = V[x] + V[y]; if carry, move 1 to V[0xF] + movzx ecx, byte [x] + movzx edx, byte [y] + movzx ax, byte [V + ecx] + movzx bx, byte [V + edx] + add ax, bx + mov byte [V + ecx], al + + xor cl, cl + cmp ax, 255 + jbe @f + inc cl + @@: + mov byte [V + 0xF], cl + jmp .sw3_end + + .sw3_case_5: ;TODO check; V[x] = V[x] - V[y]; if no borrow, move 1 to V[0xF] + movzx ecx, byte [x] + movzx edx, byte [y] + mov al, byte [V + ecx] + mov bl, byte [V + edx] + sub al, bl + mov byte [V + ecx], al + + xor cl, cl + cmp al, bl + jbe @f + inc cl + @@: + mov byte [V + 0xF], cl + jmp .sw3_end + + .sw3_case_6: ; TODO check; V[x] = V[x] SHR 1 ; V[0xF] = least-significant bit of V[x] before shift + movzx ecx, byte [x] + mov al, byte [V + ecx] + and al, 0x01 + mov byte [V + 0xF], al + shr byte [V + ecx], 1 + jmp .sw3_end + + .sw3_case_7: ; TODO check; V[x] = V[y] - V[x]; if no borrow, move 1 to V[0xF] + movzx ecx, byte [y] + movzx edx, byte [x] + mov al, byte [V + ecx] + mov bl, byte [V + edx] + sub al, bl + mov byte [V + ecx], al + + xor cl, cl + cmp al, bl + jbe @f + inc cl + @@: + mov byte [V + 0xF], cl + jmp .sw3_end + + .sw3_case_E: ; TODO check; V[0xF] = most-significant bit of V[x] before shift + movzx ecx, byte [x] + mov al, byte [V + ecx] + shr al, 7 + and al, 0x01 + mov byte [V + 0xF], al + shl byte [V + ecx], 1 + jmp .sw3_end + + .sw3_default: + movzx eax, word [opcode] + stdcall unknown_opcode, eax + + .sw3_end: + add word [P_C], 2 + jmp .sw1_end + +.sw1_case_9000: ; TODO check; 9xy0: skip instruction if V[x] != V[y] + movzx ecx, byte [x] + mov al, byte [V + ecx] + movzx ecx, byte [y] + mov bl, byte [V + ecx] + mov cx, 2 + cmp al, bl + je @f + mov cx, 4 +@@: + add word [P_C], cx + jmp .sw1_end + +.sw1_case_A000: ; Annn: set I to address nnn + mov ax, word [nnn] + mov word [I], ax + add word [P_C], 2 + jmp .sw1_end + +.sw1_case_B000: ; Bnnn: jump to location nnn + V[0] + mov ax, word [nnn] + movzx bx, byte [V] + add ax, bx + mov word [P_C], ax + jmp .sw1_end + +.sw1_case_C000: ; TODO check; Cxkk: V[x] = random byte AND kk + stdcall _rand + and al, byte [kk] + movzx ecx, byte [x] + mov byte [V + ecx], al + add word [P_C], 2 + jmp .sw1_end + +.sw1_case_D000: ; TODO check; Dxyn: Display an n-byte sprite starting at memory location I at (Vx, Vy) on the screen, VF = collision + movzx ecx, byte [x] + movzx eax, byte [V + ecx] + movzx ecx, byte [y] + movzx ebx, byte [V + ecx] + movzx ecx, byte [n] + stdcall chip8_draw_sprite, eax, ebx, ecx + mov byte [chip8_draw_flag], 1 + add word [P_C], 2 + jmp .sw1_end + +.sw1_case_E000: ; TODO check; key-pressed events + cmp byte [kk], 0x9E + je .sw5_case_9E + + cmp byte [kk], 0xA1 + je .sw5_case_A1 + + jmp .sw5_default + + .sw5_case_9E: ; skip next instruction if key V[X] is pressed + movzx ecx, byte [x] + movzx edx, byte [V + ecx] + mov bl, byte [key + edx] + mov ax, 2 + cmp bl, 1 + jne .sw5_case_9E_endcheck + mov ax, 4 + mov byte [key + edx], 0 ; release pressed key + .sw5_case_9E_endcheck: + add word [P_C], ax + jmp .sw5_end + + .sw5_case_A1: ; skip next instruction if key V[X] is NOT pressed + movzx ecx, byte [x] + movzx eax, byte [V + ecx] + mov bl, byte [key + eax] + mov ax, 4 + cmp bl, 1 + jne .sw5_case_A1_endcheck + mov ax, 2 + .sw5_case_A1_endcheck: + mov byte [key + edx], 0 ; release pressed key + add word [P_C], ax + jmp .sw5_end + + .sw5_default: + movzx eax, word [opcode] + stdcall unknown_opcode, eax + .sw5_end: + jmp .sw1_end + +.sw1_case_F000: ; misc + cmp byte [kk], 0x07 + je .sw4_case_07 + + cmp byte [kk], 0x0A + je .sw4_case_0A + + cmp byte [kk], 0x15 + je .sw4_case_15 + + cmp byte [kk], 0x18 + je .sw4_case_18 + + cmp byte [kk], 0x1E + je .sw4_case_1E + + cmp byte [kk], 0x29 + je .sw4_case_29 + + cmp byte [kk], 0x33 + je .sw4_case_33 + + cmp byte [kk], 0x55 + je .sw4_case_55 + + cmp byte [kk], 0x65 + je .sw4_case_65 + + jmp .sw4_default + + .sw4_case_07: ; TODO check; V[X] = delay timer + mov al, byte [delay_timer] + movzx ecx, byte [x] + mov byte [V + ecx], al + add word [P_C], 2 + jmp .sw4_end + + .sw4_case_0A: ; TODO check; wait for key instruction + ;.sw4_case_0A_loop: + ;mcall 2 + ;stdcall keyboard_update, eax + xor ecx, ecx + .sw4_case_0A_loop2: + cmp ecx, KEY_SIZE + jae .sw4_case_0A_loop_end ; + + cmp byte [key + ecx], 1 + jne .sw4_case_0A_loop2_endcheck + + movzx edx, byte [x] + mov byte [V + edx], cl + mov byte [key + ecx], 0 ; release pressed key + ;jmp .sw4_case_0A_loop_end + add word [P_C], 2 + jmp .sw4_end + + .sw4_case_0A_loop2_endcheck: + inc ecx + jmp .sw4_case_0A_loop2 + + ;jmp .sw4_case_0A_loop + .sw4_case_0A_loop_end: + ;add word [P_C], 2 + jmp .sw4_end + + .sw4_case_15: ; TODO check; delay_timer = V[X] + movzx ecx, byte [x] + mov al, byte [V + ecx] + mov byte [delay_timer], al + add word [P_C], 2 + jmp .sw4_end + + .sw4_case_18: ; TODO check; sound_timer = V[X] + movzx ecx, byte [x] + mov al, byte [V + ecx] + mov byte [sound_timer], al + add word [P_C], 2 + jmp .sw4_end + + .sw4_case_1E: ; I = I + V[X] + ; V[0xF] = (I + V[x] > 0xfff) ? 1 : 0; (TODO?? (no this line in other chip8 emulators)) + movzx ecx, byte [x] + movzx ax, byte [V + ecx] + add word [I], ax + add word [P_C], 2 + jmp .sw4_end + + .sw4_case_29: ; TODO check; I = location of font for character V[X] + movzx ecx, byte [x] + movzx ax, byte [V + ecx] + mov bx, FONTSET_BYTES_PER_CHAR + mul bx + mov word [I], ax + add word [P_C], 2 + jmp .sw4_end + + .sw4_case_33: ; TODO check; Store BCD for V[X] starting at address I + movzx ecx, byte [x] + movzx ebx, byte [V + ecx] + + stdcall mod_div, ebx, 1000, 100 + movzx ecx, word [I] + mov byte [memory + ecx], al + + stdcall mod_div, ebx, 100, 10 + mov byte [memory + ecx + 1], al + + stdcall mod_div, ebx, 10, 1 + mov byte [memory + ecx + 2], al + + add word [P_C], 2 + jmp .sw4_end + + .sw4_case_55: ; TODO check; Copy sprite from registers 0 to X into memory at address I + movzx edx, word [I] + xor ecx, ecx + .for_sw4_1: + movzx eax, byte [x] + cmp ecx, eax + ja .for_sw4_1_end + + mov al, byte [V + ecx] + mov byte [memory + ecx + edx], al + + inc ecx + jmp .for_sw4_1 + .for_sw4_1_end: + movzx ax, byte [x] + inc ax + add word [I], ax + + add word [P_C], 2 + jmp .sw4_end + + .sw4_case_65: ; TODO check; Copy sprite from memory at address X into registers 0 to I + movzx edx, word [I] + xor ecx, ecx + .for_sw4_2: + movzx eax, byte [x] + cmp ecx, eax + ja .for_sw4_2_end + + mov al, byte [memory + ecx + edx] + mov byte [V + ecx], al + + inc ecx + jmp .for_sw4_2 + .for_sw4_2_end: + movzx ax, byte [x] + inc ax + add word [I], ax + + add word [P_C], 2 + jmp .sw4_end + + .sw4_default: + movzx eax, word [opcode] + stdcall unknown_opcode, eax + .sw4_end: + jmp .sw1_end + +.sw1_default: + movzx eax, word [opcode] + stdcall unknown_opcode, eax + +.sw1_end: + ret +endp + + +; tick the timers +align 4 +proc chip8_tick stdcall +; destroys: flags + cmp byte [delay_timer], 0 + jz @f + dec byte [delay_timer] +@@: + cmp byte [sound_timer], 0 + jz .ret + dec byte [sound_timer] + cmp byte [sound_timer], 0 + jnz @f + DEBUGF DBG_INFO, "BEEP!\n" +@@: +.ret: + ret +endp + + +; print unknown opcode error & exit +align 4 +proc unknown_opcode stdcall, op:word + DEBUGF DBG_ERR, "Error: unknown opcode 0x%x\n", [op]:4 + mov eax, -1 + int 0x40 + ret +endp + +; draw sprite from memory to gfx; TODO check; +; if collision then V[0xF] = 1 +align 4 +proc chip8_draw_sprite stdcall, col: dword, row: dword, n: dword + locals + byte_index dd ? + bit_index dd ? + pixelp dd ? + temp dd ? + _byte db ? + _bit db ? + endl + + DEBUGF DBG_INFO, "draw_sprite x = %u, y = %u, n = %u\n", [col], [row], [n] + + movzx eax, word [I] + mov ebx, dword [memory + eax] + mov ecx, dword [memory + eax + 4] + DEBUGF DBG_INFO, "I = %x, at I: %x, at I + 4: %x\n", eax, ebx, ecx + + mov byte [V + 0xF], 0 + mov dword [byte_index], 0 +.for1: + movzx eax, byte [n] + cmp dword [byte_index], eax + jae .for1_end + + movzx ecx, word [I] + add ecx, dword [byte_index] + mov al, byte [memory + ecx] + mov byte [_byte], al + mov dword [bit_index], 0 +.for2: + cmp dword [bit_index], 8 + jae .for2_end + + mov ecx, dword [bit_index] + mov al, byte [_byte] + shr al, cl + and al, 1 + mov byte [_bit], al + + mov eax, dword [row] + add eax, dword [byte_index] + imul eax, GFX_COLS + add eax, dword [col] + add eax, 7 + sub eax, dword [bit_index] + add eax, gfx + mov dword [pixelp], eax + + ; DEBUGF DBG_INFO, "gfx = %x, pixelp = %x\n", gfx, edx + + cmp byte [_bit], 1 + jne .if2_end + + mov eax, dword [pixelp] + cmp byte [eax], 1 + jne .if2_end + + mov byte [V + 0xF], 1 +.if2_end: + + mov ebx, dword [pixelp] + mov al, byte [ebx] + xor al, byte [_bit] + mov byte [ebx], al + + inc dword [bit_index] + jmp .for2 +.for2_end: + + inc dword [byte_index] + jmp .for1 +.for1_end: + + ret +endp diff --git a/programs/emulator/chip8/gui.inc b/programs/emulator/chip8/gui.inc new file mode 100644 index 0000000000..bc9624b25c --- /dev/null +++ b/programs/emulator/chip8/gui.inc @@ -0,0 +1,65 @@ +; draw main window +align 4 +proc draw_main_window stdcall + mcall 12, 1 ; notify about draw beginning + mcall 48, 3, sys_colors, sizeof.system_colors + + mov edx, [sys_colors.work] ; background color + or edx, 0x74000000 ; window type + ; DEBUGF DBG_INFO, "mainwindow w, h = %u, %u", GFX_COLS*GFX_PIX_SIZE + 8, GFX_ROWS*GFX_PIX_SIZE + 28 + mcall 0, <50, GFX_COLS*GFX_PIX_SIZE + 8>, <50, GFX_ROWS*GFX_PIX_SIZE + 28>, , , main_window_title ; + + stdcall draw_screen + + mcall 12, 2 ; notify about draw ending + ret +endp + +; draw screen +align 4 +proc draw_screen stdcall + pushad + locals + row_ind dd ? + col_ind dd ? + color dd ? + endl + + xor esi, esi +.loop1: + cmp esi, GFX_SIZE + jae .loop1_end + xor edx, edx + mov eax, esi + mov ecx, GFX_COLS + div ecx ; eax = row index, edx = col index + mov dword [row_ind], eax + mov dword [col_ind], edx + mov dword [color], 0x80FFFFFF ; white + cmp byte [esi + gfx], 0 ; check if cell is 0 or not 0 + jne @f + mov dword [color], 0x80000000 ; black + @@: + mov ebx, dword [col_ind] + imul ebx, GFX_PIX_SIZE + ;add ebx, WINDOW_BORDER + shl ebx, 16 + add ebx, GFX_PIX_SIZE + + mov ecx, dword [row_ind] + imul ecx, GFX_PIX_SIZE + ;add ecx, WINDOW_BORDER + shl ecx, 16 + add ecx, GFX_PIX_SIZE + + mov eax, 13 + mov edx, dword [color] + int 0x40 + + inc esi + jmp .loop1 + +.loop1_end: + popad + ret +endp \ No newline at end of file diff --git a/programs/emulator/chip8/readme.txt b/programs/emulator/chip8/readme.txt new file mode 100644 index 0000000000..611a50d013 --- /dev/null +++ b/programs/emulator/chip8/readme.txt @@ -0,0 +1,12 @@ +CHIP-8 Emulator for KolibriOS. +Author: rgimad +Year: 2021 + +How to use: + +Open shell in current directory (in Eolite Ctrl+G). +Type for example: + +chip8 roms/ibm.chip8 + +P.S. ROMs from https://github.com/dmatlack/chip8/tree/master/roms diff --git a/programs/emulator/chip8/roms/chip8.ch8 b/programs/emulator/chip8/roms/chip8.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..74ab4bf9169a6022ca29ba20d515cf0d34dd1409 GIT binary patch literal 164 zcmZR0u*f5UAyF=g;S$p&p&||=2O%yX-!6%1k=F&ii+V+TAaYSe0(TGMG)%?h@}f+eFd@1 zL9A~;)?cn%)xV-b3AxsPMU`HZF}iUHWhuS*%Wlgcl)!XDC@76V>4nmVQbt!cp(>>h zLJ6r1N*|PdlrXw*33Vy`5K2g4Q2L?FP|WDeCNxVKs3)00nL(MOh|!5fXqOY0&^l!f zp@bx$h_XN-qq2Zdf+LgAA%s{W14t}`ebIyK?d6ON?|%GRuitNV`^p~(U`V$Bxsl;r ex*o{YKw1gp4OV literal 0 HcmV?d00001 diff --git a/programs/emulator/chip8/roms/invaders1.ch8 b/programs/emulator/chip8/roms/invaders1.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..3ada8dfe93a7d95925a9f52a1bf2cf385101bacd GIT binary patch literal 1301 zcmZWnQAk@?82)d1y_piamh`f#L!@1A(Y5IsX0;I_gJ9ar3bKc*%*)hU?J_db zHpya)flW574=cK`vDvNnWm{kNuoto79NUwy4is_bVa)mx!w{k+p1c1hX*=A&s%{wQRRi1nI9zSDv1 zJPLJiw$)+c()-6IejM1JczHl<1M;gJPYIVr@_U<${9*fTlx&tciEOvEkR3Vx2Doc) z3O)2`Rq?%YRUtWY*g(dLwdaA19X80Jse2cRyD{b%E?soEWt@3AL7PtV1uN2{^5bAUzCT&h3Dh;D&&*7BvD{v<+c;@^XuA) zH6HSE=u=q^X*C5})%ghW!|+LNgx~p!T>I8}=qU^;mivcc7~!vTqn!KhH+2OMYKU>} z7WaCs`!3{t3Co(DMbp%@ov?hq#Kw3RavLshi~)QI9XJ3ujyr|`=#Z-D5Jd}sK?w&y zqN{5F0f1Zu=vWYk&Uj5c4zRSe;x}5nN8?3TeX+>%05LyXLL80nN-d8cpE(0?F!?j$ z;AngSBt=?S04Va?lauMaqw#0;@3!nhP8<7Q{AvA*uxJCI#8**rxvYcnpQh&Kp1_lj z=jJ}2-T%9}d9S^_9bT+&KY#9(U$%OC-(FkWf~^-$lE#%uGJz_j>(EFpM&Xr;xN?2m}F*reTjZFfRof zTXCYrXT%-lj7$_oj4%ggWO!B(SbBMx@jOd1^z9a~JBLFMgmS+Om+1SMA3w7KqYs?a zRDua0R5#2%U2W1K5TL{qrcD9~g_vn27^Zq<`?MN015qPv5^98Xb=nNK{^xX9MFEQ0(9n^cvUT~Vjc{JvVlv!A|lx9~5`cIUbP literal 0 HcmV?d00001 diff --git a/programs/emulator/chip8/roms/keytest.ch8 b/programs/emulator/chip8/roms/keytest.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..c60558ee714cf811ff746caafb09072c03f20b83 GIT binary patch literal 114 zcmWgg+(kkkkcXp-o`1&^jg| zCI%{j2MZoFeB^TE7ut};(Iw=?AT;flgiuSPNTbLHt_GF{7LffyE%^)=1be<*5M+4w zi7SC2ks*(v=)V(#P)iQOqUH;NEgvoj7W`3KR>-2XtN@A&L3|(viGwjqA&U>t-9F4h Qzf2f~n1u9zq@EBH0N{~CP5=M^ literal 0 HcmV?d00001 diff --git a/programs/emulator/chip8/roms/maze_alt.ch8 b/programs/emulator/chip8/roms/maze_alt.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..0dca98194909931a4c217eca56340d0b693542d8 GIT binary patch literal 38 tcmYdbNMu-~bcoT2agp2wkpdP22O*XOhC&uY1tAt8nFa?10R@K!0RXgI2xI^N literal 0 HcmV?d00001 diff --git a/programs/emulator/chip8/roms/pong1.ch8 b/programs/emulator/chip8/roms/pong1.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..e371e91c0fc158fc82c81d15205404406f5446f0 GIT binary patch literal 246 zcmc~|%I3+j&*fS4>ejY9*YX&YuB0(%FeM~>5dFYzz#t@bT)dnk^Z%j`*9svj5*QyW ztp1n40wkFd;4%tW-tkfRsQ$ejY9*YX&YuB0(%FeM~>5dFYzz#t@bT)dnk^Z%j`*9svj5*QyW ztp1n40wkFd%J`Se1!P^dXB1kL$kZSTq<4YI6JYXCg2D$0 zr7L|VK$qDWGXh=r&zLcTNl2{nzvsWq|3W7!89kXY853AONR%t`$O5BrrZ$ zSp6@71xPX_$agc{+LplcU}5dQ1YRHs6zOHUa}A`rt)+cRf_)oPqI|oM8-q}Vo4rtz zyL{$v79}z@hyv+dVDbc*Jd~jDK|<+D zp9#=qcE*fA*ZnhQ%wQ4{tNiczFY~|9iAqLKrcA~JmJbqTjBXBP|AcZDefnqoDfOdf qibyKMmCzE=PnuUk8QwKO00RhQE95XNdUg9?4Wo^M(4Q;@hIarEDr-&v literal 0 HcmV?d00001 diff --git a/programs/emulator/chip8/roms/rnd.ch8 b/programs/emulator/chip8/roms/rnd.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..c9f79e4c3133a2a5654a735477c23b42baf5b883 GIT binary patch literal 34 qcmZR0aNz$Ul@G?BQa@-WFeEcvF#V{RzECUSQkzA`E3$(!pGZNeofuqCKzq>aaVGDGK{Y9of^-l< z_`*_drRd$#lGK%#oQkj&DRT4^N<|QsXiTa%aA7?2!&Mh$nmyw?gbe%a$ywNWfUM63 zh_%o;hFKGBWHMaXpWSOjh%A?x7cpWzsRk@+M;_@;Qb{Z3sqpTvOy?sI9GPl!VP1*` grHA>+Jjh$`-cX+{=bR?8qd|+E@7K1<8|(+bFV!QG%>V!Z literal 0 HcmV?d00001 diff --git a/programs/emulator/chip8/roms/test_opcode.ch8 b/programs/emulator/chip8/roms/test_opcode.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..f540f69ecfc06e6c7b30881234469cc10847bff9 GIT binary patch literal 478 zcmYk&ze~eF6bJC8;uTt`rKMFO)T4t#DiW)7aF9c=aT1sA@kf%j0Y~YWsiDx-J-lv3 z1R;Mw{sLz$9o|@rh&s6H-K7zijmckgkn@idt{U=U!$7$S`jO^N|hYBI{u zLvEBJ7rsFj@J-|)dXX2fAb|8VOudV>H0vnc)n3+BEX}S%S^y zPBf8sb5KpBZAvM?b|SYwtvFSN-Nf$VlnRYlYR8@R?q-j=3S_x7MK{?=O%i)k6%A5AWWClvaIsC#&di4 cz5iuFT33MVtpI6c5XNWCX+%VlyR+~?lv`40X#skLaSFO}dqV`E0DV;L4@g-?2y(W#Fx1@3Mc&ZNDkgLrFV^Cm-+M2rg zjM!iiTd~UQN-JPj=OW-q-SKIcNeUgIT_rCjxXUNY#8M`~7%h>@zU5Ei75S|TQ{~eY zGs<{VJTwsmk7WRuU;r5gjH4KbX3PZ3XD-eHy%{*pF6KkV))=4 z`JzkR?q2g_p!I^6O7DgGA{(S6IyO{AmyQH@i$ojd4RavFCM58G9nu!nCcxtj#eU@= zw4d4X3(SKF+M#^Vj^%BF6v*8n(9SpgEx(^=+f