#name lc3d
#type &FF8
#base &8000

; Fog/sky colour - black
#set fog = 0
; Grid colour for floor
#set grid = 8
#set biggrid = 10
; Surrounding wall colour
#set wall = 201
; Light cycle colours...
#set red = 20
#set yellow = 116
#set blue = 168
; Draw distance
#set far = 256
; Surrounding wall height
#set wallh = 255
; Light cycle height
#set lch = 6
; Trail height
#set th = 8
; Floor height
#set fh = 1
; Min frame time
#set mft = 5

; Set this thing up...
SWI OS_GetEnv
MOV R13,R1
SUB R1,R1,#&8000
LDR R0,blanksize ; Get how much space is needed
CMP R1,R0
ADRLT R0,error1
SWILT OS_GenerateError
SWILT OS_Exit
SUB R0,R0,#2048 ; Stack space
STR R0,blanksize
SWI &100+22
SWI &100+13+128
SWI &100+22
SWI &100+13
BL switchbanks
SWI &100+12
BL switchbanks
SWI &100+12
BL makevoxel
BL resetplayers
SWI OS_ReadMonotonicTime
STR R0,tstore
MOV R6,#0
.mainloop
BL rnd_num
ADR R1,voxel
MOV R2,#12
ORR R2,R2,#fh<<24
STR R2,[R1,R0,LSL #2]
BL calcdir
TST R6,#1
BLEQ vox_draw
BLNE vox_tdraw
BL vox_mdraw
BL vox_cdraw
LDR R1,tstore
.framewait
SWI OS_ReadMonotonicTime
SUB R2,R0,R1
CMP R2,#mft
BLT framewait
STR R0,tstore
SWI &100+30
SWI &100+32
SWI &100+32
SWI &100+32
SWI &100+32
SWI &100+30
MOV R0,R2
ADR R1,tbuff
MOV R2,#32
SWI XOS_ConvertInteger4
SWI OS_Write0
BL player_code
BL comp_code
MOV R0,#121
MOV R1,#99+128 ; V?
SWI OS_Byte
CMP R1,#255
BICNE R6,R6,#2
BNE vcskip
TST R6,#2
EOREQ R6,R6,#3
.vcskip
LDR R1,player_keys
TST R1,#12
BLNE vox_tdraw ; Draw top down view for death
TST R1,#4
ADRNE R0,player_dead
SWINE OS_Write0
TST R1,#8
ADRNE R0,comp_dead
SWINE OS_Write0
BL switchbanks
TST R1,#12
BNE exit
SWI OS_ReadEscapeState
BCC mainloop
.exit
BL nokeys
SWI OS_Exit

.switchbanks
STMFD R13!,{R0-R1,R14}
MOV R0,#112
LDR R1,bank
SWI OS_Byte
MOV R0,#113
LDR R1,bank
EOR R1,R1,#3
STR R1,bank
SWI OS_Byte
MOV R0,#19
SWI OS_Byte
ADR R0,screen_stuff
ADR R1,screen_start
SWI OS_ReadVduVariables
LDMFD R13!,{R0-R1,PC}

.makevoxel ; Draw grid pattern on voxel, and add surrounding walls
	   ; Perhaps do a version without the walls? (i.e. wrap around world)
STMFD R13!,{R0-R8,R14}
ADR R4,voxel
MOV R5,#0 ; X
MOV R6,#0 ; Y
.makeloop
MOV R7,#fog
MOV R8,#fh ; Floor height
TST R5,#3 ; Every 4 blocks is a grid line
TSTNE R6,#3
MOVEQ R7,#grid
TST R5,#15
TSTNE R6,#15
MOVEQ R7,#biggrid
CMP R5,#0
CMPNE R6,#0
CMPNE R5,#255
CMPNE R6,#255
MOVEQ R7,#wall
MOVEQ R8,#wallh ; Wall height
ORR R7,R7,R8,LSL #24
STR R7,[R4],#4
ADD R5,R5,#1
CMP R5,#255
MOVGT R5,#0
ADDGT R6,R6,#1
CMP R6,#255
BLE makeloop
LDMFD R13!,{R0-R8,PC}

.calcdir
STMFD R13!,{R0-R9,R14}
; Work out cam pos
LDR R0,player_x
LDR R1,player_y
ORR R0,R0,#1<<23 ; Add a half to make sure the world is drawn from the center of the block
ORR R1,R1,#1<<23
STR R0,xstart
STR R1,ystart
; Get pointers to the right rotations
LDR R7,player_rot
CMP R7,#1
ADRLT R6,xtop0 ; Top left corner
ADRLT R8,xright0 ; Top right corner
ADRLT R9,xbot0 ; Bottom left corner
ADREQ R6,xtop1
ADREQ R8,xright1
ADREQ R9,xbot1
CMP R7,#2
ADREQ R6,xtop2
ADREQ R8,xright2
ADREQ R9,xbot2
ADRGT R6,xtop3
ADRGT R8,xright3
ADRGT R9,xbot3
LDMIA R6,{R3-R5} ; Work out direction steps per screen x
LDMIA R8,{R0-R2}
SUB R0,R0,R3
SUB R1,R1,R4
SUB R2,R2,R5
MOV R0,R0,ASR #8 ; 256 steps
MOV R1,R1,ASR #8
MOV R2,R2,ASR #8
ADR R6,xforx
STMIA R6,{R0-R2}
LDMIA R9,{R0-R2} ; Steps per screen Y
ADR R6,xdir ; Go from the bottom up
STMIA R6,{R0-R2}
SUB R0,R3,R0
SUB R1,R4,R1
SUB R2,R5,R2
MOV R0,R0,ASR #8
MOV R1,R1,ASR #8
MOV R2,R2,ASR #8
ADR R6,xfory
STMIA R6,{R0-R2}
LDMFD R13!,{R0-R9,PC}

.vox_tdraw
STMFD R13!,{R0-R4,R14}
LDR R0,screen_start
ADD R0,R0,#32
ADR R1,voxel
ADD R1,R1,#255*256*4 ; Bottom line
MOV R2,#65536
MOV R3,#256
.vtloop
LDR R4,[R1],#4
STRB R4,[R0],#1
SUBS R3,R3,#1
ADDLE R0,R0,#64
MOVLE R3,#256
SUBLE R1,R1,#512*4
SUBS R2,R2,#1
BGT vtloop
LDMFD R13!,{R0-R4,PC}

.vox_draw
STMFD R13!,{R0-R12,R14}
LDR R0,screen_start
ADD R0,R0,#32 ; Only drawing 256x256
ADD R0,R0,#320*255 ; Work from bottom up
ADR R1,voxel ; Top byte is height, bottom is colour (&HH0000CC)
ADR R14,xdir
LDMIA R14,{R5-R7} ; 8.24 increments (Except for Z, 9.23)
MOV R11,#0 ; Current X count
STMFD R13!,{R0,R5-R7} ; Remember dir & screen pos at column start
.vstart
MOV R12,#255 ; Current Y pos
ADR R8,xstart ; Get start pos
LDMIA R8,{R2-R4}
MOV R8,#0
.vloop ; Now go through loading points
MOV R9,R2,LSR #24
MOV R10,R3,LSR #24
ADD R9,R9,R10,LSL #8
LDR R9,[R1,R9,LSL #2]
MOV R10,R9,LSR #24
CMP R10,R4,LSR #23
CMPGE R8,#1 ; Don't draw the starting block since it's the players light cycle
BGE foundpix
ADD R2,R2,R5
ADD R3,R3,R6
ADDS R4,R4,R7
BLT fogdraw ; Off the top
ADD R8,R8,#1
CMP R8,#far
BGE fogdraw
B vloop
.foundpix ; Now draw on screen - increase row num (and so line pos) until it clears the voxel
; So increase pos by fory*dist (R8) until it goes over R10
ADR R14,xfory
LDMIA R14,{R5-R7}
MUL R5,R8,R5
MUL R6,R8,R6
MUL R7,R8,R7
.dloop
STRB R9,[R0]
SUB R0,R0,#320
SUBS R12,R12,#1
BLT dfin
ADD R2,R2,R5
ADD R3,R3,R6
ADD R4,R4,R7
CMP R10,R4,LSR #23
BGE dloop
; Loop round again...
; Need to change the dir
MOV R9,R0
LDMIA R13,{R0,R5-R7}
MOV R0,R9
EOR R12,R12,#255 ; Get how many times we need to add fory's
ADR R14,xfory
LDMIA R14,{R9,R10,R14}
MLA R5,R9,R12,R5
MLA R6,R10,R12,R6
MLA R7,R14,R12,R7
EOR R12,R12,#255 ; Revert to normal
B vloop
.fogdraw ; Draw fog up to top of screen
MOV R9,#fog
.fogloop
STRB R9,[R0]
SUB R0,R0,#320
SUBS R12,R12,#1
BGE fogloop
.dfin ; New X pos...
LDMFD R13!,{R0,R5-R7}
ADD R0,R0,#1
ADR R14,xforx
LDMIA R14,{R2-R4}
ADD R5,R5,R2
ADD R6,R6,R3
ADD R7,R7,R4
STMFD R13!,{R0,R5-R7}
ADD R11,R11,#1
CMP R11,#256
BLT vstart
LDMFD R13!,{R0,R5-R7}
LDMFD R13!,{R0-R12,PC}

.vox_mdraw ; Draw minimap
STMFD R13!,{R0-R7,R14}
LDR R0,player_x
LDR R1,player_y
SUB R0,R0,#16<<24
ADD R1,R1,#16<<24
MOV R2,#0
MOV R3,#0
ADR R4,voxel
LDR R5,screen_start
ADD R5,R5,#32+256
.mdraw_loop
MOV R6,R0,LSR #24
MOV R7,R1,LSR #24
ADD R6,R6,R7,LSL #8
LDR R7,[R4,R6,LSL #2]
STRB R7,[R5],#1
ADD R2,R2,#1
ADD R0,R0,#1<<24
CMP R2,#32
MOVGE R2,#0
SUBGE R0,R0,#32<<24
ADDGE R5,R5,#320-32
ADDGE R3,R3,#1
SUBGE R1,R1,#1<<24
CMP R3,#32
BLT mdraw_loop
LDMFD R13!,{R0-R7,PC}

.vox_cdraw ; Draw minimap for computer
STMFD R13!,{R0-R7,R14}
LDR R0,comp_x
LDR R1,comp_y
SUB R0,R0,#16<<24
ADD R1,R1,#16<<24
MOV R2,#0
MOV R3,#0
ADR R4,voxel
LDR R5,screen_start
ADD R5,R5,#32+256
ADD R5,R5,#320*64
.cdraw_loop
MOV R6,R0,LSR #24
MOV R7,R1,LSR #24
ADD R6,R6,R7,LSL #8
LDR R7,[R4,R6,LSL #2]
STRB R7,[R5],#1
ADD R2,R2,#1
ADD R0,R0,#1<<24
CMP R2,#32
MOVGE R2,#0
SUBGE R0,R0,#32<<24
ADDGE R5,R5,#320-32
ADDGE R3,R3,#1
SUBGE R1,R1,#1<<24
CMP R3,#32
BLT cdraw_loop
LDMFD R13!,{R0-R7,PC}

.player_code ; Get input from keyboard
STMFD R13!,{R0-R6,R14}
LDR R3,player_rot
LDR R4,player_keys
MOV R0,#121
MOV R1,#25+128 ; Left arrow?
SWI OS_Byte
CMP R1,#&FF
BICNE R4,R4,#1 ; Clear previously-pressed-left flag
BNE lkskip
TST R4,#1
ORREQ R4,R4,#1
SUBEQ R3,R3,#1
.lkskip
MOV R1,#121+128
SWI OS_Byte
CMP R1,#&FF
BICNE R4,R4,#2
BNE rkskip
TST R4,#2
ORREQ R4,R4,#2
ADDEQ R3,R3,#1
.rkskip
AND R3,R3,#3
STR R3,player_rot
STR R4,player_keys
LDR R0,player_x
LDR R1,player_y
ADR R2,voxel
; Work out dir info
ADR R4,xvel0
ADD R4,R4,R3,LSL #3 ; *8
LDMIA R4,{R5-R6}
; Set current pos to wall colour
MOV R3,R0,LSR #24
MOV R4,R1,LSR #24
ADD R3,R3,R4,LSL #8
MOV R4,#yellow
ORR R4,R4,#th<<24 ; Wall height
STR R4,[R2,R3,LSL #2]
; Now advance pos & check for collision
ADD R0,R0,R5
ADD R1,R1,R6
STR R0,player_x
STR R1,player_y
MOV R3,R0,LSR #24
MOV R4,R1,LSR #24
ADD R3,R3,R4,LSL #8
LDR R4,[R2,R3,LSL #2]
MOV R4,R4,LSR #24
CMP R4,#fh
LDRGT R4,player_keys ; Used as general flags
ORRGT R4,R4,#4 ; player dead
STRGT R4,player_keys
LDMGTFD R13!,{R0-R6,PC}
MOV R4,#red ; Light cycle colour
ORR R4,R4,#lch<<24
STR R4,[R2,R3,LSL #2]
; Yay!
LDMFD R13!,{R0-R6,PC}

.comp_code
STMFD R13!,{R0-R12,R14}
; Need to do the computer movement & AI...
LDR R0,player_x
LDR R1,player_y
LDR R2,comp_x
LDR R3,comp_y
MOV R0,R0,LSR #24
MOV R1,R1,LSR #24
MOV R2,R2,LSR #24
MOV R3,R3,LSR #24
MOV R4,#0 ; X dir
MOV R5,#0 ; Y dir
CMP R0,R2
MOVLT R4,#255
MOVGT R4,#1
BNE setdir
CMP R1,R3
MOVLT R5,#255
MOVGE R5,#1
.setdir
; Now some semi-intelligent direction choosing code...
; It's a conversion of some old uncommented BASIC code of mine
; Basically it works out whether it's better to turn left or right to reach the target (The player)
MOV R0,R0,LSL #24
MOV R1,R1,LSL #24
MOV R2,R2,LSL #24
MOV R3,R3,LSL #24
MOV R4,R4,LSL #24 ; xd
MOV R5,R5,LSL #24 ; yd
SUB R8,R0,R2 ; x offset
SUB R9,R1,R3 ; y offset
MOV R10,#4 ; Default value
MOV R11,#0 ; Used for some comparisons
CMP R9,#0
CMPGT R5,#0
MOVGT R11,#1 ; y>0 and yd>0
CMP R8,#0
CMPGT R4,#0
MOVGT R11,#1 ; x>0 and xd>0
CMP R9,#0
CMPLT R5,#0
MOVLT R11,#1 ; y<0 and yd<0
CMP R8,#0
CMPLT R4,#0
MOVLT R11,#1 ; x<0 and xd<0
CMP R8,#0
CMPGT R5,#0
MOVGT R11,#2 ; x>0 and yd>0
CMP R9,#0
CMPLT R10,R4
MOVLT R11,#2 ; y<0 and xd>0
CMP R8,#0
CMPLT R5,#0
MOVLT R11,#2 ; x<0 and yd<0
CMP R9,#0
CMPGT R10,R4
MOVGT R11,#2 ; y>0 and xd<0
CMP R9,#0
CMPLT R10,R5
MOVLT R11,#3 ; y<0 and yd>0
CMP R8,#0
CMPLT R10,R4
MOVLT R11,#3 ; x<0 and xd>0
CMP R9,#0
CMPGT R10,R5
MOVGT R11,#3 ; y>0 and yd<0
CMP R8,#0
CMPGT R10,R4
MOVGT R11,#3 ; x>0 and xd<0
; Now do the collision checks...
MOV R0,R2
MOV R1,R3
MOV R6,#0 ; Check count
ADR R7,voxel
.checkloop
; Calc pos & check for collision
ADD R2,R0,R4
ADD R3,R1,R5
MOV R2,R2,LSR #24
MOV R3,R3,LSR #24
ADD R2,R2,R3,LSL #8
LDR R3,[R7,R2,LSL #2]
MOV R3,R3,LSR #24
CMP R3,#fh
BLE checkdone
ADD R6,R6,#1 ; Rotate to avoid wall...
CMP R10,#2
RSBGT R3,R5,#0 ; temp=-yd
MOVGT R5,R4 ; yd=xd
MOVGT R4,R3 ; xd=temp
RSBLE R3,R4,#0 ; temp=-xd
MOVLE R4,R5 ; xd=yd
MOVLE R5,R3 ; yd=temp
CMP R6,#8
BLT checkloop
.checkdone
; Now set current pos to be a wall
MOV R2,R0,LSR #24
MOV R3,R1,LSR #24
ADD R2,R2,R3,LSL #8
MOV R3,#blue
ORR R3,R3,#th<<24
STR R3,[R7,R2,LSL #2]
; Move to new pos...
ADD R0,R0,R4
ADD R1,R1,R5
STR R0,comp_x
STR R1,comp_y
MOV R0,R0,LSR #24
MOV R1,R1,LSR #24
ADD R0,R0,R1,LSL #8
LDR R1,[R7,R0,LSL #2]
MOV R1,R1,LSR #24
CMP R1,#1
LDRGT R1,player_keys
ORRGT R1,R1,#8
STRGT R1,player_keys
LDMGTFD R13!,{R0-R12,PC}
MOV R1,#red
ORR R1,R1,#lch<<24
STR R1,[R7,R0,LSL #2]
LDMFD R13!,{R0-R12,PC}

.resetplayers
STMFD R13!,{R0-R6,R14}
ADR R0,origdata
LDMIA R0,{R1-R6}
ADR R0,player_x
STMIA R0,{R1-R6}
LDMFD R13!,{R0-R6,PC}

.nokeys
STMFD R13!,{R0-R2,R14}
SWI OS_ReadMonotonicTime
ADD R1,R0,#75 ; Wait a bit
.nokeywaitloop
SWI OS_ReadMonotonicTime
CMP R0,R1
BLLT nokeywaitloop
MOV R0,#124
SWI OS_Byte
.nopressednowloop
MOV R0,#&7A
MOV R1,#&87
SWI OS_Byte
CMP R1,#255
BNE nopressednowloop
MOV R0,#21
MOV R1,#0
SWI OS_Byte
LDMFD R13!,{R0-R2,PC}

.rnd_xor DCD &1D872B41
.rnd_num ; Returns a 16 bit random num in R0
STMFD R13!,{R1-R3,R14}
LDR R0,rnd_seed
CMP R0,#0
SWIEQ OS_ReadMonotonicTime ; Get a seed
MOV R1,R0
LDR R2,rnd_xor
MOV R3,#16
MOV R0,#0
.rnd_loop
MOVS R1,R1,LSL #1
EORCS R1,R1,R2
ADC R0,R0,R0 ; *2, possible carry add
SUBS R3,R3,#1
BGT rnd_loop
STR R1,rnd_seed
LDMFD R13!,{R1-R3,PC}

; original light cycle info
.origdata DCD 128<<24 ; Same format as ray coordinates
	  DCD 12<<24
	  DCD 0
	  DCD 0
	  DCD 128<<24
	  DCD 243<<24

; velocity info
.xvel0 DCD 0
.yvel0 DCD 1<<24
.xvel1 DCD 1<<24
.yvel1 DCD 0
.xvel2 DCD 0
.yvel2 DCD 255<<24
.xvel3 DCD 255<<24
.yvel3 DCD 0<<24

; the vectors...
.xstart DCD 0 ; These get set by the code
.ystart DCD 0
.zstart DCD th<<22
.xtop0 DCD 255<<24 ; Offsets from start pos. Faces up the Y axis
.ytop0 DCD 1<<24
.ztop0 DCD 1<<23
.xright0 DCD 1<<24
.yright0 DCD 1<<24
.zright0 DCD 1<<23
.xbot0 DCD 255<<24
.ybot0 DCD 1<<24
.zbot0 DCD 511<<23
.xtop1 DCD 1<<24 ; Up the X axis
.ytop1 DCD 1<<24
.ztop1 DCD 1<<23
.xright1 DCD 1<<24
.yright1 DCD 255<<24
.zright1 DCD 1<<23
.xbot1 DCD 1<<24
.ybot1 DCD 1<<24
.zbot1 DCD 511<<23
.xtop2 DCD 1<<24 ; Down the Y axis
.ytop2 DCD 255<<24
.ztop2 DCD 1<<23
.xright2 DCD 255<<24
.yright2 DCD 255<<24
.zright2 DCD 1<<23
.xbot2 DCD 1<<24
.ybot2 DCD 255<<24
.zbot2 DCD 511<<23
.xtop3 DCD 255<<24 ; Down the X axis
.ytop3 DCD 255<<24
.ztop3 DCD 1<<23
.xright3 DCD 255<<24
.yright3 DCD 1<<24
.zright3 DCD 1<<23
.xbot3 DCD 255<<24
.ybot3 DCD 255<<24
.zbot3 DCD 511<<23

; Screen info...
.screen_stuff DCD 148 : DCD -1
.bank DCD 1

.blanksize DCD 270*1024 ; 270K should do
.error1 DCD 0 : DCB "Hey f00! Me need 270K!",0 : ALIGN

.player_dead DCB 13,10,"Player dead!",13,10,0 : ALIGN
.comp_dead DCB 13,10,"Computer dead!",13,10,0 : ALIGN

; All the vars outside the file...
.screen_start
#set rnd_seed = screen_start+4
#set player_x = rnd_seed+4
#set player_y = player_x+4
#set player_rot = player_y+4
#set player_keys = player_rot+4
#set comp_x = player_keys+4
#set comp_y = comp_x+4
#set tstore = comp_y+4
#set tbuff = tstore+4
#set xdir = tbuff+32
#set ydir = xdir+4
#set zdir = ydir+4
#set xforx = zdir+4
#set yforx = xforx+4
#set zforx = yforx+4
#set xfory = zforx+4
#set yfory = xfory+4
#set zfory = yfory+4
#set voxel = zfory+4