boot3/Drivers/EDisk/EDiskDriver.a
Elliot Nunn 5b0f0cc134 Bring in CubeE sources
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included.

The Tools directory, containing mostly junk, is also excluded.
2017-12-26 10:02:57 +08:00

1767 lines
65 KiB
Plaintext

;
; File: EDiskDrvr.a
;
; Contains: Electronic Disk Driver
;
; Written by: Gary G. Davidian
;
; Copyright: © 1988-1993 by Apple Computer, Inc., all rights reserved.
;
; Change History (most recent first):
;
; <SM7> 10/28/93 CCH Optimized read path to not disable writes on every call and
; disabled checksums on PDM.
; <SM6> 10/14/93 CCH Added write-protection support for PowerPC machines.
; <SM5> 4/15/93 chp Fix RADAR #1078337. Format csCode would crash given an invalid
; drive number. An error handler was branching to the wrong label.
; <SM4> 2/8/93 rab Sync up with Horror. Comments follow.
; <H6> 9/30/92 BG (for Dave Nelson) Added a patch to fix the problem of the driver
; not adjusting the usage count correctly if someone calls the
; driver with an invalid _Control call.
; <SM3> 8/31/92 PN Fix header
; <SM2> 8/31/92 PN First time this file is moved over from Horror so that RAMDisk
; works with Cyclone
; ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
; SuperMario ROM comments start here.
; ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
; <H3> 2/5/92 SWC Set bit 15 in the driveInfo for RAM disks so the Finder can have
; a way to determine that the media is volatile.
; <H2> 12/12/91 CCH Fixed Read-Verify bug in 24-bit mode.
; ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
; Pre-HORROR ROM comments begin here.
; ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
; <7> 6/30/91 CCH Updated icon.
; <6> 6/13/91 CCH Moved RamDiskSize to EDiskDriveInfo record and modified the way
; it is initialized.
; <5> 5/24/91 CCH Turned on write-protection of the EDisk's address space when not
; being accessed by the driver.
; <4> 3/19/91 CCH Modified to use seperate ram disk memory area set up by the MMU.
; <3> 2/25/91 CCH Removed redundant RAM test, changed to a jump vector for
; _SwapMMUMode, and preserved the result code in the verify
; routine. Also made modifications to keep RAM disk intact across
; memory-mode changes (24<->32).
; <2> 12/20/90 CCH Added changes to support 32-bit addressing.
; ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
; Pre-TERROR ROM comments begin here.
; ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
; <3> 5/16/90 JJ Modify to use only 1 byte of Pram.
; <2> 5/16/90 JJ Restructured to provide RAMDisk on Elsie and Erickson.
; {2} 2/6/90 GMR Fixed bug in Prime where it didn't update ioNumDone,
; dCtlPosition properly.
; {1.8} 8/16/89 BBM version 1.7 broke hcmac build by the addition of flag hasSlim.
; fixed same.
; {1.7} 8/16/89 BBM temporary hack to get edisks working on emacs.
; <1.6> 5/26/89 GMR Saves RAM disk size in new EDisk global in Open code, added new
; status call to retrieve it.
; <1.5> 4/25/89 GMR Let d6 accumulate MOD3 test results on each RAM test pass, since
; the MOD3 test OR's errors into d6. This fixes the format bug in
; version 1.4.
; <1.4> 4/11/89 GMR Tests RAM in 16K chunks, for better mouse movement.
; <1.3> 4/4/89 GMR Added new ICON's. Format now tests RAM in 64K chunks, so the
; mouse won't freeze on large RAM disks.
; <1.2> 2/21/89 GGD Added support for immediate calls, and KillIO. Made the Apple
; look better in SLIM Icon. Added support for new SLIM interface
; adapter.
; <1.1> 11/11/88 CCH Fixed Header.
; <1.0> 11/9/88 CCH Adding to EASE.
; <1.5> 11/2/88 GGD Added code to post events to drive some sort of user interface
; for the lack of an eject/locking mechanism (currently, and
; hopefully permanently, disabled). Added support for internal ROM
; Disks (untested). Removed drivers need to staticly know its
; driver refnum, no code needs to change if refnum needs to
; change. Storage is now only allocated for drives that exist.
; EDiskHeader format and signature changed. Probably lots of other
; improvements too.
; <1.3> 9/1/88 GGD Added official drive icons. Added partial support for ROM disks.
; Added DriveInfo support. Changed drive queue order and numbering
; to interface better with the Startup Device CDEV. Optimized a
; few routines. Deleted code associated with normandy work around.
; Deleted support for Non-Battery-Backed-Up SLIM EDisks. Added
; special RAM Disk format code to erase and delete from drive
; queue.
; <1.2> 8/5/88 MSH Cleared hardware workaround flag. New normandy necessary to use
; SLIMs.
; <1.1> 6/21/88 GGD Changed Internal EDisk size computation and space allocation to
; be in 64KB blocks ending at MemTop. Changed FormatVerify to
; check checksums if present, otherwise NoErr. Changed Format to
; call TestManager to test RAM.
; <1.0> 4/21/88 MSH New Today
;
; To Do:
;
;--------------------------------------------------------
;
;_______________________________________________________________________
;
; © Apple Computer, Inc. 1988,1989
;
; written by Gary G. Davidian 13-Mar-88
;
;_______________________________________________________________________
BLANKS ON
PRINT OFF
LOAD 'StandardEqu.d'
INCLUDE 'HardwareEqu.a'
INCLUDE 'MMUEqu.a'
INCLUDE 'BootEqu.a'
INCLUDE 'SonyEqu.a'
PRINT ON
INCLUDE 'EDiskEqu.a'
PRINT NOMDIR
MACHINE MC68020
NewEdiskProt EQU 1
_TestManager OPWORD $A06B
macro
assert &boolExpr
if not(&Eval(&boolExpr)) then
aerror &concat('Assertion Failed - ',&boolExpr)
endif
endm
title 'EDisk Driver - DRVR Header'
; Driver Header for Device Manager
EDiskDRVR PROC EXPORT
with EDiskVars,EDiskDriveInfo,StartGlobals,MMUConfigInfo
EDiskDRVRflags SET 0
EDiskDRVRflags SET EDiskDRVRflags!1<<dReadEnable
EDiskDRVRflags SET EDiskDRVRflags!1<<dWritEnable
EDiskDRVRflags SET EDiskDRVRflags!1<<dCtlEnable
EDiskDRVRflags SET EDiskDRVRflags!1<<dStatEnable
EDiskDRVRflags SET EDiskDRVRflags!1<<dNeedLock
dc.b EDiskDRVRflags,0 ; drvFlags (upper byte)
dc.w 0 ; drvDelay, no delay
dc.w 0 ; drvEMask, no event mask
dc.w 0 ; drvMenu, no menu
dc.w EDiskOpen-EDiskDRVR ; drvOpen, offset of OPEN routine
dc.w EDiskPrime-EDiskDRVR ; drvPrime, offset of PRIME routine
dc.w EDiskControl-EDiskDRVR ; drvCtl, offset of ConTroL routine
dc.w EDiskStatus-EDiskDRVR ; drvStatus, offset of STATUS routine
dc.w EDiskClose-EDiskDRVR ; drvClose, offset of CLOSE routine
string pascal
dc.b '.EDisk' ; drvName, including leading length byte
align 2 ; force back to even boundaries
; Decode table for Status Calls
StatusDecode
dc.w statFmtLst-(*+4)
dc.w fmtLstCode ; Get Format List status call
dc.w statDrvSts-(*+4)
dc.w drvStsCode ; Drive Status call
dc.w statDrvSize-(*+4) ; <1.7>
dc.w drvSizeCode ; Drive Size Status call <1.7>
dc.w -1 ; indicate end of status decode table
dc.w StatusErr ; return StatusErr if unknown csCode
; Decode table for Control Calls
ControlDecode
dc.w ctlKillIO-(*+4) ; <1.2>
dc.w killCode ; KillIO control call (not supported) <1.2>
dc.w ctlVerify-(*+4)
dc.w VerifyCC ; Verify control call
dc.w ctlFormat-(*+4)
dc.w FormatCC ; Format control call
dc.w ctlEject-(*+4)
dc.w EjectCode ; Eject control call
dc.w ctlDriveIcon-(*+4)
dc.w IconCC ; Physical Drive Icon control call
dc.w ctlMediaIcon-(*+4)
dc.w IconLogCC ; Disk Media Icon control call
dc.w ctlDriveInfo-(*+4)
dc.w infoCC ; Drive Info control call
dc.w -1 ; indicate end of control decode table
dc.w ControlErr ; return ControlErr if unknown csCode
title 'EDisk Driver - Open processing'
;_______________________________________________________________________
;
; Routine: EDiskOpen
; Inputs: A0 - pointer to I/O ParamBlock
; A1 - pointer to Device Control Entry (DCE)
; Outputs: D0 - Result Code (noErr/openErr)
; Destroys:
; Calls: none
; Called by: Device Manager
;
; Function: Driver initialization routine
;
; NOTE: EDisks are installed in the following order, Slim1 (lower),
; Slim0 (upper), ROM Disk, RAM Disk. This is the order that
; the start code will attempt to boot from them, unless this
; has been overriden with the Startup Device CDEV. This order
; was chosen as follows, removable devices first, fixed devices
; last. For Slims, the lower Slim is checked first to be consistent
; with the two drive Mac SE / Harpo Floppy drive search order,
; which checks the lower drive first. For ROM/RAM disks, the RAM
; disk is searched first, and then the ROM disks are searched. The
; user can still override this order, by picking one with the CDEV.
; Also, gaps may be left in the drive numbering for drive which
; aren't found, when installing them in the drive queue, because
; the startup device drive number is saved in parameter ram,
; and we don't want it moving around across boots.
;_______________________________________________________________________
InitTable
dc.l 0 ; RamDisk doesn't have poll pointer
dc.w RAMDiskHandler-(*+2) ; RamDisk function handler
dc.w RAMDiskMediaIcon-(*+2) ; RamDisk media icon pointer
dc.w RAMDiskDriveIcon-(*+2) ; RamDisk drive icon pointer
dc.w RAMDiskName-(*+2) ; RamDisk drive name pointer
dc.l (1<<15)+\ ; volatile media
(0<<11)+\ ; primary
(1<<10)+\ ; fixed
(0<<9)+\ ; iwm (not SCSI)
(0<<8)+\ ; internal
ramDiskType ; RAM disk
dc.b (1<<CreateWithXSums) ; flags
dc.b $48 ; DiskInPlaceInit = non-ejectable, re-mount
InitTablePPC
dc.l 0 ; RamDisk doesn't have poll pointer
dc.w RAMDiskHandlerPPC-(*+2) ; RamDisk function handler for PowerPC
dc.w RAMDiskMediaIcon-(*+2) ; RamDisk media icon pointer
dc.w RAMDiskDriveIcon-(*+2) ; RamDisk drive icon pointer
dc.w RAMDiskName-(*+2) ; RamDisk drive name pointer
dc.l (1<<15)+\ ; volatile media
(0<<11)+\ ; primary
(1<<10)+\ ; fixed
(0<<9)+\ ; iwm (not SCSI)
(0<<8)+\ ; internal
ramDiskType ; RAM disk
dc.b (0<<CreateWithXSums) ; flags
dc.b $48 ; DiskInPlaceInit = non-ejectable, re-mount
PrimaryROMdisk
dc.l 0 ; RomDisk doesn't have poll pointer
dc.w ROMDiskHandler-(*+2) ; RomDisk function handler
dc.w ROMDiskMediaIcon-(*+2) ; RomDisk media icon pointer
dc.w ROMDiskDriveIcon-(*+2) ; RomDisk drive icon pointer
dc.w ROMDiskName-(*+2) ; RomDisk drive name pointer
dc.l (0<<11)+\ ; primary
(1<<10)+\ ; fixed
(0<<9)+\ ; iwm (not SCSI)
(0<<8)+\ ; internal
romDiskType ; ROM disk
dc.b (1<<CreateWithXSums) ; flags
dc.b $08 ; DiskInPlaceInit = non-ejectable
InitEntrySize equ *-PrimaryROMdisk ; size of an initialization table entry
SecondaryROMdisk
dc.l 0 ; RomDisk doesn't have poll pointer
dc.w ROMDiskHandler-(*+2) ; RomDisk function handler
dc.w ROMDiskMediaIcon-(*+2) ; RomDisk media icon pointer
dc.w ROMDiskDriveIcon-(*+2) ; RomDisk drive icon pointer
dc.w ROMDiskName-(*+2) ; RomDisk drive name pointer
dc.l (1<<11)+\ ; secondary
(1<<10)+\ ; fixed
(0<<9)+\ ; iwm (not SCSI)
(0<<8)+\ ; internal
romDiskType ; ROM disk
dc.b (1<<CreateWithXSums) ; flags
dc.b $08 ; DiskInPlaceInit = non-ejectable
EDiskOpen ; all regs saved by Device Manager
move.l a0,-(sp) ; save pointer to I/O param block
; Find the highest used drive number
move.l DCtlRefNum(a1),d7 ; d7.high := driver ref num
move.w #5,d7 ; d7 := refnum/highest drive number
move.l DrvQHdr+QHead,d0 ; get the drive queue head
beq.s @FoundDriveNumber ; if drive queue is empty
@NextDriveQueue
movea.l d0,a0 ; a0 := drive queue element
cmp.w DQDrive(a0),d7 ; check for a match
bhs.s @NextDriveNumber ; if drive number already in use
move.w DQDrive(a0),d7 ; this is now the highest drive number
@NextDriveNumber
move.l QLink(a0),d0 ; check next drive queue element
bne.s @NextDriveQueue ; if queue element exists
@FoundDriveNumber
addq.w #1,d7 ; d7 := next drive number
lea InitTable,a6 ; a6 := assume pointer to 68k initial values <2>
IF newEdiskProt THEN
cmpi.b #EMMU1,MMUType ; check for an emulated MMU
bne.s @NoEMMU
lea InitTablePPC,a6 ; a6 := pointer to PowerPC initial values
@NoEMMU
ENDIF
move.l BootGlobPtr,a2 ; get ptr to BootGlobs <T2>
move.l sgRamDiskBase(a2),d2 ; base of RAM disk <T4>
move.l d2,d3 ; get a copy in d3 <T4>
add.l sgRamDiskSize(a2),d3 ; end of ram disk in d3 <T4>
suba.l a2,a2 ; a2 := HeaderInfoPtr (none for RamDisk) <2>
move.l d2,d1 ; get start addr in d2 <T4>
moveq.l #0,d2 ; DataStartPtr := 0 <2>
cmp.l d1,d3 ; see if it has any bytes
bsr CreateEDrive ; create the RAM disk
@noDisk ; ENDIF
; search for, and install ROM disks
movea.l RomBase,a2 ; start searching at the base of ROM
@RomDiskLoop ;
bsr.w CheckForRomDisk ; see if it is a ROM disk
bne.s @NextRomDisk ; if not, try the next block
moveq.l #0,d1 ; assume no checksums unless header says otherwise
move.l a2,d2 ; DataStartPtr := base of rom disk for now
move.l a2,d3 ; DataEndPtr := base of rom disk for now
bsr.s CreateEDrive ; create the RAM disk
lea SecondaryROMdisk,a6 ; all future ROM disks are secondary
@NextRomDisk ;
adda.l #RomDiskAlign,a2 ; point to next block to check
cmpa.l #RomSpaceEnd,a2 ; see if end reached
blo.s @RomDiskLoop ; search the entire space
; see if we have any EDisks to drive, abort OPEN if none.
moveq.l #OpenErr,d0 ; if no drives found, return OpenErr
swap d7 ; d7.low := driver ref num
lea DrvQHdr+QHead-QLink,a0 ; point to the drive queue head
@CheckNeXTDrive
move.l QLink(a0),d1 ; check next drive queue element
beq.s @OpenDone ; if end of queue, and not found, kill the open
movea.l d1,a0 ; a0 := drive queue element
cmp.w dQRefNum(a0),d7 ; is this one of ours
bne.s @CheckNeXTDrive ; if not, check the NeXT one
; there are some EDisks in the drive queue, guess we need a driver for them.
move.l #EDiskVarsSize,d0 ; size of block to allocate
_NewPtr ,SYS,CLEAR ; allocate memory for globals
movea.l a0,a2 ; a2 := pointer to globals
move.l a2,DCtlStorage(a1) ; save globals pointer in DCE
move.l a1,DCEpointer(a2) ; save pointer to DCE in globals
move.w @RTS,FindDqePatch(a2) ; initialize FindDQE patch routine
move.w @RTS,PrimePatch(a2) ; initialize Prime patch routine
; Install the VBL task to look for SLIM insertions and removals
lea EDiskPollTask,a4 ; get the task address
moveq.l #8-1,d4 ; loop counter (8 times)
@InitPolling
lea VTask(a2),a0 ; a0 := pointer to VTask
jsr (a4) ; poll 8 times to init inserted status
dbra d4,@InitPolling ; and mount drives, and compute sizes
lea VTask(a2),a0 ; a0 <- VBL task
addq.w #vType,qType(a0) ; initialize qType field
move.l a4,vblAddr(a0) ; initialize vblAddr field
_VInstall ; install the VBL task
moveq.l #NoErr,d0 ; no error
@OpenDone
movea.l (sp)+,a0 ; restore pointer to I/O param block
move.w d0,ioResult(a0) ; return open status in ioResult
@rts rts
title 'EDisk Driver - Create EDrive'
;_______________________________________________________________________
;
; Routine: CreateEDrive
; Inputs: ccr.z - bne if drive should be created, beq if not
; A2 - HeaderInfoPtr
; D1 - CheckSumPtr
; D2 - DataStartPtr
; D3 - DataEndPtr
; D7.hi - EDisk Driver RefNum
; D7.lo - drive number
; A6 - pointer to InitTable info for this drive
; Outputs: D7.hi - EDisk Driver RefNum
; D7.lo - drive number + 1
; A6 - pointer to InitTable info for next drive
; Destroys: D0, A0, A3, A4
; Calls: none
; Called by: EDiskOpen
;
; Function: Creates and initializes the EDiskDriveInfo for the specified
; EDisk, and installs the drive queue entry for it.
;
;_______________________________________________________________________
CreateEDrive
beq.s @Done ; skip it if not used
moveq.l #EDiskDriveInfoSize,d0 ; size of info to allocate
_NewPtr ,Sys,Clear ; allocate the drive info
bne.s @Done ; if can't allocate, don't create drive
add.l #DQE-SLIMRegPtr,a0 ; point to the drive queue entry <T6>
movea.l a6,a3 ; a3 := running pointer to init table entry
move.l (a3)+,SLIMRegPtr(a0) ; initialize the SLIMRegPtr <T6>
movea.w (a3)+,a4 ; get the icon/name ptr
adda.l a3,a4 ; make it absolute
move.l a4,HWDepProcPtr(a0) ; initialize the HWDepProcPtr <T6>
move.l a2,HeaderInfoPtr(a0) ; initialize the HeaderInfoPtr <T6>
move.l d1,CheckSumPtr(a0) ; initialize CheckSumPtr <T6>
move.l d2,DataStartPtr(a0) ; initialize DataStartPtr |
move.l d3,DataEndPtr(a0) ; initialize DataEndPtr v
movea.w (a3)+,a4 ; get the icon/name ptr
adda.l a3,a4 ; make it absolute
move.l a4,MediaIconPtr(a0) ; initialize the icon ptr
movea.w (a3)+,a4 ; get the icon/name ptr
adda.l a3,a4 ; make it absolute
move.l a4,DriveIconPtr(a0) ; initialize the icon ptr
movea.w (a3)+,a4 ; get the icon/name ptr
adda.l a3,a4 ; make it absolute
move.l a4,WhereStringPtr(a0) ; initialize the name ptr
move.l (a3)+,DriveInfo(a0) ; initialize DriveInfo
move.w (a3)+,Flags(a0) ; initialize flags, DiskInPlaceInit
move.b #%01010101,InsertedStatus(a0) ; initialize inserted status to bouncing ^
sub.l d1,d3 ; calculate ram disk size |
move.l d3,RamDiskSize(a0) ; store it in globals <T6>
addq.w #1,qType(a0) ; use long drive size format
addq.w #1,Installed(a0) ; mark the drive as installed
move.l d7,d0 ; get disk drive number, and refNum
swap d0 ; move to high word, refNum to low word
move.l a0,a3 ; make a copy of ptr to info
_AddDrive ; add the drive to the drive queue
move.l d1,a0 ; get ptr to base of Edisk data
move.l d3,d1 ; get RamDiskSize
moveq.l #DisableEDiskWrites,d0 ; always disable writes at startup
bsr.w HWDependent ; disable writing to the EDisk
@Done addq.w #1,d7 ; update drive number
adda.w #InitEntrySize,a6 ; point to next drive init entry
rts ; EDisk drive is installed
title 'EDisk Driver - Check for ROM Disk'
;_______________________________________________________________________
;
; Routine: CheckForRomDisk
; Inputs: A2 - HeaderInfoPtr
; D7.hi - EDisk Driver RefNum
; Outputs: D0 - zero if valid ROM disk header, else non-zero
; ccr.z - bne if invalid header
; Destroys: D0, D1, A0, A3, A4
; Calls: none
; Called by: EDiskOpen
;
; Function: Checks the EDisk header to see if it exists (no bus error),
; and has a valid signature. If it is valid, compares it
; against all other ROM Disks found, and if it's header block
; is not identical to any other ROM Disk (in case address
; wrap around causes repeated images), returns a result
; indicating that it should be created.
;
;_______________________________________________________________________
with EDiskHeader
CheckForRomDisk
move.w sr,-(sp) ; save old interrupt mask
ori.w #HiIntMask,sr ; disable interrupts (stealing BusErrVct)
move.l BusErrVct,-(sp) ; save old bus error vector
lea @NotFound,a0 ; new handler address
move.l a0,BusErrVct ; setup bus error handler
movea.l sp,a4 ; mark the stack
lea HeaderTemplate,a3 ; point to expected values
lea HdrBlockSize(a2),a0 ; point to header data
moveq.l #(HdrDeviceSize-HdrBlockSize)/4-1,d0 ; loop counter
@SigCmpLoop
cmpm.l (a0)+,(a3)+ ; compare the values
dbne d0,@SigCmpLoop ; compare the blocks
@NotFound
movea.l a4,sp ; pop stack in case of bus error
move.l (sp)+,BusErrVct ; restore bus error vector
move.w (sp)+,sr ; restore interrupt mask
addq.w #1,d0 ; see if we had a match
bne.s @Done ; exit if not
; valid header found, now let's see if it's unique.
move.l d7,d1 ; get driver ref num
swap d1 ; d1.low := driver ref num
lea DrvQHdr+QHead-QLink,a0 ; point to the drive queue head
@CheckNeXTDrive
move.l QLink(a0),d0 ; check next drive queue element
beq.s @Done ; if not found, it's unique (d0=0)
movea.l d0,a0 ; a0 := drive queue element
cmp.w dQRefNum(a0),d1 ; is this one of ours
bne.s @CheckNeXTDrive ; if not, check the NeXT one
cmpi.b #romDiskType,\ ; it's one of ours
DriveInfo+3(a0) ; is it a ROM disk
bne.s @CheckNeXTDrive ; if not, check the NeXT one
moveq.l #EDiskHeaderSize/4-1,d0 ; compare the entire header
movea.l a2,a3 ; point to our header
movea.l HeaderInfoPtr(a0),a4 ; point to existing drive's header
@HdrCmpLoop
cmpm.l (a3)+,(a4)+ ; compare the values
dbne d0,@HdrCmpLoop ; compare the blocks
bne.s @CheckNeXTDrive ; if not the same, check the NeXT one
@Done tst.l d0 ; set ccr based on D0
rts ; all done
endwith
title 'EDisk Driver - Close processing'
;_______________________________________________________________________
;
; Routine: EDiskClose
; Inputs: A0 - pointer to I/O ParamBlock
; A1 - pointer to Device Control Entry (DCE)
; Outputs: D0 - Result Code (closErr)
; Destroys:
; Calls: none
; Called by: Device Manager
;
; Function: Driver shutdown routine
;
;_______________________________________________________________________
EDiskClose ; all regs saved by Device Manager
moveq.l #closErr,d0 ; return Close Error status
rts ; not closable at present
title 'EDisk Driver - Read / Write processing'
;_______________________________________________________________________
;
; Routine: EDiskPrime
; Inputs: A0 - pointer to I/O ParamBlock
; A1 - pointer to Device Control Entry (DCE)
; Outputs: D0 - Result Code
; Destroys:
; Calls: IODone
; Called by: Device Manager
;
; Function: Driver Read/Write routines
;
;_______________________________________________________________________
EDiskPrime ; all regs saved by Device Manager
;_Debugger
move.w ioTrap(a0),-(sp) ; save trap word to test immediate on exit <1.2>
moveq.l #0,d4 ; d4 := bytes transferred
movem.l a0/a1/d4,-(sp) ; save iopb, dce. allocate scratch longword
bsr.w FindDQE ; Find the drive queue element
bne.w @PrimeAbort ; return with error if not found or offLine <H1>
; check buffer paramaters
moveq.l #ParamErr,d0 ; assume parameter error
move.l ioByteCount(a0),d1 ; d1 := byte count
beq @PrimeAbort ; if byte count is zero <H1>
move.l dCtlPosition(a1),d2 ; d2 := starting byte offset
movem.l CheckSumPtr(a3),a4/a5/a6; get checksum/start/end pointers
adda.l d2,a5 ; a5 := RAM disk start addr
suba.l a5,a6 ; a6 := max byte count allowed
cmpa.l d1,a6 ; check for out of bounds
blt @PrimeAbort ; read past end of disk <H1>
; check alignment and direction
move.l d1,d7 ; get byte count
or.l d2,d7 ; check offset and byte count
andi.w #$01FF,d7 ; for multiples of block size (512)
bne @PrimeAbort ; if request not block aligned <H1>
move.l ioBuffer(a0),d6 ; d6 := buffer addr
moveq.l #rdVerify,d7 ; prepare to test for read verify
and.w IOPosMode(a0),d7 ; test for verify mode
sne d7 ; d7 := $FF if read verify
neg.b d7 ; d7 := 0 if read, 1 if read verify
move.l d6,d0 ; get destination addr in d0 <T2>
_StripAddress ; clear upper byte if necessary <T2>
cmpi.b #aWrCmd,ioTrap+1(a0) ; check for write cmd
movea.l a5,a0 ; a0 := source address
movea.l d0,a1 ; a1 := clean dest address <T2>
bne.s @paramsOK ; if not write, continue setting up for I/O
; check write params
moveq.l #2,d7 ; d7 := 2 if write
moveq.l #wPrErr,d0 ; assume write protect error
tst.b WriteProtected(a3) ; can we write to this disk?
bmi.s @PrimeAbort ; return with error if write protected <H1>
; d1 := byte count
; a0 := source address
; a1 := clean dest address
movem.l a0/d1,-(sp) ; save size, length for disabling the disk
moveq.l #EnableEDiskWrites,d0 ; hardware dependent function code
bsr.w HWDependent ; allow writing to the EDisk
exg a0,a1 ; swap source and dest if write
; select data transfer routine
@paramsOK
roxr.b #1,d6 ; ccr.x := ioBuffer is odd
addx.b d7,d7 ; shift result into d7
move.l a4,d6 ; prepare to test for checksumming
subq.l #1,d6 ; ccr.x := checksumming is disabled
roxl.w #2,d7 ; shift result into d7, and mult by 2 for index
lsr.l #9-2,d2 ; compute index into checksum table
adda.l d2,a4 ; a4 := pointer to checksum for first block
lea @DispatchTable,a5 ; point to the dispatch table
adda.w (a5,d7.w),a5 ; a5 := pointer to the routine
jsr PrimePatch(a2) ; allow patching here
move.l #512,d3 ; d3 := constant 512
; transfer the data a block at a time (usually)
@BlockLoop
moveq.l #0,d5 ; d5 := checksum, initialize to zero
moveq.l #(512/4)-1,d2 ; d2 := loop count for 1 block (4 bytes per loop)
jmp (a5) ; dispatch to the routine
@BlockDone
add.l d3,d4 ; update num done (512 more bytes done)
sub.l d3,d1 ; decrement bytecount
bhi.s @BlockLoop ; do next block if any blocks left
; transfer complete
@PrimeDone
moveq.l #noErr,d0 ; success
@PrimeErr
btst #EDiskProtect,Flags(a3) ; check if EDisk is protected
bne.s @ediskProtected ; IF EDisk is not protected THEN
move.l d0,d2 ; save result code in d2
movem.l (sp)+,a0/d1 ; restore size, length for disable code
moveq.l #DisableEDiskWrites,d0 ; hardware dependent function code
bsr.w HWDependent ; disable writing to the EDisk
move.l d2,d0 ; restore d0 with result code
@ediskProtected ; ENDIF
@PrimeAbort
movem.l (sp)+,a0/a1/d1 ; restore iopb, dce. pop scratch longword <2>
move.l d4,ioNumDone(a0) ; return number of bytes transferred
add.l d4,dCtlPosition(a1) ; update position pointer
bra.w EDiskDone ; return with error
; EDisk <-> Buffer, even or odd aligned, no checksumming (all blocks at once)
@ReadWrite
move.l d1,d0 ; get the byte count
_BlockMove ; move the data
move.l d1,d4 ; indicate all bytes moved
bra.s @PrimeDone ; return with success
title 'EDisk Driver - Read processing'
; EDisk -> Buffer, with checksumming, buffer even aligned (one block at a time)
@ReadEvenXsum
moveq.l #(512/8)-1,d2 ; d2 := loop count for 1 block (8 bytes per loop)
@ReadEvenLoop
move.l (a0)+,d7 ; fetch the source
add.l d5,d5 ; rotate the checksum
addx.l d7,d5 ; add source to the checksum
move.l d7,(a1)+ ; write it to the destination
move.l (a0)+,d7 ; fetch the source
add.l d5,d5 ; rotate the checksum
addx.l d7,d5 ; add source to the checksum
move.l d7,(a1)+ ; write it to the destination
dbra d2,@ReadEvenLoop ; loop through the entire block
@ReadDone
cmp.l (a4)+,d5 ; check the checksum
beq.s @BlockDone ; done with this block
moveq.l #badDCksum,d0 ; indicate data checksum error
bra.s @PrimeErr ; return with checksum error
; EDisk -> Buffer, with checksumming, buffer odd aligned (one block at a time)
@ReadOddXsum
subq.l #3,a1 ; back up the dest ptr, make it even
move.l (a1),d7 ; d7 := x x x A
addq.l #*+2-@ReadOddXsum,a5 ; after first block skip the code above
@ReadOddLoop
move.l (a0)+,d6 ; d6 := A B C D
add.l d5,d5 ; rotate the checksum
addx.l d6,d5 ; add source to the checksum
rol.l #8,d6 ; d6 := B C D A
move.b d6,d7 ; d7 := x x x A
move.l d7,(a1)+ ; write it to the destination
move.l d6,d7 ; d7 := B C D A
dbra d2,@ReadOddLoop ; loop through the entire block
move.b 3(a1),d7 ; d7 := B C D x
move.l d7,(a1) ; update last long word
bra.s @ReadDone ; done with this block
title 'EDisk Driver - Verify processing'
; Read Verify calls with and without checksumming
; buffer even aligned (one block at a time)
@VerifyEven
movea.l sp,a4 ; if no checksum, point to stack
@VerifyEvenXsum
@VerifyEvenLoop
move.l (a0)+,d7 ; fetch the source
add.l d5,d5 ; rotate the checksum
addx.l d7,d5 ; add source to the checksum
cmp.l (a1)+,d7 ; compare the buffers
dbne d2,@VerifyEvenLoop ; loop through the entire block
beq.s @VerifyGood ; if blocks are equal
@VerifyError
moveq.l #dataVerErr,d0 ; indicate verify failed
bra.s @PrimeErr ; return with error
; Read Verify calls with and without checksumming
; buffer odd aligned (one block at a time)
@VerifyOddXsum
lea @VerifyOddLoop,a5 ; skip the following code after the first time
@VerifyOddSetup
subq.l #1,a1 ; back up 1 byte to align
move.l (a1)+,d6 ; d6 := x A B C
rol.l #8,d6 ; d6 := A B C x
jmp (a5) ; start the loop
@VerifyOdd
addq.l #4,a5 ; resume after the next instruction
bra.s @VerifyOddSetup ; setup for the odd aligned compare
movea.l sp,a4 ; if no checksum, point to stack
@VerifyOddLoop
move.l d6,d7 ; d7 := A B C x
move.l (a1)+,d6 ; d6 := D E F G
rol.l #8,d6 ; d6 := E F G D
move.b d6,d7 ; d7 := A B C D
add.l d5,d5 ; rotate the checksum
addx.l d7,d5 ; add source to the checksum
cmp.l (a0)+,d7 ; compare the buffers
dbne d2,@VerifyOddLoop ; loop through the entire block
bne.s @VerifyError ; if not a match
@VerifyGood
move.l d5,(sp) ; force compare when not checksumming
bra.s @ReadDone ; check the checksum
title 'EDisk Driver - Write processing'
; EDisk <- Buffer, with checksumming, buffer even aligned (one block at a time)
@WriteEvenXsum
moveq.l #(512/8)-1,d2 ; d2 := loop count for 1 block (8 bytes per loop)
@WriteEvenLoop
move.l (a0)+,d7 ; fetch the source
add.l d5,d5 ; rotate the checksum
addx.l d7,d5 ; add source to the checksum
move.l d7,(a1)+ ; write it to the destination
move.l (a0)+,d7 ; fetch the source
add.l d5,d5 ; rotate the checksum
addx.l d7,d5 ; add source to the checksum
move.l d7,(a1)+ ; write it to the destination
dbra d2,@WriteEvenLoop ; loop through the entire block
@WriteDone
move.l d5,(a4)+ ; write out the checksum
bra.w @BlockDone ; done with this block
; EDisk <- Buffer, with checksumming, buffer odd aligned (one block at a time)
@WriteOddXsum
subq.l #1,a0 ; back up the source ptr, make it even
move.l (a0)+,d7 ; d7 := x A B C
rol.l #8,d7 ; d7 := A B C x
addq.l #*+2-@WriteOddXsum,a5 ; after first block skip the code above
@WriteOddLoop
move.l (a0)+,d6 ; d6 := D E F G
rol.l #8,d6 ; d6 := E F G D
move.b d6,d7 ; d7 := A B C D
add.l d5,d5 ; rotate the checksum
addx.l d7,d5 ; add source to the checksum
move.l d7,(a1)+ ; write it to the destination
move.l d6,d7 ; d7 := E F G D
dbra d2,@WriteOddLoop ; loop through the entire block
bra.s @WriteDone ; done with this block
; Table to dispatch to proper routine
@DispatchTable
dc.w @ReadEvenXsum-@DispatchTable ; 0 0 0 0 - read, even, xsum enabled
dc.w @ReadWrite-@DispatchTable ; 0 0 0 1 - read, even, xsum disabled
dc.w @ReadOddXsum-@DispatchTable ; 0 0 1 0 - read, odd, xsum enabled
dc.w @ReadWrite-@DispatchTable ; 0 0 1 1 - read, odd, xsum disabled
dc.w @VerifyEvenXsum-@DispatchTable ; 0 1 0 0 - verify, even, xsum enabled
dc.w @VerifyEven-@DispatchTable ; 0 1 0 1 - verify, even, xsum disabled
dc.w @VerifyOddXsum-@DispatchTable ; 0 1 1 0 - verify, odd, xsum enabled
dc.w @VerifyOdd-@DispatchTable ; 0 1 1 1 - verify, odd, xsum disabled
dc.w @WriteEvenXsum-@DispatchTable ; 1 0 0 0 - write, even, xsum enabled
dc.w @ReadWrite-@DispatchTable ; 1 0 0 1 - write, even, xsum disabled
dc.w @WriteOddXsum-@DispatchTable ; 1 0 1 0 - write, odd, xsum enabled
dc.w @ReadWrite-@DispatchTable ; 1 0 1 1 - write, odd, xsum disabled
title 'EDisk Driver - Control / Status processing'
;_______________________________________________________________________
;
; Routine: EDiskControl / EDiskStatus / KillIO
; Inputs: A0 - pointer to I/O ParamBlock
; A1 - pointer to Device Control Entry (DCE)
; Outputs: D0 - Result Code
; Destroys:
; Calls: FindDQE, EDiskDone
; Called by: Device Manager
;
; Function: Driver Control and Status routines
;
;_______________________________________________________________________
EDiskControl ; all regs saved by Device Manager
lea ControlDecode,a2 ; offset of decode table
bra.s HandleControlStatus ; join common code
EDiskStatus ; all regs saved by Device Manager
lea StatusDecode,a2 ; offset of decode table
* bra.s HandleControlStatus ; join common code (fall into it)
HandleControlStatus
move.w ioTrap(a0),-(sp) ; save trap word to test immediate on exit <1.2>
pea EDiskDone ; return through EDiskDone
move.w csCode(a0),d1 ; get the control/status code
@search move.l (a2)+,d0 ; get table entry
bmi.s @exit ; exit with error if not in list
cmp.w d0,d1 ; is it a match
bne.s @search ; keep looking until found
swap d0 ; get offset of routine
pea (a2,d0.w) ; push address of the routine
bra.w FindDQE ; find the DQE before calling routine
@exit movea.l dCtlStorage(a1),a2 ; a2 := pointer to EDiskVars
addq.l #4,sp ; <SM4>
bra.w EDiskDone2 ; return through EDiskDone2 <SM4>
ctlKillIO
move.w #immed,4(sp) ; make saved ioTrap an immediate call <1.2>
moveq.l #ControlErr,d0 ; we don't support KillIO, return an error <1.2>
rts ; return through EDiskDone
title 'EDisk Driver - Control / Status processing - Eject'
ctlEject
move.l d1,d0 ; get found status, ignore offline
bne.s @EjectErr ; return error if not found
bclr.b #MountedFlag,Flags(a3) ; mark it as not mounted
move.b DiskInPlace(a3),d0 ; see if any disk
ble.s @EjectDone ; if no disk, just ignore it
subq.b #8,d0 ; see if ejectable
beq.s @EjectDone ; if not ejectable, just ignore it
; if disk in place was $48 (non-ejectable, but call driver anyway) we will force a
; disk in place event to be posted by the polling task, to re-mount the volume.
moveq.l #EjectEDisk,d0 ; hardware dependent function code
bsr.w HWDependent ; start the eject, update DiskInPlace
@EjectDone
moveq.l #noErr,d0 ; return with success
@EjectErr
rts ; all done
title 'EDisk Driver - Control / Status processing - Format'
ctlFormat
bne @exit ; return with error if no drive or offline
; d1 := byte count
; a0 := source address
; a3 := ptr to drive queue element
move.l CheckSumPtr(a3),a0 ; put base addr of Edisk in a0
tst.l a0 ; check for no checksums
bne.s @checksumsOn ; IF no checksums THEN
move.l DataStartPtr(a3),a0 ; get real start of disk
@checksumsOn ; ENDIF
move.l DataEndPtr(a3),d1 ; put end of edisk in d1
sub.l a0,d1 ; get length in d1
movem.l a0/d1,-(sp) ; save size, length for enabling the disk
moveq.l #EnableEDiskWrites,d0 ; hardware dependent function code
bsr.w HWDependent ; enable writing to the EDisk
moveq.l #paramErr,d0 ; assume parameter error
move.w (a4),d1 ; get the format kind parameter
subq.w #1,d1 ; only allow 0 or 1
bls.s @CheckWrProt ; if in range, continue
addq.w #2,d1 ; -1 is a special case (destroys signature too)
bne.s @done ; if not -1, 0, or 1, return paramErr
cmpi.b #ramDiskType,DriveInfo+3(a3) ; see if it is a ram disk
bne.s @done ; if not, return the paramErr
lea DrvQHdr,a1 ; get the queue header
movea.l a3,a0 ; get the Ram Disk drive queue element
_DeQueue ; remove it from the drive queue
moveq.l #0,d6 ; fake RAM test passed
bra.s @SkipTest ; skip the test, and just clear it
@CheckWrProt
moveq.l #wPrErr,d0 ; assume write protect error
tst.b WriteProtected(a3) ; can we write to this disk?
bmi.s @done ; return write protect error if not
bsr.w CreateEDiskHeader ; write out a signature
moveq.l #0,d6 ; fake RAM test passed
@SkipTest
movem.l CheckSumPtr(a3),a0/a1/a4; get the checksum/start/end pointers
move.l a0,d2 ; see if has checksums, assume no error.
bne.s @ClearDisk ; if has checksums, clear them
movea.l a1,a0 ; otherwise, clear data as checksums
@ClearDisk
moveq.l #0,d0 ; clear to zero, assume success
tst.l d6 ; see if test passed <1.3>
beq.s @FormatStart ; if passed, initialize RAM and checksums
moveq.l #Fmt1Err,d0 ; indicate that format failed
bra.s @done ; all done
@NextBlock
moveq.l #(512/4)-1,d1 ; inner loop counter
@BlockLoop
move.l d0,(a1)+ ; clear a longword at a time
dbra d1,@BlockLoop ; clear the whole block
move.l d0,(a0)+ ; clear the checksum too
@FormatStart
cmpa.l a1,a4 ; see if all blocks cleared
bhi.s @NextBlock ; loop through all of the blocks
@done move.l d0,d2 ; save error code
IF newEdiskProt THEN
; d1 := byte count
; a0 := source address
; a3 := ptr to drive queue element
movem.l (sp)+,a0/d1 ; restore size, length for disabling the disk
ENDIF
moveq.l #DisableEDiskWrites,d0 ; hardware dependent function code
bsr.w HWDependent ; disable writing to the EDisk
move.l d2,d0 ; restore error code
@exit rts ; all done
title 'EDisk Driver - Control / Status processing - Verify'
; Verify that each block on the disk can be read
ctlVerify
bne.s @done ; return with error if no drive or offline
movem.l CheckSumPtr(a3),a0/a1/a4; get the checksum/start/end pointers
move.l a0,d0 ; see if has checksums, assume no error.
beq.s @done ; if has checksums, check them
moveq.l #noErr,d0 ; assume success, init checksum
@NextBlock
cmpa.l a1,a4 ; see if all blocks checked
bls.s @done ; exit when all blocks checked
moveq.l #(512/4)-1,d1 ; inner loop counter
@BlockLoop
move.l (a1)+,d3 ; fetch a longword at a time
add.l d0,d0 ; rotate the checksum
addx.l d3,d0 ; add in the data word
dbra d1,@BlockLoop ; check the whole block
sub.l (a0)+,d0 ; check the checksum
beq.s @NextBlock ; if checksum ok, check the next block
@error moveq.l #verErr,d0 ; return verify error.
@done rts ; all done
title 'EDisk Driver - Control / Status processing - FmtLst/DrvSts/DriveInfo'
; Return the list of sizes that can be formatted
statFmtLst
bne.s @done ; return with error if no drive or offline
moveq.l #paramErr,d0 ; assume parameter error
tst.w (a4) ; test number of entries in buffer
ble.s @Done ; if less than 1 entry, report error
move.w #1,(a4)+ ; indicate 1 entry returned
movea.l (a4),a4 ; get the buffer pointer
move.l dQDrvSz(a3),d0 ; get the swapped drive size
swap d0 ; un-swap it
move.l d0,(a4)+ ; return the drive size
move.l #$40000000,(a4) ; current disk has this format
moveq.l #noErr,d0 ; indicate success
@Done rts ; all done
; Return the drive status and drive queue element
statDrvSts
move.l d1,d0 ; get found status, ignore offline
bne.s @Done ; return error if not found
movea.l a4,a1 ; dest address is csParam area
clr.w (a1)+ ; return zero for track
lea WriteProtected(a3),a0 ; start copy with write protected flag
moveq.l #(DQE-WriteProtected)+dQDrvSz2+2,d0 ; number of bytes to copy
_BlockMove ; copy the drive info, and return noErr
@Done rts ; all done
; Return the RAM disk size
statDrvSize
move.l RamDiskSize(a3),(a4) ; return the RAM disk size, in bytes <T6>
rts ; all done <1.6>
; Return the drive type and info flags.
ctlDriveInfo
move.l d1,d0 ; get found status, ignore offline
bne.s @Done ; return error if not found
move.l DriveInfo(a3),(a4) ; return the drive info
@Done rts ; all done
title 'EDisk Driver - Control / Status processing - Drive/Media Icon'
; Return the Icon representing the location of the disk drive
ctlDriveIcon
movea.l DriveIconPtr(a3),a0 ; get drive icon ptr, setup source address
bra.s IconCommon ; join common icon code
; Return the Icon representing the disk media
ctlMediaIcon
movea.l MediaIconPtr(a3),a0 ; get media icon ptr, setup source address
IconCommon
move.l d1,d0 ; get found status, ignore offline
bne.s @Done ; return error if not found
moveq.l #ControlErr,d0 ; return control error if no icon found
move.l a0,d1 ; see if icon exists
beq.s @Done ; if no icon exists, return with error
; copy the icon into the buffer
lea IconBuffer(a2),a1 ; get buffer address (dest address)
move.l a1,(a4) ; return buffer pointer
move.l #IconAndMaskSize,d0 ; setup byte count for icon and mask
_BlockMove ; copy the icon into the buffer
; copy the "Where String" into the buffer
lea WhereStringBuff(a2),a1 ; point past icon, to "where string"
clr.b (a1) ; string length of zero, in case no string found
move.l WhereStringPtr(a3),d1 ; get where string pointer
beq.s @Done ; if no string, don't copy it
movea.l d1,a0 ; setup source address
moveq.l #1,d0 ; zero extend / adjust count
add.b (a0),d0 ; fetch the string length, count the length byte too
cmpi.w #WhereStringSize,d0 ; check to see if too long
bls.s @CopyString ; if in range just copy it
moveq.l #WhereStringSize,d0 ; otherwise truncate to max size
@CopyString
_BlockMove ; copy the string into the buffer
@Done rts ; all done (d0 is noErr from _BlockMove)
title 'EDisk Driver - FindDQE - Find Drive Queue Element'
;_______________________________________________________________________
;
; Routine: FindDQE
; Inputs: A0 - pointer to I/O ParamBlock
; A1 - pointer to Device Control Entry (DCE)
; Outputs: D0 - Drive Found and OnLine Status (noErr, NSDrvErr or offLinErr)
; D1 - Drive Found Status (noErr or NSDrvErr)
; A2 - pointer to EDiskVars
; A3 - pointer to DriveQueueElement for specified drive
; A4 - pointer to csParam field of I/O ParamBlock
; Destroys: D2, D3
; Calls: none
; Called by: Prime, Control, Status routines
;
; Function: Searches the drive queue for the Drive Queue Element
; associated with this Driver Request.
;
;_______________________________________________________________________
FindDQE ; find the drive queue element
movea.l dCtlStorage(a1),a2 ; a2 := pointer to EDiskVars
addq.b #1,Active(a2) ; request in progress, exclude polling <1.2>
jsr FindDqePatch(a2) ; allow patching here
lea DrvQHdr+QHead-QLink,a3 ; get the drive queue head
move.w IODrvNum(a0),d2 ; d2 := drive number
bpl.s @search ; start searching if drivenum is positive
neg.w d2 ; d2 := abs (IODrvNum)
; search the drive queue for the requested drive
@search
move.l QLink(a3),d3 ; check next drive queue element
beq.s @notFound ; if drive queue is empty
movea.l d3,a3 ; a3 := drive queue element
cmp.w DQDrive(a3),d2 ; check for a match
bne.s @search ; if not our drive
move.w dCtlRefNum(a1),d2 ; d2 := our driver ref number
cmp.w DQRefNum(a3),d2 ; compare to drive queue ref num
bne.s @search ; if not for our driver
moveq.l #noErr,d1 ; indicate drive is found
; now check to see if it is really present (onLine)
tst.b DiskInPlace(a3) ; is there a disk
ble.s @offLine ; return off line error if not
moveq.l #CheckEDiskInserted,d0 ; hardware dependent function code
bsr.s HWDependent ; see if disk is inserted
beq.s @offLine ; if not, return offline error
moveq.l #noErr,d0 ; indicate no disk-in-place
@Done lea csParam(a0),a4 ; point to param area
rts ; return with success (d0 = 0 = noErr)
@offLine
moveq.l #offLinErr,d0 ; indicate no disk-in-place
bra.s @Done ; return with error
@notFound
moveq.l #NSDrvErr,d1 ; indicate no such drive error
moveq.l #NSDrvErr,d0 ; indicate no such drive error
bra.s @Done ; return with error
title 'EDisk Driver - EDiskDone - Command Completion'
;_______________________________________________________________________
;
; Routine: EDiskDone
; Inputs: A2 - pointer to EDiskVars
; D0 - Result Code
; Destroys:
; Calls: IODone
; Called by: Prime, Control, Status routines
;
; Function: Completes request processing, by checking error result code,
; and returns control to the device manager through IODone.
;
;_______________________________________________________________________
EDiskDone
subq.b #1,Active(a2) ; request complete, allow polling <1.2>
EDiskDone2 ; <H6><SM4>
movea.l DCEpointer(a2),a1 ; setup DCE pointer in a1 for JIODone
btst.b #NoQueueBit-8,(sp)+ ; check for immediate request <1.2>
bne.s @immed ; if immediate, don't use jIODone <1.2>
move.l jIODone,-(sp) ; get IODone routine address
@immed ext.l d0 ; test for errors
beq.s @done ; if no error
move.w d0,DskErr ; save last error for file system
@done rts ; all done
title 'EDisk Driver - HWDependent - Hardware Dependent Functions'
;_______________________________________________________________________
;
; Routine: HWDependent
; Inputs: A3 - pointer to DriveQElement for specified drive
; D0 - function selector
; Outputs: none
; Destroys: D0
; Calls: none
; Called by:
;
; Function: Tests or performs a hardware dependent function.
;
;_______________________________________________________________________
HWDependent
move.l HWDepProcPtr(a3),-(sp) ; get the handler address
rts ; jump to it
RAMDiskHandler
move.b @decode(pc,d0.w),d0 ; get the routine offset
jmp @decode(pc,d0.w) ; jump to it
@decode
assert (*-@decode)=CheckEDiskInserted
dc.b @checkInserted-@decode
assert (*-@decode)=CheckEDiskReadOnly
dc.b @checkReadOnly-@decode
assert (*-@decode)=EnableEDiskWrites
dc.b @enableWrites-@decode
assert (*-@decode)=DisableEDiskWrites
dc.b @disableWrites-@decode
assert (*-@decode)=EjectEDisk
dc.b @eject-@decode
align 2
@checkInserted
moveq.l #-1,d0 ; always inserted
rts
@checkReadOnly
moveq.l #0,d0 ; never read only
@enableWrites
movem.l a0-a1/d1,-(sp) ; save registers <T5>
moveq #8,d0 ; _EDiskProtect selector |
move.l CheckSumPtr(a3),a0 ; put base addr of ram disk in a0 v
move.l #0,a1 ; indicate we want to unprotect edisk
_HwPriv ; unprotect the disk
movem.l (sp)+,a0-a1/d1
bclr #EDiskProtect,Flags(a3) ; indicate the disk is not protected
rts
@disableWrites
movem.l a0-a1/d1,-(sp) ; save registers
moveq #8,d0 ; _EDiskProtect selector
move.l CheckSumPtr(a3),a0 ; put base addr of ram disk in a0
move.l #1,a1 ; indicate we want to protect edisk
_HwPriv ; write-protect the disk ^
movem.l (sp)+,a0-a1/d1 ; restore registers |
bset #EDiskProtect,Flags(a3) ; indicate the disk is protected
rts ; <T5>
@eject clr.b DiskInPlace(a3) ; mark it as offline
@done rts
IF newEdiskProt THEN
;ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
; PowerPC Edisk Routines
;ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
RAMDiskHandlerPPC
move.b @decode(pc,d0.w),d0 ; get the routine offset
jmp @decode(pc,d0.w) ; jump to it
@decode
assert (*-@decode)=CheckEDiskInserted
dc.b @checkInserted-@decode
assert (*-@decode)=CheckEDiskReadOnly
dc.b @checkReadOnly-@decode
assert (*-@decode)=EnableEDiskWrites
dc.b @enableWrites-@decode
assert (*-@decode)=DisableEDiskWrites
dc.b @disableWrites-@decode
assert (*-@decode)=EjectEDisk
dc.b @eject-@decode
align 2
@checkInserted
moveq.l #-1,d0 ; always inserted
rts
@checkReadOnly
moveq.l #0,d0 ; never read only
@enableWrites
; a3 = ptr to driveQElement
; d1 = transfer length
; a0 = transfer base
movem.l a0-a1/d1,-(sp) ; save registers
move.l #$80000008,d0 ; selector for PPC EDiskProtect, "unprotect"
move.l d1,a1 ; put size of xfer in a1
_HwPriv ; unprotect the disk
move.l CheckSumPtr(a3),a0 ; put base of checksums in a0
tst.l a0 ; check if checksums are in use
beq.s @noCksums ; IF using checksums THEN
move.l #$80000008,d0 ; selector for PPC EDiskProtect, "unprotect"
move.l DataStartPtr(a3),a1 ; put end of checksums in a1
suba.l a0,a1 ; leave length of checksum area in a1
_HwPriv ; unprotect the disk
@noCksums ; ENDIF
movem.l (sp)+,a0-a1/d1
bclr #EDiskProtect,Flags(a3) ; indicate the disk is not protected
rts
@disableWrites
movem.l a0-a1/d1,-(sp) ; save registers
move.l #$80010008,d0 ; selector for PPC EDiskProtect, "protect"
move.l d1,a1 ; put size of xfer in a1
_HwPriv ; write-protect the disk
move.l CheckSumPtr(a3),a0 ; put base of checksums in a0
tst.l a0 ; check if checksums are in use
beq.s @noCksumsDis ; IF using checksums THEN
move.l #$80010008,d0 ; selector for PPC EDiskProtect, "protect"
move.l DataStartPtr(a3),a1 ; put end of checksums in a1
suba.l a0,a1 ; leave length of checksum area in a1
_HwPriv ; write-protect the checksums
@noCksumsDis ; ENDIF
movem.l (sp)+,a0-a1/d1 ; restore registers
bset #EDiskProtect,Flags(a3) ; indicate the disk is protected
rts ;
@eject clr.b DiskInPlace(a3) ; mark it as offline
@done rts
ENDIF
ROMDiskHandler
move.b @decode(pc,d0.w),d0 ; get the routine offset
jmp @decode(pc,d0.w) ; jump to it
@decode
assert (*-@decode)=CheckEDiskInserted
dc.b @checkInserted-@decode
assert (*-@decode)=CheckEDiskReadOnly
dc.b @checkReadOnly-@decode
assert (*-@decode)=EnableEDiskWrites
dc.b @enableWrites-@decode
assert (*-@decode)=DisableEDiskWrites
dc.b @disableWrites-@decode
assert (*-@decode)=EjectEDisk
dc.b @eject-@decode
align 2
@checkInserted
@checkReadOnly
moveq.l #-1,d0 ; always inserted, always read only
@enableWrites
@disableWrites
rts
@eject clr.b DiskInPlace(a3) ; mark it as offline
@done rts
title 'EDisk Driver - EDiskPollTask - Drive Polling Task'
;_______________________________________________________________________
;
; Routine: EDiskPollTask
; Inputs: A0 - address of VTask (passed by Vertical Retrace Manager)
; Outputs: none
; Destroys:
; Calls: none
; Called by: Vertical Retrace Manager
;
; Function: Polls the SLIM cards to check for insertions and removals.
; Debounces the result, and post events to reflect the action.
;
;_______________________________________________________________________
EDiskPollTask ; a0-a3/d0-d3 saved by int handler
lea EDiskVars-VTask(a0),a2 ; a2 := pointer to EDiskVars
lea DrvQHdr+QHead-QLink,a3 ; get the drive queue head
movea.l DCEpointer(a2),a1 ; get DCE pointer
move.w dCtlRefNum(a1),d3 ; d3 := driver refnum
tst.b Active(a2) ; see if request in progress
beq.s @next ; if not, start searching the drive queue
addq.w #1,VTask+vblCount(a2) ; if active, try again real soon
rts ; return
@search movea.l d2,a3 ; a3 := DriveQElement
cmp.w dQRefNum(a3),d3 ; see if we are the driver
beq.s @CheckDrive ; check our drives
@next move.l QLink(a3),d2 ; check next drive queue element
bne.s @search ; search until end of drive queue
move.w #EDiskPollRate,VTask+vblCount(a2) ; re-initialize the VBL count
rts ; return
@CheckDrive
moveq.l #CheckEDiskInserted,d0 ; hardware dependent function code
bsr.w HWDependent ; see if disk is inserted
addq.w #1,d0 ; ccr.x := 1 if EDisk inserted
move.b InsertedStatus(a3),d1 ; prepare to shift in new inserted status
addx.b d1,d1 ; shift in new inserted status
move.b d1,InsertedStatus(a3) ; update inserted status
moveq.l #InsertedMask,d0 ; prepare to de-bounce, and test inserted
and.b d0,d1 ; see if offline, without bounce
bne.s @CheckOnLine ; if not offline, check online or bounce
@OffLine
tst.b DiskInPlace(a3) ; check online status
bpl.s @next ; if not ejecting, do nothing
clr.b DiskInPlace(a3) ; now mark it offline (ejected)
bra.s @next ; eject complete, check next drive
@CheckOnLine
cmp.b d0,d1 ; see if online, without bounce
bne.s @next ; if bouncing, ignore it until it stops
tst.b DiskInPlace(a3) ; check online status
bmi.s @next ; if ejecting, do nothing
beq.s @OnLine ; if was offline, and just went online
@mount btst.b #MountedFlag,Flags(a3) ; see if it was mounted
bne.s @next ; if already mounted, don't do it again
moveq.l #DiskInsertEvt,d0 ; disk inserted event (zero high word)
movea.l d0,a0 ; disk inserted event passed in a0
move.w DQDrive(a3),d0 ; disk drive number in low word
_PostEvent ; post the disk inserted event
bne.s @next ; if error, don't mark as mounted
bset.b #MountedFlag,Flags(a3) ; mark it as mounted
@toNext bra.s @next ; check the next drive
@OnLine
@SetupDriveInfo
move.b DiskInPlaceInit(a3),\
DiskInPlace(a3) ; mark it as inserted
clr.b WriteProtected(a3) ; mark it as writeable (assume RAM)
move.l HeaderInfoPtr(a3),d0 ; see if it supports headers
beq.s @SetupRAMDisk ; if no headers, must be a RAM based disk
with EDiskHeader
movea.l d0,a0 ; a0 points to device header
move.l HdrFormatTime(a0),\
FormatTime(a3) ; get the unique ID from the header
move.l HdrFormatTicks(a0),\
FormatTicks(a3) ; get the unique ID from the header
; see if device is ROM or RAM
moveq.l #CheckEDiskReadOnly,d0 ; hardware dependent function code
bsr.w HWDependent ; see if disk is read only
move.b d0,WriteProtected(a3) ; setup the RAM=$00 / ROM=$FF flag
bne.s @SetupROMDisk ; if ROM disk, all info comes from header
tst.l SLIMRegPtr(a3) ; see if this is a SLIM (which needs to be sized)
beq.s @SetupRAMDisk ; if not, base and end must already be valid
moveq.l #EnableEDiskWrites,d0 ; hardware dependent function code
bsr.w HWDependent ; allow writes while sizing
bsr.w ComputeSLIMSize ; see how big the SLIM card is
add.l a0,d0 ; compute end ptr
move.l d0,DataEndPtr(a3) ; setup pointer to end of device data
adda.w #EDiskHeaderSize,a0 ; point past the header
move.l a0,CheckSumPtr(a3) ; checksums start just after the header
moveq.l #DisableEDiskWrites,d0 ; hardware dependent function code
bsr.w HWDependent ; no writes now
endwith
@SetupRAMDisk
movem.l CheckSumPtr(a3),d0/d1/d2; get checksum/start/end ptrs
tst.l d0 ; see if checksum ptr is valid
beq.s @BaseFound ; if not, then d1 = base address = start ptr
move.l d0,d1 ; otherwise d1 := base address = checksum ptr
moveq.l #0,d0 ; default to no checksums for now
@BaseFound
btst.b #CreateWithXSums,Flags(a3) ; see if we want checksums
beq.s @UpdateBasePtrs ; if no checksums, DataStartPtr is base
move.l d1,d0 ; checksum ptr := base address
sub.l d2,d1 ; base - end
neg.l d1 ; total size := end - base
addi.l #512*128-1,d1 ; prepare to round up
lsr.l #7,d1 ; divide by 128 (512/4)
andi.w #~(512-1),d1 ; round to block boundary
add.l d0,d1 ; data starts after checksum table
@UpdateBasePtrs
movem.l d0/d1,CheckSumPtr(a3) ; write back checksum/start ptrs
@SetupDriveSize
sub.l d1,d2 ; byte size := end ptr - start ptr
bhs.s @DriveSizeOK ; if size >= 0, use it, else force null range
move.l d1,DataEndPtr(a3) ; setup end of null range
moveq.l #0,d2 ; range is zero bytes
@DriveSizeOK
rol.l #16-9,d2 ; convert to blocks, and swap halves
move.l d2,dqDrvSz(a3) ; fill in the drive size (in blocks)
bra.w @mount ; now mount the drive
@SetupROMDisk
move.l a0,d0 ; remember header address
lea HeaderTemplate,a1 ; point to expected values
with EDiskHeader
lea HdrBlockSize(a0),a0 ; point to header data
moveq.l #(HdrDeviceSize-HdrBlockSize)/4-1,d2 ; loop counter
@cmpLoop
cmpm.l (a0)+,(a1)+ ; compare the values
dbne d2,@cmpLoop ; compare the blocks
beq.s @SigOK ; get header info if signature valid
move.l d0,d1 ; start ptr is header ptr
move.l d1,d2 ; end ptr is start ptr
bra.s @UpdateBasePtrs ; create an empty ROM disk (pretty useless)
@SigOK move.l (a0)+,d0 ; get device size
addq.w #HdrCheckSumOff-HdrFormatTime,a0 ; point to checksum offset in header
lea CheckSumPtr(a3),a1 ; point to checksum ptr in drive info
moveq.l #(HdrMediaIconOff-\
HdrCheckSumOff)/4-1,d2 ; loop for next 3 fields
tst.l SLIMRegPtr(a3) ; see if this is a SLIM (which is ejectable)
bne.s @offsetLoop ; if ejectable, don't get the icon info
moveq.l #(HdrDriveInfo-\
HdrCheckSumOff)/4-1,d2 ; loop for next 6 fields
move.l HdrDriveInfo-HdrCheckSumOff(a0),d1 ; get drive info
beq.s @offsetLoop ; if none specified, use default
move.l d1,DriveInfo(a3) ; setup new drive info
@offsetLoop
addq.l #4,a1 ; leave pointer alone, assume default
move.l (a0)+,d1 ; get the offset
beq.s @nextOffset ; if none, ignore it
cmp.l d0,d1 ; see if offset is reasonable
bhi.s @nextOffset ; if offset past end of device, ignore it
add.l HeaderInfoPtr(a3),d1 ; add device base to offset
move.l d1,-4(a1) ; update the pointer
@nextOffset
dbra d2,@offsetLoop ; loop through all of the fields
movem.l DataStartPtr(a3),d1/d2 ; get start/end ptrs
bra.s @SetupDriveSize ; setup the drive queue size info
endwith
title 'EDisk Driver - ComputeSLIMSize - Get Signature Information'
;_______________________________________________________________________
;
; Routine: ComputeSLIMSize
; Inputs: A0 - pointer to base of SLIM address space for this card
; Outputs: D0 - Device Size in bytes
; Destroys: A1, D1, D2
; Calls: none
; Called by:
;
; Function: Returns the device size for the specified SLIM card.
;
;_______________________________________________________________________
SegmentSize equ 512*1024 ; slims have up to 4 512K byte segments
ComputeSLIMSize
adda.l #4*SegmentSize,a0 ; point past end of last segment
movea.l a0,a1 ; save copy of end address
moveq.l #%0000,d0 ; assume all 4 segments are missing
move.l #'Gary',d1 ; rotating pattern
moveq.l #4-1,d2 ; loop counter
@FillLoop
suba.l #SegmentSize,a0 ; point to base of previous segment
move.l (a0),-(sp) ; save the old contents
rol.l #8,d1 ; change the pattern
move.l d1,(a0) ; write to the ram
cmp.l (a0),d1 ; see if we can read it back
bne.s @NotRAM1 ; if not, it's not RAM
not.l d1 ; complement the pattern
move.l d1,(a0) ; write to the ram
cmp.l (a0),d1 ; see if we can read it back
bne.s @NotRAM2 ; if not, it's not RAM
bset.l d2,d0 ; indicate that the segment is RAM
@NotRAM2
not.l d1 ; restore pattern
@NotRAM1
dbra d2,@FillLoop ; loop through all 4 segments
not.l d1 ; get expected pattern
moveq.l #4-1,d2 ; loop counter
@CheckLoop
suba.l #SegmentSize,a1 ; point to base of previous segment
rol.l #8,d1 ; change the pattern
cmp.l (a1),d1 ; see if we can read it back
beq.s @IsRAM ; if so, it's RAM that didn't wrap around
bclr.l d2,d0 ; indicate that the segment isn't RAM
@IsRAM
dbra d2,@CheckLoop ; loop through all 4 segments
moveq.l #4-1,d2 ; loop counter
@RestoreLoop
move.l (sp)+,(a1) ; restore the old contents
adda.l #SegmentSize,a1 ; point to base of next segment
dbra d2,@RestoreLoop ; loop through all 4 segments
move.b @SizesTable(d0.w),d0 ; encode the segment present bits
moveq.l #19,d1 ; shift amount
lsl.l d1,d0 ; convert segment count to byte count
rts ; all done
@SizesTable
dc.b 0 ; 0000 - RAM, 0.0MB, 0 segments
dc.b 1 ; 0001 - RAM, 0.5MB, 1 segment
dc.b 0 ; 0010 - RAM, illegal configuration
dc.b 2 ; 0011 - RAM, 1.0MB, 2 segments
dc.b 0 ; 0100 - RAM, illegal configuration
dc.b 0 ; 0101 - RAM, illegal configuration
dc.b 0 ; 0110 - RAM, illegal configuration
dc.b 3 ; 0111 - RAM, 1.5MB, 3 segments
dc.b 0 ; 1000 - RAM, illegal configuration
dc.b 0 ; 1001 - RAM, illegal configuration
dc.b 0 ; 1010 - RAM, illegal configuration
dc.b 0 ; 1011 - RAM, illegal configuration
dc.b 0 ; 1100 - RAM, illegal configuration
dc.b 0 ; 1101 - RAM, illegal configuration
dc.b 0 ; 1110 - RAM, illegal configuration
dc.b 4 ; 1111 - RAM, 2.0MB, 4 segments
title 'EDisk Driver - CreateEDiskHeader - Create Header Information'
;_______________________________________________________________________
;
; Routine: CreateEDiskHeader
; Inputs: A3 - pointer to Drive Queue Element for specified drive
; Destroys: A0, A1, D0, D1, D2, D3
; Calls: none
; Called by: ctlFormat
;
; Function: Writes an EDisk Header to the specified device.
;
;_______________________________________________________________________
CreateEDiskHeader
with EDiskHeader
move.l HeaderInfoPtr(a3),d0 ; get header base
beq.s @Done ; if no headers, nothing to do
movea.l d0,a0 ; setup header pointer
moveq.l #EDiskHeaderSize/4-1,d3 ; loop counter
@clrLoop
clr.l (a0)+ ; clear the header block
dbra d3,@clrLoop ; 4 bytes at a time
lea HeaderTemplate,a1 ; point to default values
lea HdrBlockSize-EDiskHeaderSize(a0),a0 ; point to header data
moveq.l #(HdrDeviceSize-HdrBlockSize)/4-1,d3 ; loop counter
@cpyLoop
move.l (a1)+,(a0)+ ; copy the template
dbra d3,@cpyLoop ; 4 bytes at a time
move.l DataEndPtr(a3),d2 ; find end of device (assume = end of data)
sub.l d0,d2 ; d2 := device size
move.l d2,(a0)+ ; setup HdrDeviceSize
lea FormatTime(a3),a1 ; point to drive info
move.l Time,(a1) ; setup the format time
move.l (a1)+,(a0)+ ; setup HdrFormatTime
move.l Ticks,(a1) ; setup the format ticks
move.l (a1)+,(a0)+ ; setup HdrFormatTicks
moveq.l #(HdrDriveInfo-\
HdrCheckSumOff)/4-1,d3 ; loop for next 6 fields
@offsetLoop
addq.l #4,a0 ; leave offset zero, assume not supported
move.l (a1)+,d1 ; get next ptr field
sub.l d0,d1 ; make it an offset from base of device
cmp.l d1,d2 ; see if in range
blo.s @nextOffset ; if out of range, try next one
move.l d1,-4(a0) ; if in range, put it into the header
@nextOffset
dbra d3,@offsetLoop ; loop through all of the fields
@Done rts ; all done
endwith
string asis ; no length on strings
align 2
HeaderTemplate
dc.w 512 ; signature block size
dc.w 1 ; version number 1
dc.b 'EDisk ' ; 12 byte signature
dc.b 'Gary D' ; (brought to you by Gary D)
string pascal ; name strings have leading length byte
IF not padForOverPatch THEN ; removing string to adjust for mods <H1>
align 2
SlimDrive0Name
dc.b 'Upper SLIM Drive'
ENDIF
align 2
SlimDrive1Name
dc.b 'Lower SLIM Drive'
align 2
ROMDiskName
dc.b 'Internal ROM Disk'
align 2
RAMDiskName
dc.b 'Internal RAM Disk'
align 2
title 'EDisk Driver - RAM / ROM Disk Icons'
ROMDiskMediaIcon
ROMDiskDriveIcon
RAMDiskMediaIcon
RAMDiskDriveIcon
dc.l %01111111111111111111111111110000
dc.l %10000001000000000000000100001000
dc.l %10000001000000000111000100000100
dc.l %10000001000000001000100100000010
dc.l %10000001000000001000100100000001
dc.l %10000001000000001000100100000001
dc.l %10000001000000001000100100000001
dc.l %10000001000000001000100100000001
dc.l %10000001000000001000100100000001
dc.l %10000001000000000111000100000001
dc.l %10000000111111111111111100000001
dc.l %10000000000000000000000000000001
dc.l %10000000000000000010000000000001
dc.l %10000000000000001101000000000001
dc.l %10000000000000001000100000000001
dc.l %10000000000000110000010000000001
dc.l %10000000000000100000011000000001
dc.l %10000000000011000000110000000001
dc.l %10000000000010000001111000000001
dc.l %10000000001100000011001000000001
dc.l %10000000001000000111101000000001
dc.l %10000000110000001100101000000001
dc.l %10000000110000011110100000000001
dc.l %10000000111000110010100000000001
dc.l %10000000111101111010000000000001
dc.l %10000000101111001010000000000001
dc.l %10000000100111101000000000000001
dc.l %10000000000010101000000000000001
dc.l %10000000000000100000000000000001
dc.l %10000000000000100000000000000001
dc.l %10000000000000000000000000000001
dc.l %11111111111111111111111111111110
dc.l %01111111111111111111111111110000
dc.l %11111111111111111111111111111000
dc.l %11111111111111111111111111111100
dc.l %11111111111111111111111111111110
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111111
dc.l %11111111111111111111111111111110
END