***************************************************************************
* / *
* _____.__ _ .___. *
* / /_____________. _________.__________.________ | |________ *
* ___/____ / ____|_/ | /| \\| ._ / *
* \\ \\/ \\ \\ \\ / | :___/?| \\ \\ |/ / *
* \\_____________\\___/_____/___/_____|____| |____|\\_____________/ *
* -========================/===========|______\\================- *
* *
* .---.----(*( HARDWARE TRACKLOADER )*)---.---. *
* `-./ \\.-' *
* (c)oded by StingRay *
* -------------------- *
* August 2007 *
* *
* SPECIAL "PLEASE NEW ZEALAND'S BEST" RELEASE *
* *
***************************************************************************
; 10/11/12-Aug-2007
;
; thanks to Cr?xshadows, :Wumpscut:, Das Ich, Funker Vogt, [:SITD:],
; God Module, Suicide Commando, etc. for music while coding this :)
***************************************************
*** HISTORY ***
***************************************************
; 16-Aug-2007 - special "please New Zealand's best" release
; - checksum checks added, increases loadersize to 530
; bytes, it's off by default, enable it by setting
; LD_CHECKSUM to 1
; - according to New Zealand's best, this loader is
; "extremely limited and buggy". So better use
; a loader coded by New Zealand's best. Good luck
; finding one. ;) If you should find one, better check
; the size of the mfm buffer in that very loader then
; as New Zealand's best has quite a complicated and
; very special approach to calculate mfm buffer
; sizes. At least it's too complicated for me because
; I can't see that my MFM buffer size is too small but
; if New Zealand's best says it is, then it is!
; 13-Aug-2007 - due to a bug the fast "step to next track" routine
; is gone (happened when the loader was called multiple
; times), using LD_StepToTrack again, loader is 30
; bytes smaller now and works reliable as added bonus :)
; - optimized the size a bit more, LD_CURRENTRACK is now
; d5, loadersize is 494 bytes now :)
; - boundary check in the mfm decoder improved, if
; end is reached, it simply exits, no need to decode
; another sector then :)
; - current loadersize (safety off): 494 bytes
; - quick and dirty bugfix, loader didn't work correctly
; if # of bytes to skip was >512bytes, for now this
; quickfix should do (uses 512*11 bytes memory, guess why),
; might do a better fix later
; 12-Aug-2007 - missing cia timers implemented
; - rasterline timing removed as it's not easy
; to implement without a lot of changes due to the
; way current cia timing is implemented, doesn't
; matter anway as there are 4 selectable CIA timers,
; should be more than adequate
; - instead of using LD_StepToTrack a much simpler
; and faster routine is used to step to the next
; track now, increases loadersize but also the speed :)
; - forgot to set error code in LD_DriveReady, wrong
; error returned if the drive actually DID clear the
; DSKRDY bit :)
; - fixed timeout in LD_LoadTrack, apparently 5ms was
; way too short, timeout value is now 20*50=1000 ms=1sec
; - added LD_SAFETY, if enabled some error checks are
; performed
; - doesn't trash any bytes (max. 3) at the end of the buffer
; anymore
; - offset logic fixed, everything seems to work fine now
; - size optimized LD_DriveOn and LD_StepToTrack
; - some hours later: should work with start offsets
; that are not a multiple of 4 now, slows down mfm decoding
; quite a bit because of the needed checks and writing
; bytes only instead of longs, could be made faster
; with special case code but that would enlarge the loader
; and it's big enough already (558 bytes), at least
; there should be no memory trashing at all anymore and
; it should work with all offsets/lengths
; - some minutes later: 'FUCK IT' I say! :) Got sick of that
; fucked up innerloop, I now use the MFM buffer as
; temporary buffer and copy it afterwards to the real
; address, a lot faster, a lot shorter and only uses
; 512 bytes more memory :)
; 11-Aug-2007 - added the loader part
; - some bugfixes and size optimizing
; - first working version
; - offset logic wrong, trashes 2 bytes
; - drive check improved, split into 2 different
; routines now (LD_DriveOn, LD_DriveReady)
; after the 500ms timer is expired, a read attempt
; is performed to check if the drive is available/ready
; - added timeout check in LD_LoadTrack, if no IRQ signal
; is received within 5ms an error is returned, according
; to the HW manual, max. time needed to get to the
; next track is 3ms so 5ms is more than enough time
; - split the timer routines in LD_StartTimer,
; LD_CheckTimer and LD_WaitTimer
; 10-Aug-2007 - started coding
; - cia timers, step to track, selectable drives,
; no actual loading/mfm decoding yet
IF 0 ; example code, change 0 to 1 to assemble it
SECTION CODE,CODE_c
TEST move.w #$4000,$dff09a
lea BUFFER,a0 ; destination
lea MFM,a1 ; mfm buffer
moveq #0,d0 ; start offset in bytes
move.l #100000,d1 ; length in bytes
moveq #1,d2 ; drive to use
jsr LOADER
lea BUFFER,a0 ; destination
;lea MFM,a1 ; mfm buffer
move.l #80*11*512,d0 ; start offset in bytes
move.l #200000,d1 ; length in bytes
moveq #1,d2 ; drive to use
jsr LOADER
lea BUFFER,a0 ; destination
;lea MFM,a1 ; mfm buffer
move.l #40*11*512,d0 ; start offset in bytes
move.l #200000,d1 ; length in bytes
moveq #1,d2 ; drive to use
jsr LOADER
move.w #$c000,$dff09a
rts
BUFFER dcb.l 200000/4,"STR!"
dcb.l 1024/4,"STR!"
MFM dcb.l $2400/2,"MFM!"
MFMEND dcb.l $2400/2,"END!"
ENDC
; internal constants, don't touch
LD_TIME3MS = 2148 ; 3000/1.3968255
LD_TIME5MS = 3580 ; 5000/1.3968255,
LD_TIME18MS = 12886 ; 18000/1.3968255, after reversing dir
LD_NODRIVE = 1 ; drive not accessible
LD_READERROR = 2 ; timeout while reading track
LD_OUTOFBOUNDS = 3 ; requested offset/length out of bounds
LD_CRCERROR = 4 ; checksum error
LD_CIAA_A = 1 ; cia a, timer a
LD_CIAA_B = 2 ; cia a, timer b
LD_CIAB_A = 3 ; cia b, timer a
LD_CIAB_B = 4 ; cia b, timer b
; these are the ONLY user changeable settings
LD_USECIA = LD_CIAB_A ; which timer to use, look above
LD_SAFETY = 0 ; set to 1 for more safety, e.g boundary
; checking, increases loadersize
LD_CHECKSUM = 0 ; set to 1 to enable checksum check
*******************************************
*** TRACKLOADER ***
*******************************************
; a0.l: load address
; a1.l: mfm buffer (only once, $2400 words)
; d0.l: offset (bytes, 0-160*11*512)
; d1.l: size (bytes, 0-160*11*512)
; d2.w: drive (0-3)
;------
; d0.l: error code (0 = no error)
LOADER movem.l d1-a6,-(a7)
lea $dff000,a6
lea $bfd100,a5
lea LD_VARS(pc),a4
tst.l LD_MFM(a4) ; mfm buffer already set?
bne.b .mfmok
move.l a1,LD_MFM(a4)
.mfmok IFNE LD_SAFETY
move.l d1,d4
sub.l d0,d4
cmp.l #160*11*512,d4
ble.b .ok
moveq #LD_OUTOFBOUNDS,d0
bra.b .error
ENDC
.ok lea (a0,d1.l),a3 ; end of buffer
move.l d0,d4
divu.w #512*11,d4
move.w d4,d5 ; LD_CURRENTTRACK(a4)
swap d4 ; number of bytes to skip
addq.b #3,d2
bsr.b LD_DriveOn ; select drive
bsr.b LD_DriveReady ; selected drive ready?
tst.l d0
beq.b .driveok
moveq #LD_NODRIVE,d0 ; return correct errorcode :)
bra.b .exit
.driveok
.loop bsr.b LD_StepToTrack ; move stepper motor to start track
bsr.w LD_LoadTrack ; load and decode track
tst.l d0 ; error?
bne.b .exit
bsr.w LD_DecodeTrack
IFNE LD_CHECKSUM
tst.l d0
bne.b .exit
ENDC
addq.w #1,d5
cmp.l a0,a3
bhi.b .loop
moveq #0,d0 ; no error
.exit bsr.b LD_DriveOff ; switch off drive
.error movem.l (a7)+,d1-a6
rts
*******************************************
*** DRIVE HANDLING ROUTINES ***
*******************************************
; d2.b: drive
LD_DriveOn
moveq #$7d,d1 ; deselect all drives
bra.b LD_DriveSet
; d2.b: drive
LD_DriveOff
moveq #-3,d1 ; move.b #$fd,d1
LD_DriveSet
move.b d1,(a5)
bclr d2,d1
move.b d1,(a5)
rts
; waits for DSKRDY bit to go low or a 500ms timer to expire
; after that, a read attempt is performed to check if the
; drive is available
;
; required because there are drives (f.e. some external drives,
; hello Mr.Spiv :D or the crappy drives in the Escom Amigas
; without the floppy fix (which btw is also a shitload of
; crap because it never switches off the drive motor!))
; that don't ever clear the DSKREADY bit
LD_DriveReady
moveq #10-1,d7
.loop move.w #LD_TIME5MS*10,d0 ; 50 ms
bsr.b LD_StartTimer
.check btst #5,$bfe001-$bfd100(a5)
beq.b .motorok
bsr.b LD_CheckTimer
beq.b .check
dbf d7,.loop ; 10*50ms
; no DSKREADY signal within 500ms, try to read a track
; to check if drive is ready
bra.w LD_LoadTrack
.motorok
moveq #0,d0 ; no error
rts
*******************************************
*** TIMER ROUTINES ***
*******************************************
IFEQ LD_USECIA-1 ; CIA A, TIMER A
; d0.w: time in ms
LD_StartTimer
move.b #1$bfee01-$bfd100(a5) ; set one-shot mode
move.b d0,$bfe401-$bfd100(a5) ; set timerA low byte
lsr.w #8,d0
move.b d0,$bfe501-$bfd100(a5) ; set timerA high byte and start timer
rts
; returns timer status (z-flag set: timer still running)
LD_CheckTimer
btst #0,$bfed01-$bfd100(a5) ; wait for timerA interrupt
rts
ELSE
IFEQ LD_USECIA-2 ; CIA A, TIMER B
; d0.w: time in ms
LD_StartTimer
move.b #1$bfef01-$bfd100(a5) ; set one-shot mode
move.b d0,$bfe601-$bfd100(a5) ; set timerB low byte
lsr.w #8,d0
move.b d0,$bfe701-$bfd100(a5) ; set timerB high byte and start timer
rts
; returns timer status (z-flag set: timer still running)
LD_CheckTimer
btst #1,$bfed01-$bfd100(a5) ; wait for timerB interrupt
rts
ELSE
IFEQ LD_USECIA-3 ; CIA B, TIMER A
; d0.w: time in ms
LD_StartTimer
move.b #1$e00-$100(a5) ; set one-shot mode
move.b d0,$400-$100(a5) ; set timerA low byte
lsr.w #8,d0
move.b d0,$500-$100(a5) ; set timerA high byte and start timer
rts
; returns timer status (z-flag set: timer still running)
LD_CheckTimer
btst #0,$d00-$100(a5) ; wait for timerA interrupt
rts
ELSE
IFEQ LD_USECIA-4 ; CIA B, TIMER B
; d0.w: time in ms
LD_StartTimer
move.b #1$f00-$100(a5) ; set one-shot mode
move.b d0,$600-$100(a5) ; set timerB low byte
lsr.w #8,d0
move.b d0,$700-$100(a5) ; set timerB high byte and start timer
rts
; returns timer status (z-flag set: timer still running)
LD_CheckTimer
btst #1,$d00-$100(a5) ; wait for timerB interrupt
rts
ENDC
ENDC
ENDC
ENDC
; simply waits for the timer to expire
LD_WaitTimer
.wait bsr.b LD_CheckTimer
beq.b .wait
rts
LD_Wait18ms
move.w #LD_TIME18MS,d0
LD_Timer
bsr.b LD_StartTimer
bra.b LD_WaitTimer
*******************************************
*** STEP TO ACTUAL TRACK ***
*******************************************
LD_StepToTrack
tst.b LD_INITFLAG(a4)
bne.b .noinit
bsr.b LD_MoveToTrack0 ; move to track 0 for orientation
st LD_INITFLAG(a4)
.noinit bclr #1,(a5) ; default direction = inwards
bset #2,(a5) ; default side: 0
move.w LD_LASTTRACK(a4),d0
move.w d5,d1
lsr.w #1,d0 ; /2 because we have 2 sides/track
lsr.w #1,d1
bcc.b .sideok
bclr #2,(a5) ; odd track, side is 1
.sideok sub.w d0,d1
beq.b .exit ; already at correct track
bgt.b .step
neg.w d1 ; we need positive loopcounter
bset #1,(a5) ; direction = outwards
.step bsr.b LD_Step
subq.w #1,d1
bne.b .step
move.w d5,LD_LASTTRACK(a4)
.exit rts
LD_MoveToTrack0
.loop btst #4,$e001-$d100(a5) ; already on track0?
beq.b .ok ; yes, nothing to do
bset #1,(a5) ; direction = outwards
bsr.b LD_Step ; step one track
bra.b .loop
.ok clr.w LD_LASTTRACK(a4)
rts
LD_Step bclr #0,(a5) ; set step signal
bset #0,(a5) ; clear step signal
bra.b LD_Wait18ms ; wait for steppermotor to finish
*******************************************
*** LOAD AND DECODE ONE TRACK ***
*******************************************
; a0: destination
; a3: end of buffer
; d4: offset
;----
; d0: error code (0: no error)
; split into 2 parts because of the drive check
LD_LoadTrack
move.w #1$96(a6) ; enable disk dma
move.w #~(1$ff00,$9e(a6) ; clear adkcon (just to be sure)
; set fastmode (mfm), wordsync, mfmprec, no precomp
move.w #1$9e(a6)
move.w #$4000,$24(a6)
move.w #$4489,$7e(a6)
move.w #1$9c(a6) ; clear disk irq
move.l LD_MFM(a4),a1
move.l a1,$20(a6)
move.w #1$1900,$24(a6)
move.w #1$1900,$24(a6)
bsr.b .timer
moveq #20,d3 ; 1s, should be more than enough
.waitDMA
move.w $1e(a6),d1
btst #1,d1
bne.b .done
bsr.w LD_CheckTimer
beq.b .waitDMA
bsr.b .timer ; reload timer
subq.w #1,d3
bne.b .waitDMA
moveq #LD_READERROR,d0 ; timeout!
bra.b .error
.done moveq #0,d0 ; no error
.error move.w #$4000,$24(a6)
move.w #1$96(a6) ; disable disk dma
rts
.timer move.w #LD_TIME5MS*10,d0 ; 50ms
bra.w LD_StartTimer
; decode mfm
; a0: destination
; a1: mfm buffer
; a3: end of buffer
; d4: offset
LD_DecodeTrack
IFNE LD_CHECKSUM
movem.l d2/d5/a5,-(a7) ; save
ELSE
move.l a5,-(a7)
ENDC
lea $1900*2(a1),a5 ; temp
move.l #$55555555,d3
moveq #0,d7
.secloop
move.l a1,a2
.getsync
cmp.w #$4489,(a2)+
bne.b .getsync
cmp.w #$4489,(a2)
beq.b .getsync
; get info data
movem.l (a2),d0/d1
bsr.b .decode
lsr.w #8,d0 ; d0: sector number
cmp.w d0,d7 ; are we on the correct sector?
beq.b .sectorok
lea $440-8(a2),a2
bra.b .getsync
.sectorok
; calc checksum
IFNE LD_CHECKSUM
movem.l $38-8(a2),d0/d1
bsr.b .decode
move.l d0,d5 ; save
moveq #0,d2 ; checksum for this sector
ENDC
lea $40-8(a2),a2 ; point to data
moveq #512/4-1,d6
.loop move.l 512(a2),d1 ; data (even)
move.l (a2)+,d0 ; data (odd)
IFNE LD_CHECKSUM
eor.l d0,d2
eor.l d1,d2
ENDC
bsr.b .decode
move.l d0,(a5)+
dbf d6,.loop
IFNE LD_CHECKSUM
moveq #LD_CRCERROR,d0
and.l d3,d2
cmp.l d2,d5
bne.b .crcerror
ENDC
addq.w #1,d7
cmp.w #11,d7
blt.b .secloop
lea $1900*2(a1),a5
add.w d4,a5
move.w #512*11,d0
sub.w d4,d0 ; # of bytes to copy
.copy cmp.l a3,a0
bge.b .exit
move.b (a5)+,(a0)+
subq.w #1,d0
bne.b .copy
moveq #0,d4 ; next track starts at the beginning
.exit IFNE LD_CHECKSUM
moveq #0,d0 ; no errors
ENDC
.crcerror
IFNE LD_CHECKSUM
movem.l (a7)+,d2/d5/a5
ELSE
move.l (a7)+,a5
ENDC
rts
.decode and.l d3,d0
and.l d3,d1
add.l d0,d0 ; $ff: init done
rs.b 1 ; padding
LD_VARS_SIZEOF rs.b 0
dcb.b LD_VARS_SIZEOF
PRINTT
PRINTT "LOADERSIZE:"
PRINTV *-LOADER
PRINTT
Sting’s HARDWARE TRACKLOADER
/
<-- All Terrain Racing” (c) 1995 Team 17 | [ Code ]
<-- All Terrain Racing” (c) 1995 Team 17 | [ Code ] | Angels Loader -->