Aktuelle Zeit: 25.05.2020 00:29

Alle Zeiten sind UTC + 1 Stunde [ Sommerzeit ]




Ein neues Thema erstellen Auf das Thema antworten  [ 1 Beitrag ] 
Autor Nachricht
 Betreff des Beitrags: [MODULE] Chip8 Interpreter [ALL OS]
BeitragVerfasst: 01.03.2020 14:40 
Offline
Benutzeravatar

Registriert: 25.09.2016 01:42
Chip8 eignet sich besonders als Einstiegsprojekt wenn es um Emulatoren geht.

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


Hier noch Code speziell für Windows (x64):

Bild

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


Die Chip8 Render-Lib (chip8dib.lib) kann hier herundtergeladen werden:
https://www.dropbox.com/s/hls03os9rzt1hv4/chip8dib.zip?dl=0

Viel Spaß beim Testen :D

_________________

Links:
PureBasic Discord
[ENGINE] 2D Engine Nautilus (Win)
[INCLUDE] GLFW 3.3 Library
[MODULE] Bass Library 2.4 (Win)
[LIBRARY] Hexi Binary2Hex (Win)



Nach oben
 Profil  
Mit Zitat antworten  
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 1 Beitrag ] 

Alle Zeiten sind UTC + 1 Stunde [ Sommerzeit ]


Wer ist online?

Mitglieder in diesem Forum: hawk009 und 2 Gäste


Sie dürfen keine neuen Themen in diesem Forum erstellen.
Sie dürfen keine Antworten zu Themen in diesem Forum erstellen.
Sie dürfen Ihre Beiträge in diesem Forum nicht ändern.
Sie dürfen Ihre Beiträge in diesem Forum nicht löschen.

Suche nach:
Gehe zu:  

 


Powered by phpBB © 2008 phpBB Group | Deutsche Übersetzung durch phpBB.de
subSilver+ theme by Canver Software, sponsor Sanal Modifiye