add chip8 emulator + to iso autobuild

git-svn-id: svn://kolibrios.org@8725 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
Rustem Gimadutdinov (rgimad) 2021-05-22 21:55:47 +00:00
parent bb845a71ca
commit 7f7312d98b
24 changed files with 1178 additions and 0 deletions

View File

@ -201,6 +201,9 @@ extra_files = {
{"kolibrios/emul/e80/keyboard.png", PROGS .. "/emulator/e80/trunk/keyboard.png"}, {"kolibrios/emul/e80/keyboard.png", PROGS .. "/emulator/e80/trunk/keyboard.png"},
{"kolibrios/emul/fceu/fceu", PROGS .. "/emulator/fceu/fceu"}, {"kolibrios/emul/fceu/fceu", PROGS .. "/emulator/fceu/fceu"},
{"kolibrios/emul/fceu/FCEU ReadMe.txt", PROGS .. "/emulator/fceu/FCEU ReadMe.txt"}, {"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/kwine", PROGS .. "/emulator/kwine/bin/kwine"},
{"kolibrios/emul/kwine/lib/", PROGS .. "/emulator/kwine/bin/lib/*"}, {"kolibrios/emul/kwine/lib/", PROGS .. "/emulator/kwine/bin/lib/*"},
{"kolibrios/emul/uarm/", "common/emul/uarm/*"}, {"kolibrios/emul/uarm/", "common/emul/uarm/*"},

View File

@ -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")

View File

@ -0,0 +1 @@
fasm chip8.asm chip8

View File

@ -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:

View File

@ -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'
;=========================================

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,112 @@
; note: proc that defines without stdcall, call using "call"
; note: by stdcall convention callee is responsible for cleaning up the stack,
; arguments are pushed onto the stack in right-to-left order
align 4
proc _memset stdcall, dest:dword, val:byte, cnt:dword ; doesnt clobber any registers
;DEBUGF DBG_INFO, "memset(%x, %u, %u)\n", [dest], [val], [cnt]
push eax ecx edi
mov edi, dword [dest]
mov al, byte [val]
mov ecx, dword [cnt]
rep stosb
pop edi ecx eax
ret
endp
align 4
proc _srand stdcall, seed:dword
push eax
;DEBUGF DBG_INFO, "_srand: next_rand = %u\n", [next_rand]
mov eax, dword [seed]
mov dword [next_rand], eax
pop eax
ret
endp
align 4
proc _rand stdcall
push ebx edx
;DEBUGF DBG_INFO, "_rand: next_rand = %u\n", [next_rand]
mov eax, dword [next_rand]
mov ebx, 214013
mul ebx
add eax, 2531011
mov dword [next_rand], eax
shr eax, 16
and eax, 0x7fff
pop edx ebx
ret
endp
align 4
proc _getseed stdcall
push edx
rdtsc
xor eax, edx
pop edx
ret
endp
; calculate (num % num_mod) / num_div
align 4
proc mod_div stdcall, num: dword, num_mod: dword, num_div:dword ; TODO check
; in: num - number
; num_mod - first divisor
; num_div - second divisor
; out: eax - the result
; destroys: mb flags
push ecx edx
xor edx, edx
mov eax, dword [num]
mov ecx, dword [num_mod]
div ecx
mov eax, edx
xor edx, edx
mov ecx, dword [num_div]
div ecx
pop edx ecx
ret
endp
; update key map; TODO impl;
align 4
proc keyboard_update stdcall, _code: dword
push eax ebx ecx edx
mov eax, dword [_code]
mov edx, eax
and edx, 0xF000
mov ebx, eax
and ebx, 0x0F00
shr ebx, 8
xor ecx, ecx
cmp edx, 0x6000
jne .if1_end
mov ecx, 0x9
.if1_end:
add ebx, ecx
; DEBUGF DBG_INFO, "keynum %x\n", ebx
cmp ebx, KEY_SIZE
jae .ret
DEBUGF DBG_INFO, "keynum %x\n", ebx
mov byte [key + ebx], 1
.ret:
pop edx ecx ebx eax
ret
endp
; ; get seconds count
; align 4
; proc get_clock stdcall
; ; out: eax - time from system start in 10^-2 secs
; push ebx
; mov eax, 26
; mov ebx, 9
; int 0x40
; pop ebx
; ret
; endp