#name voxie
#type &FF8
#base &8000

; 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
SUB R1,R1,#&8000
CMP R1,#270*1024 ; 270K should do (256K voxel, 4K code, 10K other vars & stack)
ADRLT R0,error1
SWILT OS_GenerateError
SWILT OS_Exit
SWI &100+22
SWI &100+13+128
SWI &100+22
SWI &100+13
BL switchbanks
SWI &100+12
BL switchbanks
SWI &100+12
MOV R4,#128<<24 ; Start pos
MOV R5,#128<<24
MOV R6,#0 ; Misc options flags
MOV R7,#0 ; Viewing angle
.newvoxel
BL makevoxel
.erode
BL erodevoxel
BL decodecolours
SWI OS_ReadMonotonicTime
MOV R11,R0
.mainloop
; 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
TST R6,#1
BLEQ vox_draw
TST R6,#1
BLNE vox_tdraw
SWI OS_ReadMonotonicTime
SUB R10,R0,R11
MOV R11,R0
SWI &100+30
SWI &100+32
SWI &100+32
SWI &100+32
SWI &100+32
SWI &100+30
MOV R0,R10
ADR R1,tbuff
MOV R2,#32
SWI XOS_ConvertInteger4
TST R6,#4
SWINE &100+ASC("V")
SWI OS_Write0
TST R6,#4 ; VSync wait?
MOVNE R0,#19
SWINE 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 R7,R7,R10
MOV R1,#121+128 ; Right?
SWI OS_Byte
CMP R1,#&FF
ADDEQ R7,R7,R10
STR R7,ang
MOV R1,#85+128 ; N? (New voxel)
SWI OS_Byte
CMP R1,#255
BEQ newvoxel
MOV R1,#34+128 ; E? (Erode voxel)
SWI OS_Byte
CMP R1,#255
BEQ erode
MOV R1,#99+128 ; V? (Change view)
SWI OS_Byte
CMP R1,#255
BICNE R6,R6,#2
BNE vcskip
TST R6,#2
EOREQ R6,R6,#3
.vcskip
MOV R1,#81+128 ; S? (Toggle VSync)
SWI OS_Byte
CMP R1,#255
BICNE R6,R6,#8
BNE scskip
TST R6,#8
EOREQ R6,R6,#12
.scskip
SWI OS_ReadEscapeState
BCC mainloop
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
ADR R0,screen_stuff
ADR R1,screen_start
SWI OS_ReadVduVariables
LDMFD R13!,{R0-R1,PC}

.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<g<1, 0<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
MOV R11,R0 ; g
BL rnd_num
MOV R12,R0 ; 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
CMP R2,#256
BLT mloop
LDMFD R13!,{R0-R12,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}

.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}

.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
SUBS R2,R2,#1
BGT cloop
LDMFD R13!,{R0-R8,PC}

.vox_tdraw
STMFD R13!,{R0-R4,R14}
LDR R0,screen_start
ADD R0,R0,#32
ADR R1,voxel
MOV R2,#65536
MOV R3,#256
.vtloop
LDR R4,[R1],#4
AND R4,R4,#255
STRB R4,[R0],#1
SUBS R3,R3,#1
ADDLE R0,R0,#64
MOVLE R3,#256
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 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
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
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
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
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 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]
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}

.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
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
CMP R3,#256
BLT eloop2
LDMFD R13!,{R0-R10,PC}

; the vectors...
.xtop DCD 255<<24 ; Offsets
.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

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

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

; All the vars outside the file...
.screen_start ; Screen start pointer
; Text buffer
#set tbuff = screen_start+4
; Viewing angle
#set ang = tbuff+32
; Camera X & Y dirs
#set cxdir = ang+4
#set cydir = cxdir+4
; Random no. generators seed value
#set rnd_seed = cydir+4
; Camera XYZ pos
#set xstart = rnd_seed+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