sys7.1-doc-wip/OS/NetBoot/BootUtils.a
2019-07-27 22:37:48 +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