; Sphere Hunter by Aske Simon Christensen ; For the Ludum Dare Retro Challenge, June 2014 WRITEFLAG = 0 ; 1 TO WRITE BOOTBLOCK TO DF0 CODEBASE= $6B000 ; This include provides compression and testing harness include Inc:BootblockSystem2.S SCREENWIDTH = 320 SCREENHEIGHT = 256 SCREENDEPTH = 4 N_BLOCKS = 8 LEVELWIDTH = 20 LEVELHEIGHT = 16 STARTX = 10 STARTY = 10 printv BB_END-BB_START printv SPACE_SIZE BB_START: ; Bootblock code goes here. ; Global pointer to chip space in A4 lea.l Space(pc),a4 UnpackAtan: lea.l AtanTable(pc),a0 lea.l AtanTableCompressed(pc),a1 moveq.l #8-1,d7 .yloop: move.l (a1)+,d0 .xloop: moveq.l #15,d1 and.l d0,d1 move.b d1,(a0)+ lsr.l #4,d0 bne.b .xloop move.l a0,a2 moveq.l #8-1,d6 .mirror: moveq.l #16,d0 sub.b -(a2),d0 move.b d0,(a0)+ dbf d6,.mirror dbf d7,.yloop move.l a0,a2 moveq.l #128-1,d7 .half: move.b -(a0),(a2)+ dbf d7,.half MakeBlocks: ; lea.l BlockParams(pc),a1 ; lea.l Blocks-Space(a4),a2 .blockloop: ; lea.l AtanTable-Space(a4),a6 move.l a0,a6 move.b (a1)+,d3 move.b (a1)+,d4 move.b (a1)+,d5 moveq.l #-15,d7 .yloop: moveq.l #-15,d6 .xloop: move.w d6,d0 move.w d7,d1 moveq.l #22,d2 muls.w d1,d1 muls.w d0,d0 add.l d1,d0 .sqrt: move.w d2,d1 muls.w d1,d1 cmp.l d0,d1 dble d2,.sqrt clr.l d1 cmp.b d3,d2 bgt.b .outside move.b (a6),d1 ; D1 = Angle ; D2 = Radius muls.w d4,d1 muls.w d5,d2 add.l d2,d1 lsr.w #1,d1 and.b #7,d1 moveq.l #8,d2 and.b d3,d2 add.b d2,d1 .outside: addq.l #1,a6 move.l a2,a3 moveq.l #SCREENDEPTH-1,d0 .plot: move.w (a3),d2 lsr.w #1,d1 addx.w d2,d2 move.w d2,(a3)+ dbf d0,.plot addq.l #2,d6 cmp.b #15,d6 ble.b .xloop move.l a3,a2 addq.l #2,d7 cmp.b #15,d7 ble.b .yloop lea.l Blocks_End-Space(a4),a3 cmp.l a3,a2 blo.b .blockloop MakeLevel: move.b $bfd800,d2 lea.l Level-Space(a4),a0 lea.l Screen-Space(a4),a2 moveq.l #LEVELHEIGHT-1,d7 .yloop: moveq.l #LEVELWIDTH-1,d6 .xloop: ror.w #7,d2 mulu.w #61021,d2 moveq.l #5<<2,d1 mulu.w d2,d1 swap.w d1 addq.w #2<<2,d1 moveq.l #-1<<2,d0 ; Wall cmp.w #LEVELHEIGHT-1,d7 beq.b .write cmp.w #LEVELWIDTH-1,d6 beq.b .write tst.w d6 beq.b .write tst.w d7 beq.b .write moveq.l #0<<2,d0 ; Space asl.l #2,d2 bvs.b .write moveq.l #1<<2,d0 ; Stone asl.l #3,d2 bvs.b .write move.w d1,d0 ; Sphere addq.b #2,d3 bcs.b MakeLevel ; Too many spheres .write: swap.w d2 bsr.w WriteAndDraw dbf d6,.xloop lea.l SCREENWIDTH/8*(16*SCREENDEPTH-1)(a2),a2 dbf d7,.yloop Init: ; Turn off audio filter or.b #2,$bfe001 ; D4 = xxyy (2 pixel res) move.l #($4016+(STARTX<<11)+(STARTY<<3))+($FFFF<<16),d4 ; Enable copper blitter access lea.l $DFF000,A5 move.w d7,$02e(a5) ; Main per-frame loop MainLoop: lea.l CopperData(pc),a0 .vbl0: tst.b $005(a5) bne.b .vbl0 lea.l Blocks-Space(a4),a2 moveq.l #127,d0 lea.l $0a0(a5),a1 moveq.l #4-1,d7 .channel: move.l a2,(a1)+ ; Pointer move.w d0,(a1)+ ; Length move.w d0,(a1)+ ; Period move.w d5,(a1)+ ; Volume addq.l #6,a1 subq.b #8,d5 scc.b d1 and.b d1,d5 mulu.w #3,d0 ror.l #8,d5 lsr.w #1,d0 dbf d7,.channel lea.l $08e(a5),a1 moveq.l #8-1,d7 .copy1: move.w (a0)+,(a1)+ dbf d7,.copy1 ; Set bitplane pointers lea.l Screen-Space(a4),a2 move.l a2,a3 ; End of sprite lea.l $0e0(a5),a1 moveq.l #8-1,d7 .bplptrloop: ; A2 = Pointer ; A1 = Hardware Address move.l a2,(a1)+ lea.l SCREENWIDTH/8(a2),a2 dbf d7,.bplptrloop ; lea.l $100(a5),a1 moveq.l #8-1,d7 .copy2: move.w (a0)+,(a1)+ dbf d7,.copy2 ; Place sprite lea.l Sprite-Space(a4),a2 lea.l $120(a5),a1 moveq.l #8-1,d7 .sprloop: move.l a2,(a1)+ dbf d7,.sprloop moveq.l #8,d1 add.b d4,d1 lsl.w #8,d1 move.w d4,d0 add.b d0,d0 addx.w d1,d1 addx.b d1,d1 add.b d1,d1 ror.w #8,d0 move.w d0,(a2)+ move.w d1,(a2)+ moveq.l #63,d0 lsl.w #5,d0 moveq.l #8-1,d7 clr.l -(a3) .spr: move.l d0,(a2)+ move.l d0,-(a3) lsr.b #1,d0 lsl.w #1,d0 lsr.b #1,d0 dbf d7,.spr swap.w d4 ; Set colors addq.w #$001,d0 lea.l $180(a5),a1 moveq.l #8-1,d7 .colorloop: move.w d4,d2 move.w #$ff0,d1 add.w d7,d2 lsr.w #3,d2 bcc.b .col0 move.w #$f00,d1 .col0: move.w d0,(a1)+ move.w d1,7*2(a1) add.w #$111,d0 dbf d7,.colorloop ; Flashing marker move.w d4,$1a4(a5) .vbl1: tst.b $005(a5) beq.b .vbl1 addq.w #1,d4 ; VBlank counter ; Simulate every 8th frame move.w d4,d0 swap.w d4 and.w #7,d0 bne.b .nosim lea.l Copper-Space(a4),a1 bsr.w Simulate ; D0.B and flags: ; <0: OK ; =0: Win ; >0: Lose bge.b .freeze lea.l Copper-Space(a4),a1 .freeze: move.l a1,$080(a5) tst.b d0 bge.w MakeLevel asr.w #2,d3 .nosim: ; Move sprite add.w d3,d4 if WRITEFLAG bra.w MainLoop else ; Loop until LMB btst.b #6,$bfe001 bne.w MainLoop ; Turn off sound move.w #$000f,$096(a5) rts endc WriteAndDraw: move.w d0,(a0)+ asr.w #2,d0 ; muls.w #16*SCREENDEPTH*2,d0 asl.w #7,d0 lea.l Blocks+16*SCREENDEPTH*2-Space(a4),a3 add.w d0,a3 moveq.l #16*SCREENDEPTH-1,d0 .draw: move.w (a3)+,(a2) lea.l SCREENWIDTH/8(a2),a2 dbf d0,.draw lea.l -SCREENWIDTH/8*SCREENDEPTH*16+2(a2),a2 rts Simulate: move.l a7,a6 clr.b (a4) ; Sphere flag lea.l Level-Space(a4),a0 lea.l Screen-Space(a4),a2 moveq.l #LEVELHEIGHT-1,d7 .yloop: moveq.l #LEVELWIDTH-1,d6 .xloop: move.w (a0),d0 subq.b #2<<2,d0 bmi.b .next moveq.l #4,d2 and.w d0,d2 lsr.w #1,d2 subq.w #1,d2 st.b (a4) ; At least one sphere still alive subq.b #2<<2,d0 bpl.b .nohugger tst.b (a0) bne.b .nohugger neg.w d2 add.w d2,d0 bsr.w ReadAdjacent beq.b .turn neg.w d2 add.w d2,d0 .nohugger: move.w (a0),d0 bsr.w ReadAdjacent beq.b .move cmp.b #6<<2,d0 blt.b .notbouncer moveq.l #2,d2 .notbouncer: .turn: bset.l #13,d5 ; D2 = Delta ; (a0) = Value move.w (a0),d0 and.w #-4,(a0) add.w d2,d0 and.w #3,d0 or.w d0,(a0) addq.b #1,(a0) cmp.b #10,(a0) blt.b .next ; Turn to stone bset.l #31,d5 moveq.l #1<<2,d0 bsr.w WriteAndDraw bra.b .lend .move: movem.l a0/a2/a3,-(a7) addq.w #1,(a3) clr.b (a0) .next: addq.w #2,a0 addq.l #2,a2 .lend: dbf d6,.xloop lea.l SCREENWIDTH/8*(16*SCREENDEPTH-1)(a2),a2 dbf d7,.yloop ; Location of player lea.l Level-Space(a4),a0 move.w d4,d1 clr.w d2 sub.w #$4016,d1 move.b d1,d2 ; Y lsr.w #8,d1 ; X lsr.w #2,d1 mulu.w #LEVELWIDTH*2/8,d2 add.l d2,a0 add.w d1,a0 move.l a0,d6 ; Save location ; Read joystick move.w $00C(a5),d0 ; Right move.w #((1<<8)<<2)+1,d3 ror.w #2,d0 bcs.b .joy1 ; Left move.w #((-1<<8)<<2)+3,d3 ror.w #8,d0 .joy1: bcs.b .joy2 ; Down moveq.l #(1<<2)+2,d3 ror.w #7,d0 .joy2: bcs.b .joy3 ; Up moveq.l #(-1<<2)+0,d3 ror.w #8,d0 .joy3: bcc.b .stay bset.l #20,d5 move.w d3,d0 bsr.w ReadAdjacent beq.b .nojoy ; Space subq.w #1<<2,d1 bne.b .stay move.l a3,a0 bsr.w ReadAdjacent bne.b .stay ; Push bset.l #29,d5 move.w (a0),(a3) clr.w (a0) move.l a0,d1 lea.l Level-Space(a4),a0 lea.l Screen-Space(a4),a2 sub.l a0,d1 divu.w #LEVELWIDTH*2,d1 move.w #SCREENWIDTH/8*16*SCREENDEPTH,d2 mulu.w d1,d2 swap.w d1 add.w d1,a2 add.l d2,a2 bsr.w WriteScroll bra.b .nojoy .stay: moveq.l #0<<2,d3 .nojoy: ; Move spheres .moveloop: cmp.l a6,a7 beq.b .movelend movem.l (a7)+,a0/a2/a3 move.w (a0),d0 move.w (a0),(a3) clr.w (a0) bsr.b WriteScroll bra.b .moveloop .movelend: moveq.l #-2,d0 move.l d0,(a1) move.b (a4),d0 beq.b .win move.l d6,a0 move.w (a0),d0 subq.w #7,d0 ble.b .win bset.l #7,d5 .win: ; D0.B and flags: ; <0: OK ; =0: Win ; >0: Lose rts ReadAdjacent: ; D0 = Direction moveq.l #3,d1 and.w d0,d1 move.b .offsets(pc,d1.w),d1 lea.l -128(a0,d1.w),a3 move.w (a3),d1 rts .offsets: dc.b 128-LEVELWIDTH*2,128+1*2,128+LEVELWIDTH*2,128-1*2 ScrollBlitterParams: ; bltcon0,bltcon1,bltalwm,source offset,dest offset,modulo,size,extra size ; Up dc.w $09F0,$0000,$FFFF,SCREENWIDTH/8*(-14*SCREENDEPTH),SCREENWIDTH/8*(-16*SCREENDEPTH),SCREENWIDTH/8-2,((30*SCREENDEPTH)<<6)+1,((2*SCREENDEPTH)<<6)+1 ; Right dc.w $29F0,$0000,$FFFC,0,0,SCREENWIDTH/8-4,((16*SCREENDEPTH)<<6)+2,0 ; Down dc.w $09F0,$0002,$FFFF,SCREENWIDTH/8*(30*SCREENDEPTH-1),SCREENWIDTH/8*(32*SCREENDEPTH-1),SCREENWIDTH/8-2,((30*SCREENDEPTH)<<6)+1,((2*SCREENDEPTH)<<6)+1 ; Left dc.w $29F0,$0002,$3FFF,SCREENWIDTH/8*(16*SCREENDEPTH-1),SCREENWIDTH/8*(16*SCREENDEPTH-1),SCREENWIDTH/8-4,((16*SCREENDEPTH)<<6)+2,0 WriteScroll: ; D0 = Direction ; A1 = Copper ; A2 = Cell moveq.l #1,d2 swap.w d2 and.w #3,d0 lsl.b #4,d0 lea.l ScrollBlitterParams(pc,d0.w),a0 move.l d2,(a1)+ moveq.l #$040,d1 move.w d1,(a1)+ addq.w #2,d1 move.w (a0)+,(a1)+ move.w d1,(a1)+ addq.w #2,d1 move.w (a0)+,(a1)+ move.w d1,(a1)+ addq.w #2,d1 move.w #$FFFF,(a1)+ move.w d1,(a1)+ addq.w #2,d1 move.w (a0)+,(a1)+ moveq.l #$050,d1 .pointers: move.l a2,a3 add.w (a0)+,a3 move.w d1,(a1)+ addq.w #2,d1 move.l a3,(a1) addq.l #2,a1 move.w d1,(a1)+ addq.w #2,d1 move.w a3,(a1)+ not.w d0 bmi.b .pointers moveq.l #$064,d1 move.w d1,(a1)+ addq.w #2,d1 move.w (a0),(a1)+ move.w d1,(a1)+ addq.w #2,d1 move.w (a0)+,(a1)+ move.w #$058,(a1)+ move.w (a0)+,(a1)+ tst.w (a0) beq.b .onlyoneblit move.l d2,(a1)+ moveq.l #$040,d1 move.w d1,(a1)+ addq.w #2,d1 move.w #$0100,(a1)+ move.w #$058,(a1)+ move.w (a0)+,(a1)+ .onlyoneblit: rts CopperData: dc.w $2C81,$2CC1,$0038,$00D0 dc.w $83EF,$0000,$4000,$0000 dc.w $0200+(SCREENDEPTH<<12) dc.w $0000,$003F,$0000 dc.w (SCREENWIDTH/8)*(SCREENDEPTH-1) dc.w (SCREENWIDTH/8)*(SCREENDEPTH-1) dc.w $0011 AtanTableCompressed: dc.l $FEDCAA98 dc.l $FECBA987 dc.l $FDCA9876 dc.l $FDB98766 dc.l $FCA87654 dc.l $EA865443 dc.l $D8643322 dc.l $83211111 ; dc.l $8,$9,$A,$A,$C,$D,$E,$F ; dc.l $7,$8,$9,$A,$B,$C,$E,$F ; dc.l $6,$7,$8,$9,$A,$C,$D,$F ; dc.l $6,$6,$7,$8,$9,$B,$D,$F ; dc.l $4,$5,$6,$7,$8,$A,$C,$F ; dc.l $3,$4,$4,$5,$6,$8,$A,$E ; dc.l $2,$2,$3,$3,$4,$6,$8,$D ; dc.l $1,$1,$1,$1,$1,$2,$3,$8 ; -1 = Wall ; 0 = Space ; 1 = Stone ; 2-6 = Sphere BlockParams: dc.b 23,3,0,0,0,0,17,0,9 dc.b 15,1,1,15,-1,1,15,1,0,15,-1,0,15,0,2 BB_DATAEND: dcb.b (BB_START-BB_DATAEND)&3,0 BB_END: Space: AtanTable: ds.b 256 Blocks: ds.w N_BLOCKS*16*SCREENDEPTH Blocks_End: Level: ds.w LEVELWIDTH*LEVELHEIGHT Copper: ds.l LEVELWIDTH*LEVELHEIGHT/2*15 Sprite: ds.w 2+16*2+2 Screen: ds.l SCREENWIDTH/32*SCREENHEIGHT*SCREENDEPTH BB_SPACE_END: SPACE_SIZE = BB_SPACE_END-BB_START