mac-rom/OS/NetBoot/BootUtils.a
Elliot Nunn 4325cdcc78 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 09:52:23 +08:00

632 lines
22 KiB
Plaintext

;__________________________________________________________________________________________________
;
; File: BootUtils.a
;
; Contains: Network booting utilities
;
; Written by: Patrick Dyson
;
; Copyright © 1989-1993 by Apple Computer, Inc. All rights reserved.
;
; Change History (most recent first):
;
; <SM3> 6/14/93 kc Roll in Ludwig.
; <LW2> 4/8/93 fau Fixed a bug with the opening of the .ATBoot driver. It was
; using an _Open which uses the res-id for the refnum. This came
; out to -50, which was a driver that was already there. I moved
; some code from Startinit.a that installs a driver at the lowest
; refnum available after 47 and changed the .ATBoot open to use it.
; <SM2> 11/5/92 SWC Changed INCLUDEs to a LOAD of StandardEqu.d.
; <7> 3/20/90 PWD Fix to DoATBootOpen to push #'DRVR' instead of 'DRVR'.
; <6> 3/9/90 PWD Merge from xo splitoff - changes to drive queue element size,
; system heap growth routine.
; <5> 2/20/90 PWD Fixed coincident zones grow zone proc by reiniting the app heap
; if I grow the system heap. Also added icons for startup sequence
; and a pc-relative open for the netboot driver.
; <4> 2/1/90 PWD Fixed coincident zones grow zone proc by reiniting the app heap
; if I grow the system heap.
; <4> 01/30/90 PWD Changed grow zone proc to not clobber ApplZone and HeapEnd.
; <3> 12/28/89 SWC Fixed header and set tabs to 4.
; <2> 12/19/89 PWD Adding to bbs.
; <1.1> 12/12/89 PWD Added support for self-authenticating images
; <1.0> 10/30/89 PWD Adding to EASE
;__________________________________________________________________________________________________
PRINT OFF
LOAD 'StandardEqu.d'
INCLUDE 'Slots.a' ; Slot equates
INCLUDE 'ROMequ.a' ; Slot ROM declarations
INCLUDE 'NetBootEQU.a' ; netBoot defs
PRINT ON
;__________________________________________________________________
;
; TestPRam - C procedure to call our pram routines
;
; On Entry: 8(SP) is 0 for write, 1 for read
; 4(SP) points to a buffer to use
;
; On Exit: read pram in buffer
;
;__________________________________________________________________
TestPRam PROC EXPORT
MOVE.L 4(SP), A0 ; buffer to read/write
MOVE.L 8(SP), D0 ; get selector
BEQ.S @write
BSR.S ReadPRAM
BRA.S @Exit
@write BSR.S WritePRAM
@Exit RTS
;__________________________________________________________________
;
; ReadPRAM - read from our PRAM
;
; On Entry: A0 points to a read buffer
; On Exit: the z bit is set if there was an error
; A0 points to the buffer read:
; password
; user name
; server name
;
; Trashes D0,D1,A0,A1
;__________________________________________________________________
pRamEntries EQU 5 ; size of table
pRamTable
DC.L $00040004 ; first four password bytes
DC.L $000300AB ; next three byte
DC.L $000100BC ; last password byte
DC.L $00200020 ; User name (32 bytes)
DC.L $0020008B ; Server name (31 bytes)
ReadPRAM CLR.L D0 ; assume error
BTST #14,HWCfgFlags ; New clock chip?
BEQ.S @10 ; Assume bridge if not
LEA pRamTable, A1 ; start of table
MOVEQ.L #pRamEntries-1, D1 ; entries (-1)
@1 MOVE.L (A1)+,D0 ; first count
MOVE.L D0,D2
; A0 has buffer area
_ReadXPRam ; trashes D0
SWAP D2 ; get count in low byte
ADDA D2,A0 ; bump buffer
DBRA D1,@1 ; go get more
@10 RTS
;__________________________________________________________________
;
; WritePRAM - write to our PRAM
;
; On Entry: A0 points to the buffer to write:
; password
; user name
; server name
; On Exit: the z bit is set if there was an error
;__________________________________________________________________
WritePRAM CLR.L D0 ; assume error
BTST #14,HWCfgFlags ; New clock chip?
BEQ.S @10 ; Assume bridge if not
LEA pRamTable, A1 ; start of table
MOVEQ.L #pRamEntries-1, D1 ; entries (-1)
@1 MOVE.L (A1)+,D0 ; first count
MOVE.L D0,D2
; A0 has buffer area
_WriteXPRam
SWAP D2 ; get count in low byte
ADDA D2,A0 ; bump buffer
DBRA D1,@1 ; go get more
@10 RTS
ENDP
AddMyDrive PROC EXPORT
;---------------------------------------------------------------------------
;FUNCTION AddMyDrive(drvSize: LONGINT; drvrRef: INTEGER; drvStorage: Ptr): INTEGER;
;---------------------------------------------------------------------------
;Add a drive to the drive queue. Returns the new drive number, or a negative
;error code (from trying to allocate the memory for the queue element).
;---------------------------------------------------------------------------
DQESize EQU 18+8 ;size of a drive queue element + private storage
;We use a constant here because the number in SysEqu.a doesn't take into
;account the flags LONGINT before the element, or the size word at the end.
;---------------------------------------------------------------------------
StackFrame RECORD {link},DECR
result DS.W 1 ;function result
params EQU *
drvSize DS.L 1 ;drive size parameter
drvrRef DS.W 1 ;drive refNum parameter
drvNStorage DS.L 1 ; private storage on the end of the drive Q element
paramSize EQU params-*
return DS.L 1 ;return address
link DS.L 1 ;saved value of A6 from LINK
block DS.B ioQElSize ;parameter block for call to MountVol
linkSize EQU *
ENDR
;---------------------------------------------------------------------------
WITH StackFrame ;use the offsets declared above
LINK A6,#linkSize ;create stack frame
;search existing drive queue for an unused number
LEA DrvQHdr,A0 ;get the drive queue header
MOVEQ #4,D0 ;start with drive number 4
CheckDrvNum
MOVE.L qHead(A0),A1 ;start with first drive
CheckDrv
CMP.W dqDrive(A1),D0 ;does this drive already have our number?
BEQ.S NextDrvNum ;yep, bump the number and try again.
CMP.L qTail(A0),A1 ;no, are we at the end of the queue?
BEQ.S GotDrvNum ;if yes, our number's unique! Go use it.
MOVE.L qLink(A1),A1 ;point to next queue element
BRA.S CheckDrv ;go check it.
NextDrvNum
;this drive number is taken, pick another
ADDQ.W #1,D0 ;bump to next possible drive number
BRA.S CheckDrvNum ;try the new number
GotDrvNum
;we got a good number (in D0.W), set it aside
MOVE.W D0,result(A6) ;return it to the user
;get room for the new DQE
MOVEQ #DQESize,D0 ;size of drive queue element, adjusted
_NewPtr ,SYS ;get memory for it
BEQ.S GotDQE ;no error...continue
MOVE.W D0,result(A6) ;couldn't get the memory! return error
BRA.S FinishUp ;and exit
GotDQE
;fill out the DQE
MOVE.L #$80000,(A0)+ ;flags: non-ejectable; bump past flags
MOVE.W #1,qType(A0) ;qType of 1 means we do use dQDrvSz2
MOVE.W #dFSID,dQFSID(A0) ;"external file system"
MOVE.W drvSize(A6),dQDrvSz2(A0) ;high word of number of blocks
MOVE.W drvSize+2(A6),dQDrvSz(A0) ;low word of number of blocks
MOVE.L drvNStorage(A6), drvStorage(A0); private storage
;call AddDrive
MOVE.W result(A6),D0 ;get the drive number back
SWAP D0 ;put it in the high word
MOVE.W drvrRef(A6),D0 ;move the driver refNum in the low word
_AddDrive ;add this drive to the drive queue
FinishUp
UNLK A6 ;get rid of stack frame
MOVE.L (SP)+,A0 ;get return address
ADD #paramSize,SP ;get rid of parameters
JMP (A0) ;back to caller
;---------------------------------------------------------------------------
ENDPROC
;
; RmvDriver - glue for this undocumented trap
;
KillDriver PROC EXPORT
MOVE.L 4(SP), D0 ; pop parameter
_DrvrRemove
; function result in D0
RTS ; C functs clean up after themselves
ENDP
;---------------------------------------------------------------------------
;
; pdbzero - our own copy of the famous "zero this many bytes routine"
;
;
;
;---------------------------------------------------------------------------
CASE OBJ ; make this be case as specified
pdbzero PROC EXPORT
move.l 4(sp),a0 ; pointer to block to zero
move.l 8(sp),d0 ; count of bytes to zero
ble.s @theend ; punt if done already
sub.l #1,d0 ; for dbra
move.l #0,d1 ; something to clear with
@11
move.b d1,(a0)+ ; clear that byte...
dbra d0,@11 ; until done.
@theend
rts
ENDP
;__________________________________________________________________
;
; myExtFSFilter
;
; This routine handles the _MountVol call to BootDrive after the boot
; blocks have been read (handled by .netBoot). It is installed just before
; the netboot read completes and de-installs itself when done.
;
; We install our grow zone proc before making the control call in case
; it wants memory.
;
; Entry: A0 -> the mountvol param block
;
; Exit: D0 any error
;
; We trash D0, all other registers preserved.
;
CASE OBJ ; save case for C linker
WDCBsPtr EQU $372 ; Working Directory queue header
myExtFSFilter PROC EXPORT ; imported by netBoot.c
IMPORT myGrowZone
MOVEM.L A0-A4/D1-D2, -(SP) ; savem
CMP.B #$0F,ioTrap+1(A0) ; is this a MountVol call?
BNE @NotOurs
MOVE.W IODrvNum(A0), D0 ; pick up drive
CMP.W #4, D0 ; is this for us?
BNE @NotOurs
;
; go find the drive queue entry
;
LEA DrvQHdr,A2 ; get the drive queue header
MOVE.L qHead(A2),A1 ; start with first drive
@CheckDrv
CMP.W dqDrive(A1),D0 ; our drive?
BEQ.S @GotDrvNum ; yep, cruise
CMP.L qTail(A2),A1 ; no, are we at the end of the queue?
BEQ @Error ; yes, we're hosed
MOVE.L qLink(A1),A1 ; point to next queue element
BRA.S @CheckDrv ; go check it.
@GotDrvNum ; in A1
CMP.W #dFSID, dQFSID(A1) ; ours?
BNE @Error
MOVE.L drvStorage(A1), D0 ; pick up driver globals (on end of entry)
MOVE.L A1, A4 ; save drive entry pointer
BEQ @Error
MOVE.L D0, A2 ; get globals pointer
MOVE.L SysZone, A0 ; pick up system heap pointer
move.l a0,TheZone ; make sure that we keep our default zone...
MOVE.L gzProc(A0), -(SP) ; save old grow zone
LEA myGrowZone, A1 ; pick up my grow zone proc
MOVE.L A1, gzProc(A0) ; tell the memory manager
SUB #ioQElSize,SP ; make a queue element
MOVE.L SP,A0 ; A0 -> queue element
MOVE #mountSysVol,csCode(A0) ; set control code
MOVE dProtoRefNum(A2),ioRefNum(A0) ; set driver refNum
_Control
BEQ.S @Good ; branch if no error
ADD #ioQElSize, SP ; restore stack
MOVE.L SysZone, A0 ; pick up system heap pointer
MOVE.L (SP)+,gzProc(A0) ; restore old grow zone
BRA @ControlError ; punt if error
@Good MOVE.L returnVCB(A0), A1 ; pick up VCB pointer
MOVE.L A1, DefVCBPtr ; set as default volume
MOVE.L WDCBsPtr, A3
MOVE.L A1, WDVCBPtr+2(A3) ;
MOVEQ #2, D0 ; FSRtDirID
MOVE.L D0, WDDirID+2(A3) ;
CLR.L WDCatHint+2(A3)
CLR.L WDProcID+2(A3)
MOVE.L returnDrvQ(A0), A1 ; pick up drive queue pointer
MOVE dQFSID(A1), dQFSID(A4) ; set drive queue entry fsid
MOVE dQRefNum(A1), dQRefNum(A4) ; set driver refnum to handle calls
MOVE.B -3(A1), -3(A4) ; set flags
SUBQ #4, A1 ; point to buffer start
MOVE.L A1, A0 ; set up for _Dispos
_DisposPtr ; nuke it
CLR.L drvStorage(A4) ; wipe out reference to our storage
MOVE.L doldToExtFS(A2), ToExtFS ; restore ToExtFS (unhook ourselves)
ADD #ioQElSize, SP ; restore stack
MOVE.L SysZone, A0 ; pick up system heap pointer
MOVE.L (SP)+,gzProc(A0) ; restore old grow zone
MOVEQ #0, D0 ; set no error
BRA.S @Leave ; outta' here
@Error
MOVEQ #-1, D0
@ControlError
@Leave
@NotOurs
MOVEM.L (SP)+, A0-A4/D1-D2 ; restorem
RTS
ENDP
;__________________________________________________________________
;
; myGrowZone
;
; This proc is installed before making calls to the boot protocol driver
; or the downloaded code. The memory world is simple: The app and system
; heap are coincident; The app heap about 16k after the end of the system.
;
; The app heap is assumed to be clobberable - our algorithm is to extend the
; system heap by the requested amount + pad (currently 16k). We limit the size
; of the system growth to 1/2 machine memory and by the bottom of the stack.
;
; This is a pascal procedure & thus cleans up its own stack.
;
; Entry: 4(SP).L Requested memory size
; 8(SP).L Space for function return value
;
; Exit: (SP).L Size of block we freed
SysHeapEndBuf EQU 8*1024 ; amount of space between SysHeap end & SP <A286>
myGrowZone PROC EXPORT
MOVE.L 4(SP), D0 ; pick up block size
MOVE.L A2, -(SP) ; save a register
MOVE.L SysZone,A1 ; Point to System Heap. <C102>
MOVE.L bkLim(A1),A0 ; Point to end of System Heap.
MOVE.L A0, A2 ; save the old end of the heap
ADD.L #16*1024, D0 ; add slop
LEA 0(A0,D0.L), A0 ; point to "new" end
MOVE.L D0, D1 ; save off how much we are going for
MOVE.L BufPtr,A1 ; Upper bound is BufPtr <C587>
SUB.L #SysHeapEndBuf,A1 ; Éminus a tad. <A286>
CMP.L A1,A0 ; Is proposed end <= limit? <A286>
BLS.S @WithinLimit ; Branch if so.
MOVE.L A1, D1 ; pick up new end
SUB.L A2, D1 ; subtract off the bottom; size = (top-bottom)
MOVE.L A1,A0 ; If not, use upper limit instead. <A286>
@WithinLimit ; <A286>
_SetAppBase ; Set up the start of the application heap. <C102>
; _InitApplZone ; and do all the fun initialization <C793>
MOVE.L SysZone, A1 ; get the system zone
MOVE.L A1,TheZone ; We still want the System Heap to be the default. <C102>
MOVE.L A1,ApplZone ; pd< Put back in for XO >revert to coincident zones <C587>
MOVE.L bkLim(A1),HeapEnd ; pd< Put Back in for XO >end of dynamic sys/appl zone <C587>
MOVE.L (SP)+, A2 ; restore a register
MOVE.L (SP)+, A0 ; pop return address
MOVE.L (SP)+, D0 ; pop the passed size
MOVE.L D1, (SP) ; return how much we are giving
JMP (A0) ; and go back from whence we came
ENDP
myGetA5 PROC EXPORT
MOVE.L A5, D0
RTS
ENDP
mySetA5 PROC EXPORT
MOVE.L 4(SP), A5
RTS
ENDP
CASE OBJ ; c case for the linker
DoATBootOpen PROC EXPORT
move.l 4(SP), a0 ; pick up passed param block
clr.l -(sp) ; function result room
move.l #'DRVR', -(sp) ; push the type
move.l HParamBlockRec.ioNamePtr(a0), -(sp) ; push the name of the driver
_GetNamedResource ; go for it
clr d0 ; assume error (0 is error)
tst.l (sp)+ ; pop result
beq.s @openErr
move.l 4(sp), a0 ; get pb back
move.l HParamBlockRec.ioNamePtr(a0), A1
MOVE.W #60,D2 ; and resource ID <SM84>
BSR InstallDriver ; go install the driver
swap d0 ; get refnum in lower word.
Andi.l #$FFFF,D0 ; clear the top word and return the refnum
@openErr rts
;________________________________________________________________________________________
;
; Routine: InstallDriver
;
; Inputs: A1 - pointer to driver name string (pascal)
; D2 - driver's resource ID
;
; Outputs: none
;
; Trashes: D0-D2, A0-A2
;
; Function: gets a driver from the ROM, and installs and opens it in the first available
; slot after entry 48
;________________________________________________________________________________________
InstallDriver
move.l a1,a2 ; move name ptr to a2 for safe keeping
bsr.s GetDetachDRVR ; get and detach resource (d1,d1/a1)
beq.s @exit ; exit if no handle
bsr.s FirstEntryFree ; get ref num of first free entry (/d0,d1)
_DrvrInstall ; create dce (d0/d0)
tst.l d0 ; test for error
bne.s @releaseDrvr ; ... exit if error
move.l UTableBase,a0 ; point to utable array
move.l (a0,d1),a0 ; get handle to dce in a3
move.l (a0),a0 ; get pointer to dce
move.l a1,dCtlDriver(a0) ; load driver
move.l (a1),a1 ; get pointer to driver
move.w drvrFlags(a1),dCtlFlags(a0) ; copy data to dce
move.w drvrDelay(a1),dCtlDelay(a0)
move.w drvrEMask(a1),dCtlEMask(a0)
move.w drvrMenu(a1),dCtlMenu(a0)
bset.b #dNeedLock,dCtlFlags+1(a0) ; set the handle bit
@openDrvr move.l a2,a1 ; load pointer to driver name
bra.s OpenDRVR ; open the driver (a1/)
@releaseDrvr
move.l a1,a0 ; move handle to a0
_DisposHandle ; release the memory
@exit rts
;________________________________________________________________________________________
;
; Routine: GetDetachDrvr, GetDetachRes
;
; Inputs: D1 - resource type (GetDetachRes)
; D2 - driver's resource ID
;
; Outputs: A1 - handle to resource
; CCR - BEQ if successful, BNE if failure
;
; Trashes: D0-D2, A0-A2
;
; Function: gets a driver from the ROM and detaches it
;________________________________________________________________________________________
GetDetachDRVR
MOVE.L #'DRVR',D1
GetDetachRes
MOVE.W #MapTrue,ROMMapInsert ; make sure we can get it from ROM
SUBQ.L #4, SP ; For return address
MOVE.L D1, -(SP) ; Resource type
MOVE.W D2, -(SP) ; Resource ID
_GetResource
MOVE.L (SP), A1 ; Get resource handle to return
BNE.S @NoDetach ; If not found, don't try to detach it
_DetachResource
MOVE.L A1,D0 ; Set result code
RTS
@NoDetach
ADDA.L #4,SP ; recover stack
MOVEQ #0,D0 ; set error
RTS ; return
;________________________________________________________________________________________
;
; Routine: FirstEntryFree
;
; Inputs: none
;
; Outputs: D0 - driver refNum
;
; Trashes: none
;
; Function: finds the first free entry in the unit table
;________________________________________________________________________________________
StartEntry equ (48-1) ; this avoids AppleTalk area
FirstEntryFree
move.l a0,-(SP) ; save a0
@findEntry move.l UTableBase,a0 ; point to utable array
move.l #(StartEntry*4),d0 ; start at entry (48-1)
@testEntry addq.l #4,d0 ; increment to next entry
tst.l 0(a0,d0) ; test entry
bne.s @testEntry ; if != 0, next entry
@calcRefnum move.l d0,d1
lsr.l #2,d0 ; divide by 4 to get entry number
addq.l #1,d0 ; add 1 (refnum is -(entry number + 1)
neg.l d0 ; negate to get reference number
move.l (SP)+,a0 ; restore a0
rts
;________________________________________________________________________________________
;
; Routine: OpenDRVR
;
; Inputs: A1 - pointer to driver name string (pascal)
;
; Outputs: D0 - driver refNum in high word, Open result in low word
; CCR - BEQ if successful, BNE if failure
;
; Trashes: A0
;
; Function: opens a driver
;________________________________________________________________________________________
OpenDRVR LEA -ioQElSize(SP),SP ; Allocate IO stack frame
MOVE.L SP,A0 ; set a0 to point to the pb
MOVE.L A1,ioVNPtr(A0) ; load pointer to name
MOVE.B #fsCurPerm,ioPermssn(A0) ; set permission (not used)
_Open
MOVE.W ioRefNum(A0),D0 ; return ioRefNum (D0.W:HI)
SWAP D0 ; move ioRefNum HI
MOVE.W ioResult(A0),D0 ; return result (D0.W:LO)
LEA ioQElSize(SP),SP ; Release stack frame
RTS ; Sucess returned in status
ENDP
myGetIcn PROC EXPORT
move.l 4(sp), d0 ; pick up index
lsl #7, d0 ; multiply by 128
lea myIcons, a0 ; pick up icon base
add.l a0,d0 ; point to icon.
rts
myIcons
; resource 'ICN#' (1, "Outline Mac") {
dc.b $0F, $FF, $FF, $E0, $18, $00, $00, $30, $10, $00, $00, $10, $11, $FF, $FF, $10
dc.b $12, $00, $00, $90, $12, $00, $00, $90, $12, $00, $00, $90, $12, $00, $00, $90
dc.b $12, $00, $00, $90, $12, $00, $00, $90, $12, $00, $00, $90, $12, $00, $00, $90
dc.b $12, $00, $00, $90, $12, $00, $00, $90, $12, $00, $00, $90, $12, $00, $00, $90
dc.b $12, $00, $00, $90, $11, $FF, $FF, $10, $10, $00, $00, $10, $10, $00, $00, $10
dc.b $10, $00, $00, $10, $10, $00, $00, $10, $13, $00, $3F, $10, $10, $00, $00, $10
dc.b $10, $00, $00, $10, $10, $00, $00, $10, $10, $00, $00, $10, $1F, $FF, $FF, $F0
dc.b $08, $00, $00, $20, $08, $00, $00, $20, $08, $00, $00, $20, $0F, $FF, $FF, $E0
; resource 'ICN#' (2, "Mac with eyes") {
dc.b $0F, $FF, $FF, $E0, $18, $00, $00, $30, $10, $00, $00, $10, $11, $FF, $FF, $10
dc.b $12, $00, $00, $90, $12, $00, $00, $90, $12, $00, $00, $90, $12, $10, $10, $90
dc.b $12, $10, $10, $90, $12, $00, $00, $90, $12, $00, $00, $90, $12, $00, $00, $90
dc.b $12, $00, $00, $90, $12, $00, $00, $90, $12, $00, $00, $90, $12, $00, $00, $90
dc.b $12, $00, $00, $90, $11, $FF, $FF, $10, $10, $00, $00, $10, $10, $00, $00, $10
dc.b $10, $00, $00, $10, $10, $00, $00, $10, $13, $00, $3F, $10, $10, $00, $00, $10
dc.b $10, $00, $00, $10, $10, $00, $00, $10, $10, $00, $00, $10, $1F, $FF, $FF, $F0
dc.b $08, $00, $00, $20, $08, $00, $00, $20, $08, $00, $00, $20, $0F, $FF, $FF, $E0
;resource 'ICN#' (3, "mac with eyes&nose") {
dc.b $0F, $FF, $FF, $E0, $18, $00, $00, $30, $10, $00, $00, $10, $11, $FF, $FF, $10
dc.b $12, $00, $00, $90, $12, $00, $00, $90, $12, $00, $00, $90, $12, $11, $10, $90
dc.b $12, $11, $10, $90, $12, $01, $00, $90, $12, $01, $00, $90, $12, $03, $00, $90
dc.b $12, $00, $00, $90, $12, $00, $00, $90, $12, $00, $00, $90, $12, $00, $00, $90
dc.b $12, $00, $00, $90, $11, $FF, $FF, $10, $10, $00, $00, $10, $10, $00, $00, $10
dc.b $10, $00, $00, $10, $10, $00, $00, $10, $13, $00, $3F, $10, $10, $00, $00, $10
dc.b $10, $00, $00, $10, $10, $00, $00, $10, $10, $00, $00, $10, $1F, $FF, $FF, $F0
dc.b $08, $00, $00, $20, $08, $00, $00, $20, $08, $00, $00, $20, $0F, $FF, $FF, $E0
ENDP
myGetMask PROC EXPORT
dc.b $0F, $FF, $FF, $E0, $1F, $FF, $FF, $F0, $1F, $FF, $FF, $F0, $1F, $FF, $FF, $F0
dc.b $1F, $FF, $FF, $F0, $1F, $FF, $FF, $F0, $1F, $FF, $FF, $F0, $1F, $FF, $FF, $F0
dc.b $1F, $FF, $FF, $F0, $1F, $FF, $FF, $F0, $1F, $FF, $FF, $F0, $1F, $FF, $FF, $F0
dc.b $1F, $FF, $FF, $F0, $1F, $FF, $FF, $F0, $1F, $FF, $FF, $F0, $1F, $FF, $FF, $F0
dc.b $1F, $FF, $FF, $F0, $1F, $FF, $FF, $F0, $1F, $FF, $FF, $F0, $1F, $FF, $FF, $F0
dc.b $1F, $FF, $FF, $F0, $1F, $FF, $FF, $F0, $1F, $FF, $FF, $F0, $1F, $FF, $FF, $F0
dc.b $1F, $FF, $FF, $F0, $1F, $FF, $FF, $F0, $1F, $FF, $FF, $F0, $1F, $FF, $FF, $F0
dc.b $0F, $FF, $FF, $E0, $0F, $FF, $FF, $E0, $0F, $FF, $FF, $E0, $0F, $FF, $FF, $E0
ENDP
END