; ; 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): ; ; 10/28/93 CCH Optimized read path to not disable writes on every call and ; disabled checksums on PDM. ; 10/14/93 CCH Added write-protection support for PowerPC machines. ; 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. ; 2/8/93 rab Sync up with Horror. Comments follow. ;
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. ; 8/31/92 PN Fix header ; 8/31/92 PN First time this file is moved over from Horror so that RAMDisk ; works with Cyclone ; ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ ; SuperMario ROM comments start here. ; ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ ;

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. ;

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 _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< 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 BrandNewFunc-(*+4) dc.w 65 dc.w -1 ; indicate end of control decode table dc.w ControlErr ; return ControlErr if unknown csCode title 'EDisk Driver - Open processing' ;_______________________________________________________________________ ; ; Routine: EDiskOpen 60 ; 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 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< IF 0 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 move.l sgRamDiskBase(a2),d1 ; base of RAM disk move.l d1,d3 ; get a copy in d3 add.l sgRamDiskSize(a2),d3 ; end of ram disk in d3 suba.l a2,a2 ; a2 := HeaderInfoPtr (none for RamDisk) <2> 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 ; 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 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 D8 ; 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 lea DQE-SLIMRegPtr(a0),a0 ; point to the drive queue entry movea.l a6,a3 ; a3 := running pointer to init table entry move.l (a3)+,SLIMRegPtr(a0) ; initialize the SLIMRegPtr movea.w (a3)+,a4 ; get the icon/name ptr adda.l a3,a4 ; make it absolute move.l a4,HWDepProcPtr(a0) ; initialize the HWDepProcPtr move.l a2,HeaderInfoPtr(a0) ; initialize the HeaderInfoPtr move.l d1,CheckSumPtr(a0) ; initialize CheckSumPtr 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 addq.w #1,qType(a0) ; use long drive size format addq.b #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 HWDependent ; disable writing to the EDisk bsr EDiskPollTask @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 - Close processing' ;_______________________________________________________________________ ; ; Routine: EDiskClose 154 ; 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 158 ; 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

; 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

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

; 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

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 _StripAddress ; clear upper byte if necessary 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 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

; 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 _BlockMoveData ; 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 2EE / EDiskStatus 2F4 / 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 FindDQE ; find the DQE before calling routine @exit movea.l dCtlStorage(a1),a2 ; a2 := pointer to EDiskVars rts 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 cmp.b #8,-3(a3) 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 HWDependent ; start the eject, update DiskInPlace bset.b #5,dCtlFlags(a1) @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 ; d1 := byte count ; a0 := source address ; a3 := ptr to drive queue element movem.l (sp)+,a0/d1 ; restore size, length for disabling the disk 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 _BlockMoveData ; 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 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 _BlockMoveData ; 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 _BlockMoveData ; copy the string into the buffer @Done rts ; all done (d0 is noErr from _BlockMove) title 'EDisk Driver - BrandNewFunc' ;_______________________________________________________________________ ; ; Routine: BrandNewFunc 472 ; ;_______________________________________________________________________ BrandNewFunc bclr.b #5, 4(a1) beq.s @return move $18(a1),d1 lea DrvQHdr+QHead,a3 @loop move.l (a3),d2 beq.s @return move.l d2,a3 cmp dQRefNum(a3),d1 bne.s @loop btst.b #0,-12(a3) bne.s @loop bsr Mount btst.b #0,-12(a3) bne.s @loop bset.b #5,4(a1) bra.s @loop @return moveq.l #0,d0 rts title 'EDisk Driver - FindDQE - Find Drive Queue Element' ;_______________________________________________________________________ ; ; Routine: FindDQE 4AE ; 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 jsr FindDqePatch(a2) ; allow patching here lea $1c(a0),a4 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 rts ; return with success (d0 = 0 = noErr) @notFound moveq.l #NSDrvErr,d1 ; indicate no such drive error moveq.l #NSDrvErr,d0 ; indicate no such drive error rts title 'EDisk Driver - EDiskDone - Command Completion' ;_______________________________________________________________________ ; ; Routine: EDiskDone 4E4 ; 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 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 4FC ; 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 ;ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ ; PowerPC Edisk Routines ;ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ 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 rts @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 rts title 'EDisk Driver - EDiskPollTask - Drive Polling Task' ;_______________________________________________________________________ ; ; Routine: EDiskPollTask 57A ; 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 move.b DiskInPlaceInit(a3),\ DiskInPlace(a3) ; mark it as inserted clr.b WriteProtected(a3) ; mark it as writeable (assume RAM) @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) Mount btst.b #MountedFlag,Flags(a3) ; see if it was mounted bne.s @done ; 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 @done ; if error, don't mark as mounted bset.b #MountedFlag,Flags(a3) ; mark it as mounted @done rts title 'EDisk Driver - CreateEDiskHeader - Create Header Information' ;_______________________________________________________________________ ; ; Routine: CreateEDiskHeader 5E0 ; 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 align 2 RAMDiskName dc.b 'Internal RAM Disk' align 2 title 'EDisk Driver - RAM Disk Icons' 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