Wobei Chip8 im Prinzip nur ein Interpreter ist.
Wikipedia:https://de.wikipedia.org/wiki/CHIP-8
Angemerkt sei noch, dass sich einige Opcodes vom Original unterscheiden (u.a. SHR / SHL)
Die meisten ROMs wurden/werden jedoch für diese abgwandelte, 'moderne' Variante geschrieben.
Als Leitfaden diente Cowgods Reference:
http://devernay.free.fr/hacks/chip8/C8TECH10.HTM#2.2
Um Input, Sound und das Rendern muss sich der Nutzer kümmern
Code:
Code: Alles auswählen
DeclareModule CHIP8
;CHIP8 INTERPRETER / EMULATOR
;AUTHOR: MIJIKAI
;VERSION: WIP.0.7.2
;COMPILER: PB 5.62 (x64)
;TARGET OS: ALL!
Structure CHIP8_OPCODE
StructureUnion
dw.u
db.a[2]
EndStructureUnion
EndStructure
Structure CHIP8
nfo.u
opc.CHIP8_OPCODE
dec.u[6]
adr.u[3]
reg.a[$12]
stk.u[$10]
ram.a[$1000]
vid.q[$20]
key.a[$10]
dat.u[$10]
EndStructure
Interface OBJECT
Link.i();get the a pointer to (CHIP8) have access to all internals!
State.i();get the current state: 0 = idle / 1 = running / opcode = crash @ that opcode!
Input.i(*Callback,Parameter.i = #Null);handle input
Render.i(*Callback,Parameter.i = #Null);handle rendering
Sound.i(*Callback,Parameter.i = #Null);play a sound -> nonblocking!
Restart.i();restart (resets rom if loaded)
Load.i(ROM.s,*Buffer = #Null,BufferSize.i = #Null);load a rom
Step.i(Cycles.i = 16);step / execute (how many cycles at once?)
Clock.i();clock (needed for the sound and delay timers)
Reset.i();reset (wipes all)
Release.i();release all resources
EndInterface
Declare.i Create()
EndDeclareModule
Module CHIP8
EnableExplicit
Prototype.i CALLBACK(*buffer,custom.i)
Structure VM
*vtable
*ptr[7]
chip8.CHIP8
rom.a[$1000]
siz.u
clk_start.q
clk_time.q
*input.CALLBACK
input_custom.i
*render.CALLBACK
render_custom.i
*sound.CALLBACK
sound_custom.i
EndStructure
Procedure.i vt_Link(*vm.VM)
ProcedureReturn *vm\ptr[$0]
EndProcedure
Procedure.i vt_State(*vm.VM)
ProcedureReturn *vm\chip8\nfo
EndProcedure
Procedure.i vt_Input(*vm.VM,*input,parameter.i = #Null)
*vm\input = *input
*vm\input_custom = parameter
ProcedureReturn *vm\ptr[$4]
EndProcedure
Procedure.i vt_Render(*vm.VM,*render,parameter.i = #Null)
*vm\render = *render
*vm\render_custom = parameter
ProcedureReturn *vm\ptr[$3]
EndProcedure
Procedure.i vt_Sound(*vm.VM,*sound,parameter.i = #Null)
*vm\sound = *sound
*vm\sound_custom = parameter
ProcedureReturn #Null
EndProcedure
Procedure.i vt_Restart(*vm.VM)
FillMemory(*vm\ptr[$0],SizeOf(CHIP8))
If *vm\siz
*vm\chip8\nfo = $1
*vm\chip8\adr[$0] = $200
CopyMemory(?chip8_font,*vm\ptr[$1],?chip8_eod - ?chip8_font)
CopyMemory(*vm\ptr[$6],*vm\ptr[$2],*vm\siz)
EndIf
ProcedureReturn #Null
EndProcedure
Procedure.i vt_Load(*vm.VM,file.s,*mem,siz.i)
Protected handle.i
Protected file_siz.i
FillMemory(*vm\ptr[$0],SizeOf(CHIP8))
*vm\siz = 0
If file
handle = ReadFile(#PB_Any,file)
If handle
file_siz = Lof(handle)
If Not file_siz > $E00
If ReadData(handle,*vm\ptr[$6],file_siz) = file_siz
*vm\siz = file_siz
EndIf
EndIf
CloseFile(handle)
EndIf
ElseIf *mem And siz > 0 And Not siz > $E00
CopyMemory(*vm\ptr[$6],*mem,siz)
*vm\siz = siz
EndIf
If *vm\siz
*vm\chip8\nfo = $1
*vm\chip8\adr[$0] = $200
CopyMemory(?chip8_font,*vm\ptr[$1],?chip8_eod - ?chip8_font)
CopyMemory(*vm\ptr[$6],*vm\ptr[$2],*vm\siz)
Else
FillMemory(*vm\ptr[$6],$1000)
EndIf
ProcedureReturn *vm\siz
EndProcedure
Procedure.i vt_Step(*vm.VM,cycles.i = 16)
Protected px.i
Protected py.i
If *vm\chip8\nfo = $1
If *vm\input
*vm\Input(*vm\ptr[4],*vm\input_custom)
EndIf
With *vm\chip8
Repeat
\opc\db[$0] = \ram[\adr[$0] + $1]
\opc\db[$1] = \ram[\adr[$0]]
\adr[$0] + $2
\dec[$0] = (\opc\dw >> $C)
\dec[$1] = (\opc\dw & $0FFF)
\dec[$2] = (\opc\dw & $00FF)
\dec[$3] = (\opc\dw & $000F)
\dec[$4] = (\opc\dw & $0F00) >> $8
\dec[$5] = (\opc\dw & $00F0) >> $4
Select \dec[$0]
Case $0
Select \dec[$2]
Case $E0
FillMemory(*vm\ptr[3],$100)
Case $EE
\adr[$2] - $1
\adr[$0] = \stk[\adr[$2]]
Default:\nfo = \opc\dw
EndSelect
Case $1
\adr[$0] = \dec[$1]
Case $2
\stk[\adr[$2]] = \adr[$0]
\adr[$2] + $1
\adr[$0] = \dec[$1]
Case $3
If \reg[\dec[$4]] = \dec[$2]
\adr[$0] + $2
EndIf
Case $4
If \reg[\dec[$4]] <> \dec[$2]
\adr[$0] + $2
EndIf
Case $5
If \reg[\dec[$4]] = \reg[\dec[$5]]
\adr[$0] + $2
EndIf
Case $6
\reg[\dec[$4]] = \dec[$2]
Case $7
\reg[\dec[$4]] + \dec[$2]
Case $8
Select \dec[$3]
Case $0
\reg[\dec[$4]] = \reg[\dec[$5]]
Case $1
\reg[\dec[$4]] | \reg[\dec[$5]]
Case $2
\reg[\dec[$4]] & \reg[\dec[$5]]
Case $3
\reg[\dec[$4]] ! \reg[\dec[$5]]
Case $4
If ($FF - \reg[\dec[$4]]) < \reg[\dec[$5]]
\reg[$F] = $1
Else
\reg[$F] = $0
EndIf
\reg[\dec[$4]] + \reg[\dec[$5]]
Case $5
If \reg[\dec[$4]] < \reg[\dec[$5]]
\reg[$F] = $0
Else
\reg[$F] = $1
EndIf
\reg[\dec[$4]] - \reg[\dec[$5]]
Case $6
\reg[$F] = (\reg[\dec[$4]] & $1)
\reg[\dec[$4]] >> $1
Case $7
If \reg[\dec[$4]] > \reg[\dec[$5]]
\reg[$F] = $0
Else
\reg[$F] = $1
EndIf
\reg[\dec[$4]] = (\reg[\dec[$5]] - \reg[\dec[$4]])
Case $E
\reg[$F] = ((\reg[\dec[$4]] >> $7) & $1)
\reg[\dec[$4]] << $1
Default:\nfo = \opc\dw
EndSelect
Case $9
If \reg[\dec[$4]] <> \reg[\dec[$5]]
\adr[$0] + $2
EndIf
Case $A
\adr[$1] = \dec[$1]
Case $B
\adr[$0] = (\reg[\dec[$0]] + \dec[$1])
Case $C
\reg[\dec[$4]] = (Random($FF) & \dec[$2])
Case $D
\dat[$0] = \reg[\dec[$4]]
\dat[$1] = \reg[\dec[$5]]
\dat[$2] = \dec[$3]
\dat[$3] = \adr[$1]
\dat[$4] = $0
\dat[$5] = $0
\dat[$6] = \dat[$0] + 7
\dat[$7] = \dat[$1] + \dat[$2] - 1
\reg[$F] = 0
If \dat[$0] < $0
\dat[$4] = \dat[$0] * -$1
\dat[$0] = $0
EndIf
If \dat[$1] < $0
\dat[$5] = \dat[$1] * -$1
\dat[$1] = $0
EndIf
If \dat[$6] > $3F
\dat[$6] = $3F
EndIf
If \dat[$7] > $1F
\dat[$7] = $1F
EndIf
\dat[$8] = \dat[$4]
\dat[$9] = \dat[$5] + \dat[$3]
For py = \dat[$1] To \dat[$7]
For px = \dat[$0] To \dat[$6]
If ((\ram[\dat[$9]] >> ($7 - \dat[$8])) & $1)
If ((\vid[py] >> ($7 - px)) & $1)
\reg[$F] = $1
EndIf
\vid[py] ! ($1 << ($7 - px))
EndIf
\dat[$8] + $1
Next
\dat[$8] = \dat[$4]
\dat[$9] + $1
Next
Case $E
Select \dec[$2]
Case $9E
If \key[\reg[\dec[$4]]]
\adr[$0] + 2
EndIf
Case $A1
If Not \key[\reg[\dec[$4]]]
\adr[$0] + 2
EndIf
Default:\dat = \opc\dw
EndSelect
Case $F
Select \dec[$2]
Case $07
\reg[\dec[$4]] = \reg[$10]
Case $0A
py = $0
For px = $0 To $F
If \key[px]
\reg[\dec[$4]] = px
py = $1
Break
EndIf
Next
If py = $0
\adr[$0] - $2
EndIf
Case $15
\reg[$10] = \reg[\dec[$4]]
Case $18
\reg[$11] = \reg[\dec[$4]]
Case $1E
\adr[$1] + \reg[\dec[$4]]
Case $29
\adr[$1] = (\reg[\dec[$4]] * $5)
Case $33
\ram[\adr[$1]] = (\reg[\dec[$4]] / 100)
\ram[\adr[$1] + $1] = (\reg[\dec[$4]] / 10) % 10
\ram[\adr[$1] + $2] = (\reg[\dec[$4]] % 100) % 10
Case $55
CopyMemory(@\reg[$0],@\ram[$0] + \adr[$1],\dec[$4] + $1)
\adr[$1] + (\dec[$4] + $2)
Case $65
CopyMemory(@\ram[$0] + \adr[$1],@\reg[$0],\dec[$4] + $1)
\adr[$1] + (\dec[$4] + $2)
Default:\nfo = \opc\dw
EndSelect
Default:\nfo = \opc\dw
EndSelect
cycles - 1
Until cycles < 1
If \reg[$10] > $0
\reg[$10] - $1
EndIf
If \reg[$11] > $0
\reg[$11] - $1
If *vm\sound
*vm\sound(\opc\dw,*vm\sound_custom)
EndIf
EndIf
EndWith
If *vm\render
*vm\render(*vm\ptr[3],*vm\render_custom)
EndIf
Else
If *vm\render
*vm\render(*vm\ptr[3],*vm\render_custom)
EndIf
EndIf
EndProcedure
Procedure.i vt_Clock(*vm.VM)
Repeat
*vm\clk_time = ElapsedMilliseconds() - *vm\clk_start
If *vm\clk_time < 10
Delay(1)
EndIf
Until *vm\clk_time > 16
*vm\clk_start = (*vm\clk_start + *vm\clk_time)
ProcedureReturn #Null
EndProcedure
Procedure.i vt_Reset(*vm.VM)
FillMemory(*vm\ptr[$0],SizeOf(CHIP8))
ProcedureReturn #Null
EndProcedure
Procedure.i vt_Release(*vm.VM)
FreeMemory(*vm)
ProcedureReturn #Null
EndProcedure
Procedure.i Create()
Protected *vm.VM
*vm = AllocateMemory(SizeOf(VM))
If *vm
*vm\vtable = ?vtable
*vm\ptr[$0] = @*vm\chip8
*vm\ptr[$1] = @*vm\chip8\ram[$0]
*vm\ptr[$2] = @*vm\chip8\ram[$200]
*vm\ptr[$3] = @*vm\chip8\vid[$0]
*vm\ptr[$4] = @*vm\chip8\key[$0]
*vm\ptr[$5] = @*vm\chip8\dat[$0]
*vm\ptr[$6] = @*vm\rom[$0]
EndIf
ProcedureReturn *vm
EndProcedure
DataSection
vtable:
Data.i @vt_Link()
Data.i @vt_State()
Data.i @vt_Input()
Data.i @vt_Render()
Data.i @vt_Sound()
Data.i @vt_Restart()
Data.i @vt_Load()
Data.i @vt_Step()
Data.i @vt_Clock()
Data.i @vt_Reset()
Data.i @vt_Release()
chip8_font:
!db 0xF0,0x90,0x90,0x90,0xF0;0
!db 0x20,0x60,0x20,0x20,0x70;1
!db 0xF0,0x10,0xF0,0x80,0xF0;2
!db 0xF0,0x10,0xF0,0x10,0xF0;3
!db 0x90,0x90,0xF0,0x10,0x10;4
!db 0xF0,0x80,0xF0,0x10,0xF0;5
!db 0xF0,0x80,0xF0,0x90,0xF0;6
!db 0xF0,0x10,0x20,0x40,0x40;7
!db 0xF0,0x90,0xF0,0x90,0xF0;8
!db 0xF0,0x90,0xF0,0x10,0xF0;9
!db 0xF0,0x90,0xF0,0x90,0x90;A
!db 0xE0,0x90,0xE0,0x90,0xE0;B
!db 0xF0,0x80,0x80,0x80,0xF0;C
!db 0xE0,0x90,0x90,0x90,0xE0;D
!db 0xF0,0x80,0xF0,0x80,0xF0;E
!db 0xF0,0x80,0xF0,0x80,0x80;F
chip8_eod:
EndDataSection
EndModule
Procedure.i CallbackRender(*Buffer.Quad,*Parameter);example renders to the debug window
Protected px.i
Protected py.i
Protected sl.s
For py = 0 To 31
For px = 0 To 63
If ((*Buffer\q >> ($7 - px)) & $1)
sl + "1"
Else
sl + "0"
EndIf
Next
*Buffer + $8
Debug sl
sl = #Null$
Next
EndProcedure
Procedure.i Dummy()
Protected *chip8.CHIP8::OBJECT
*chip8 = CHIP8::Create()
If *chip8
*chip8\Render(@CallbackRender())
*chip8\Load("...");<- load a rom!!!!!!!!!!!!!
Repeat
*chip8\Step()
*chip8\Clock()
ForEver
*chip8\Release()
EndIf
ProcedureReturn #Null
EndProcedure
Dummy()
End
Code: Alles auswählen
Import "chip8dib.lib"
chip8CreateDIB.i(hwnd.i)
EndImport
Interface CHIP8DIB
Clear.i()
Pixel.i(X.i,Y.i)
Blit.i()
Color.i(*Buffer,Flag.i = #Null);0 back- / 1 front color // upload colors (64 x 32)
Resize.i()
Release.i()
EndInterface
Structure CHIP8KEY
key.a[$10]
EndStructure
Procedure.i chip8_Input(*key.CHIP8KEY,*Parameter)
*key\key[$0] = GetAsyncKeyState_(#VK_0) & $1
*key\key[$1] = GetAsyncKeyState_(#VK_1) & $1
*key\key[$2] = GetAsyncKeyState_(#VK_2) & $1
*key\key[$3] = GetAsyncKeyState_(#VK_3) & $1
*key\key[$4] = GetAsyncKeyState_(#VK_4) & $1
*key\key[$5] = GetAsyncKeyState_(#VK_5) & $1
*key\key[$6] = GetAsyncKeyState_(#VK_6) & $1
*key\key[$7] = GetAsyncKeyState_(#VK_7) & $1
*key\key[$8] = GetAsyncKeyState_(#VK_8) & $1
*key\key[$9] = GetAsyncKeyState_(#VK_9) & $1
*key\key[$A] = GetAsyncKeyState_(#VK_A) & $1
*key\key[$B] = GetAsyncKeyState_(#VK_B) & $1
*key\key[$C] = GetAsyncKeyState_(#VK_C) & $1
*key\key[$D] = GetAsyncKeyState_(#VK_D) & $1
*key\key[$E] = GetAsyncKeyState_(#VK_E) & $1
*key\key[$F] = GetAsyncKeyState_(#VK_F) & $1
ProcedureReturn #Null
EndProcedure
Procedure.i chip8_Render(*vram.Quad,*chip8dib.CHIP8DIB)
Protected px.i
Protected py.i
*chip8dib\Clear()
For py = 0 To 31
For px = 0 To 63
If ((*vram\q >> ($7 - px)) & $1)
*chip8dib\Pixel(px,py)
EndIf
Next
*vram + $8
Next
*chip8dib\Blit()
ProcedureReturn #Null
EndProcedure
Procedure.i chip8_Sound(opcode.i,*Parameter)
;//play sound
EndProcedure
Procedure.i Chip8(Title.s = #Null$,Width.i = 800,Height.i = 600)
Protected wnd.i
Protected wnd_flags.i
Protected wnd_event.i
Protected wnd_exit.i
Protected menu.i
Protected *chip8.CHIP8::OBJECT
Protected *chip8debug.CHIP8::CHIP8
Protected *chip8dib.CHIP8DIB
wnd_flags = #PB_Window_SystemMenu|#PB_Window_ScreenCentered|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget|#PB_Window_SizeGadget
wnd = OpenWindow(#PB_Any,#Null,#Null,Width,Height,Title,wnd_flags)
If wnd
WindowBounds(wnd,Width,Height,#PB_Ignore,#PB_Ignore)
SetWindowColor(wnd,0)
menu = CreateMenu(#PB_Any,WindowID(wnd))
If menu
MenuTitle("Main")
MenuItem(1,"Load ROM")
MenuTitle("State")
MenuItem(2,"Restart")
MenuItem(3,"Reset")
*chip8 = CHIP8::Create()
If *chip8
*chip8dib = chip8CreateDIB(WindowID(wnd))
If *chip8dib
*chip8\Input(@chip8_Input())
*chip8\Render(@chip8_Render(),*chip8dib)
*chip8\Sound(@chip8_Sound())
*chip8debug = *chip8\Link()
Repeat
Repeat
wnd_event = WindowEvent()
Select wnd_event
Case #PB_Event_Menu
Select EventMenu()
Case 1
*chip8dib\Clear()
*chip8dib\Blit()
*chip8\Load(OpenFileRequester("Load ROM",#Null$,#Null$,#Null))
Case 2
*chip8\Restart()
Case 3
*chip8\Reset()
*chip8dib\Clear()
*chip8dib\Blit()
EndSelect
Case #PB_Event_SizeWindow
*chip8dib\Resize()
Case #PB_Event_CloseWindow
wnd_exit = #True
EndSelect
Until wnd_event = #Null
*chip8\Step(16)
*chip8\Clock()
Until wnd_exit
*chip8dib\Release()
EndIf
*chip8\Release()
EndIf
EndIf
CloseWindow(wnd)
EndIf
ProcedureReturn #Null
EndProcedure
Chip8("CHIP8 Interpreter (c) 2020 by Mijikai")
End
https://www.dropbox.com/s/hls03os9rzt1h ... b.zip?dl=0
Viel Spaß beim Testen