Skip to content

Instantly share code, notes, and snippets.

@tobiasvl
Created November 23, 2020 10:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tobiasvl/486e81543c27107f40d6f76c89202a1c to your computer and use it in GitHub Desktop.
Save tobiasvl/486e81543c27107f40d6f76c89202a1c to your computer and use it in GitHub Desktop.
; *******************************************************************
; *** ***
; *** VIP2K Chip 8 interpreter ***
; *** ***
; *** This software is copyright 2008 by Marcel van Tongeren ***
; *** You have permission to use, modify, copy, and distribute ***
; *** this software so long as this copyright notice is retained. ***
; *** This software may not be used in commercial applications ***
; *** without express written permission from the author. ***
; *******************************************************************
; Origin set to 07000H, EOF = 0768FH
ORG 07000H
; CPU Type:
CPU 1802
; Labels:
CHIP8_EXEC EQU 7000H
CHIP8_COMMAND_F EQU 7005H
R700D EQU 700DH
R7028 EQU 7028H
R703C EQU 703CH
R7040 EQU 7040H
NEXT_DIGIT EQU 704BH
S705B EQU 705BH
R706B EQU 706BH
CTL_R EQU 7075H
CTL_K EQU 707CH
CHIP8_COMMAND_3 EQU 7083H
S7084 EQU 7084H
SKIP_INSTRUCTION EQU 7088H
NO_SKIP EQU 708AH
CHIP8_COMMAND_4 EQU 708BH
S708C EQU 708CH
CHIP8_COMMAND_9 EQU 7091H
CHIP8_COMMAND_5 EQU 7095H
CHIP8_COMMAND_E EQU 7099H
R70A4 EQU 70A4H
CHIP8_ExA1 EQU 70A7H
CHIP8_Ex9E EQU 70ACH
CHIP8_Fx07_Start EQU 70B1H
CHIP8_Fx15_Start EQU 70B7H
KEY_UP EQU 70BDH
R70CE EQU 70CEH
CHIP8_00DD EQU 70DDH
CHIP8_00E0 EQU 70E0H
CHIP8_00EE EQU 70EEH
R70E4 EQU 70E4H
CHIP8_COMMAND_D_2P EQU 70F3H
R7123 EQU 7123H
R7138 EQU 7138H
CHIP8_COMMAND_D_2P_DRAW EQU 7141H
R714A EQU 714AH
R7154 EQU 7154H
R7168 EQU 7168H
R7173 EQU 7173H
R717E EQU 717EH
R7189 EQU 7189H
CHIP8_COMMAND_D_2P_END EQU 7196H
CTL_S EQU 71A2H
STORE_SCREEN_RES EQU 71A5H
CTL_L EQU 71B1H
CHIP8_COMMAND_TABLE EQU 71B6H
CHIP8_START EQU 71E0H
R71F0 EQU 71F0H
R71FC EQU 71FCH
R7205 EQU 7205H
R721A EQU 721AH
RES_3x5 EQU 722EH
R723A EQU 723AH
RESTORE_KEY_MAP EQU 724FH
R7258 EQU 7258H
REPEAT_BYTES EQU 725FH
R7263 EQU 7263H
R726D EQU 726DH
MAIN_CHIP8_LOOP EQU 7278H
SPEED_KEY EQU 7296H
S729E EQU 729EH
SPEED_DELAY EQU 72B2H
R72B6 EQU 72B6H
CTL_KEY EQU 72C9H
J72CC EQU 72CCH
CHIP8_COMMAND_0 EQU 72D0H
CHIP8_COMMAND_00 EQU 72D9H
R72DB EQU 72DBH
CTL_Q EQU 72DFH
CHIP8_COMMAND_D_3P EQU 72E8H
J7319 EQU 7319H
R732F EQU 732FH
CHIP8_COMMAND_D_3P_DRAW EQU 7339H
R7345 EQU 7345H
R734F EQU 734FH
S7367 EQU 7367H
R7372 EQU 7372H
R737D EQU 737DH
R7388 EQU 7388H
R7393 EQU 7393H
CHIP8_COMMAND_D_3P_END EQU 73A0H
CHIP8_COMMAND_2 EQU 73ACH
CHIP8_COMMAND_1 EQU 73B3H
CHIP8_COMMAND_B EQU 73BCH
R73CC EQU 73CCH
CHIP8_COMMAND_6 EQU 73CEH
CHIP8_COMMAND_7 EQU 73D1H
CHIP8_COMMAND_8 EQU 73D6H
R73DE EQU 73DEH
S73EA EQU 73EAH
CHIP8_COMMAND_C EQU 73F3H
CHIP8_COMMAND_A EQU 7405H
FIVEL_INTERRUPT_END EQU 740EH
FIVEL_INTERRUPT EQU 7415H
FIVEL_DISPLAY_9 EQU 7429H
FIVEL_DISPLAY_8 EQU 742CH
FIVEL_COUNTER0 EQU 7457H
FIVEL_NO_SHIFT EQU 7463H
FIVEL_NO_CTL EQU 7469H
FIVEL_KEY_PRESS EQU 7492H
R7493 EQU 7493H
R7499 EQU 7499H
FIVEL_NO_KEY_PRESS EQU 749DH
PATTERN_3x5 EQU 7544H
PATTERN_2x4 EQU 755CH
FOURL_INTERRUPT_END EQU 756CH
FOURL_INTERRUPT EQU 7573H
FOURL_DISPLAY_9 EQU 758AH
FOURL_DISPLAY_8 EQU 758DH
FOURL_COUNTER0 EQU 75B2H
FOURL_NO_SHIFT EQU 75BEH
FOURL_NO_CTL EQU 75C4H
FOURL_KEY_PRESS EQU 75EDH
R75EE EQU 75EEH
R75F4 EQU 75F4H
FOURL_NO_KEY_PRESS EQU 75F8H
ROM_KEY_MAP EQU 7640H
CHIP8_IDENTIFIER EQU 768BH
; Register Definitions:
R0 EQU 0
R1 EQU 1
R2 EQU 2
R3 EQU 3
R4 EQU 4
R5 EQU 5
R6 EQU 6
R7 EQU 7
R8 EQU 8
R9 EQU 9
RA EQU 10
RB EQU 11
RC EQU 12
RD EQU 13
RE EQU 14
RF EQU 15
; Start code segment
CHIP8_EXEC
DB 00H, 0DDH ; SYS 0DD - Chip 8 code to clear complete VIP2K video RAM
DB 12H, 00H ; JP 200 - Start Chip 8 user code at 0x8200
DB 00H
; F commands: Fx07, Fx0A, Fx15, Fx18, Fx1E, Fx29, Fx33, Fx55, Fx65
; Second byte is used for lower address (R4.0) which means a jump to 70aa so below code has a 'fixed location in the page
CHIP8_COMMAND_F
LDA R5 ; Get second byte Fxaa instruction
PLO R4 ; Store second byte in PC, R4.0.
; LD Vx, DT -> Vx = Delaytimer
CHIP8_Fx07
BR CHIP8_Fx07_Start
DB 00H
; LD Vx, K -> Vx = key, wait for keypress
CHIP8_Fx0A
LDI 0F9H
PLO R7 ; R7 = FFF9 i.e. keyboard code
R700D
LDN R7 ; Get keyboard code
SMI 7FH ; Loop if keyboard code != 7F, i.e. wait till all keys are 'up'
BNZ R700D ; this loop is needed otherwise Chip 8 code will detect multiple key presses at a time
BR KEY_UP
DB 00H
; LD DT, Vx -> Delaytimer = Vx
CHIP8_Fx15
BR CHIP8_Fx15_Start
DB 00H
; ST, Vx -> Soundtimer = Vx
CHIP8_Fx18
SEP R3 ; No sound, so routine is just returning to main loop
DB 00H
DB 00H
DB 64H ; Used for BCD conversion
DB 0AH ; Used for BCD conversion
DB 01H ; Used for BCD conversion
; ADD I, Vx -> I = I + Vx
CHIP8_Fx1E
SEX R6
GLO RA
ADD ; Add M(R6) to RA.0, i.e. I = I + Vx
PLO RA
BNF R7028 ; If DF we need to add 1 to RA.1
GHI RA
ADI 01H ; Add 1 to RA.1
PHI RA
R7028
SEP R3
; LD F, Vx -> Point I to 5 byte numeric sprite for value in Vx
CHIP8_Fx29
LDI 75H
PHI RA ; RA.1 = 75, 7500-75FF contains the VIP sprite table
LDN R6
ANI 0FH
PLO RA ; RA.0 = Vx (0-F)
LDN RA
PLO RA ; RA.0 = M(RA), lower byte of sprite location is stored on 7500-750F.
SEP R3
; LD B, Vx -> Store BCD value of Vx in [I], [I+1], [I+2]
CHIP8_Fx33
SEX R6
LDN R6 ; Get Vx
PHI RF ; RF.1 = Vx
GHI R4 ;7036: 94
PHI RE ; RE.1 = R3.1 = 70, RE should point to bytes on 701B where the BCD constants are stored
LDI 1BH
PLO RE ; RE = 701B
DEC RA ; RA = RA - 1 (i.e register I), as it should point to start value after INC A at start of loop
R703C
INC RA ; RA = RA + 1, next BCD digit
LDI 00H ;
STR RA ; Start with M(RA) = 0
R7040
LDN RE ; Get current BCD constant
SD ; Subtract constant from Vx
BNF NEXT_DIGIT ; If minus go to next digit
STR R6 ; Vx = Vx - BCD constant
LDN RA
ADI 01H
STR RA ; M(RA) = M(RA) + 1
BR R7040 ; Continue loop
NEXT_DIGIT
LDA RE ; Get current BCD constant and move RE to next constant
SHR
BNF R703C ; Continue until constant 1 (i.e. bit 0 = 1)
GHI RF
STR R6 ; Vx = saved Vx value
DEC RA
DEC RA ; Restore RA / I
SEP R3
DB 00H
; LD [I], Vx -> Store V0 .. Vx in [I] .. [I+x]
CHIP8_Fx55
DEC R2 ; R2 = R2 - 1 to have stack ready for a push
GLO R6
STR R2 ; Save Vx pointer on stack for comparison
LDI 0E0H
PLO R7 ; Point R7 to Vy, y=0 (FFE0)
S705B
LDN R7
STR RA ; M(RA) = I = Vy
GLO R7
XOR ; XOR Vy with Vx
INC R7 ; R7 = R7 + 1, i.e, R7 to next variable
INC RA ; RA = RA + 1
BNZ S705B ; Loop if more variables should be copied
INC R2 ; Set stack back
SEP R3
; LD Vx, [I] -> Read V0 .. Vx from [I] .. [I+x
CHIP8_Fx65
DEC R2 ; R2 = R2 - 1 to have stack ready for a push
GLO R6
STR R2 ; Save Vx pointer on stack for comparison
LDI 0E0H
PLO R7 ; Point R7 to Vy, y=0 (FFE0)
R706B
LDN RA
STR R7 ; Vy = M(RA) = I
GLO R7
XOR ; XOR Vy with Vx
INC R7 ; R7 = R7 + 1, i.e, R7 to next variable
INC RA ; RA = RA + 1
BNZ R706B ; Loop if more variables should be copied
INC R2 ; Set stack back
SEP R3
CTL_R
LOAD R3,CHIP8_START ; Restart Chip 8
SEP R3
CTL_K
LOAD R3,RESTORE_KEY_MAP
SEP R3 ; Restore keyboard mapping
; SE Vx , kk -> Skip next instruction if Vx == kk
CHIP8_COMMAND_3
LDA R5 ; Get value kk
S7084
SEX R6
XOR ; XOR kk with Vx
BNZ NO_SKIP ; Skip instrucion if Vx == kk
SKIP_INSTRUCTION
INC R5
INC R5 ; Chip 8 PC + 2 to skip instruction
NO_SKIP
SEP R3
; SNE Vx, kk -> Skip next instruction if Vx != kk
CHIP8_COMMAND_4
LDA R5 ; Get value kk
S708C
SEX R6
XOR ; XOR kk with Vx
BNZ SKIP_INSTRUCTION; Skip instrucion if Vx != kk
SEP R3
; SNE Vx, Vy -> Skip next instruction if Vx != Vy
CHIP8_COMMAND_9
LDA R5 ; R5 + 1 so it points to the next instrction
LDN R7 ; Get Vy
BR S708C ; Do the same action as CHIP8_COMMAND_4 but with Vy
CHIP8_COMMAND_5
LDA R5 ; R5 + 1 so it points to the next instrction
LDN R7 ; Get Vy
BR S7084 ; Do the same action as CHIP8_COMMAND_3 but with Vy
; E commands: Ex9E, ExA1
; Second byte is used for lower address (R4.0) which means a jump to 70aa so below code has a 'fixed location in the page
CHIP8_COMMAND_E
LDI 0F9H
PLO R7 ; R7 = FFF9, i.e. key code
BR R70A4 ; Continue on 70A4
BR CHIP8_Ex9E ; Branch as we only have 3 bytes here and we needed some more space
DB 00H
BR CHIP8_ExA1 ; Branch as we only have 3 bytes here and we needed some more space
DB 00H
R70A4
SEX R6
LDA R5 ; Get second byte
PLO R4 ; Store second byte in R4.0 which is the PC and as such exectue CHIP8_Ex9E or CHIP8_ExA1
; SKP Vx -> Skip next instruction if key Vx up
CHIP8_ExA1
LDN R7 ; Get key code
SM
BNZ SKIP_INSTRUCTION; Skip instruction if key pressed is not equal to Vx
SEP R3
; SKNP Vx -> Skip next instruction if key Vx down
CHIP8_Ex9E
LDN R7 ; Get key code
SM
BZ SKIP_INSTRUCTION; Skip instruction if key pressed is equal to Vx
SEP R3
; LD Vx, DT -> Vx = Delaytimer
CHIP8_Fx07_Start
LDI 0FAH
PLO R7 ; R7 = FFFA i.e. Delaytimer
LDN R7 ; Get timer value
STR R6 ; Vx = M(R6) = Delaytime
SEP R3
; LD DT, Vx -> Delaytimer = Vx
CHIP8_Fx15_Start
LDI 0FAH
PLO R7 ; R7 = FFFA i.e. Delaytimer
LDN R6
STR R7 ; FFFA = M(R6) = Delaytime
SEP R3
KEY_UP
LDN R7
SMI 7FH
BZ KEY_UP ; Loop if FFF9 == F7, i.e. wait until a key is pressed
LDN R7
ANI 80H
BNZ R70CE ; Exit routine if bit 7=1 (for all CTL keys) in that case the main Chip 8 loop will handle CTL function
LDN R7
SMI 10H
BDF R700D ; Loop back to start key detection routine if key code < 10, i.e. not a valid Chip 8 key (0-F)
LDN R7
STR R6 ; Vx = M(R6)
R70CE
SEP R3
DB 00H ;70CF: 00
DB 00H ;70D0: 00
DB 00H ;70D1: 00
DB 00H ;70D2: 00
DB 00H ;70D3: 00
DB 00H ;70D4: 00
DB 00H ;70D5: 00
DB 00H ;70D6: 00
DB 00H ;70D7: 00
DB 00H ;70D8: 00
DB 00H ;70D9: 00
DB 00H ;70DA: 00
DB 00H ;70DB: 00
DB 00H ;70DC: 00
; Same as CLS except this routine is called from the interpreter to make sure the complete video RAM is cleared
CHIP8_00DD
LDI 0FCH
LSKP ; Load FC to get RC to point to FCFC and as such clear E800-FCFC
; CLS -> Clear display
CHIP8_00E0
LDI 0ECH
PHI RC ; Load EC to get RC to point to ECEC and as such clear E800-ECEC
PLO RC ; RC.0 = RC.1 simplified to keep the code to fit in available space
R70E4
LDI 00H
STR RC ; M(RC) = 0
DEC RC ; RC = RC - 1
GHI RC
SMI 0E7H
BNZ R70E4 ; Loop if RC != E7xx
SEP R3
; RETURN -> Return from subroutine
CHIP8_00EE
LDA R2
PHI R5
LDA R2
PLO R5 ; Pull R5 from stack, R5 = Chip 8 PC
SEP R3
; DRW Vx, Vy, n -> Draw n byte sprite stored at [I] at Vx, Vy. Set VF = collision
; Drawing routine to draw a pattern of bytes where every bit will result in 2 pixels
; This routine is used for the small screen resolution, i.e. 2x4
CHIP8_COMMAND_D_2P
DEC R2 ; R2 = R2 - 1 to have stack ready for a push
LDN R6
ANI 3FH
STR R2 ; Stack = Vx, horizontal drawing position
ADD ;
STR R2 ; Stack = 2*Vx as we want two pixels wide so horizontal position is also doubled
ANI 07H
PHI RD ; RD.1 containts the number of bits left to shift later as byte contains 8 bits, i.e. 8 positions
LDN R2 ; Get 2xVx
SHR
SHR
SHR ; D = 2*Vx/8, so we can identify the 'byte' position in memory
ADI 0D5H
PLO RC
LDI 0E8H
PHI RC ; RC = horizontal screen location, we have added E8D5 as that is the memory location of top left of screen
; We don't use E800 so the viewable screen is in the 'middle'
LDN R7
ANI 1FH
PLO RD ; RD.0 = Vy, vertical drwaing position
SHL
PLO RE ; RE.0 = Vy * 2
LDI 76H
PHI RE ; RE.1 = 76, resulting in M(RE) containing the value to be added to get the horizontal poitions
; Table at 7600 contains 32 16 bit values low byte first followed by high for all 32 lines
SEX RE
GLO RC
ADD ; Add low byte
PLO RC
INC RE
GHI RC
ADC ; Add high byte + carry from low
PHI RC ; RC is drawing position in memory
LDA R5
ANI 0FH
PLO RD ; RD.0 = number of lines to be drawn
PLO R8 ; R8.0 = number of lines to be drawn
LDI 0EFH
PLO R6 ; R6 = VF for storing of collision flag
LDI 00H
STR R6 ; VF / collision flag = 0, i.e. no graphic collision detected as yet
R7123
GLO R8
BZ CHIP8_COMMAND_D_2P_END
; All lines drawn, finalize routine
DEC R8 ; Number of lines - 1
LOAD RE,PATTERN_2x4 ; RE = Pattern table
LDI 0F2H
PLO R7 ; R7 = FFF2, FFF0-FFF2 will contain 24 bit pattern based on byte to be drawn.
; Reason to use 24 bit is that every bit needs to be drawn as 2 pixels and could be shifted right max 8 pixels
SEX R7
LDI 00H
STXD
STXD
STR R7 ; FFF0-FFF2 = 0, pattern reset
LDA RA
PHI RF ; RF.1 = byte to be drawn (M(RA) or M(I), RA/I is incremented for next byte)
R7138
GHI RF
SHR ; DF = first bit, 1 means a pixels should be drawn
PHI RF ; RF is shifted right 1 bit so next time we take the next bit into DF
BDF CHIP8_COMMAND_D_2P_DRAW
INC RE
INC RE ; Increment pattern table
BR R714A
CHIP8_COMMAND_D_2P_DRAW
LDI 0F1H
PLO R7 ; R7 = FFF1
LDA RE ; Get second byte for 2 pixel pattern for current bit
OR
STXD ; OR result on pattern location
LDA RE ; Get first byte for 2 pixel pattern for current bit
OR
STR R7 ; OR result on pattern location
R714A
GLO RE
SMI 6CH ; Check if all bits are done (end of 2 pixel pattern table is xx6C)
BNZ R7138 ; if not done, loop back for next bit
LDI 00H
PLO RF ; RF.0 = 0, don't think this was needed, maybe we can remove it in a later version?
GHI RD
PLO RE ; RE.0 number of bits to shift to get the final horizontal position
R7154
GLO RE
BZ R7168 ; If RE.0 = 0 we are ready shifting
LDI 0F0H
PLO R7 ; R7 = FFF0
LDN R7
SHR
STR R7 ; Shift first byte one right
INC R7
LDN R7
SHRC
STR R7 ; Shift second byte one right
INC R7
LDN R7
SHRC
STR R7 ; Shift third byte one right
DEC RE ; Decrement number of bits to be shifted
BR R7154 ; Loop back
R7168
LDI 0F0H
PLO R7 ; R7 = FFF0, which now contains the 24 bit pattern to be drawn on the first line
SEX RC
LDN R7
AND ; AND first byte pattern to be drawn with video memory
BZ R7173 ; If zero we have no collision with what is already on the screen
LDI 01H
STR R6 ; If not zero set the collision flag
R7173
LDA R7
XOR
STR RC ; XOR first byte pattern to be drawn with video memory and store it back in video memory
INC RC ; RC = next screen position
LDN R7
AND ; AND second byte pattern to be drawn with video memory
BZ R717E ; If zero we have no collision with what is already on the screen
LDI 01H
STR R6 ; If not zero set the collision flag
R717E
LDA R7
XOR
STR RC ; XOR second byte pattern to be drawn with video memory and store it back in video memory
INC RC ; RC = next screen position
LDN R7
AND ; AND second byte pattern to be drawn with video memory
BZ R7189 ; If zero we have no collision with what is already on the screen
LDI 01H
STR R6 ; If not zero set the collision flag
R7189
LDA R7
XOR
STR RC ; XOR second byte pattern to be drawn with video memory and store it back in video memory
GLO RC
ADI 18H
PLO RC
GHI RC
ADCI 00H
PHI RC ; RC = next line (RC + 0x18)
BR R7123 ; Loop back
CHIP8_COMMAND_D_2P_END
SEX R2
GLO RD
STR R2 ; Stack = number of lines dranw
GLO RA
SM
PLO RA
GHI RA
SMBI 00H
PHI RA ; RA = RA - number of lines drawn, i.e. reset RA to what it was at entry
INC R2 ; Set stack back
SEP R3
CTL_S
LDI 01H
STR R2 ; Stack = 1 which will be used to indicate 2x4 screen resolution
STORE_SCREEN_RES
LOAD R3,CHIP8_START ; Set R3 to start of Chip 8 interpreter
LDI 0A9H
PLO R6 ; R6 = FFA9, screen resolution location
LDX
STR R6 ; Store resolution on FFA9 (i.e 1 0r 0)
SEP R3 ; Restart Chip 8
CTL_L
LDI 00H
STR R2 ; Stack = 1 which will be used to indicate 2x4 screen resolution
BR STORE_SCREEN_RES
CHIP8_COMMAND_TABLE
DW CHIP8_COMMAND_1
DW CHIP8_COMMAND_2
DW CHIP8_COMMAND_3
DW CHIP8_COMMAND_4
DW CHIP8_COMMAND_5
DW CHIP8_COMMAND_6
DW CHIP8_COMMAND_7
DW CHIP8_COMMAND_8
DW CHIP8_COMMAND_9
DW CHIP8_COMMAND_A
DW CHIP8_COMMAND_B
DW CHIP8_COMMAND_C
DW CHIP8_COMMAND_D_3P
DW CHIP8_COMMAND_E
DW CHIP8_COMMAND_F
DB 00H ;71D4: 00
DB 00H ;71D5: 00
DB 00H ;71D6: 00
DB 00H ;71D7: 00
DB 00H ;71D8: 00
DB 00H ;71D9: 00
DB 00H ;71DA: 00
DB 00H ;71DB: 00
DB 00H ;71DC: 00
DB 00H ;71DD: 00
DB 00H ;71DE: 00
DB 00H ;71DF: 00
CHIP8_START
SEX R3
DIS
DB 23H ; Disable interrupt, PC = 3, X = 2
INP 7 ; TV OFF
LOAD R7,CHIP8_IDENTIFIER
LDI 0FFH
PHI R6
LDI 0A0H
PLO R6 ; R6 = FFA0, FFA0 will contain CHIP8 identifier (CHIP8) if system RAM page (FFxx) is initialized
R71F0
LDA R6
STR R2
LDA R7
SM
PLO RC ; RC.0 is difference between FFA0 and the CHIP8 identifier
BNZ R71FC ; if not zero we have not initialized system RAM page
GLO R6
SMI 0A5H
BNZ R71F0 ; Idenifier is checked if we reach FFA5, 5 bytes checked
R71FC
LOAD R7,CHIP8_COMMAND_TABLE
LDI 0B2H
PLO R6
R7205
LDA R7
STR R6
INC R6
GLO R6
SMI 0D0H
BNZ R7205 ; Copy CHIP8 command table to FFB2, we need to have it RAM so we can have two differen D command locations
; depending on screen resolution
GLO RC
BZ R721A ; Skip ressetting of FFA8 (speed) and FFA9 (resolution) if RAM was already initialized
LDI 0A8H
PLO R6
LDI 00H
STR R6
INC R6
STR R6 ; Reset speed and resolution
BR RES_3x5 ; If RAM was not initialized start in 3x5 resolution
R721A
LDI 0A9H
PLO R6
LDN R6
BZ RES_3x5 ; If FFA9 = 0 we need to set 3x5 resolution
RES_2x4
LOAD R1,FOURL_INTERRUPT
LOAD R7,CHIP8_COMMAND_D_2P
BR R723A
RES_3x5
LOAD R1,FIVEL_INTERRUPT
LOAD R7,CHIP8_COMMAND_D_3P
R723A
LDI 0CAH
PLO R6 ; R6 = location COMMAND D
GHI R7
STR R6
INC R6
GLO R7
STR R6 ; Store correct COMMAND D start address which was loaded in R7 above, depending on resolution mode
LOAD R5,CHIP8_EXEC ; R5 = CHIP 8 PC = Start address 7000 which contains a clear video RAM and a jump to 200/ 8200.
INP 6 ; TV ON
SEX R3
RET
DB 23H ; Enable interrupt, PC = 3, X = 2
GLO RC
BZ R726D ; IF RC = 0 System RAM was initialized so skip keyboard map restore
RESTORE_KEY_MAP
LDI 00H
PLO R6 ; R6 = FF00 start of RAM keyboard map
LOAD R7,ROM_KEY_MAP ; R7 = ROM keyboard map
R7258
LDA R7
BZ REPEAT_BYTES ; If ROM keyboard map is zero branch to go to repeat bytes
STR R6 ; RAM keyboard MAP = ROM keyboard MAP
INC R6
BR R7258 ; Next byte
REPEAT_BYTES
LDA R7
BZ R726D ; If ROM keyboard map has 2 zero's in a row we are 'done'
PLO RC ; RC.0 = number of bytes to be repeated
R7263
LDN R7
STR R6 ; RAM keyboard MAP = ROM keyboard MAP
INC R6 ; Next RAM byte
DEC RC ; number of bytes -1
GLO RC
BNZ R7263 ; If more bytes loop
INC R7 ; Next ROM byte
BR R7258 ; Loop back
R726D
LDI 0A8H
PLO R6 ; R6 = FFA8, i.e. speed delay
LDN R6
PHI R8 ; R8.1 = Speed delay
LDI 0F9H
PLO R6 ; R6 = FFF9, i.e keyboard code
LDI 7FH
STR R6 ; Reset keyboard code as 'no key pressed'
; This loop is fetching the CHIP8 instruction and executing it by a SEP R4, instruction returns with a SEP R3
MAIN_CHIP8_LOOP
SEX R2
GHI R6
PHI R7 ; R7.1 = FF
LDI 0F9H
PLO R6 ; R6 = FFF9, i.e keyboard code
LDN R6
PLO R6 ; Get keycode
ANI 80H
BZ S729E ; If bit 7 = 0 no CTL key is pressed, continue with main loop
GLO R6 ;
SMI 0F0H
BM SPEED_KEY ; If key code between 80-F0 we have a speed key
GLO R6
SHL
ANI 0FH
ORI 0F0H
PLO RC
LDI 74H
PHI RC ; RC = CTL jump table
LDN RC ; If jump table contains zero, just assume speed key
BNZ CTL_KEY
SPEED_KEY
LDI 0A8H
PLO R7
GLO R6
ANI 7FH
STR R7 ; M(R7) = new speed factor
PHI R8 ; R8.1 = new speed factor
S729E
LDI 0FFH
PHI RC ; RC.1 = FF
LDA R5
PLO RF ; RF.0 = first instruction byte
SHR
SHR
SHR
SHR ; D = CHIP8 COMMAND
BZ CHIP8_COMMAND_0
PLO R6 ; R6.0 = CHIP8 COMMAND
SMI 0DH
BZ R72B6 ; If COMMAND D skip delay for speed
GHI R8
BZ R72B6 ; If R8 (speed delay) is zero continue loop
PLO RC
SPEED_DELAY
DEC RC
GLO RC
BNZ SPEED_DELAY ; WAIT according to speed delay
R72B6
GLO R6
SHL
ADI 0B0H
PLO RC ; RC = CHIP8 COMMAND table pointing to current instruction address (FFB2-FFCF)
GLO RF
ANI 0FH
ORI 0E0H
PLO R6 ; R6 = Vx (FFEx, i.e FFE0-FFEF)
LDN R5
SHR
SHR
SHR
SHR
ORI 0E0H
PLO R7 ; R6 = Vy (FFEy, i.e FFE0-FFEF)
CTL_KEY
LDA RC
PHI R4
LDN RC
J72CC
PLO R4 ; R4 = Instruction address (or CTL_KEY address)
SEP R4 ; Execute!
BR MAIN_CHIP8_LOOP
; SYS aaa -> Call CDP1802 code at aaa
CHIP8_COMMAND_0
GLO RF
ANI 0FH
BZ CHIP8_COMMAND_00
ORI 80H ; SYS 1aa to SYS Faa will branch to 8aaa, as CHIP8 user program is stored at 8200 instead of 200
BR R72DB
CHIP8_COMMAND_00
ADI 70H ; SYS 00aa, the original 00aa routines are located on 70aa in VIP2K Chip 8
R72DB
PHI R4
LDA R5
BR J72CC ; R4 = aaa, with additional changes as above. Then 'Execute'
CTL_Q
SEX R4
DIS
DB 04H ; Disable interrupt, PC = 4, X = 0
INP 7 ; TV OFF
LDI 00H
PHI R0
PLO R0 ; R0 = 0
SEP R0 ; Restart VIP2K Monitor
; DRW Vx, Vy, n -> Draw n byte sprite stored at [I] at Vx, Vy. Set VF = collision
; Drawing routine to draw a pattern of bytes where every bit will result in 3 pixels
; This routine is used for the small screen resolution, i.e. 2x4
CHIP8_COMMAND_D_3P
DEC R2 ; R2 = R2 - 1 to have stack ready for a push
LDN R6
ANI 3FH
STR R2 ; Stack = Vx, horizontal drawing position
ADD
ADD
STR R2 ; Stack = 3*Vx as we want three pixels wide so horizontal position is also tripled
ANI 07H
PHI RD ; RD.1 containts the number of bits left to shift later as byte contains 8 bits, i.e. 8 positions
LDN R2 ; Get 3xVx
SHR
SHR
SHR ; D = 3*Vx/8, so we can identify the 'byte' position in memory
ADI 4FH
PLO RC
LDI 0E8H
PHI RC ; RC = horizontal screen location, we have added E84F as that is the memory location of top left of screen
; We don't use E800 so the viewable screen is in the 'middle'
LDN R7
ANI 1FH
PLO RD ; RD.0 = Vy, vertical drwaing position
SHL
PLO RE ; RE.0 = Vy * 2
LDI 76H
PHI RE ; RE.1 = 76, resulting in M(RE) containing the value to be added to get the horizontal poitions
; Table at 7600 contains 32 16 bit values low byte first followed by high for all 32 lines
SEX RE
GLO RC
ADD ; Add low byte
PLO RC
INC RE
GHI RC
ADC ; Add high byte + carry from low
PHI RC ; RC is drawing position in memory
LDA R5
ANI 0FH
PLO RD ; RD.0 = number of lines to be drawn
PLO R8 ; R8.0 = number of lines to be drawn
LDI 0EFH
PLO R6 ; R6 = VF for storing of collision flag
LDI 00H
STR R6 ; VF / collision flag = 0, i.e. no graphic collision detected as yet
J7319
GLO R8 ;7319: 88
BZ CHIP8_COMMAND_D_3P_END
; All lines drawn, finalize routine
DEC R8 ; Number of lines - 1
LOAD RE,PATTERN_3x5 ; RE = Pattern table
LDI 0F3H
PLO R7 ; R7 = FFF3, FFF0-FFF3 will contain 32 bit pattern based on byte to be drawn.
; Reason to use 32 bit is that every bit needs to be drawn as 3 pixels and could be shifted right max 8 pixels
SEX R7
LDI 00H
STXD
STXD
STXD
STR R7 ; FFF0-FFF3 = 0, pattern reset
LDA RA
PHI RF ; RF.1 = byte to be drawn (M(RA) or M(I), RA/I is incremented for next byte)
R732F
GHI RF
SHR ; DF = first bit, 1 means a pixels should be drawn
PHI RF ; RF is shifted right 1 bit so next time we take the next bit into DF
BDF CHIP8_COMMAND_D_3P_DRAW
INC RE
INC RE
INC RE ; Increment pattern table
BR R7345 ;7337: 30 45
CHIP8_COMMAND_D_3P_DRAW
LDI 0F2H
PLO R7 ; R7 = FFF2
LDA RE ; Get last byte for 3 pixel pattern for current bit
OR
STXD ; OR result on pattern location
LDA RE ; Get second byte for 3 pixel pattern for current bit
OR
STXD ; OR result on pattern location
LDA RE ; Get first byte for 3 pixel pattern for current bit
OR
STR R7 ; OR result on pattern location
R7345
GLO RE
SMI 5CH ; Check if all bits are done (end of 3 pixel pattern table is xx5C)
BNZ R732F ; if not done, loop back for next bit
LDI 00H
PLO RF ; RF.0 = 0, don't think this was needed, maybe we can remove it in a later version?
GHI RD
PLO RE ; RE.0 number of bits to shift to get the final horizontal position
R734F
GLO RE
BZ S7367 ; If RE.0 = 0 we are ready shifting
LDI 0F0H
PLO R7 ; R7 = FFF0
LDN R7
SHR
STR R7 ; Shift first byte one right
INC R7
LDN R7
SHRC
STR R7 ; Shift second byte one right
INC R7
LDN R7
SHRC
STR R7 ; Shift third byte one right
INC R7
LDN R7
SHRC
STR R7 ; Shift fourth byte one right
DEC RE ; Decrement number of bits to be shifted
BR R734F ; Loop back
S7367
LDI 0F0H
PLO R7 ; R7 = FFF0, which now contains the 24 bit pattern to be drawn on the first line
SEX RC
LDN R7
AND ; AND first byte pattern to be drawn with video memory
BZ R7372 ; If zero we have no collision with what is already on the screen
LDI 01H
STR R6 ; If not zero set the collision flag
R7372
LDA R7
XOR
STR RC ; XOR first byte pattern to be drawn with video memory and store it back in video memory
INC RC ; RC = next screen position
LDN R7
AND ; AND second byte pattern to be drawn with video memory
BZ R737D ; If zero we have no collision with what is already on the screen
LDI 01H
STR R6 ; If not zero set the collision flag
R737D
LDA R7
XOR
STR RC ; XOR second byte pattern to be drawn with video memory and store it back in video memory
INC RC ; RC = next screen position
LDN R7
AND ; AND third byte pattern to be drawn with video memory
BZ R7388 ; If zero we have no collision with what is already on the screen
LDI 01H
STR R6 ; If not zero set the collision flag
R7388
LDA R7
XOR
STR RC ; XOR third byte pattern to be drawn with video memory and store it back in video memory
INC RC ; RC = next screen position
LDN R7
AND ; AND fourth byte pattern to be drawn with video memory
BZ R7393 ; If zero we have no collision with what is already on the screen
LDI 01H
STR R6 ; If not zero set the collision flag
R7393
LDA R7
XOR
STR RC ; XOR fourth byte pattern to be drawn with video memory and store it back in video memory
GLO RC
ADI 17H
PLO RC
GHI RC
ADCI 00H
PHI RC ; RC = next line (RC + 0x17)
BR J7319 ; Loop back
CHIP8_COMMAND_D_3P_END
SEX R2
GLO RD
STR R2 ; Stack = number of lines dranw
GLO RA
SM
PLO RA
GHI RA
SMBI 00H
PHI RA ; RA = RA - number of lines drawn, i.e. reset RA to what it was at entry
INC R2 ; Set stack back
SEP R3
; CALL aaa -> Call subroutine at aaa
CHIP8_COMMAND_2
INC R5
GLO R5
DEC R2
STXD
GHI R5
STR R2
DEC R5 ; Store CHIP 8 PC (R5) on stack, which will be restored at RETURN
; JP aaa -> Jump to address aaa
CHIP8_COMMAND_1
LDA R5
PLO R5
GLO R6
ANI 0FH
ORI 80H
PHI R5 ; R5 = CHIP 8 PC = aaa + 8000, 8000 is added as Chip 8 code on VIP2K is stored at 8200 instead of 200
SEP R3
; JP V0, aaa -> Jump to address aaa + V0
CHIP8_COMMAND_B
LDI 0E0H
PLO R7 ; R7 = V0
SEX R7
LDA R5
ADD ; Add V0
PLO R5
GLO R6
ANI 0FH
ORI 80H
BNF R73CC
ADI 01H
R73CC
PHI R5 ; R5 = CHIP 8 PC = aaa + V0 8000, 8000 is added as Chip 8 code on VIP2K is stored at 8200 instead of 200
SEP R3 ;73CD: D3
; LD Vx, kk -> Vx = kk
CHIP8_COMMAND_6
LDA R5 ; Get kk
STR R6 ; M(R6) = Vx = kk
SEP R3
; ADD Vx, kk -> Vx = Vx + kk
CHIP8_COMMAND_7
LDA R5 ; Get kk
SEX R6
ADD
STR R6 ; M(R6) = Vx = Vx + kk
SEP R3
; LD, OR, AND, XOR etc (like Fx 1802 instrucions) Vx, Vy
CHIP8_COMMAND_8
LDA R5 ; Get second instuction byte
ANI 0FH
BNZ R73DE ; Branch for all instructions except 8xy0
LDN R7
STR R6 ; M(R6) = Vx = Vy
SEP R3
R73DE
PLO RF ; RF.0 is command type (last nibble)
DEC R2
LDI 0D4H ;
STXD ; Store SEP R4 on stack
GLO RF
ORI 0F0H
STR R2 ; Store command type as 1802 instruction on stack Fz
SEX R6 ; Stack = Vx
LDN R7 ; D = Vy
SEP R2 ; Execute 1802 instruction Fz and then return with SEP R4 to S73EA
S73EA
STR R6
LDI 0EFH
PLO R6 ; R6 = VF, carry flag
LDI 00H
SHLC
STR R6 ; Store carry
SEP R3 ;73F2: D3
; RND Vx , kk -> Vx = random AND kk
CHIP8_COMMAND_C
INC R9 ; R9 is the random number generator, incremented at every interrupt and on command C,
; just in case no interrupt happend.
GLO R9
PLO RE ; RE.0 is random number
GHI R4
PHI RE ; RE.1 = R4.1 = 73, RE is a random location in page 73
GHI R9
SEX RE
ADD ; Add value on RE to high byte of random number
STR R6 ; Store in Vx
SHRC ; Divide by 2
SEX R6
ADD ; Add Vx
PHI R9 ; Save in R9.1
STR R6 ; Store in Vx
LDA R5
AND ; AND with kk
STR R6 ; Store in Vx
SEP R3
; LD I, aaa -> I = aaa
CHIP8_COMMAND_A
LDA R5
PLO RA
GLO R6
ANI 0FH
ORI 80H
PHI RA ; RA = aaa + 8000, 8000 is added as Chip 8 code on VIP2K is stored at 8200 instead of 200
SEP R3
FIVEL_INTERRUPT_END
INC R2
LDA R2
PLO R6 ; Pull R6.0 from stack
LDA R2
SHL
LDA R2
RET
FIVEL_INTERRUPT
DEC R2
SAV
; --- DMA burst for first 'single' display line, should be empty (from video RAM EBF6-EC0F)
DEC R2
STXD
SHRC
; --- DMA burst for second 'single' display line, should be empty (from video RAM EC10-EC29)
STXD
INC R9 ; Increment R9 which is used in Chip 8 for random number generation
GLO R6
; --- DMA burst for third 'single' display line, should be empty (from video RAM EC2A-EC43)
STXD ; Push R6.0 to stack
LDI 09H
PLO R6 ; R6.0 = 9 line counter (9 lines until R0.1 carry)
; --- DMA burst for fourth 'single' display line, should be empty (from video RAM EC44-EC5D)
LDI 0E8H ;
PHI RB ; RB.1 = start of video RAM
IDL ; re-sync DMAs, just in case....
; --- DMA burst for fifth 'single' display line, should be empty (from video RAM EC5E-EC77)
PHI R0 ; R0.1 = start of video RAM
LDI 00H
PLO R0 ; R0.0 = start of video RAM
; --- DMA burst for display line 1, same line will be repeated 5 times
FIVEL_DISPLAY_9
PLO RB ; RB.0 = R0.0 so it can be used to reset R0.0 later
PLO R0 ; Reset R0.0
DEC R6 ; decrement line count
; --- DMA burst for display line 2
FIVEL_DISPLAY_8
PLO R0 ; Reset R0.0
GHI R0
PHI RB ; RB.1 = R0.1 so it can be used to reset R0.1 later
; --- DMA burst for display line 3
GLO RB ; D was equal to R0.1 so we have to fetch RB.0
PLO R0 ; Reset R0.0
PLO R0 ; dummy / NOP
; --- DMA burst for display line 4
PLO R0 ; Reset R0.0
GLO R6 ; Get line count
SMI 01H ; set DF if last line
; --- DMA burst for display line 5
GLO R0
PLO RB ; RB.0 = R0.0 so it can be used for line 10 if needed
BPZ FIVEL_DISPLAY_9 ; Loop for 9 lines
; -- DMA burst for display line 1 - 10th line
FIVEL_DISPLAY_10
PLO R0 ; Reset R0.0
GHI RB
PHI R0 ; Reset R0.1
; -- DMA burst for display line 2 - 10th line
PHI R0 ; Reset R0.1
GLO RB
PLO R0 ; Reset R0.0
; -- DMA burst for display line 3 - 10th line
PLO R0 ; Reset R0.0
GHI RB
PHI R0 ; Reset R0.1
; -- DMA burst for display line 4 - 10th line
PHI R0 ; Reset R0.1
GLO RB
PLO R0 ; Reset R0.0
; -- DMA burst for display line 5 - 10th line
LDI 08H ;
PLO R6 ; R6.0 = 8 line counter
; 9 lines until R0.1 carry but load 8 as we skip the first DEC R6 as we branch to FIVEL_DISPLAY_8
GLO R0
; -- DMA burst for display line 1 - 11th line
PLO RB ; RB.0 = R0.0 so it can be used to reset R0.0 later
PLO R0 ; Reset R0.0
B1 FIVEL_DISPLAY_8 ; Reason to do this additional line here is because we need 1 additional instruction (B1)
; to branch back which makes us skip one DEC R9.
LDI 0FAH
PLO R6 ; R6 = FFFA, R6.1 is always FF in Chip 8
LDN R6 ; D = countdown counter
BZ FIVEL_COUNTER0 ; Do nothing if counter = 0
SMI 01H
STR R6 ; Countdown counter - 1
FIVEL_COUNTER0
DEC R6 ; R6 = FFF9, keyboard code
LDI 0FFH
PHI RB
LDI 00H
PLO RB ; RB = Keyboard mapping table, FF00
BN3 FIVEL_NO_SHIFT
ADI 50H
PLO RB ; RB = SHIFT area keyboard mapping table
FIVEL_NO_SHIFT
BN2 FIVEL_NO_CTL
GLO RB
ADI 28H
PLO RB ; RB = CTL area keyboard mapping table
FIVEL_NO_CTL
INP 1
SMI 0FFH
BNZ FIVEL_KEY_PRESS
GLO RB
ADI 08H
PLO RB ; RB = Keycol 1
INP 2
SMI 0FFH
BNZ FIVEL_KEY_PRESS
GLO RB
ADI 08H
PLO RB ; RB = Keycol 2
INP 3
SMI 0FFH
BNZ FIVEL_KEY_PRESS
GLO RB
ADI 08H
PLO RB ; RB = Keycol 3
INP 4
SMI 0FFH
BNZ FIVEL_KEY_PRESS
GLO RB
ADI 08H
PLO RB ; RB = Keycol 4
INP 5
SMI 0FFH
BZ FIVEL_NO_KEY_PRESS
FIVEL_KEY_PRESS
LDX
R7493
SHL
BNF R7499
INC RB
BR R7493
R7499
LDN RB ; Get keyboard code
STR R6 ; Store keyboard code on FFF9
BR FIVEL_INTERRUPT_END
FIVEL_NO_KEY_PRESS
LDI 7FH
STR R6 ; 'Clear' keyboard code on FFF9
BR FIVEL_INTERRUPT_END
DB 00H ;74A2: 00
DB 00H ;74A3: 00
DB 00H ;74A4: 00
DB 00H ;74A5: 00
DB 00H ;74A6: 00
DB 00H ;74A7: 00
DB 00H ;74A8: 00
DB 00H ;74A9: 00
DB 00H ;74AA: 00
DB 00H ;74AB: 00
DB 00H ;74AC: 00
DB 00H ;74AD: 00
DB 00H ;74AE: 00
DB 00H ;74AF: 00
DB 00H ;74B0: 00
DB 00H ;74B1: 00
DB 00H ;74B2: 00
DB 00H ;74B3: 00
DB 00H ;74B4: 00
DB 00H ;74B5: 00
DB 00H ;74B6: 00
DB 00H ;74B7: 00
DB 00H ;74B8: 00
DB 00H ;74B9: 00
DB 00H ;74BA: 00
DB 00H ;74BB: 00
DB 00H ;74BC: 00
DB 00H ;74BD: 00
DB 00H ;74BE: 00
DB 00H ;74BF: 00
DB 00H ;74C0: 00
DB 00H ;74C1: 00
DB 00H ;74C2: 00
DB 00H ;74C3: 00
DB 00H ;74C4: 00
DB 00H ;74C5: 00
DB 00H ;74C6: 00
DB 00H ;74C7: 00
DB 00H ;74C8: 00
DB 00H ;74C9: 00
DB 00H ;74CA: 00
DB 00H ;74CB: 00
DB 00H ;74CC: 00
DB 00H ;74CD: 00
DB 00H ;74CE: 00
DB 00H ;74CF: 00
DB 00H ;74D0: 00
DB 00H ;74D1: 00
DB 00H ;74D2: 00
DB 00H ;74D3: 00
DB 00H ;74D4: 00
DB 00H ;74D5: 00
DB 00H ;74D6: 00
DB 00H ;74D7: 00
DB 00H ;74D8: 00
DB 00H ;74D9: 00
DB 00H ;74DA: 00
DB 00H ;74DB: 00
DB 00H ;74DC: 00
DB 00H ;74DD: 00
DB 00H ;74DE: 00
DB 00H ;74DF: 00
DB 00H ;74E0: 00
DB 00H ;74E1: 00
DB 00H ;74E2: 00
DB 00H ;74E3: 00
DB 00H ;74E4: 00
DB 00H ;74E5: 00
DB 00H ;74E6: 00
DB 00H ;74E7: 00
DB 00H ;74E8: 00
DB 00H ;74E9: 00
DB 00H ;74EA: 00
DB 00H ;74EB: 00
DB 00H ;74EC: 00
DB 00H ;74ED: 00
DB 00H ;74EE: 00
DB 00H ;74EF: 00
; Branch table for Keyboard codes F0-F4 @ 74F0
DW CTL_R ; Keyboard code F0 - CTL R
DW CTL_Q ; Keyboard code F1 - CTL Q
DW CTL_K ; Keyboard code F2 - CTL K
DW CTL_L ; Keyboard code F3 - CTL L
DW CTL_S ; Keyboard code F4 - CTL S
DB 00H ;74FA: 00
DB 00H ;74FB: 00
DB 00H ;74FC: 00
DB 00H ;74FD: 00
DB 00H ;74FE: 00
DB 00H ;74FF: 00
; Chip 8 character table pointers @ 7500
DB 30H ; 0 -> 7530
DB 39H ; 1 -> 7539
DB 22H ; 2 -> 7522
DB 2AH ; 3 -> 752A
DB 3EH ; 4 -> 753E
DB 20H ; 5 -> 7520
DB 24H ; 6 -> 7524
DB 34H ; 7 -> 7534
DB 26H ; 8 -> 7526
DB 28H ; 9 -> 7528
DB 2EH ; A -> 752E
DB 18H ; B -> 7518
DB 14H ; C -> 7514
DB 1CH ; D -> 751C
DB 10H ; E -> 7510
DB 12H ; F -> 7512
; Chip 8 charcater table @ 7510
DB 0F0H ;7510: F0
DB 80H ;7511: 80
DB 0F0H ;7512: F0
DB 80H ;7513: 80
DB 0F0H ;7514: F0
DB 80H ;7515: 80
DB 80H ;7516: 80
DB 80H ;7517: 80
DB 0F0H ;7518: F0
DB 50H ;7519: 50
DB 70H ;751A: 70
DB 50H ;751B: 50
DB 0F0H ;751C: F0
DB 50H ;751D: 50
DB 50H ;751E: 50
DB 50H ;751F: 50
DB 0F0H ;7520: F0
DB 80H ;7521: 80
DB 0F0H ;7522: F0
DB 10H ;7523: 10
DB 0F0H ;7524: F0
DB 80H ;7525: 80
DB 0F0H ;7526: F0
DB 90H ;7527: 90
DB 0F0H ;7528: F0
DB 90H ;7529: 90
DB 0F0H ;752A: F0
DB 10H ;752B: 10
DB 0F0H ;752C: F0
DB 10H ;752D: 10
DB 0F0H ;752E: F0
DB 90H ;752F: 90
DB 0F0H ;7530: F0
DB 90H ;7531: 90
DB 90H ;7532: 90
DB 90H ;7533: 90
DB 0F0H ;7534: F0
DB 10H ;7535: 10
DB 10H ;7536: 10
DB 10H ;7537: 10
DB 10H ;7538: 10
DB 60H ;7539: 60
DB 20H ;753A: 20
DB 20H ;753B: 20
DB 20H ;753C: 20
DB 70H ;753D: 70
DB 0A0H ;753E: A0
DB 0A0H ;753F: A0
DB 0F0H ;7540: F0
DB 20H ;7541: 20
DB 20H ;7542: 20
DB 00H ;7543: 00
; Pattern table used to convert 8 bit pattern to 3 pixels per bit
PATTERN_3x5
DB 07H ;7544: 07
DB 00H ;7545: 00
DB 00H ;7546: 00
DB 38H ;7547: 38
DB 00H ;7548: 00
DB 00H ;7549: 00
DB 0C0H ;754A: C0
DB 01H ;754B: 01
DB 00H ;754C: 00
DB 00H ;754D: 00
DB 0EH ;754E: 0E
DB 00H ;754F: 00
DB 00H ;7550: 00
DB 70H ;7551: 70
DB 00H ;7552: 00
DB 00H ;7553: 00
DB 80H ;7554: 80
DB 03H ;7555: 03
DB 00H ;7556: 00
DB 00H ;7557: 00
DB 1CH ;7558: 1C
DB 00H ;7559: 00
DB 00H ;755A: 00
DB 0E0H ;755B: E0
; Pattern table used to convert 8 bit pattern to 2 pixels per bit
PATTERN_2x4
DB 03H ;755C: 03
DB 00H ;755D: 00
DB 0CH ;755E: 0C
DB 00H ;755F: 00
DB 30H ;7560: 30
DB 00H ;7561: 00
DB 0C0H ;7562: C0
DB 00H ;7563: 00
DB 00H ;7564: 00
DB 03H ;7565: 03
DB 00H ;7566: 00
DB 0CH ;7567: 0C
DB 00H ;7568: 00
DB 30H ;7569: 30
DB 00H ;756A: 00
DB 0C0H ;756B: C0
FOURL_INTERRUPT_END
INC R2
LDA R2
PLO R6 ; Pull R6.0 from stack
LDA R2
SHL
LDA R2
RET
FOURL_INTERRUPT
DEC R2
SAV
; --- DMA burst for first 'single' display line, should be empty (from video RAM ECFA-ED13)
DEC R2
STXD
SHRC
; --- DMA burst for second 'single' display line, should be empty (from video RAM ED14-ED2D)
STXD
INC R9 ; Increment R9 which is used in Chip 8 for random number generation
GLO R6
; --- DMA burst for third 'single' display line, should be empty (from video RAM ED2E-ED47)
STXD ; Push R6.0 to stack
LDI 09H
PLO R6 ; R6.0 = 9 line counter (9 lines until R0.1 carry)
; --- DMA burst for fourth 'single' display line, should be empty (from video RAM ED48-ED61)
LDI 00H
PLO RB ; RB.0 = 0 so it can be used to reset R0.0 later
LDI 0E8H
; --- DMA burst for fifth 'single' display line, should be empty (from video RAM ED62-ED7B)
PHI RB ; RB.1 = start of video RAM
IDL ; re-sync DMAs
; --- DMA burst for fifth 'single' display line, should be empty (from video RAM ED7C-ED95)
PHI R0 ; R0.1 = start of video RAM
LDI 00H
PLO R0 ; R0.0 = start of video RAM
; --- DMA burst for display line 1, same line will be repeated 4 times
FOURL_DISPLAY_9
PLO R0 ; Reset R0.0
DEC R6 ; decrement line count
GHI R0
; --- DMA burst for display line 2
FOURL_DISPLAY_8
PHI RB ; RB.1 = R0.1 so it can be used to reset R0.1 later
GLO RB
PLO R0 ; Reset R0.0
; --- DMA burst for display line 3
PLO R0 ; Reset R0.0
GLO R6 ; Get line count
SMI 01H ; set DF if last line
; --- DMA burst for display line 4
GLO R0
PLO RB ; RB.0 = R0.0 so it can be used for line 10 if needed
BPZ FOURL_DISPLAY_9 ; Loop for 9 lines
; -- DMA burst for display line 1 - 10th line
FOURL_DISPLAY_10
PLO R0 ; Reset R0.0
GHI RB
PHI R0 ; Reset R0.1
; -- DMA burst for display line 2 - 10th line
PHI R0 ; Reset R0.1
GLO RB
PLO R0 ; Reset R0.0
; -- DMA burst for display line 3 - 10th line
PLO R0 ; Reset R0.0
GHI RB
PHI R0 ; Reset R0.1
; -- DMA burst for display line 4 - 10th line
LDI 08H
PLO R6 ; R6.0 = 8 line counter
GLO R0
; -- DMA burst for display line 1 - 11th line
PLO RB ; RB.0 = R0.0 so it can be used to reset R0.0 later
PLO R0 ; Reset R0.0
B1 FOURL_DISPLAY_8 ; Reason to do this additional line here is because we need 1 additional instruction (B1)
; to branch back which makes us skip one DEC R9.
LDI 0FAH
PLO R6 ; R6 = FFFA, R6.1 is always FF in Chip 8
LDN R6 ; D = countdown counter
BZ FOURL_COUNTER0 ; Do nothing if counter = 0
SMI 01H
STR R6 ; Countdown counter - 1
FOURL_COUNTER0
DEC R6 ; R6 = FFF9, keyboard code
LDI 0FFH
PHI RB
LDI 00H
PLO RB ; RB = Keyboard mapping table, FF00
BN3 FOURL_NO_SHIFT
ADI 50H
PLO RB ; RB = SHIFT area keyboard mapping table
FOURL_NO_SHIFT
BN2 FOURL_NO_CTL
GLO RB
ADI 28H
PLO RB ; RB = CTL area keyboard mapping table
FOURL_NO_CTL
INP 1
SMI 0FFH
BNZ FOURL_KEY_PRESS
GLO RB
ADI 08H
PLO RB ; RB = Keycol 1
INP 2
SMI 0FFH
BNZ FOURL_KEY_PRESS
GLO RB
ADI 08H
PLO RB ; RB = Keycol 2
INP 3
SMI 0FFH
BNZ FOURL_KEY_PRESS
GLO RB
ADI 08H
PLO RB ; RB = Keycol 3
INP 4
SMI 0FFH
BNZ FOURL_KEY_PRESS
GLO RB
ADI 08H
PLO RB ; RB = Keycol 4
INP 5
SMI 0FFH
BZ FOURL_NO_KEY_PRESS
FOURL_KEY_PRESS
LDX
R75EE
SHL
BNF R75F4
INC RB
BR R75EE
R75F4
LDN RB ; Get keyboard code
STR R6 ; Get keyboard code
BR FOURL_INTERRUPT_END
FOURL_NO_KEY_PRESS
LDI 7FH
STR R6 ; 'Clear' keyboard code on FFF9
BR FOURL_INTERRUPT_END
DB 00H ;75FD: 00
DB 00H ;75FE: 00
DB 00H ;75FF: 00
; Line location table low byte followed by high byte
DB 00H ;7600: 00
DB 00H ;7601: 00
DB 1AH ;7602: 1A
DB 00H ;7603: 00
DB 34H ;7604: 34
DB 00H ;7605: 00
DB 4EH ;7606: 4E
DB 00H ;7607: 00
DB 68H ;7608: 68
DB 00H ;7609: 00
DB 82H ;760A: 82
DB 00H ;760B: 00
DB 9CH ;760C: 9C
DB 00H ;760D: 00
DB 0B6H ;760E: B6
DB 00H ;760F: 00
DB 0D0H ;7610: D0
DB 00H ;7611: 00
DB 0EAH ;7612: EA
DB 00H ;7613: 00
DB 04H ;7614: 04
DB 01H ;7615: 01
DB 1EH ;7616: 1E
DB 01H ;7617: 01
DB 38H ;7618: 38
DB 01H ;7619: 01
DB 52H ;761A: 52
DB 01H ;761B: 01
DB 6CH ;761C: 6C
DB 01H ;761D: 01
DB 86H ;761E: 86
DB 01H ;761F: 01
DB 0A0H ;7620: A0
DB 01H ;7621: 01
DB 0BAH ;7622: BA
DB 01H ;7623: 01
DB 0D4H ;7624: D4
DB 01H ;7625: 01
DB 0EEH ;7626: EE
DB 01H ;7627: 01
DB 08H ;7628: 08
DB 02H ;7629: 02
DB 22H ;762A: 22
DB 02H ;762B: 02
DB 3CH ;762C: 3C
DB 02H ;762D: 02
DB 56H ;762E: 56
DB 02H ;762F: 02
DB 70H ;7630: 70
DB 02H ;7631: 02
DB 8AH ;7632: 8A
DB 02H ;7633: 02
DB 0A4H ;7634: A4
DB 02H ;7635: 02
DB 0BEH ;7636: BE
DB 02H ;7637: 02
DB 0D8H ;7638: D8
DB 02H ;7639: 02
DB 0F2H ;763A: F2
DB 02H ;763B: 02
DB 0CH ;763C: 0C
DB 03H ;763D: 03
DB 26H ;763E: 26
DB 03H ;763F: 03
; ROM Keyboard mapping table which is copied to FF00 on start-up.
; When a 00 is used the folloing byte indicates how many time the third byte should be repeated
ROM_KEY_MAP
DB 06H ;7640: 06
DB 7FH ;7641: 7F
DB 7FH ;7642: 7F
DB 7FH ;7643: 7F
DB 0BH ;7644: 0B
DB 7FH ;7645: 7F
DB 7FH ;7646: 7F
DB 05H ;7647: 05
DB 07H ;7648: 07
DB 7FH ;7649: 7F
DB 04H ;764A: 04
DB 08H ;764B: 08
DB 7FH ;764C: 7F
DB 0FH ;764D: 0F
DB 7FH ;764E: 7F
DB 04H ;764F: 04
DB 08H ;7650: 08
DB 02H ;7651: 02
DB 06H ;7652: 06
DB 7FH ;7653: 7F
DB 0CH ;7654: 0C
DB 0DH ;7655: 0D
DB 0EH ;7656: 0E
DB 03H ;7657: 03
DB 09H ;7658: 09
DB 7FH ;7659: 7F
DB 7FH ;765A: 7F
DB 05H ;765B: 05
DB 7FH ;765C: 7F
DB 7FH ;765D: 7F
DB 7FH ;765E: 7F
DB 02H ;765F: 02
DB 00H ;7660: 00
DB 01H ;7661: 01
DB 00H ;7662: 00
DB 00H ;7663: 00
DB 04H ;7664: 04
DB 7FH ;7665: 7F
DB 0AH ;7666: 0A
DB 7FH ;7667: 7F
DB 01H ;7668: 01
DB 92H ;7669: 92
DB 00H ;766A: 00
DB 06H ;766B: 06
DB 7FH ;766C: 7F
DB 98H ;766D: 98
DB 8CH ;766E: 8C
DB 00H ;766F: 00
DB 05H ;7670: 05
DB 7FH ;7671: 7F
DB 0F0H ;7672: F0
DB 9EH ;7673: 9E
DB 86H ;7674: 86
DB 7FH ;7675: 7F
DB 0F2H ;7676: F2
DB 00H ;7677: 00
DB 04H ;7678: 04
DB 7FH ;7679: 7F
DB 0A4H ;767A: A4
DB 80H ;767B: 80
DB 7FH ;767C: 7F
DB 0F3H ;767D: F3
DB 7FH ;767E: 7F
DB 7FH ;767F: 7F
DB 0F4H ;7680: F4
DB 7FH ;7681: 7F
DB 0AAH ;7682: AA
DB 00H ;7683: 00
DB 06H ;7684: 06
DB 7FH ;7685: 7F
DB 0F1H ;7686: F1
DB 0B0H ;7687: B0
DB 00H ;7688: 00
DB 50H ;7689: 50
DB 7FH ;768A: 7F
CHIP8_IDENTIFIER
DB 'CHIP8' ;768B: 43
;768C: 48
;768D: 49
;768E: 50
;768F: 38
END
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment