#name voxie
#type &FF8
#base &8000

; Voxel settings

; Fog/sky colour - whiteish blue
#set fog = 248
; Fog colour boundaries
#set far = 256
#set mid = far*2/3
#set near = far/3

; Set this thing up...
SWI OS_GetEnv
MOV R13,R1
CMP R1,#&4A000  ; 264K should do (256K voxel, 4K code, 4K other vars & stack)
ADRLT R0,error1 ; (&4A000 = 264K + &8000)
SWILT OS_GenerateError
CMP R1,#&4A000
SWILT OS_Exit

; Warp code...

; Must run that FX first, since it scans the screen
BL scanscreen
SWI &100+22
SWI &100+13+128
SWI &100+22
SWI &100+13
SWI OS_RemoveCursors
BL switchbanks
BL makewarp
SWI OS_ReadMonotonicTime
STR R0,rnd_seed
MOV R1,R0
.warploop
SWI OS_ReadMonotonicTime
SUB R0,R0,R1
STR R0,vert_offset
BL drawwarp
BL switchbanks
CMP R0,#1024
BLT warploop

; Switchover code...

BL drawwarp
SWI OS_ReadMonotonicTime
MOV R1,R0
.switch_loop
SWI OS_ReadMonotonicTime
SUB R0,R0,R1
MOV R0,R0,LSL #1
CMP R0,#255
BGT switch_done
LDR R2,screen_start
RSB R3,R0,#256
.switch_skip
SUBS R3,R3,#1
ADDGE R2,R2,#320
BGT switch_skip
MOV R4,#320
MUL R0,R4,R0
.switch_blank
SUBS R0,R0,#4
STRGE R3,[R2],#4
BGT switch_blank
BL switchbanks
B switch_loop
.switch_done

