; This is designed to be serial-line downloaded to cdcode. ; ; Our memory map: ; ; [8c000000,8c010000) Stack (r15 set by cdcode) ; [8c010000,8c01????) cdcode ; [8c020000,8c02????) Us .include "regs.s" .include "ta-cmds.s" .include "maple-bits.s" VRAM_BASE_32 = 0xa5000000 VRAM_BASE_64 = 0xa4000000 VRAM_SIZE = 8 << 20 FRAME_X = 640 FRAME_Y = 480 ; Layout of the stuff we keep in video RAM. . = VRAM_BASE_64 ; Textures. We have only one texture, used for maze walls, ; which we use with three different palettes depending on the ; orientation of the wall in question. It's an 8x8 texture, ; occupying 64 bytes. texture__base = . texture_wall: .space 64 texture__end = . . = VRAM_BASE_32 + [[texture__end - texture__base] / 2] .align 8 ; Space to render into. Each field takes FRAME_X*FRAME_Y ; pixels at two bytes per pixel. render_buf_a: .space 2 * FRAME_X * FRAME_Y render_buf_b: .space 2 * FRAME_X * FRAME_Y . = 0x8c020000 .sz any .pr any SETS.L #main,r0 jmp @r0 nop SETCONST ; Our "data segment". ; Stack pointer for abrupt return to cdcode. throw_sp: .space 4 ; RNG state. .align 4 RNG_STATE_WORDS = 64 rng_state: .space 4 * RNG_STATE_WORDS rng_hand: .space 1 ; The maze itself. MZX = 3 MZY = 3 MZZ = 3 MZXY = MZX * MZY MZWALLS = [MZX * MZY * MZZ * 3] - [MZX * MZY] - [MZY * MZZ] - [MZZ * MZX] MZCELLS = MZX * MZY * MZZ MZMAX = MZX .if MZY > MZMAX MZMAX = MZY .endif .if MZZ > MZMAX MZMAX = MZZ .endif ; Note there is code in init_maze that knows that MZC_Mx can be ; converted to MZC_Px by shifting right by four bits. MZC_PX = 0x0001 MZC_PY = 0x0002 MZC_PZ = 0x0004 MZC_S = 0x0008 MZC_MX = 0x0010 MZC_MY = 0x0020 MZC_MZ = 0x0040 MZC_G = 0x0080 MZC_P = 0x0100 .align 2 maze: .space 2 * MZCELLS .align 4 mwalls: .space 4 * MZWALLS .align 2 mcells: .space 2 * MZCELLS ; Our "text segment". .align 2 main: ; Things we need to do here: ; - Set up the FPU (including clearing SR.FD) ; - Set up the VBR (including clearing SR.BL and SR.RB) ; - Save r10-r15 against a possible return to cdcode ; Things we do not need to do because cdcode has done them: ; - Set up the SCIF (r14 is the SCIF's base address) ; - Set up a stack (r15 is cdcode's stack pointer) ; We do, though, need to put r14 in the gbr; all we use the gbr for is ; access to the SCIF. We could use other addressing modes, but the ; gbr-relative modes have 8 bits of offset, whereas the other ; register-plus-offset modes have only 4. mov.l r14,@-r15 mov.l r13,@-r15 mov.l r12,@-r15 mov.l r11,@-r15 mov.l r10,@-r15 ldc r14,gbr SETS.L #0,r1 lds r1,fpscr .sz 0 .pr 0 SETS.L #intvec,r0 ldc r0,vbr stc sr,r1 SETS.L #~[SR_FD|SR_RB|SR_BL],r2 and r2,r1 ldc r1,sr ; Note that r0-r7 may have changed if we switched banks. SETS.L #throw_sp,r0 mov.l r15,@r0 ; Application code begins here. bsr init_rng nop bsr init_maze nop bsr print_maze nop throw_out: mov.l @r15+,r10 mov.l @r15+,r11 mov.l @r15+,r12 mov.l @r15+,r13 mov.l @r15+,r14 jmp @r11 nop print_maze: sts.l pr,@-r15 SETS.L #maze,r8 SETS.L #MZX,r9 SETS.L #MZY,r10 SETS.L #MZZ,r11 SETS.L #0,r14 ; z 3: SETS.L #0,r13 ; y 2: SETS.L #0,r12 ; x 1: bsr putchar mov #'(,r1 bsr printdec mov r12,r1 bsr putchar mov #',,r1 bsr printdec mov r13,r1 bsr putchar mov #',,r1 bsr printdec mov r14,r1 bsr putchar mov #'),r1 mul.l r10,r14 sts macl,r0 add r13,r0 mul.l r9,r0 sts macl,r0 add r12,r0 SHLL #1,r0 mov.w @(r0,r8),r2 SHLL #25,r2 shll r2 movt r1 bsr putchar add #'0,r1 shll r2 movt r1 bsr putchar add #'0,r1 shll r2 movt r1 bsr putchar add #'0,r1 shll r2 shll r2 movt r1 bsr putchar add #'0,r1 shll r2 movt r1 bsr putchar add #'0,r1 shll r2 movt r1 bsr putchar add #'0,r1 bsr putchar mov #13,r1 bsr putchar mov #10,r1 add #1,r12 cmp/hi r12,r9 bt 1b add #1,r13 cmp/hi r13,r10 bt 2b add #1,r14 cmp/hi r14,r11 bt 3b lds.l @r15+,pr rts nop init_maze: sts.l pr,@-r15 ; Clear all maze cells and all cell clump IDs. SETS.L #MZCELLS,r1 SETS.L #0,r2 SETS.L #maze+[2*MZCELLS],r3 SETS.L #mcells+[2*MZCELLS],r4 1: dt r1 mov.w r2,@-r3 bf/s 1b mov.w r2,@-r4 ; Initialize the walls. SETS.L #mwalls,r11 SETS.L #MZWALLS+1,r10 SETS.L #MZX-1,r14 4: SETS.L #MZY-1,r13 3: SETS.L #MZZ-1,r12 2: mov r12,r2 SHLL #8,r2 or r13,r2 SHLL #8,r2 or r14,r2 SHLL #8,r2 tst r12,r12 bt 1f bsr 9f mov #MZC_MZ,r3 1: tst r13,r13 bt 1f bsr 9f mov #MZC_MY,r3 1: tst r14,r14 bt 1f bsr 9f mov #MZC_MX,r3 1: add #-1,r12 cmp/pz r12 bt 2b add #-1,r13 cmp/pz r13 bt 3b add #-1,r14 cmp/pz r14 bt 4b dt r10 bt 1f 8: bsr panic nop 9: dt r10 bt/s 8b or r2,r3 mov.l r3,@r11 rts add #4,r11 1: SETS.L #1,r14 ; next clump ID SETS.L #MZCELLS-1,r13 ; highest non-1 clump ID SETS.L #mcells,r12 SETS.L #MZWALLS,r11 ; number of walls remaining SETS.L #mwalls,r10 SETS.L #maze,r9 6: 1: mov r13,r0 SHLL #1,r0 mov.w @(r0,r12),r1 dt r1 bf 1f add #-1,r13 cmp/pz r13 bt 1b ; No cells with non-1 clump IDs - all done! lds.l @r15+,pr rts nop 2: bsr panic nop 1: cmp/pl r11 bf 2b bsr random_mod mov r11,r0 mov r0,r8 ; random index to look at SHLL #2,r0 mov.l @(r0,r10),r1 ; wall in question add #-1,r11 cmp/hi r8,r11 bf 1f mov r11,r2 SHLL #2,r2 add r10,r2 mov.l @r2,r3 mov.l r3,@(r0,r10) 1: mov r1,r4 SHLR #8,r4 mov r4,r3 SHLR #8,r3 mov r3,r2 SHLR #8,r2 SETS.L #MZY,r5 mul.l r2,r5 sts macl,r6 extu.b r3,r3 add r3,r6 .if MZX != MZY SETS.L #MZX,r5 .endif mul.l r6,r5 sts macl,r6 extu.b r4,r4 add r4,r6 ; linear index of maze cell ; linear index of cell to look at is in r6 ; walls element is in r1 mov r6,r7 extu.b r1,r2 mov r2,r0 cmp/eq #MZC_MX,r0 bf 1f bra 2f add #-1,r7 1: cmp/eq #MZC_MY,r0 bf 1f bra 2f add #-MZX,r7 1: cmp/eq #MZC_MZ,r0 bf 1f SETS.L #MZX*MZY,r0 bra 2f sub r0,r7 1: bsr panic nop 2: ; randomly-chosen cell index is in r6 ; adjacent cell's index is in r7 ; walls element is in r1 ; bit is in r2 mov r6,r0 SHLL #1,r0 mov.w @(r0,r12),r3 ; chosen cell clump ID mov r7,r0 SHLL #1,r0 mov.w @(r0,r12),r4 ; adjacent cell clump ID tst r3,r3 bt 1f tst r4,r4 bt 2f cmp/eq r3,r4 bt 9f ; both cells are the same clump; do nothing ; opening between two different clumps ; renumber the greater to match the lesser cmp/hi r3,r4 bt 3f xor r3,r4 xor r4,r3 xor r3,r4 3: mov r3,r1 SETS.L #MZCELLS,r8 mov r12,r5 5: mov.w @r5,r0 cmp/eq r0,r4 bf 4f mov.w r3,@r5 4: dt r8 bf/s 5b add #2,r5 bra 8f nop 2: ; opening into new space - just store new clump ID bra 8f mov.w r3,@(r0,r12) 1: tst r4,r4 bt 2f ; opening from new space - just store new clump ID mov r6,r0 SHLL #1,r0 bra 8f mov.w r4,@(r0,r12) 2: ; opening between two hitherto untouched cells - new clump mov.w r14,@(r0,r12) mov r6,r0 SHLL #1,r0 mov.w r14,@(r0,r12) add #1,r14 8: ; bookkeeping done; just open wall mov r6,r0 SHLL #1,r0 mov.w @(r0,r9),r1 or r2,r1 mov.w r1,@(r0,r9) ; This shift assumes that MZC_Mx can be converted into MZC_Px ; a right shift of four bits. SHLR #4,r2 mov r7,r0 SHLL #1,r0 mov.w @(r0,r9),r1 or r2,r1 mov.w r1,@(r0,r9) 9: ; all done, including any opening bra 6b nop SETCONST ; Returns a random integer modulo r0. ; Uses r1, r2, r3, r4, dr0, dr1, fpul, macl. ; Result is returned in r0. random_mod: lds r0,fpul sts fpscr,r1 mov r1,r2 SETS.L #FPSCR_PR,r0 or r0,r1 lds r1,fpscr .pr 1 float fpul,dr0 SETS.L #rng_hand,r1 mov.b @r1,r4 SETS.L #rng_state,r3 mov r4,r0 SHLL #2,r0 mov.l @(r0,r3),r0 cmp/pl r4 bt 1f bsr stir_rng_preserving nop bra 2f nop 1: add #-1,r4 mov.b r4,@r1 2: SHLR #1,r0 sts fpul,r1 lds r0,fpul float fpul,dr2 fdiv dr0,dr2 ftrc dr2,fpul float fpul,dr2 fmul dr0,dr2 ftrc dr2,fpul sts fpul,r3 lds r2,fpscr .pr 0 rts sub r3,r0 ; We deliberately don't initialize rng_buf here; we stir the area first ; thing, and this way we may get some extra entropy from whatever is ; lying around in that memory. We don't need cryptographic levels of ; unguessability here. init_rng: sts.l pr,@-r15 bsr stir_rng nop bsr rng_rtc nop bsr stir_rng nop lds.l @r15+,pr rts nop rng_rtc: sts pr,r3 SETS.L #G2DRAIN_ADDR,r1 SETS.L #G2DRAIN_BIT,r2 1: mov.l @r1,r0 tst r2,r0 bf 1b SETS.L #G2RTC_BASE,r7 SETS.L #0,r1 1: SETS.L #3,r5 mov r1,r6 2: bsr read_rtc_once cmp/eq r1,r6 bf 1b dt r5 bf 2b mov r1,r4 SETS.L #0,r5 1: bsr read_rtc_once add #1,r5 cmp/eq r1,r4 bt 1b SETS.L #rng_state,r1 mov.l @r1,r0 add r4,r0 mov.l r0,@r1 mov.l @(4,r1),r0 add r5,r0 jmp @r3 mov.l r0,@(4,r1) ; Assumes G2RTC_BASE is in r7 and r0-r2 are scratch. ; Returned value is in r1. Disturbs nothing else. read_rtc_once: mov.l @r7,r1 mov.l @(4,r7),r2 SHLL #16,r1,r0 extu.w r2,r2 rts or r2,r1 stir_rng_preserving: mov.l r0,@-r15 mov.l r1,@-r15 mov.l r2,@-r15 mov.l r3,@-r15 sts.l pr,@-r15 mov.l r4,@-r15 mov.l r5,@-r15 mov.l r6,@-r15 bsr stir_rng mov.l r7,@-r15 mov.l @r15+,r7 mov.l @r15+,r6 mov.l @r15+,r5 mov.l @r15+,r4 lds.l @r15+,pr mov.l @r15+,r3 mov.l @r15+,r2 mov.l @r15+,r1 rts mov.l @r15+,r0 ; Uses r0-r7. Disturbs nothing else. stir_rng: SETS.L #0x12345678,r1 SETS.L #0x04c11db7,r2 ; edb88320 bit-reversed SETS.L #rng_state,r3 SETS.L #RNG_STATE_WORDS,r4 mov r4,r5 mov r3,r6 3: mov.l @r6+,r7 xor r7,r1 SETS.L #32,r7 2: shll r1 bf 1f xor r2,r1 1: dt r7 bf 2b dt r5 bf 3b 3: mov.l @r3,r7 mov.l r1,@r3 add r7,r1 SETS.L #8,r7 2: shll r1 bf 1f xor r2,r1 1: dt r7 bf 2b dt r4 bf/s 3b add #4,r3 .if rng_hand == rng_state+[4*RNG_STATE_WORDS] ; nothing .elif @IS_SB[rng_hand-[rng_state+[4*RNG_STATE_WORDS]]] add #rng_hand-[rng_state+[4*RNG_STATE_WORDS]],r3 .else SETS.L #rng_hand,r3 .endif SETS.L #RNG_STATE_WORDS-1,r0 rts mov.b r0,@r3 ; Input value in r1 ; Destroys r0, r1 printdec01: mov.l r4,@-r15 mov.l r3,@-r15 sts.l pr,@-r15 bsr printdec mov.l r2,@-r15 mov.l @r15+,r2 lds.l @r15+,pr mov.l @r15+,r3 rts mov.l @r15+,r4 ; Input value in r1 ; Destroys r0, r1, r2, r3, r4 printdec: sts.l pr,@-r15 tst r1,r1 bf 1f lds.l @r15+,pr bra putchar mov #'0,r1 1: mov r1,r2 SETS.L #p10table,r3 1: mov.l @r3+,r4 cmp/hs r4,r2 bf 1b 2: mov #'0,r1 1: cmp/hs r4,r2 bf 1f sub r4,r2 bra 1b add #1,r1 1: bsr putchar nop mov.l @r3+,r4 tst r4,r4 bf 2b lds.l @r15+,pr rts nop .align 4 p10table: .long 1000000000 .long 100000000 .long 10000000 .long 1000000 .long 100000 .long 10000 .long 1000 .long 100 .long 10 .long 1 .long 0 .align 2 printhex8: mov #8,r0 printhexN: mov.l r4,@-r15 mov r0,r4 add #-8,r0 neg r0,r0 SHLL #2,r0 shld r0,r1 mov.l r3,@-r15 mov.l r2,@-r15 sts.l pr,@-r15 mova 9f,r0 mov r0,r3 mov r1,r2 1: mov r2,r0 SHLR #28,r0,r1 SHLL #4,r2 add r3,r0 bsr putchar mov.b @r0,r1 dt r4 bf 1b lds.l @r15+,pr mov.l @r15+,r2 mov.l @r15+,r3 rts mov.l @r15+,r4 .align 4 9: .ascii "0123456789abcdef" .align 2 putchar: 1: mov.w @(SCFDR2-SCIF_BASE,gbr),r0 SHXR #SCFDR2_TX_SHIFT,r0 and #SCFDR2_TX_MASK,r0 cmp/eq #16,r0 bt 1b mov r1,r0 mov.b r0,@(SCFTDR2-SCIF_BASE,gbr) 1: mov.w @(SCFDR2-SCIF_BASE,gbr),r0 SHXR #SCFDR2_TX_SHIFT,r0 tst #SCFDR2_TX_MASK,r0 bf 1b rts nop putstr: 1: mov.w @(SCFDR2-SCIF_BASE,gbr),r0 SHXR #SCFDR2_TX_SHIFT,r0 and #SCFDR2_TX_MASK,r0 cmp/eq #16,r0 bt 1b mov.b @r1+,r0 tst r0,r0 bt 1f bra 1b mov.b r0,@(SCFTDR2-SCIF_BASE,gbr) 1: ; don't bother waiting for drain here; we do a putchar call, ; which will drain everything, after all putstr calls and ; before anything for which it matters. rts nop panic: sts.l pr,@-r15 mov.l r14,@-r15 mov.l r13,@-r15 mov.l r12,@-r15 mov.l r11,@-r15 mov.l r10,@-r15 mov.l r9,@-r15 mov.l r8,@-r15 mov.l r7,@-r15 mov.l r6,@-r15 mov.l r5,@-r15 mov.l r4,@-r15 mov.l r3,@-r15 mov.l r2,@-r15 mov.l r1,@-r15 mov.l r0,@-r15 SETS.L #panic_msg,r1 bsr putstr nop SETS.L #16,r2 1: bsr printhex8 mov.l @r15+,r1 SETS.L #panic_crlf,r1 bsr putstr nop dt r2 bf 1b SETS.L #throw_sp,r0 mov.l @r0,r15 bra throw_out nop panic_msg: .ascii "panic" panic_crlf: .asciz (13,10) .align 2 SETCONST .include "crash-handler.s"