************************************************************************* * * * The 81st Track MusicDisk 1 Loader by The 81st Track * * * ************************************************************************* incdir "asrc:include/" include "exec/exec_lib.i" include "hardware/custom.i" include "hardware/blit.i" include "devices/keyboard.i" include "81/my_macros.i" org $11000 ; loader starts at $11000 BBa1save equ $7f400 ; long for passing BB->Loader BBLoadMemAddr equ $7f404 ; long for passing BB->Loader mt_data equ $28000 ; address of uncrunched module Hardware equ $dff000 ; base of custom chip registers PortA equ $bfe001 ; CIA reg to test left mouse LeftMouse equ 6 ; left mouse button bit number NumOfTunes equ 12 ; number of tunes on the disk ************************************************************************* MainCode move.l BBa1save,a1save ; get values passed from BB move.l BBLoadMemAddr,LoadMemAddr bsr OpenKeyboard ; setup keyboard system move.l #Hardware,a6 moveq.l #0,d0 ; read first tune before jsr ReadTune ; the main section runs bsr SetupAll ; setup main data move.l $6c.w,oldlevel3 ; set up frame based stuff move.l #newlevel3,$6c.w move.w #$8010,intena(a6) move.w #$0001,intena(a6) ; serial stuff move.w #368,serper(a6) ; 9600 baud (PAL) jsr mt_init ; start tune bsr DrawTrack ; draw initial track number bsr DrawTime ; draw initial time value MainCodeLoop bsr WaitForFrame ; the main loop bsr DoTimeDisplay ; update and/or display time bsr ReadKeyboard ; look at keyboard jsr mt_music ; play the music bsr DecodeKeyboard ; find what keys are pressed lea DecodedMatrix,a0 tst.b $4e(a0) ; Right Arrow? bne.s NextTune ; if so, goto next tune tst.b $4f(a0) ; Left Arrow bne PrevTune ; if so, goto previous tune tst.b $46(a0) ; DEL key bne ClearNumEntry ; if so, clear digit entry bsr CheckNumKeys ; number keys (n.pad & main) tst.b newtrack bne.s TuneNumberOkay btst #LeftMouse,PortA ; Left mouse? beq.s PrevTune ; if so, goto previous tune move.w #0,potgo(a6) ; Right mouse? move.w potinp(a6),d0 and.w #$0400,d0 beq.s NextTune ; if so, goto next tune tst.w mt_restartflag ; Tune at end? beq.s MainCodeLoop ; if not, continue loop NextTune move.b TuneNumber,d0 ; else, goto next tune addq.b #1,d0 cmp.b #NumOfTunes,d0 bcc.s NextTuneWrap ; if last tune, goto first TuneNumberOkay move.b d0,TuneNumber ; get new tune and re-init move.l #0,TimeMinutes ; time display move.b #50,TimeFramesTick bsr DrawTime bsr SetDots bsr DrawTrack move.b TuneNumber,d0 jsr ReadTune ; okay then, get the tune! move.w #0,mt_restartflag bra MainCodeLoop NextTuneWrap moveq.b #0,d0 bra.s TuneNumberOkay PrevTune move.b TuneNumber,d0 subq.b #1,d0 bcc.s TuneNumberOkay move.b #NumOfTunes-1,d0 bra.s TuneNumberOkay ClearNumEntry move.b #$ff,numdig1 move.b #0,newtrack bra MainCodeLoop CheckNumKeys move.b #0,newtrack lea DecodedMatrix,a0 tst.b $0a(a0) ; 0 main keyboard bne.s num0 tst.b $01(a0) ; 1 m k bne.s num1 tst.b $02(a0) ; 2 m k bne.s num2 tst.b $03(a0) bne.s num3 tst.b $04(a0) bne.s num4 tst.b $05(a0) bne.s num5 tst.b $06(a0) bne.s num6 tst.b $07(a0) bne.s num7 tst.b $08(a0) bne.s num8 tst.b $09(a0) ; 9 m k bne.s num9 tst.b $0f(a0) ; 0 numeric keypad bne.s num0 tst.b $1d(a0) ; 1 n k bne.s num1 tst.b $1e(a0) ; 2 n k bne.s num2 tst.b $1f(a0) ; 3 n k bne.s num3 tst.b $2d(a0) bne.s num4 tst.b $2e(a0) bne.s num5 tst.b $2f(a0) bne.s num6 tst.b $3d(a0) bne.s num7 tst.b $3e(a0) bne.s num8 tst.b $3f(a0) ; 9 n k bne.s num9 rts num0 move.b #0,d0 bra.s numokay num1 move.b #1,d0 bra.s numokay num2 move.b #2,d0 bra.s numokay num3 move.b #3,d0 bra.s numokay num4 move.b #4,d0 bra.s numokay num5 move.b #5,d0 bra.s numokay num6 move.b #6,d0 bra.s numokay num7 move.b #7,d0 bra.s numokay num8 move.b #8,d0 bra.s numokay num9 move.b #9,d0 numokay cmp.b #$ff,numdig1 bne.s numgotdig1 move.b d0,numdig1 rts numgotdig1 move.w #0,d1 move.b numdig1,d1 mulu #10,d1 add.b d1,d0 beq.s numnottrack cmp.b #NumOfTunes+1,d0 bcc.s numnottrack move.b #1,newtrack move.b #$ff,numdig1 subq.b #1,d0 rts numnottrack move.b #$ff,numdig1 rts newlevel3 movem.l d0-d7/a0-a6,-(sp) move.l #Hardware,a6 btst #5,intreqr+1(a6) beq.s notvblank move.w #1,vblankflag notvblank btst #4,intreqr+1(a6) ; copper - level 3 beq.s notcopper move.w #$10,intreq(a6) bsr DoScroll ; the scroll text bsr DoHMsg notcopper movem.l (sp)+,d0-d7/a0-a6 dc.w $4ef9 oldlevel3 dc.l 0 ************************************************************************* OpenKeyboard lea keyio,a1 move.l #keyport,14(a1) lea KeyName,a0 moveq.l #0,d0 moveq.l #0,d1 move.l 4.w,a6 jsr _LVOOpenDevice(a6) rts ReadKeyboard move.l 4.w,a6 lea keyio,a1 move.w #KBD_READMATRIX,28(a1) move.l #KeyMatrix,40(a1) move.l #13,36(a1) jsr _LVOSendIO(a6) move.l #Hardware,a6 rts DecodeKeyboard lea KeyMatrix,a1 lea DecodedMatrix,a2 moveq.b #0,d4 moveq.l #15,d1 DK1 move.b (a1)+,d0 moveq.l #7,d2 DK2 moveq.b #0,d3 roxr.b #1,d0 roxl.b #1,d3 move.b d3,(a2)+ add.b d3,d4 dbf d2,DK2 dbf d1,DK1 tst.b prevkeys bne.s holding move.b d4,prevkeys rts holding lea DecodedMatrix,a2 moveq.l #31,d0 holdloop move.l #0,(a2)+ dbf d0,holdloop move.b d4,prevkeys rts ************************************************************************* SetupAll move.l #NewCopper,cop1lc(a6) ; setup main copper list move.w #0,copjmp1(a6) lea screencolors,a0 ; set them colour registers! lea color(a6),a1 moveq.l #31,d0 SetupAllLoop move.w (a0)+,(a1)+ dbf d0,SetupAllLoop move.w #$87e0,dmacon(a6) ; enable DMA bra.s WaitForBlit ; wait for any blits to finish ; (uses WaitForBlit rts!) WaitForFrame tst.w vblankflag ; Wait for VBlank beq.s WaitForFrame ; (my own internal flag) move.w #0,vblankflag rts WaitForBlit btst #14,dmaconr(a6) ; Wait for Blit finished flag WFBLoop btst #14,dmaconr(a6) bne.s WFBLoop rts ************************************************************************* DoScroll bsr.s WaitForBlit ; wait for blitter move.l #$7fffffff,bltafwm(a6) ; then move it: mask left bit move.l #$f9f00000,bltcon0(a6) ; shift 15 right move.w #0,bltamod(a6) move.w #0,bltdmod(a6) move.l #scrollarea,bltapt(a6) move.l #scrollarea-2,bltdpt(a6) ; dest 1 word left move.w #$0256,bltsize(a6) ; was 216 (bottom row missing) bsr.s WaitForBlit ; wait for blit to finish sub.b #1,scrollpixel ; 1 less blit to next char tst.b scrollpixel ; if no more, beq.s scrollnewchar ; get new character scrolldrawright lea charimage,a0 ; draw right pixel of scroll lea scrollarea+43,a1 move.l #7,d0 drawrightloop move.b (a1),d1 move.b (a0),d2 lsr.b #1,d1 roxl.b #1,d2 roxl.b #1,d1 move.b d1,(a1) move.b d2,(a0)+ add.l #44,a1 dbf d0,drawrightloop rts scrollnewchar add.l #1,scrollptr ; next character in message move.l scrollptr,a0 ; get message pointer tst.b (a0) ; if char=0(NUL), restart beq.s scrollrestart cmp.b #32,(a0) ; if char<32, skip char bcs.s scrollnewchar move.l #0,d0 ; get char (into long) move.b (a0),d0 sub.b #32,d0 ; get address of char def lsl.w #3,d0 add.l #scrollfont,d0 move.l d0,a0 lea charimage,a1 ; get address of bitmap move.l (a0)+,(a1)+ ; copy it! move.l (a0)+,(a1)+ move.b #8,scrollpixel ; 8 blits until next char bra.s scrolldrawright scrollrestart move.l #scrollmessage,scrollptr ; pretty obvious! bra.s scrollnewchar DoHMsg sub.b #1,HCounter ; HCounter says when to send tst.b HCounter ; another character. It's beq.s HSendChar ; used as a speed control rts HSendChar move.l hmsgptr,a0 ; get character HSendChar2 move.b (a0)+,d0 beq.s HRestart ; if NULL, restart message cmp.b #1,d0 ; if CHR$(1), set new speed beq.s HSetSpeed and.w #$ff,d0 ; add stop bit or.w #$0100,d0 move.w d0,serdat(a6) ; send it! move.l a0,hmsgptr move.b HSpeed,HCounter ; set 'speed' counter rts HRestart move.l #hmessage,hmsgptr ; pretty obvious! bra.s HSendChar HSetSpeed move.b (a0)+,HSpeed ; get speed bra.s HSendChar2 ************************************************************************* DoTimeDisplay cmp.b #26,TimeFramesTick ; turn dots off? beq.s DotsOff cmp.b #1,TimeFramesTick ; turn dots on? beq.s DotsOn DotsOkay subq.b #1,TimeFramesTick ; decrement 50ths second beq.s NewTimeDisplay ; if 0, need new time display rts DotsOff lea TimeDImg,a1 bra.s DrawDots DotsOn lea TimeDImg+TimeDImgSize,a1 DrawDots lea TimeDots,a0 lea TimeDMask,a2 move.l #TimeDImgRows,d0 bsr DrawDigit bra.s DotsOkay SetDots lea TimeDImg+TimeDImgSize,a1 lea TimeDots,a0 lea TimeDMask,a2 move.l #TimeDImgRows,d0 bsr DrawDigit rts NewTimeDisplay move.b #50,TimeFramesTick ; reset 50ths second value lea TimeSeconds+1,a0 addq.b #1,(a0) ; add 1 to low seconds cmp.b #10,(a0) ; if not 10, display time bne.s NewTimeOkay move.b #0,(a0) ; zero low seconds addq.b #1,-(a0) ; add 1 to high seconds cmp.b #6,(a0) ; if not 6, display time bne.s NewTimeOkay move.b #0,(a0) ; zero high seconds addq.b #1,-(a0) ; add 1 to low minutes cmp.b #10,(a0) ; if not 10, display time bne.s NewTimeOkay move.b #0,(a0) ; zero low minutes addq.b #1,-(a0) ; add 1 to high minutes cmp.b #6,(a0) ; if not 6, display time bne.s NewTimeOkay move.b #0,(a0) ; zero high minutes NewTimeOkay bsr DrawTime ; Draw Time rts ************************************************************************* ; This section reads a tune when given a number from 0 to 5. It 'knows' where ; the tunes are on disk by looking in a table ReadTune jsr mt_end ; first, stop current tune move.l a1save,a1 ; get trackdisk IO req lea TuneParamTable,a0 ; get tune offset&length and.l #$f,d0 asl.l #3,d0 add.l d0,a0 move.w #2,28(a1) ; "read" move.l #$28000,d2 ; target address move.l (a0)+,d3 move.l (a0),d4 move.l d4,TuneLength ReadLoop move.l d2,40(a1) move.l d3,44(a1) move.l #$200,36(a1) tst.b loaderstyle beq rtnowait bsr WaitForFrame rtnowait move.l 4.w,a6 jsr _LVODoIO(a6) move.l a1save,a1 add.l #$200,d2 ; next block buffer address add.l #$200,d3 ; next block disk offset sub.l #$200,d4 ; one block less to load bne.s ReadLoop ; if not all done, next block move.l a1save,a1 move.w #9,28(a1) ; motor off (tidy!) move.l #0,36(a1) jsr _LVODoIO(a6) move.l #Hardware,a6 move.l #$28000,a0 ; relocate to loader mem move.l LoadMemAddr,a1 move.l TuneLength,d0 lsr.l #2,d0 RelocTuneLoop move.l (a0)+,(a1)+ dbf d0,RelocTuneLoop move.l LoadMemAddr,a1 jsr (a1) ; tune decrunches to $28000 jsr mt_init ; setup new tune move.b #1,loaderstyle rts ************************************************************************* ; This section draws the 'led' displays ; Routine to draw track number DrawTrack lea TuneDigits,a0 move.b TuneNumber,d0 addq.b #1,d0 and.l #$ff,d0 divu #10,d0 move.b d0,d1 swap d0 move.b d0,d2 lea TrackImg,a1 and.w #$ff,d1 and.w #$ff,d2 mulu #TrackImgSize,d1 mulu #TrackImgSize,d2 add.l d1,a1 lea TrackMask,a2 move.l #TrackImgRows,d0 bsr.s DrawDigit lea TrackImg,a1 add.l d2,a1 bsr.s DrawDigit rts ; routine to draw time value DrawTime lea TimeDigits,a0 lea TimeMask,a2 lea TimeMinutes,a3 move.l #TimeImgRows,d0 moveq.l #3,d1 DrawTimeLoop move.b (a3)+,d2 and.l #$ff,d2 mulu #TimeImgSize,d2 lea TimeImg,a1 add.l d2,a1 bsr.s DrawDigit dbf d1,DrawTimeLoop rts ; routine to draw digits (actually blits the image to the screen) DrawDigit move.l a2,a5 move.l d0,d7 lea MaskBuffer,a4 subq.l #1,d0 DrawDigitLoop1 move.w (a2)+,(a4)+ addq.l #2,a4 dbf d0,DrawDigitLoop1 move.l a5,a2 move.l d7,d0 move.l #loadscreen,d7 moveq.l #5,d6 DrawDigitLoop2 bsr WaitForBlit move.w #-2,bltamod(a6) move.w #0,bltbmod(a6) move.w #40,bltcmod(a6) move.w #40,bltdmod(a6) move.b (a0),d5 and.l #$ff,d5 mulu #44,d5 add.l d7,d5 move.b 1(a0),d4 and.l #$ff,d4 add.l d4,d5 move.l a1,bltapt(a6) move.l #MaskBuffer,bltbpt(a6) move.l d5,bltcpt(a6) move.l d5,bltdpt(a6) move.l #-1,bltafwm(a6) move.b 2(a0),d4 lsl.w #8,d4 lsl.w #4,d4 move.w d4,bltcon1(a6) or.w #$fe2,d4 move.w d4,bltcon0(a6) move.w d0,d4 lsl.w #6,d4 addq.w #2,d4 move.w d4,bltsize(a6) add.l #12760,d7 add.l d0,a1 add.l d0,a1 dbf d6,DrawDigitLoop2 addq.l #3,a0 rts ************************************************************************* ; This is the copper list for the display NewCopper cmove $6200,bplcon0 ; some general screen stuff cmove $0000,bplcon1 cmove $1b71,diwstrt cmove $3dd1,diwstop cmove $0030,ddfstrt cmove $00d8,ddfstop cmove $0000,bpl1mod cmove $0000,bpl2mod ; set up all sprites as null cmove ((nullsprite&$ffff0000)>>16),sprpt cmove (nullsprite&$ffff),sprpt+$02 cmove ((nullsprite&$ffff0000)>>16),sprpt+$04 cmove (nullsprite&$ffff),sprpt+$06 cmove ((nullsprite&$ffff0000)>>16),sprpt+$08 cmove (nullsprite&$ffff),sprpt+$0a cmove ((nullsprite&$ffff0000)>>16),sprpt+$0c cmove (nullsprite&$ffff),sprpt+$0e cmove ((nullsprite&$ffff0000)>>16),sprpt+$10 cmove (nullsprite&$ffff),sprpt+$12 cmove ((nullsprite&$ffff0000)>>16),sprpt+$14 cmove (nullsprite&$ffff),sprpt+$16 cmove ((nullsprite&$ffff0000)>>16),sprpt+$18 cmove (nullsprite&$ffff),sprpt+$1a cmove ((nullsprite&$ffff0000)>>16),sprpt+$1c cmove (nullsprite&$ffff),sprpt+$1e ; set up bitplane pointers cmove ((screenplane1&$ffff0000)>>16),bplpt cmove (screenplane1&$ffff),bplpt+$02 cmove ((screenplane2&$ffff0000)>>16),bplpt+$04 cmove (screenplane2&$ffff),bplpt+$06 cmove ((screenplane3&$ffff0000)>>16),bplpt+$08 cmove (screenplane3&$ffff),bplpt+$0a cmove ((screenplane4&$ffff0000)>>16),bplpt+$0c cmove (screenplane4&$ffff),bplpt+$0e cmove ((screenplane5&$ffff0000)>>16),bplpt+$10 cmove (screenplane5&$ffff),bplpt+$12 cmove ((screenplane6&$ffff0000)>>16),bplpt+$14 cmove (screenplane6&$ffff),bplpt+$16 ; wait till low part of screen (end of scroll) dc.w $ff09,$fffe,$ffdd,$fffe cwait $21,$09 ; cause a copper interrupt request cmove $8010,intreq ; end of copper list cend ; This NULL long word is used to blank all the sprites nullsprite dc.l 0 ************************************************************************* a1save dc.l 0 LoadMemAddr dc.l 0 vblankflag dc.w 0 ; The data for where the tunes are held on the disk TuneParamTable dc.l $07400,$1fc00,$27000,$21200 ; tunes 1 and 2 dc.l $48200,$0c600,$54800,$18e00 ; 3 and 4 dc.l $6d600,$06400,$73a00,$13000 ; 5 and 6 dc.l $86a00,$0c200,$92c00,$15a00 ; 7 and 8 dc.l $a8600,$04600,$acc00,$18600 ; 9 and 10 dc.l $c5200,$0e800,$d3a00,$08200 ; 11 and 12 ; How big is the crunched tune? (temp for relocation) TuneLength dc.l 0 ; What tune is playing? TuneNumber dc.b 0 ; Loader style (wait4frame[1] or normal[0]) loaderstyle dc.b 0 ; Data for screen (and associated stuff) loadscreen incbin "cdtv-audio.raw" screenplane1 equ loadscreen screenplane2 equ screenplane1+12760 screenplane3 equ screenplane2+12760 screenplane4 equ screenplane3+12760 screenplane5 equ screenplane4+12760 screenplane6 equ screenplane5+12760 screencolors equ screenplane6+12760 ; Tune Number Display Data ; digit positions (Y Pos, X Byte, X Shift) TuneDigits dc.b 51,10,11,51,14,3 ; Time Display Data ; digit positions (Y Pos, X Byte, X Shift) TimeDigits dc.b 145,6,2,145,8,4,145,10,10,145,12,12 ; dot positions (Y Pos, X Byte, X Shift) TimeDots dc.b 148,10,4 ; frames before next tick TimeFramesTick dc.b 50 ; time data TimeMinutes dc.b 0,0 ; first digit (0-5), second digit (0-9) TimeSeconds dc.b 0,0 ; first digit (0-5), second digit (0-9) ; LED graphics data ledgfx incbin "raw-led-gfx" TimeImg equ ledgfx TimeMask equ ledgfx+2280 TimeDImg equ ledgfx+2318 TimeDMask equ ledgfx+2630 TrackImg equ ledgfx+2656 TrackMask equ ledgfx+5416 TimeImgSize equ 228 TimeDImgSize equ 156 TrackImgSize equ 276 TimeImgRows equ 19 TimeDImgRows equ 13 TrackImgRows equ 23 MaskBuffer ds.b 92 ; The data for the messages scrollarea equ screenplane1+11308 ; pixel line 257 charimage ds.b 8 scrollfont include "asrc:fontsrc/hsspace.fontsrc" scrollptr dc.l scrollmessage hmsgptr dc.l hmessage scrollmessage incbin "load.msg" dc.b 0 hmessage incbin "hidden.txt" dc.b 0 scrollpixel dc.b 1 HSpeed dc.b 2 HCounter dc.b 1 ; If user types in new tune number, the data goes here numdig1 dc.b $ff newtrack dc.b 0 ; Keyboard stuff KeyName dc.b 'keyboard.device',0 prevkeys dc.b 0 even keyio dcb.b 48,0 keyport dcb.l 8,0 KeyMatrix dcb.b 16,0 DecodedMatrix dcb.b 128,0 ************************************************************************* include "nt20play.s" ; my own slightly modified player fillersize SET $27ffe-* IFLT fillersize FAIL !!! Loader overflowing module area ELSE ds.b fillersize ENDC ; *** This rts has to go at $27ffe - the tunes JMP $27FFE after decrunch rts end