gigatron/rom/Contrib/at67/gasm/tetronis/tetronis.gasm
2025-01-28 19:17:01 +03:00

453 lines
16 KiB
Plaintext

%include ../include/gigatron.i
; 0x30 -> 0x38 Vars
; 0x3A -> 0x45 Midi
; 0x4A -> 0x96 CallTable *DO NOT* pull CallTable above 0xBE, future ROMS will *NOT* work as the CallTable is preloaded and will conflict with future loading mechanisms
; 0x98 -> 0xE5 Vars
; 0xE6 -> 0xFF Stack The amount of stack space you will require depends on how many nested CALLS you make, 28 bytes allows for upto 14 nested calls
kk EQU 0x30
ll EQU kk + 0x01
mm EQU kk + 0x02
nn EQU kk + 0x03
tt EQU kk + 0x04
buttonStatePrev EQU kk + 0x05
refresh EQU kk + 0x06
blocked EQU kk + 0x07
tetrominoNext EQU kk + 0x08
midiStreamPtr EQU 0x3A
midiCommand EQU midiStreamPtr + 0x02
midiChannel EQU midiStreamPtr + 0x04
midiDelay EQU midiStreamPtr + 0x06
midiNote EQU midiStreamPtr + 0x08
midiScratch EQU midiStreamPtr + 0x0A
xx EQU 0x98
yy EQU xx + 0x01
ii EQU xx + 0x02
jj EQU xx + 0x03
index EQU xx + 0x04
rotation EQU xx + 0x06
indexNext EQU xx + 0x08
rotationNext EQU xx + 0x0A
tx EQU xx + 0x0C
ty EQU xx + 0x0E
tw EQU xx + 0x10
th EQU xx + 0x12
tu EQU xx + 0x14
tv EQU xx + 0x16
ox EQU xx + 0x18
oy EQU xx + 0x1A
ov EQU xx + 0x1C
oindex EQU xx + 0x1E
orotation EQU xx + 0x20
tetrominoLut EQU xx + 0x22
tetrominoBase EQU xx + 0x24
colour EQU xx + 0x26
result EQU xx + 0x28
frameCounter EQU xx + 0x2A
frameCountPrev EQU xx + 0x2C
frameTicks EQU xx + 0x2E
frameTicksLevel EQU xx + 0x30
rand EQU xx + 0x32
scratch EQU xx + 0x34
vbase EQU xx + 0x36
xScroll EQU xx + 0x38
tbase EQU xx + 0x3A
dbase EQU xx + 0x3C
textStr EQU xx + 0x3E
textPos EQU xx + 0x40
textChr EQU xx + 0x42
numLines EQU xx + 0x44
scoreDelta EQU xx + 0x46
scoreLevel EQU xx + 0x48
scoreUpdate EQU xx + 0x4A
scoreScratch EQU xx + 0x4C
xTetris EQU 10
yTetris EQU 20
xPixels EQU xTetris*4
yPixels EQU yTetris*4
xOffset EQU (giga_xres - xPixels) / 2
yOffset EQU (giga_yres - yPixels) / 2
maxLines EQU 4
maxLevel EQU 8
maxTicks EQU 50
deltaTicks EQU 5
scorePos EQU 0x6D44
levelPos EQU 0x6D5F
highPos EQU 0x1644
multPos EQU 0x6D3A
fgColourB EQU 0xFF
bgColourB EQU 0x15
bgColourW EQU 0x1515
_startAddress_ EQU 0x0200 ; entry point for the code, if this is missing defaults to 0x0200
_callTable_ EQU 0x0096 ; call addresses are automatically stored here by the assembler, it grows downwards
; *NOTE* gt1 spec only allows for one zero page segment, .vasm files use this for the call table
; do *NOT* make this address higher than 0x00BE, it will conflict with future ROM loading mechanisms
; do *NOT* define constants, (DB or DW), between 0x30 -> 0x44 and 0xc0 -> 0xFF, these addresses are
; used by the loader and the vCPU stack, you can create variables in these areas as long as you don't
; corrupt your nested CALL return addresses on the stack
_singleStepWatch_ EQU frameCounter ; the single step debugger watches this variable location to decide when to step,
; choose a variable that is updated often
_cpuUsageAddressA_ EQU 0x0600 ; these two addresses are used to build an exclusion zone around vCPU code that will not be counted
_cpuUsageAddressB_ EQU 0x0620 ; in the vCPU usage meter, (almost always used to exclude your VBlank polling loop)
%include midi_scores.i
%include tetromino.i
scoringLut EQU tetromino_I + 0x0700
levellingLut EQU tetromino_I + 0x0705
scoringLut DB 10 25 50 100
levellingLut DW 250 750 1000 2000 4000 8000 16000 32000 64000
score_string EQU 0x7FA1
high_string EQU 0x7FA8
level_string EQU 0x7FAF
mult_string EQU 0x7FB2
score_string DB 6 '000000'
high_string DB 6 '000000'
;level_string DB 7 'LEVEL 0'
; TODO: build a complete font set, right now ':' = 'L' and ';' = 'x'
level_string DB 2 ':0'
mult_string DB 2 ';1'
; tetromino_I gets an extra slot to make rand easier to implement, this also means tetromino_I will appear statistically more often,
; replace with which ever tetromino you want to favour
tetromino_lut EQU 0x08E0
tetromino_lut DW tetromino_I tetromino_J tetromino_L tetromino_O tetromino_S tetromino_T tetromino_Z tetromino_I
entry_point EQU 0x0200
handleInput EQU entry_point + 0x0100
updateScore EQU entry_point + 0x0200 ; too big to store in unused area of video memory
incrementScore EQU entry_point + 0x0300 ; too big to store in unused area of video memory
waitVBlank EQU entry_point + 0x0400
; storing subroutines in unused areas of video memory
clearBoard EQU 0x15A1
clearScreen EQU clearBoard + 0x0100
clearScreen_0 EQU clearBoard + 0x0200
setTetrisBlock EQU clearBoard + 0x0300
drawTetromino EQU clearBoard + 0x0400
drawNextTet EQU clearBoard + 0x0500
drawTetrisField EQU clearBoard + 0x0600
drawTFtopH EQU clearBoard + 0x0700
drawTFbotH EQU clearBoard + 0x0800
eraseTetromino EQU clearBoard + 0x0900
eraseTrCorner EQU clearBoard + 0x0A00
getTetrisBlock EQU clearBoard + 0x0B00
checkTetromino EQU clearBoard + 0x0C00
spawnTetromino EQU clearBoard + 0x0D00
updateTetromino EQU clearBoard + 0x0E00
checkLines EQU clearBoard + 0x0F00
moveLines EQU clearBoard + 0x1000
shakeScreen EQU clearBoard + 0x1100
printText EQU clearBoard + 0x1200
printDigits EQU clearBoard + 0x1300
updateHighScore EQU clearBoard + 0x1400
resetLevel EQU clearBoard + 0x1500
resetScore EQU clearBoard + 0x1600
incrementLevel EQU clearBoard + 0x1700
loadTetromino EQU clearBoard + 0x1800
resetAudio EQU clearBoard + 0x1900
playMidi EQU clearBoard + 0x1A00
midiStartNote EQU clearBoard + 0x1B00
midiSegment EQU clearBoard + 0x1C00
%include macros.i
%include clear_board.i
%include draw_tetromino.i
%include erase_tetromino.i
%include check_tetromino.i
%include spawn_tetromino.i
%include check_lines.i
%include print_text.i
%include update_score.i
%include digit_font.i
%include audio.i
entry_point LDWI giga_vram
STW vbase ; vram base address
LDWI giga_text32
STW tbase ; text font base address, (ROM)
LDWI digit_font0
STW dbase ; tiny digit font base address, (RAM)
LDWI giga_videoTable + 1
STW xScroll
CALL resetAudio
CALL clearScreen
CALL drawTetrisField
restart CALL resetLevel
CALL resetScore
CALL clearBoard
LDWI 0x0000
STW scoreDelta
STW scoreLevel
STW frameCounter
LDWI maxTicks
STW frameTicksLevel
STW frameTicks
LD giga_rand0
ST tetrominoNext
LDI 0x00
ST blocked
LD giga_frameCount
STW frameCountPrev
LDWI tetromino_lut
STW tetrominoLut
CALL spawnTetromino
LD giga_frameCount ; Start checking midi stream immediately
ST midiDelay
; main update loop
update ST scratch
CALL saveTetromino
; wait for VBlank
vBlank CALL waitVBlank
CALL playMidi
; input
CALL handleInput
ST refresh
XORI 0xFB ; down
BEQ frame_count1
LD refresh ; all other input
BNE erase
; reset frameTicks
LDW frameTicksLevel
STW frameTicks
; frameTicks defines speed of tetrominoes
frame_count1 LDW frameCounter
ADDI 0x01
STW frameCounter
SUBW frameTicks
BLT vBlank
; don't allow input to override timing
LDI 0x00
ST refresh
; erase old tetromino
erase LD blocked
BNE check_txl
CALL eraseTetromino
; if(tx < 0 - tu) tx = 0 - tu;
check_txl LDW tx
ADDW tu
BGE check_txr
LDWI 0x00
SUBW tu
STW tx
BRA frame_count0
; if(tx > TETRIS_XEXT - tw - tu) tx = TETRIS_XEXT - tw - tu;
check_txr LDW tx
ADDW tw
ADDW tu
SUBI xTetris
BLE frame_count0
LDWI xTetris
SUBW tw
SUBW tu
STW tx
; if(frameCount >= frameTick)
frame_count0 LDW frameCounter
SUBW frameTicks
BLT check_blocks
LDWI 0x0000
STW frameCounter
LDI 0x00 ; reset flicker control
ST blocked
; gravity
INC ty
; if(y > TETRIS_YEXT - h)
LDW ty
ADDW th
SUBI yTetris
BLE check_blocks
; draw floor tetromino
LD ty
SUBI 0x01
ST ty
CALL drawTetromino
; spawn new tetromino
CALL spawnTetromino
LDI 0xFF ; control flicker
ST blocked
BRA update
; check new tetromino position
check_blocks CALL checkTetromino ; checks tetromino for occupied blocks, returns result, 0 = empty, 1 = game over, >1 = blocked
LDW result
BEQ draw_new
; check for game over
SUBI 0x01
BNE check_blocked
CALL resetAudio
LDWI game_overMidi00 ; game over music
STW midiStreamPtr
BRA restart
; check for fall blocked whilst input active, (but not drop)
check_blocked LD refresh
BEQ fall_blocked
; load old tetromino
CALL loadTetromino
BRA draw_new
; draw falling blocked tetromino at previous y position
fall_blocked LD ty
SUBI 0x01
ST ty
CALL drawTetromino
; spawn new tetromino
CALL spawnTetromino
BRA update
draw_new CALL drawTetromino
BRA update
; load tetromino state
loadTetromino LDW ox
STW tx
LDW oy
STW ty
LDW ov ; vertical offset in tetris block units
STW tv
LDW oindex
STW index
LDW orotation
STW rotation
RET
; save tetromino state
saveTetromino LDW tx
STW ox
LDW ty
STW oy
LDW tv ; vertical offset in tetris block units
STW ov
LDW index
STW oindex
LDW rotation
STW orotation
RET
; handle input, returns 0x00 if nothing was handled, otherwise returns input
handleInput LD giga_buttonState ; 0xFF is no input
XORI 0xFF
BNE handleI_down
ST buttonStatePrev ; return 0x00
RET
; level triggered buttons
handleI_down LD giga_buttonState
XORI 0xFB ; down
BNE handleI_event
LDWI 2
STW frameTicks
LD giga_buttonState
RET
; edge triggering
handleI_event LD buttonStatePrev ; only handle new events
BEQ handleI_left
LD 0x00
RET
; edge triggered buttons
handleI_left LD giga_buttonState
ST buttonStatePrev
XORI 0xFD ; left
BNE handleI_right
LDW tx ; tx--
SUBI 0x01
STW tx
LD giga_buttonState
RET
handleI_right LD giga_buttonState
XORI 0xFE ; right
BNE handleI_up
LDW tx ; tx++
ADDI 0x01
STW tx
LD giga_buttonState
RET
handleI_up LD giga_buttonState
XORI 0xF7 ; up
BNE handleI_exit
LDW rotation
STW scratch
ADDI 0x10 ; rotation table's are separated by 16 bytes
ANDI 0x30 ; 4 rotation patterns
STW rotation
PUSH
CALL updateTetromino
POP
; check rotation
LDW ty ; if(ty > TETRIS_YEXT - th - tv)
ADDW th
ADDW tv
SUBI yTetris
BLE handleI_exit
; restore rotation
LDW scratch
STW rotation
PUSH
CALL updateTetromino
POP
LD 0x00
RET
handleI_exit LD giga_buttonState
RET
waitVBlank LD giga_frameCount
SUBW frameCountPrev
BEQ waitVBlank
LD giga_frameCount
STW frameCountPrev
RET