; Voxel code...
SWI OS_ReadMonotonicTime ; Init scrolly message
STR R0,scrolly_time
SWI &100+12
BL switchbanks
SWI &100+12
MOV R4,#128<<24 ; Start pos
MOV R5,#128<<24
MOV R6,#0 ; Viewing angle
BL makevoxel
BL erodevoxel
BL decodecolours
BL scrolly_finish
SWI OS_ReadMonotonicTime
MOV R11,R0
.voxelloop
; Get height...
ADR R0,voxel
MOV R1,R4,LSR #24
MOV R2,R5,LSR #24
ADD R1,R1,R2,LSL #8
LDR R0,[R0,R1,LSL #2]
MOV R0,R0,LSR #24
ADD R0,R0,#12
MOV R0,R0,LSL #23
STR R4,xstart
STR R5,ystart
STR R0,zstart
BL calcdir
BL vox_draw
SWI OS_ReadMonotonicTime
SUB R10,R0,R11
MOV R11,R0
MOV R0,#19
SWI OS_Byte
BL switchbanks
LDR R8,cxdir
LDR R9,cydir
ADD R10,R10,#1 ; Increase speed a bit
MUL R8,R10,R8
MUL R9,R10,R9
MOV R0,#121
MOV R1,#57+128 ; Up arrow?
SWI OS_Byte
CMP R1,#&FF
ADDEQ R4,R4,R8
ADDEQ R5,R5,R9
MOV R1,#41+128 ; Down?
SWI OS_Byte
CMP R1,#&FF
SUBEQ R4,R4,R8
SUBEQ R5,R5,R9
MOV R1,#25+128 ; Left?
SWI OS_Byte
CMP R1,#&FF
SUBEQ R6,R6,R10
MOV R1,#121+128 ; Right?
SWI OS_Byte
CMP R1,#&FF
ADDEQ R6,R6,R10
STR R6,ang
SWI OS_ReadEscapeState
BCC voxelloop
SWI OS_RestoreCursors
SWI OS_Exit

; Warp FX code...

; Need to create a warp map...
; Each byte is the Y pos we should use to read the image of the desktop
.makewarp
STMFD R13!,{R0-R5,R14}
MOV R0,#0
MOV R1,#-128
ADR R2,warp
.makewarp_loop
MOV R3,R0
MOV R0,R1
BL getcos
MUL R5,R0,R3
CMP R3,#0
RSBLT R5,R5,#0
MOV R0,R3
MOV R5,R5,LSR #10
ADD R5,R5,#128
ADD R5,R5,R1
.makewarp_store
STRB R5,[R2],#1
ADD R0,R0,#1
CMP R0,#160
MOVGE R0,#-160
CMP R0,#0
ADDEQ R1,R1,#1
CMP R1,#128
BLT makewarp_loop
LDMFD R13!,{R0-R5,PC}

.drawwarp
STMFD R13!,{R0-R12,R14}
ADR R0,warp
ADR R1,desktop
LDR R2,screen_start
MOV R3,#0
LDR R4,vert_offset
MOV R5,#0
MOV R6,#320
.drawwarp_loop
LDMIA R0!,{R9-R12,R14}

AND R7,R9,#&FF ; Can't be bothered commenting this code...
ADD R7,R7,R4
AND R7,R7,#255
MUL R7,R6,R7
LDR R8,[R1,R7]
MOV R8,R8,LSL #24
AND R7,R9,#&FF00
ADD R7,R4,R7,LSR #8
AND R7,R7,#255
MUL R7,R6,R7
LDR R7,[R1,R7]
MOV R7,R7,LSR #8
MOV R8,R8,LSR #8
ORR R8,R8,R7,LSL #24
AND R7,R9,#&FF0000
ADD R7,R4,R7,LSR #16
AND R7,R7,#255
MUL R7,R6,R7
LDR R7,[R1,R7]
MOV R7,R7,LSR #16
MOV R8,R8,LSR #8
ORR R8,R8,R7,LSL #24
AND R7,R9,#&FF000000
ADD R7,R4,R7,LSR #24
AND R7,R7,#255
MUL R7,R6,R7
LDR R7,[R1,R7]
AND R7,R7,#&FF000000
MOV R8,R8,LSR #8
ORR R9,R8,R7
ADD R1,R1,#4

AND R7,R10,#&FF
ADD R7,R7,R4
AND R7,R7,#255
MUL R7,R6,R7
LDR R8,[R1,R7]
MOV R8,R8,LSL #24
AND R7,R10,#&FF00
ADD R7,R4,R7,LSR #8
AND R7,R7,#255
MUL R7,R6,R7
LDR R7,[R1,R7]
MOV R7,R7,LSR #8
MOV R8,R8,LSR #8
ORR R8,R8,R7,LSL #24
AND R7,R10,#&FF0000
ADD R7,R4,R7,LSR #16
AND R7,R7,#255
MUL R7,R6,R7
LDR R7,[R1,R7]
MOV R7,R7,LSR #16
MOV R8,R8,LSR #8
ORR R8,R8,R7,LSL #24
AND R7,R10,#&FF000000
ADD R7,R4,R7,LSR #24
AND R7,R7,#255
MUL R7,R6,R7
LDR R7,[R1,R7]
AND R7,R7,#&FF000000
MOV R8,R8,LSR #8
ORR R10,R8,R7
ADD R1,R1,#4

AND R7,R11,#&FF
ADD R7,R7,R4
AND R7,R7,#255
MUL R7,R6,R7
LDR R8,[R1,R7]
MOV R8,R8,LSL #24
AND R7,R11,#&FF00
ADD R7,R4,R7,LSR #8
AND R7,R7,#255
MUL R7,R6,R7
LDR R7,[R1,R7]
MOV R7,R7,LSR #8
MOV R8,R8,LSR #8
ORR R8,R8,R7,LSL #24
AND R7,R11,#&FF0000
ADD R7,R4,R7,LSR #16
AND R7,R7,#255
MUL R7,R6,R7
LDR R7,[R1,R7]
MOV R7,R7,LSR #16
MOV R8,R8,LSR #8
ORR R8,R8,R7,LSL #24
AND R7,R11,#&FF000000
ADD R7,R4,R7,LSR #24
AND R7,R7,#255
MUL R7,R6,R7
LDR R7,[R1,R7]
AND R7,R7,#&FF000000
MOV R8,R8,LSR #8
ORR R11,R8,R7
ADD R1,R1,#4

AND R7,R12,#&FF
ADD R7,R7,R4
AND R7,R7,#255
MUL R7,R6,R7
LDR R8,[R1,R7]
MOV R8,R8,LSL #24
AND R7,R12,#&FF00
ADD R7,R4,R7,LSR #8
AND R7,R7,#255
MUL R7,R6,R7
LDR R7,[R1,R7]
MOV R7,R7,LSR #8
MOV R8,R8,LSR #8
ORR R8,R8,R7,LSL #24
AND R7,R12,#&FF0000
ADD R7,R4,R7,LSR #16
AND R7,R7,#255
MUL R7,R6,R7
LDR R7,[R1,R7]
MOV R7,R7,LSR #16
MOV R8,R8,LSR #8
ORR R8,R8,R7,LSL #24
AND R7,R12,#&FF000000
ADD R7,R4,R7,LSR #24
AND R7,R7,#255
MUL R7,R6,R7
LDR R7,[R1,R7]
AND R7,R7,#&FF000000
MOV R8,R8,LSR #8
ORR R12,R8,R7
ADD R1,R1,#4

AND R7,R14,#&FF
ADD R7,R7,R4
AND R7,R7,#255
MUL R7,R6,R7
LDR R8,[R1,R7]
MOV R8,R8,LSL #24
AND R7,R14,#&FF00
ADD R7,R4,R7,LSR #8
AND R7,R7,#255
MUL R7,R6,R7
LDR R7,[R1,R7]
MOV R7,R7,LSR #8
MOV R8,R8,LSR #8
ORR R8,R8,R7,LSL #24
AND R7,R14,#&FF0000
ADD R7,R4,R7,LSR #16
AND R7,R7,#255
MUL R7,R6,R7
LDR R7,[R1,R7]
MOV R7,R7,LSR #16
MOV R8,R8,LSR #8
ORR R8,R8,R7,LSL #24
AND R7,R14,#&FF000000
ADD R7,R4,R7,LSR #24
AND R7,R7,#255
MUL R7,R6,R7
LDR R7,[R1,R7]
AND R7,R7,#&FF000000
MOV R8,R8,LSR #8
ORR R14,R8,R7
ADD R1,R1,#4

STMIA R2!,{R9-R12,R14}
ADD R3,R3,#4*5
CMP R3,#320
BLT drawwarp_loop
MOV R3,#0
ADD R5,R5,#1
SUB R1,R1,#320
CMP R5,#256
BLT drawwarp_loop
LDMFD R13!,{R0-R12,PC}

.scanscreen ; Calculate desktop image...
	    ; Must work in all modes :(
STMFD R13!,{R0-R12,R14}
; First, get screen info...
ADR R0,screen_stuff
ADR R1,screen_start
SWI OS_ReadVduVariables
ADR R12,desktop
MOV R5,#0 ; X
MOV R6,#0 ; Y
ADR R7,screen_xeig
LDMIA R7,{R7-R10}
MOV R7,R9,LSL R7 ; Screen size in OS units
MOV R8,R10,LSL R8
MOV R7,R7,LSL #16 ; Make a more accurate copy for the division
MOV R8,R8,LSL #16
; Divide R7 by 320...
MOV R9,R7
MOV R10,#32
MOV R11,#0
.scanscreen_div
MOVS R9,R9,LSL #1
ADC R11,R11,R11
CMP R11,#320
ADDHS R9,R9,#1
SUBHS R11,R11,#320
SUBS R10,R10,#1
BGT scanscreen_div
MOV R10,R8,LSR #8 ; /256
; Now read the pixels and store...
MOV R7,#0
LDR R11,screen_cols
.scanscreen_loop
MOV R0,R7,LSR #16
MOV R1,R8,LSR #16
SWI OS_ReadPoint
ORR R3,R2,R3 ; Combine col & tint
; Now convert to 8bpp...
CMP R11,#65535
BNE isnt_16bpp
AND R0,R3,#31*32*32 ; Blue
MOV R0,R0,LSR #5+5+3 ; 2 bit value
AND R1,R3,#31*32 ; Green
MOV R1,R1,LSR #5+3
AND R2,R3,#31
MOV R2,R2,LSR #3
AND R3,R3,R3,LSR #5
ORR R3,R3,R3,LSR #5 ; Simple tint calculation
.calc8bpp
AND R3,R3,#3
ORR R3,R3,R1,LSL #5 ; Add green
AND R1,R2,#1
ORR R3,R3,R1,LSL #2 ; Red low bit
AND R2,R2,#2
ORR R3,R3,R2,LSL #3 ; Red high bit
AND R1,R0,#1
ORR R3,R3,R1,LSL #3 ; Blue low bit
AND R0,R0,#2
ORR R3,R3,R0,LSL #6 ; Blue high bit
.scanscreen_store
STRB R3,[R12],#1
ADD R7,R7,R9
ADD R5,R5,#1
CMP R5,#320
MOVGE R5,#0
ADDGE R6,R6,#1
MOVGE R7,#0
SUBGE R8,R8,R10
CMP R6,#256
BLT scanscreen_loop
LDMFD R13!,{R0-R12,PC}
.isnt_16bpp
CMP R11,#63 ; Unpaletted 8bpp
BNE isnt_8bpp
; Bah! convert from TTBBGGRR to internal format :(
AND R0,R3,#48 ; Blue
MOV R0,R0,LSR #4
AND R1,R3,#12 ; Green
MOV R1,R1,LSR #2
AND R2,R3,#3 ; Red
MOV R3,R3,LSR #6 ; Tint
B calc8bpp
.isnt_8bpp
CMP R11,#-1
BEQ is_24bpp
; Use another SWI to lookup colour...
MOV R0,R3
MOV R1,#16
SWI OS_ReadPalette
MOV R3,R2,LSR #8
.is_24bpp
AND R0,R3,#&FF0000 ; Blue
MOV R0,R0,LSR #22 ; 2 bit value again
AND R1,R3,#&FF00
MOV R1,R1,LSR #14
AND R2,R3,#&FF
MOV R2,R2,LSR #6
AND R3,R3,R3,LSR #8
AND R3,R3,R3,LSR #8
B calc8bpp

; Voxel FX code

.makevoxel
; Voxel based around the equation:
; z=(gcos (ax+by+c)+hcos (dx+ey+f))/2
; Where -4<a<4, -1<b<1, 0<c<256, -1<d<1, -4<e<4, 0<f<256, 0.25<g<1, 0.25<h<1
; And cos is our cos func using the lookup
STMFD R13!,{R0-R12,R14}
MOV R1,#8
MOV R2,#2
BL rnd_num
MUL R3,R0,R1 ; a
SUB R3,R3,#4*65536
BL rnd_num
MUL R4,R0,R2 ; b
SUB R4,R4,#65536
BL rnd_num
MOV R5,R0,LSL #8 ; c
BL rnd_num
MUL R6,R0,R2 ; d
SUB R6,R6,#65536
BL rnd_num
MUL R7,R0,R1 ; e
SUB R7,R7,#4*65536
BL rnd_num
MOV R8,R0,LSL #8 ; f
BL rnd_num
ORR R11,R0,#&4000 ; g
BL rnd_num
ORR R12,R0,#&4000 ; h
; All 16.16 numbers
ADR R9,voxel
MOV R1,#0
MOV R2,#0
.mloop
; Calc 1st one
MLA R0,R1,R3,R5
MLA R0,R2,R4,R0
MOV R0,R0,LSR #16
BL getcos
MUL R10,R0,R11
MLA R0,R1,R6,R8
MLA R0,R2,R7,R0
MOV R0,R0,LSR #16
BL getcos
MLA R10,R0,R12,R10
MOV R10,R10,LSR #19 ; 16 for the g&h MUL, 1 for cos+cos, 2 for the extra getcos accuracy
CMP R10,#0
MOVLT R10,#0
CMP R10,#255
MOVGT R10,#255
MOV R10,R10,LSL #24
STR R10,[R9],#4
ADD R1,R1,#1
CMP R1,#256
MOVGE R1,#0
ADDGE R2,R2,#1
BLGE scrolly
CMP R2,#256
BLT mloop
LDMFD R13!,{R0-R12,PC}

.decodecolours
STMFD R13!,{R0-R8,R14}
ADR R8,voxel
ADR R1,colmap
MOV R2,#65536
.cloop
LDR R3,[R8]
MOV R4,R3,LSR #24
BL rnd_num
AND R5,R0,#3
ADD R4,R4,R5
AND R5,R0,#12
SUBS R4,R4,R5,LSR #2
MOVLT R4,#0
CMP R4,#255
MOVGT R4,#255
MOV R4,R4,LSR #1 ; 128 colours
ADD R4,R4,R4,LSL #1 ; *3
ADD R4,R1,R4
LDRB R5,[R4],#1
LDRB R6,[R4],#1
LDRB R7,[R4]
ORR R3,R3,R5 ; Near
ORR R3,R3,R6,LSL #8 ; Mid
ORR R3,R3,R7,LSL #16 ; Far
STR R3,[R8],#4
BICS R3,R2,#&FC00 ; For some reason a MOVS R3,R2,LSR #10 and a BLCC won't work...
BLEQ scrolly
SUBS R2,R2,#1
BGT cloop
LDMFD R13!,{R0-R8,PC}

.getcos ; R0=angle (0-255), returns value (0-1023)
STMFD R13!,{R1-R4,R14}
AND R0,R0,#255
MOV R1,R0
ADR R2,costab
MOV R3,R0,LSR #2
TST R3,#32
RSBNE R3,R3,#63
LDRB R3,[R2,R3] ; Get byte
ADD R0,R0,#4
MOV R4,R0,LSR #2
CMP R4,#64
MOVGE R4,#63
TST R4,#32
RSBNE R4,R4,#63
LDRB R4,[R2,R4] ; And following one
AND R1,R1,#3 ; Amount of R3
RSB R0,R1,#4 ; Amount of R4
MUL R3,R0,R3
MLA R0,R4,R1,R3
LDMFD R13!,{R1-R4,PC}

.erodevoxel ; Smooth out the edges
; Need it to be close to ([255-x]+[x])/2 when x is near 255 or 0, but close to [x] when close to 128...
; When x=128, [x]=1*[x] and [255-x]=0*[255-x]
; When x=0, [x]=0.5*[x] and [255-x]=0.5*[255-x]
; So [x] = [x+128]/256 and [255-x] = 1-[x+128]/256...
; Same for Y...
; Have to do both x and 255-x at once to solve problems
STMFD R13!,{R0-R10,R14}
ADR R0,voxel
MOV R1,#0 ; x
MOV R2,#255 ; 255-x
MOV R3,#0 ; y
.eloop
ADD R4,R3,R1,LSL #8
LDR R5,[R0,R4,LSL #2]
ADD R6,R3,R2,LSL #8
LDR R7,[R0,R6,LSL #2]
MOV R5,R5,LSR #24
MOV R7,R7,LSR #24
ADD R8,R1,#128
MUL R9,R8,R5
RSB R8,R8,#256
MLA R9,R7,R8,R9
MOV R9,R9,LSR #8
MOV R9,R9,LSL #24
STR R9,[R0,R4,LSL #2]
MUL R10,R8,R5
RSB R8,R8,#256
MLA R10,R7,R8,R10
MOV R10,R10,LSR #8
MOV R10,R10,LSL #24
STR R10,[R0,R6,LSL #2]
ADD R1,R1,#1
SUB R2,R2,#1
CMP R1,R2
BLT eloop
MOV R1,#0
MOV R2,#255
ADD R3,R3,#1
BL scrolly
CMP R3,#256
BLT eloop
MOV R1,#0 ; y
MOV R2,#255 ; 255-y
MOV R3,#0 ; x
.eloop2
ADD R4,R1,R3,LSL #8
LDR R5,[R0,R4,LSL #2]
ADD R6,R2,R3,LSL #8
LDR R7,[R0,R6,LSL #2]
MOV R5,R5,LSR #24
MOV R7,R7,LSR #24
ADD R8,R1,#128
MUL R9,R8,R5
RSB R8,R8,#256
MLA R9,R7,R8,R9
MOV R9,R9,LSR #8
MOV R9,R9,LSL #24
STR R9,[R0,R4,LSL #2]
MUL R10,R8,R5
RSB R8,R8,#256
MLA R10,R7,R8,R10
MOV R10,R10,LSR #8
MOV R10,R10,LSL #24
STR R10,[R0,R6,LSL #2]
ADD R1,R1,#1
SUB R2,R2,#1
CMP R1,R2
BLT eloop2
MOV R1,#0
MOV R2,#255
ADD R3,R3,#1
BL scrolly
CMP R3,#256
BLT eloop2
LDMFD R13!,{R0-R10,PC}

; Scrolly message bit...

.scrolly
STMFD R13!,{R0-R3,R14}
SWI &100+12
SWI OS_ReadMonotonicTime
LDR R1,scrolly_time
SUB R0,R0,R1
MOV R0,R0,LSL #3
RSB R1,R0,#1300
SWI &100+5
MOV R0,#4
MOV R2,#528
SWI OS_Plot
ADR R0,scrolly_message
SWI OS_Write0
MOV R0,#19
SWI OS_Byte
BL switchbanks
LDMFD R13!,{R0-R3,PC}

.scrolly_finish
STMFD R13!,{R0-R1,R14}
SWI &100+7
.scrolly_loop
SWI OS_ReadMonotonicTime
LDR R1,scrolly_time
SUB R0,R0,R1
BL scrolly
; Hmm... Work out how long the message is displayed for...
; Each cs it moves on 8 OS units, or 2 pixels...
; So about 4cs per char...
CMP R0,#((xtop-scrolly_message)*4)+(1300/8)
BLT scrolly_loop
LDMFD R13!,{R0-R1,PC}

.scrolly_message DCB "Hello. This is a scrolly message. Its purpose in life is to fill in some spare space I had in the code, and to give those people without fast computers something to read while the voxel is generated." : DCB " On a plain StrongARM RiscPC, the voxel should be done round about now, so you should hear a beep. Slower computers will have to wait longer, maybe even longer than the message runs for..." : DCB "                                         By the way, the cursor keys move the camera, and escape quits.        ",4,0 : ALIGN

; the vectors...
.xtop DCD 255<<24 ; Offsets from the camera pos
.ytop DCD 1<<24
.ztop DCD 1<<23
.xright DCD 1<<24
.yright DCD 1<<24
.zright DCD 1<<23
.xbot DCD 255<<24
.ybot DCD 1<<24
.zbot DCD 511<<23

; Colour map...
; 128 colours, each entry as 3 8bpp colours (near,mid,far)
; Generated by the Maketable BASIC app
.colmap
DCB 0,41,196 ; Height 0
DCB 0,41,196 ; Height 2
DCB 0,41,196 ; Height 4
DCB 0,41,197 ; Height 6
DCB 0,41,197 ; Height 8
DCB 0,41,197 ; Height 10
DCB 1,44,197 ; Height 12
DCB 1,44,197 ; Height 14
DCB 1,44,197 ; Height 16
DCB 1,44,197 ; Height 18
DCB 1,44,197 ; Height 20
DCB 1,45,197 ; Height 22
DCB 2,45,197 ; Height 24
DCB 4,45,197 ; Height 26
DCB 5,45,197 ; Height 28
DCB 5,45,197 ; Height 30
DCB 5,45,197 ; Height 32
DCB 5,45,197 ; Height 34
DCB 5,45,197 ; Height 36
DCB 5,45,197 ; Height 38
DCB 5,45,197 ; Height 40
DCB 5,45,197 ; Height 42
DCB 6,46,197 ; Height 44
DCB 6,46,198 ; Height 46
DCB 6,46,198 ; Height 48
DCB 6,46,208 ; Height 50
DCB 6,46,208 ; Height 52
DCB 6,46,208 ; Height 54
DCB 6,46,208 ; Height 56
DCB 6,57,208 ; Height 58
DCB 17,57,208 ; Height 60
DCB 48,57,208 ; Height 62
DCB 48,57,208 ; Height 64
DCB 48,57,208 ; Height 66
DCB 48,57,209 ; Height 68
DCB 48,57,209 ; Height 70
DCB 48,57,209 ; Height 72
DCB 48,57,209 ; Height 74
DCB 48,57,209 ; Height 76
DCB 49,88,209 ; Height 78
DCB 38,88,209 ; Height 80
DCB 38,88,209 ; Height 82
DCB 38,88,209 ; Height 84
DCB 38,88,209 ; Height 86
DCB 38,78,209 ; Height 88
DCB 39,78,209 ; Height 90
DCB 39,78,209 ; Height 92
DCB 39,78,209 ; Height 94
DCB 39,78,209 ; Height 96
DCB 39,78,198 ; Height 98
DCB 38,78,198 ; Height 100
DCB 38,78,198 ; Height 102
DCB 38,77,198 ; Height 104
DCB 38,77,198 ; Height 106
DCB 38,77,198 ; Height 108
DCB 37,77,198 ; Height 110
DCB 35,77,198 ; Height 112
DCB 65,77,198 ; Height 114
DCB 65,77,198 ; Height 116
DCB 65,76,198 ; Height 118
DCB 64,76,198 ; Height 120
DCB 64,74,198 ; Height 122
DCB 64,73,197 ; Height 124
DCB 64,73,197 ; Height 126
DCB 64,73,197 ; Height 128
DCB 64,73,197 ; Height 130
DCB 64,73,197 ; Height 132
DCB 64,74,198 ; Height 134
DCB 65,77,198 ; Height 136
DCB 65,77,198 ; Height 138
DCB 65,77,198 ; Height 140
DCB 66,77,198 ; Height 142
DCB 68,77,198 ; Height 144
DCB 69,77,198 ; Height 146
DCB 69,78,198 ; Height 148
DCB 69,78,198 ; Height 150
DCB 70,78,198 ; Height 152
DCB 70,78,198 ; Height 154
DCB 70,78,209 ; Height 156
DCB 70,89,209 ; Height 158
DCB 88,89,209 ; Height 160
DCB 88,89,209 ; Height 162
DCB 88,90,210 ; Height 164
DCB 88,90,210 ; Height 166
DCB 89,90,210 ; Height 168
DCB 89,90,210 ; Height 170
DCB 89,90,210 ; Height 172
DCB 89,209,210 ; Height 174
DCB 89,209,210 ; Height 176
DCB 90,209,210 ; Height 178
DCB 90,209,210 ; Height 180
DCB 90,209,241 ; Height 182
DCB 90,210,241 ; Height 184
DCB 90,210,241 ; Height 186
DCB 91,210,241 ; Height 188
DCB 91,210,241 ; Height 190
DCB 91,241,241 ; Height 192
DCB 91,241,241 ; Height 194
DCB 244,241,241 ; Height 196
DCB 244,241,241 ; Height 198
DCB 245,242,242 ; Height 200
DCB 245,242,242 ; Height 202
DCB 245,242,248 ; Height 204
DCB 246,242,249 ; Height 206
DCB 246,252,249 ; Height 208
DCB 253,252,249 ; Height 210
DCB 253,252,249 ; Height 212
DCB 254,253,249 ; Height 214
DCB 254,253,249 ; Height 216
DCB 254,253,249 ; Height 218
DCB 255,253,249 ; Height 220
DCB 255,254,250 ; Height 222
DCB 255,254,250 ; Height 224
DCB 255,254,250 ; Height 226
DCB 255,254,250 ; Height 228
DCB 255,254,250 ; Height 230
DCB 255,254,250 ; Height 232
DCB 255,254,250 ; Height 234
DCB 255,254,250 ; Height 236
DCB 255,254,250 ; Height 238
DCB 255,254,250 ; Height 240
DCB 255,254,250 ; Height 242
DCB 255,254,250 ; Height 244
DCB 255,254,250 ; Height 246
DCB 255,254,250 ; Height 248
DCB 255,254,250 ; Height 250
DCB 255,254,250 ; Height 252
DCB 255,254,250 ; Height 254

.costab ; 32 byte cos table, from Maketable2
DCB 255
DCB 255
DCB 253
DCB 250
DCB 246
DCB 240
DCB 234
DCB 226
DCB 218
DCB 209
DCB 199
DCB 188
DCB 176
DCB 165
DCB 152
DCB 140
DCB 128
DCB 115
DCB 103
DCB 90
DCB 79
DCB 67
DCB 56
DCB 46
DCB 37
DCB 29
DCB 21
DCB 15
DCB 9
DCB 5
DCB 2
DCB 0

.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
ADR R0,screen_stuff
ADR R1,screen_start
SWI OS_ReadVduVariables
LDMFD R13!,{R0-R1,PC}

.rnd_xor DCD &1D872B41
.rnd_num ; Returns a 16 bit random num in R0
STMFD R13!,{R1-R3,R14}
LDR R1,rnd_seed
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}

.calcdir
STMFD R13!,{R0-R6,R14}
LDR R1,ang ; Work out which way we're facing, then the ray dir increments
MOV R1,R1,LSR #1 ; The turning code calculates to half-angles (0-511) for a bit more accuracy, but the getcos code only works with full angles (0-255)
SUB R0,R1,#32 ; 45 degrees
BL getcos
SUB R2,R0,#512 ; -512 to +512
MOV R2,R2,LSL #15 ; Convert to correct values
SUB R0,R1,#64+32 ; 90+45 degrees (Get sin)
BL getcos
SUB R3,R0,#512
MOV R3,R3,LSL #15
RSB R4,R3,#0 ; Rotate through 90 degrees for the other dir
MOV R5,R2
STR R2,xtop
STR R3,ytop
STR R4,xright
STR R5,yright
STR R2,xbot
STR R3,ybot
MOV R0,R1
BL getcos
SUB R2,R0,#512
MOV R2,R2,LSL #14
SUB R0,R1,#64
BL getcos
SUB R3,R0,#512
MOV R3,R3,LSL #14
STR R2,cxdir
STR R3,cydir
ADR R6,xtop
LDMIA R6,{R3-R5} ; Top left corner
ADR R6,xright ; Top right corner
LDMIA R6,{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}
ADR R6,xbot
LDMIA R6,{R0-R2} ; Bottom left corner
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-R6,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 3 is colour (Near, mid & far, &HHFFMMNN)
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
BGE foundpix
ADD R2,R2,R5
ADD R3,R3,R6
ADDS R4,R4,R7
BLT fogdraw_now ; Off the top
ADD R8,R8,#1
CMP R8,#far
BGE fogdraw
B vloop
.foundpix ; Work out colour...
MOV R14,R8
TST R2,#1<<23
SUBNE R14,R14,#4 ; Make the fog a bit fuzzy
TST R3,#1<<23
SUBNE R14,R14,#4
CMP R14,#near
MOVGT R9,R9,LSR #8
CMP R14,#mid
MOVGT R9,R9,LSR #8
; 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
; Of course this won't work very well if the camera is pitched or rolled :(
ADR R14,xfory
LDMIA R14,{R5-R7}
MUL R5,R8,R5
MUL R6,R8,R6
MUL R7,R8,R7
.dloop
STRB R9,[R0],#-320
SUBS R12,R12,#1
BLT dfin
ADD R2,R2,R5
ADD R3,R3,R6
ADDS 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
RSB 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
RSB R12,R12,#255 ; Revert to normal
B vloop
.fogdraw ; Draw fog up to top of screen
MOV R14,R8
TST R2,#1<<25
SUBNE R14,R14,#4
TST R3,#1<<25
SUBNE R14,R14,#4
CMP R14,#far
BLT vloop
.fogdraw_now ; Skip the fog fuzzying
MOV R9,#fog
.fogloop
STRB R9,[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}

; Screen info...
.screen_stuff DCD 148 : DCD 3 : DCD 4 : DCD 5 : DCD 11 : DCD 12 : DCD -1
.bank DCD 1

.error1 DCD 0 : DCB "Hey f00! Me need 264K!",0 : ALIGN

; All the vars outside the file...
.screen_start ; Screen info
#set screen_cols = screen_start+4
#set screen_xeig = screen_cols+4
#set screen_yeig = screen_xeig+4
#set screen_width = screen_yeig+4
#set screen_height = screen_width+4
; Random no. generator seed value
#set rnd_seed = screen_height+4

; Warp specific vars (Use the same mem to save space)

; Vertical offset to use when scrolling
#set vert_offset = rnd_seed+4
; Warp map
#set warp = vert_offset+4
; Image of the desktop
#set desktop = warp+(320*256)

; Voxel specific vars

; Scrolly message while voxel is generated...
#set scrolly_time = rnd_seed+4
; Viewing angle
#set ang = scrolly_time+4
; Camera X & Y dirs
#set cxdir = ang+4
#set cydir = cxdir+4
; Camera XYZ pos
#set xstart = cydir+4
#set ystart = xstart+4
#set zstart = ystart+4
; Start ray XYZ dir
#set xdir = zstart+4
#set ydir = xdir+4
#set zdir = ydir+4
; Ray dir increment for every screen X
#set xforx = zdir+4
#set yforx = xforx+4
#set zforx = yforx+4
; Ray dir increment for every screen Y
#set xfory = zforx+4
#set yfory = xfory+4
#set zfory = yfory+4
; Voxel
#set voxel = zfory+4