; 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 ; and a look up table to evalute neighbours count ; this world has boundaries vram EQU 0x0800 buff0 EQU 0x09A0 buff1 EQU 0x09D0 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 32 ; 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 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 0x09A3 ; start position for gliders 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 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 ; get neighbours count ST neighbours 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 1x1 ;ADDW vbase ;STW pixels ;LD cell ;POKE pixels LDW i ; draw cell 2x2 LSLW ADDW vbase STW pixels STW scratch LD cell POKE pixels ; x, y INC pixels POKE pixels ; x+1, y INC pixels+1 POKE pixels ; x+1, y+1 INC scratch+1 POKE scratch ; x, y+1 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