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

406 lines
14 KiB
Plaintext

; **************************************************************************************************
; **************************************************************************************************
; THIS WILL NOT RUN ON REAL HARDWARE UNLESS YOU BURN THE NATIVE CODE AT THE BOTTOM
; OF THIS FILE INTO THE GIGATRONS ROM AT THE CORRECT ADDRESS, EMULATION ONLY!
; **************************************************************************************************
; **************************************************************************************************
; game of life coded by at67, original algorithm by John Conway
; 32X32 runs about 2 fps on a 6.25MHz Gigatron TTL
; more optimisation: neighbour count buffer is generated from alive cells in cell buffer, pointer arithmetic with offsets,
; a look up table to evalute neighbours count and a SYS function to plot the 2x2 pixels.
; this world has boundaries
vram EQU 0x0800
buff0 EQU 0x09A0
buff1 EQU 0x09D0
sysFn EQU 0x22
cells0 EQU 0x30
cells1 EQU 0x32
i EQU 0x34
j EQU 0x35
neighbours EQU 0x36
cell EQU 0x37
n_address EQU 0x38
buffer0 EQU 0x3A
buffer1 EQU 0x3C
generation EQU 0x3E
vbase EQU 0x40
pixels EQU 0x42
pattern EQU 0x44
gliders EQU 0x46
lut_modify EQU 0x48
j_one EQU 0x4A
offset0 EQU 0x4C
offset1 EQU 0x4E
offset2 EQU 0x50
offset3 EQU 0x52
offset4 EQU 0x54
offset5 EQU 0x56
offset6 EQU 0x58
offset7 EQU 0x5A
scratch EQU 0x5C
screen_w EQU 160
screen_h EQU 120
bounds EQU 46 ; determines max size of world, limited by available ram in blank scanlines,
; using bytes per cell, 1 pixel world boundary and two buffers means 46*46 is maximum size
_startAddress_ EQU 0x0200 ; entry point for the code, if this is missing defaults to 0x0200
_callTable_ EQU 0x007E ; 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 neighbours ; the single step debugger watches this variable location to decide when to step,
; choose a variable that is updated often
SYS_DrawPixel2x2_32 EQU 0x2300 ; 2x2 pixel plot, native code that is defined with either DBR or DWR is written to ROM at this address
SYS_ClearRow32_56 EQU 0x2400 ; clear 32 pixels at a time, native code that is defined with either DBR or DWR is written to ROM at this address
n_lut EQU 0x60
n_lut_cell EQU 0x62
counts EQU 0x0300
LDI 0x00 ; look up table that is used to evaluate neighbour counts
ST n_lut
ST n_lut+1
ST n_lut+2 ; self modifying code updates this location with previous generation cell
ST n_lut+4
ST n_lut+5
ST n_lut+6
ST n_lut+7
ST n_lut+8
LDI 0xFF
ST n_lut+3
LDWI vram
STW vbase ; vram base address
STW pixels ; pixel address
LDWI buff0
STW buffer0
LDWI buff1
STW buffer1
LDWI lut + 1 ; self modifying address
STW lut_modify
LDWI 0x0100
STW j_one
;clear LDI 0x00 ; clear screen and buffers
; POKE vbase
; LDWI 0x0001
; ADDW vbase
; STW vbase
; LD vbase+1
; SUBI 0x80
; BLT clear
LDWI SYS_ClearRow32_56 ; clears 32 row pixels at a time
STW sysFn
clear SYS 56
LDWI 0x0020
ADDW vbase
STW vbase
LD vbase+1
SUBI 0x7F
BLT clear
LDI 0x08 ; finish off last row
ST i
last_row SYS 56
LDWI 0x0020
ADDW vbase
STW vbase
LD i
SUBI 0x01
ST i
BGT last_row
LDWI 0x09A3
STW gliders
LDI 0x06 ; number of gliders
ST i
glider LDW gliders
STW pattern
LDI 0xFF
POKE pattern
LDW j_one
ADDW pattern
STW pattern
LDI 0xFF
POKE pattern
LDW j_one
ADDW pattern
STW pattern
LDI 0xFF
POKE pattern
LD pattern
SUBI 0x01
ST pattern
LDI 0xFF
POKE pattern
LDW pattern
SUBW j_one
STW pattern
LD pattern
SUBI 0x01
ST pattern
LDI 0xFF
POKE pattern
LD gliders ; gap between gliders
ADDI 0x05
ST gliders
LD i
SUBI 0x01
ST i
BGT glider
LDWI 0x0001
STW j_one
STW generation
LDI (screen_w - bounds*2)/0b10 ; position in screen to render life
ST vbase
LDI (screen_h - bounds*0q2)/0x0002 + 8 ; expressions can use equates and use the same number handling as equates
ST vbase+1
LDI bounds
ST i
ST j
LDWI 0x0101 ; -ve
STW offset0
LDWI 0x0100 ; -ve
STW offset1
LDWI 0x00FF ; -ve
STW offset2
LDWI 0x0001 ; +ve
STW offset3
LDWI 0x0101 ; +ve
STW offset4
LDWI 0x0100 ; +ve
STW offset5
LDWI 0x00FF ; +ve
STW offset6
LDWI 0x0001 ; -ve
STW offset7
LDWI SYS_DrawPixel2x2_32
STW sysFn
CALL counts ; jump across a page boundary
; counts buffer
counts LDW i ; buffer0
ADDW buffer0
PEEK ; get buffer0 cell
BEQ continue
LDW i ; buffer1
ADDW buffer1
STW cells1
SUBW offset0 ; -1, -1 neighbour
STW n_address
PEEK
ADDI 0x01
POKE n_address
LDW cells1
SUBW offset1 ; 0, -1 neighbour
STW n_address
PEEK
ADDI 0x01
POKE n_address
LDW cells1
SUBW offset2 ; 1, -1 neighbour
STW n_address
PEEK
ADDI 0x01
POKE n_address
LDW cells1
ADDW offset3 ; 1, 0 neighbour
STW n_address
PEEK
ADDI 0x01
POKE n_address
LDW cells1
ADDW offset4 ; 1, 1 neighbour
STW n_address
PEEK
ADDI 0x01
POKE n_address
LDW cells1
ADDW offset5 ; 0, 1 neighbour
STW n_address
PEEK
ADDI 0x01
POKE n_address
LDW cells1
ADDW offset6 ; -1, 1 neighbour
STW n_address
PEEK
ADDI 0x01
POKE n_address
LDW cells1
SUBW offset7 ; -1, 0 neighbour
STW n_address
PEEK
ADDI 0x01
POKE n_address
continue LD i ; dec i
SUBI 0x01
ST i
BGE counts
LDI bounds
ST i
LD j ; dec j
SUBI 0x01
ST j
BGE counts
LDI bounds
ST j
generate LDW i ; buffer1
ADDW buffer1
STW cells1
PEEK
ST neighbours ; get neighbours count
LDI 0x00
POKE cells1 ; reset neighbours count
LDW i ; buffer0
ADDW buffer0
STW cells0
PEEK ; get cell
ST n_lut_cell ; save cell into neighbours lut
LD neighbours ; lut evaluates neighbours count
ADDI n_lut
POKE lut_modify ; lut_modify points to operand in "LD n_lut"
lut LD n_lut ; get new cell using self modifying code
POKE cells0 ; save new cell
ST cell
LDW i ; draw cell 2x2
LSLW
ADDW vbase
STW pixels
SYS 32
LD i ; dec i
SUBI 0x01
ST i
BGE generate
LDI bounds
ST i
LD j ; dec j
SUBI 0x01
ST j
BGE generate
LDI bounds
ST j
LDW generation
ADDW j_one
STW generation
BRA counts
; these are native code routines that are written into ROM using the DBR command, (Define Byte ROM), at the equate defined
; above: this native code is specific to this vCPU asm module with the input registers that it accepts
;SYS_ClearRow32_56 DBR $11 $40 $15 $41 $00 $00 $DE $00 $DE $00 $DE $00 $DE $00 $DE $00 $DE $00 $DE $00 $DE $00
; DBR $DE $00 $DE $00 $DE $00 $DE $00 $DE $00 $DE $00 $DE $00 $DE $00
; DBR $DE $00 $DE $00 $DE $00 $DE $00 $DE $00 $DE $00 $DE $00 $DE $00
; DBR $DE $00 $DE $00 $DE $00 $DE $00 $DE $00 $DE $00 $DE $00 $DE $00 $14 $03 $E0 $CB $00 $E4
;SYS_DrawPixel2x2_32 DBR $01 $37 $11 $42 $15 $43 $DE $00 $CE $00 $11 $42 $00 $01 $95 $43 $01 $37 $DE $00 $CE $00 $14 $03 $E0 $CB $00 $F0
SYS_DrawPixel2x2_32 .LD [0x37]
.LD [0x42],X
.LD [0x43],Y
.ST [Y,X++]
.ST [Y,X]
.LD [0x42],X
.LD 0x01
.ADDA [0x43],Y
.LD [0x37]
.ST [Y,X++]
.ST [Y,X]
.NOP ; pad instructions so odd(14 + number of instructions) = true
.LD 0x03,Y
.JMP Y,0xCB
.LD 0xF0 ; 0 - ((14 + number of instructions + 3) / 2), odd(14 + number of instructions) = true
SYS_ClearRow32_56 .LD [0x40],X
.LD [0x41],Y
.LD 0x00
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.ST [Y,X++]
.NOP ; pad instructions so odd(14 + number of instructions) = true
.LD 0x03,Y
.JMP Y,0xCB
.LD 0xE4 ; 0 - ((14 + number of instructions + 3) / 2), odd(14 + number of instructions) = true