[...]
If you really wanted to get tricky you could write just one "copy-and-call" function along these lines.
Code: Select all
.macro "GameOverScr"
jsr LoadMainScrPal
clc
jsr ClrNamTbl
ldx #$20 ;Begin 8 colums from the left border
ldy #$68
lda #$08
jsr WriteBigPixelTiles
.dw GameOverTxtData1 ;Location of the data
ldx #$21
ldy #$08
lda #$08
jsr WriteBigPixelTiles
.dw GameOverTxtData2
ldy #$14
ldx #$22
lda #$08
jsr WriteTitleText
ldy #$16
ldx #$22
lda #$68
jsr WriteTitleText
jsr ClrSprRam ;Clear all sprites
ldy #$22
sty $2006 ;Write the KO lucia "sprite" to BG
sty $2006
ldx #$00
- stx $2007
inx
cpx #$04
bne -
sty $2006
lda #$42
sta $2006
- stx $2007
inx
cpx #$08
bne -
lda #$03
jsr StartSong ;Game over song
jsr ResetScreen
lda #$00 ;Y is used for continue/exit selection (continue by default)
sta Temp
_game_ovr_scr_loop
jsr WaitNMI
lda #$aa
sta StringBuffer ;Attribute for wounded lucia icon (constant)
sta StringBuffer+1
lda #$05
bit Temp
bpl +
lda #$50
+ sta StringBuffer+2 ;Attributes for continue/exit selection (variable)
sta StringBuffer+3
lda #$23
ldx #$e0
ldy #$04
jsr PrintVRAMString
.dw StringBuffer
jsr ReadLockJoys
lda JoyLocked
and #$2c
beq + ;If select or up or down is pressed
lda Temp
eor #$80 ;Invert the selection
sta Temp
lda #$12
jsr StartSong ;Make a sound effect
jmp _game_ovr_scr_loop
+ lda JoyLocked
and #$10 ;Loop until start is pressed
beq _game_ovr_scr_loop
lda Temp
bpl +
jmp Reboot
+
.endmCode: Select all
*** Stack operations ***
PUSH #8-bit constant (2 bytes)
PUSHW #16-bit constant (3 bytes)
DUP #4-bit constant (1-byte) : duplicate the nth byte of the stack at the top of the stack
DUPW #4-bit constant (1-byte) : same as above, but 2 consecutive bytes at a time
DROP #4-bit constant (1-byte) : drop n bytes from the top of the stack
AND, OR, XOR, ADD, SUB (1 byte) do this operation on the top 2 bytes of the stack, replacing operands with single-byte result
ASL, LSR #3-bit constant (1 byte) : Shifts the 8-bit top of stack by 1-7 positions
INC, DEC, INCW, DECW (1 byte)
ADDW, SUBW (1 byte) do a 16-bit + 16-bit = 16-bit result addition
ASLW, LSRW #4-bit constant (1 byte) : Shifts the 16-bit top of stack by 1-15 positions
*** Memory operations ***
LOAD (1-byte) : Load byte at address pointed by top of stack, push it
LOADINC (1-byte) : Same as above, and pointer is post-incremented by 1
STORE (1-byte) : Store byte at top of stack to pointer which is below it
STOREINC (1-byte) : Same as above, and pointer is post-incremented by 1
STOREPOP (1-byte) : Same as above, and pop the byte after storing it
*** Branches ***
CALL #adr (3 bytes) : Call another stack machine code (after pushing the "program counter")
CALL6502PROC #adr (3 bytes) : Call a normal 6502 procedure (the "program counter", then a fake address is pushed on the stack, so that when it does rts it jumps back into the VM)
CALL6502FUNC #adr (3 bytes) : Same as above, but a result (A) is pushed on the stack
RETPROC : Go back to calling code (automatically selects between stack machine or 6502)
RETFUNC : Same as above, but the return address is one byte downto the stack, and the top of stack "result" is pushed again for the callee to use
BCC #adr (2 bytes) : Branch if C=0
BCS #adr (2 bytes) : Branch if C=1
BZ #adr (2 bytes) : Branch if top-of-stack = 0
BNZ #adr (2 bytes) : Branch if top-of-stack is not 0
BPL #adr (2 bytes) : Branch if bit 7 of top-of-stack is clear
BMI #adr (2 bytes) : Branch if bit 7 of top-of-stack is setCode: Select all
ldy #$14
ldx #$22
lda #$08
jsr WriteTitleTextCode: Select all
.db TKNWriteTitleText, $14, $22, $08Code: Select all
.db TKNcliteral,$14
.db TKNcliteral,$22
.db TKNcliteral,$08
.db TKNWriteTitleTextCode: Select all
...
brk
.db TKNWriteTitleText, $14, $22, $08
...
Code: Select all
section above:
You can of course pass parameters on the page-1 hardware stack (as has been done a lot) but there are certain problems with it that are solved by having a data stack in ZP (indexed by X) that's separate from the return stack in page 1. Take for example the matter of using the page-1 hardware stack for passing parameters to a subroutine, but then that routine passes parameters to another, and now there's a subroutine-return address on top putting the target data farther down the stack than expected, so you get incorrect results. The separate data stack in ZP avoids this, because the subroutine-return addresses don't go on it. ZP addressing is also more efficient of course, and there are more addressing modes for ZP. Outside of that, your list of suggested routines looks very much like a token-threaded Forth where all the most commonly used instructions are a single-byte token rather than a code-field address.Code: Select all
LDA (0,X)
STA 0,XCode: Select all
[...] ; four others here
lshiftb4:
asl baseaddr,x
lshiftb3:
asl baseaddr,x
lshiftb2:
asl baseaddr,x
lshiftb1:
asl baseaddr,x
rts
[...] ; four others here
rshiftw4
lsr baseaddr,x
ror baseaddr+1,x
rshiftw3
lsr baseaddr,x
ror baseaddr+1,x
rshiftw2
lsr baseaddr,x
ror baseaddr+1,x
rshiftw1
lsr baseadddr,x
ror baseaddr+1,x
rts