mirror of
https://github.com/elliotnunn/NetBoot.git
synced 2024-12-03 00:49:44 +00:00
Working DA-based ABP server
This commit is contained in:
parent
926e94a81e
commit
b858ad813f
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,7 +3,7 @@
|
||||
BootstrapFloppy/System.rdump
|
||||
BootstrapFloppy.dsk
|
||||
BootServer.INIT*
|
||||
*.bin
|
||||
*.bin*
|
||||
*.tmp
|
||||
*.pyc
|
||||
payload
|
||||
|
739
Client.a
Normal file
739
Client.a
Normal file
@ -0,0 +1,739 @@
|
||||
move.w #$abab,D0
|
||||
dc.w $A9C9
|
||||
|
||||
|
||||
|
||||
kUserRecLen equ 568
|
||||
kInitialWaitMsec equ 10000
|
||||
kSubsequentWaitMsec equ 1000
|
||||
|
||||
Code
|
||||
|
||||
; The ROM issued a _Read call that eventually reached here.
|
||||
; We need to handle this _Read trap directly, without the intervening .netBOOT and .ATBOOT drivers.
|
||||
; We do this by rewinding the Device Manager's return address by 2 bytes.
|
||||
|
||||
; Why?
|
||||
; The ROM network boot mechanism is complicated and buggy.
|
||||
; The workarounds are difficult.
|
||||
; Therefore this big hack obviates many little hacks.
|
||||
|
||||
|
||||
; First problem: this is not a safe place to keep code. Jump away.
|
||||
; (Using the heap while all the netBOOT junk is there would cause fragmentation.)
|
||||
lea ResumeAfterCopy,A0
|
||||
move.l #CodeEnd-ResumeAfterCopy,D0
|
||||
lea 4096(A5),A1
|
||||
dc.w $A02E ; _BlockMove
|
||||
jmp 4096(A5)
|
||||
ResumeAfterCopy
|
||||
|
||||
; Salvage some possibly useful information from ATBOOT, while it is still running:
|
||||
|
||||
; Server address
|
||||
move.l 8(SP),A0 ; global pointer
|
||||
move.l 24(A0),D0 ; AddrBlock
|
||||
lea gSaveAddr,A0
|
||||
move.b #10,15(A0) ; hardcode DDP protocol ID
|
||||
move.b D0,13(A0) ; socket
|
||||
lsr.w #4,D0
|
||||
lsr.w #4,D0
|
||||
move.b D0,11(A0) ; node
|
||||
swap D0
|
||||
move.w D0,7(A0) ; network
|
||||
|
||||
; User record
|
||||
lea gUserRec,A1
|
||||
move.l 8(SP),A0 ; global pointer
|
||||
lea 46(A0),A0
|
||||
move.l #kUserRecLen,D0
|
||||
dc.w $A02E ; _BlockMove
|
||||
|
||||
|
||||
; Currently the call stack looks like this:
|
||||
; ROM _Read to get boot blocks
|
||||
; .netBOOT Read routine
|
||||
; .ATBOOT Control routine
|
||||
; direct call to this block of code
|
||||
|
||||
; But we want to close and remove .netBOOT & .ATBOOT, so we need to return
|
||||
; from their Device Manager calls, and steal control from the ROM.
|
||||
; We do this by scanning the stack for the return address of the original _Read.
|
||||
move.l $2AE,A0 ; A0 = ROMBase (lower limit)
|
||||
lea $4000(A0),A1 ; A1 = ROMBase + a bit (upper limit)
|
||||
move.l SP,A2
|
||||
.loop addq.l #2,A2 ; A2 = where we search the stack
|
||||
move.l (A2),A3 ; A3 = potential return address
|
||||
cmp.l A0,A3 ; lower limit check
|
||||
bls.s .loop
|
||||
cmp.l A1,A3 ; upper limit check
|
||||
bhi.s .loop
|
||||
cmp.w #$A002,-2(A3) ; _Read trap check
|
||||
bne.s .loop
|
||||
|
||||
pea GoHereFromReadTrap ; take over
|
||||
move.l (SP)+,(A2)
|
||||
lea ROMAfterReadTrap,A2 ; save original for later
|
||||
move.l A3,(A2)
|
||||
|
||||
moveq.l #-1,D0 ; .netBOOT/.ATBOOT don't do any more damage if an error is returned
|
||||
rts
|
||||
|
||||
ROMAfterReadTrap
|
||||
dc.l 0
|
||||
|
||||
GoHereFromReadTrap
|
||||
; Now we are outside .netBOOT/.ATBOOT. We can shut them down, and set up our driver in a clean environment.
|
||||
|
||||
move.l ROMAfterReadTrap,-(SP) ; our return address is to ROM
|
||||
sub.l #2,(SP) ; repeating the _Read trap
|
||||
movem.l A0-A6/D0-D7,-(SP) ; save registers conservatively (especially A0)
|
||||
|
||||
; A4 = param block to the .netBOOT _Read call, because we will use it a lot
|
||||
move.l A0,A4
|
||||
|
||||
; Close and delete .netBOOT (which will close .ATBOOT)
|
||||
lea -$32(SP),SP
|
||||
move.l SP,A0
|
||||
move.w $18(A4),$18(A0) ; ioRefNum
|
||||
dc.w $A001 ; _Close
|
||||
lea $32(SP),SP
|
||||
move.w $18(A4),D0 ; ioRefNum
|
||||
dc.w $A03E ; _DrvrRemove
|
||||
move.w #-51,D0 ; ioRefNum ; also delete .ATBOOT for neatness
|
||||
dc.w $A03E ; _DrvrRemove
|
||||
|
||||
; A3 = our driver in sysheap (plus user record)
|
||||
move.l #DrvrEnd-DrvrBase+kUserRecLen,D0
|
||||
dc.w $A51E ; NewPtrSys
|
||||
move.l A0,A1
|
||||
lea DrvrBase,A0
|
||||
move.l #DrvrEnd-DrvrBase+kUserRecLen,D0
|
||||
dc.w $A02E ; BlockMove
|
||||
move.l A1,A3
|
||||
|
||||
; Install the driver in the unit table. Take over netBOOT's old unit number.
|
||||
move.w $18(A4),D0 ; ioRefNum
|
||||
dc.w $A43D ; _DrvrInstall ReserveMem
|
||||
|
||||
; That call created a driver control entry (DCE). Find and lock.
|
||||
move.l $11C,A0 ; UTableBase
|
||||
move.w $18(A4),D0 ; ioRefNum
|
||||
not.w D0
|
||||
lsl.w #2,D0
|
||||
add.w D0,A0
|
||||
move.l (A0),A0
|
||||
dc.w $A029 ; _HLock
|
||||
move.l (A0),A0
|
||||
|
||||
; Populate the empty DCE that DrvrInstall left us (forget fields related desk accessories)
|
||||
move.l A3,(A0) ; dCtlDriver = driver pointer (not handle)
|
||||
move.w (A3),4(A0) ; dCtlFlags = drvrFlags
|
||||
|
||||
; Open our driver
|
||||
lea -$32(SP),SP
|
||||
move.l SP,A0
|
||||
bsr DrvrClearBlock
|
||||
lea DrvrName,A1
|
||||
move.l A1,$12(A0) ; IOFileName
|
||||
dc.w $A000 ; _Open
|
||||
lea $32(SP),SP
|
||||
|
||||
; Add the a drive queue entry (DQE).
|
||||
lea dqLink-DrvrBase(A3),A0
|
||||
move.l $16(A4),D0
|
||||
dc.w $A04E ; _AddDrive (A0=DQE, D0=drvnum/drefnum)
|
||||
|
||||
; Open .MPP (still open?) & our DDP socket
|
||||
lea -$32(SP),SP
|
||||
move.l SP,A0
|
||||
bsr DrvrClearBlock
|
||||
pea MPPName
|
||||
move.l (SP)+,$12(A0) ; ioNamePtr
|
||||
dc.w $A000 ; _Open
|
||||
move.w #248,$1A(A0) ; csCode = openSkt
|
||||
move.b #10,$1C(A0) ; socket = 10, same as ATBOOT uses
|
||||
pea DrvrSockListener-DrvrBase(A3)
|
||||
move.l (SP)+,$1E(A0) ; listener
|
||||
dc.w $A004 ; _Control
|
||||
lea $32(SP),SP
|
||||
|
||||
; Re-execute the _Read trap in ROM
|
||||
movem.l (SP)+,A0-A6/D0-D7
|
||||
rts
|
||||
|
||||
MPPName dc.b 4, '.MPP', 0
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
DrvrBase
|
||||
dc.w $4F00 ; dReadEnable dWritEnable dCtlEnable dStatEnable dNeedLock
|
||||
dc.w 0 ; delay
|
||||
dc.w 0 ; evt mask
|
||||
dc.w 0 ; menu
|
||||
|
||||
dc.w DrvrOpen-DrvrBase
|
||||
dc.w DrvrPrime-DrvrBase
|
||||
dc.w DrvrControl-DrvrBase
|
||||
dc.w DrvrStatus-DrvrBase
|
||||
dc.w DrvrClose-DrvrBase
|
||||
DrvrName dc.b 8, ".netBOOT", 0
|
||||
|
||||
g
|
||||
gImage dc.l 0 ; "configuration mode" by default
|
||||
gMyDCE dc.l 0
|
||||
gExpectHdr dc.l 0
|
||||
gProgress dc.l 0
|
||||
gQuery dcb.b 20 ; really need to consider the length of this!
|
||||
gWDS dcb.b 2+4+2+4+2+4+2 ; room for an address and two data chunks
|
||||
gMyPB dcb.b $32+2 ; allow us to clear it with move.l's
|
||||
odd
|
||||
gSaveAddr dcb.b 16
|
||||
gAddr dcb.b 16
|
||||
even
|
||||
|
||||
; Time Manager task
|
||||
tmLink dc.l 0
|
||||
tmType dc.w 0
|
||||
tmAddr dc.l 0
|
||||
tmCount dc.l 0
|
||||
|
||||
; Drive queue element
|
||||
dqFlags dc.l $00080000
|
||||
dqLink dc.l 0
|
||||
dqType dc.w 1
|
||||
dqDrive dc.w 0
|
||||
dqRefNum dc.w 0
|
||||
dqFSID dc.w 0
|
||||
dqDrvSz dc.w 0
|
||||
dqDrvSz2 dc.w 0
|
||||
|
||||
; a0=iopb, a1=dce on entry to all of these...
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
DrvrOpen
|
||||
lea gMyDCE,A2 ; dodgy, need this for IODone
|
||||
move.l A1,(A2)
|
||||
|
||||
move.w #0,$10(A0) ; ioResult
|
||||
rts
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
DrvrPrime
|
||||
|
||||
; Convert the ioPosOffset in the parameter block to a block offset (allows large vol support).
|
||||
btst.b #0,$2C(A0) ; test ioPosMode & kUseWidePositioning
|
||||
bne.s .wide
|
||||
.notwide move.l $10(A1),D0
|
||||
bra.s .gotD0
|
||||
.wide move.l $2E(A0),D0 ; the block offset can only be up to 32 bits
|
||||
or.l $32(A0),D0
|
||||
.gotD0 ror.l #4,D0 ; now D0 = (LS 23 bits) followed by (MS 9 bits)
|
||||
ror.l #5,D0
|
||||
move.l D0,$2E(A0) ; ioPosOffset = D0 = byte_offset/512
|
||||
|
||||
; Return with a "pending" ioResult.
|
||||
move.w #1,$10(A0) ; ioResult = pending. We will return without an answer.
|
||||
|
||||
; Wang the state machine!
|
||||
cmp.b #2,7(A0) ; ioTrap == _Read?
|
||||
bne DrvrSendWrite ; transition from "Idle" to "Await Comp Send Write Packet"
|
||||
bra DrvrSendRead ; transition from "Idle" to "Await Comp Send Read Packet"
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
DrvrReSendRead
|
||||
; Time Manager task
|
||||
lea tmLink,A0
|
||||
dc.w $A059 ; _RmvTime
|
||||
|
||||
move.l gMyDCE,A1 ; get Device Mgr registers
|
||||
move.l 6+2(A1),A0
|
||||
|
||||
; truncate ioActCount
|
||||
and.l #-32*512,$28(A0) ; truncate ioActCount
|
||||
|
||||
DrvrSendRead
|
||||
; Called from Prime routine, socket listener or Time Manager:
|
||||
; PB/DCE in A0/A1 must be preserved (but we only require A0)
|
||||
lea gExpectHdr,A2
|
||||
clr.w (A2)+
|
||||
addq.w #1,(A2) ; packet filter sequence word
|
||||
lea gProgress,A2
|
||||
clr.l (A2)
|
||||
|
||||
bsr.s DrvrCopyAddrStruct
|
||||
|
||||
lea gQuery,A2
|
||||
move.w #$8000,(A2)+ ; Means a polite request
|
||||
move.w gExpectHdr+2,(A2)+
|
||||
move.l gImage,(A2)+
|
||||
|
||||
move.l $28(A0),D0 ; [ioActCount (in bytes)
|
||||
lsr.l #4,D0
|
||||
lsr.l #5,D0 ; / 512]
|
||||
add.l $2E(A0),D0 ; + ioPosOffset (in blocks)
|
||||
move.l D0,(A2)+ ; -> "offset" field of request
|
||||
|
||||
move.l $24(A0),D0 ; [ioReqCount (in bytes)
|
||||
sub.l $28(A0),D0 ; - ioActCount (in bytes)]
|
||||
lsr.l #4,D0
|
||||
lsr.l #5,D0 ; / 512
|
||||
move.l D0,(A2)+ ; -> "length" field of request
|
||||
|
||||
lea gWDS,A2
|
||||
clr.w (A2)+ ; WDS+0: reserved field
|
||||
pea gAddr
|
||||
move.l (SP)+,(A2)+ ; WDS+2: pointer to address struct
|
||||
move.w #16,(A2)+ ; WDS: push pointer/length
|
||||
pea gQuery
|
||||
move.l (SP)+,(A2)+
|
||||
clr.w (A2)+ ; WDS: end with zero
|
||||
|
||||
move.l A0,-(SP)
|
||||
lea gMyPB,A0
|
||||
bsr DrvrClearBlock
|
||||
pea DrvrDidSendRead
|
||||
move.l (SP)+,$C(A0) ; ioCompletion
|
||||
move.w #-10,$18(A0) ; ioRefNum = .MPP
|
||||
move.w #246,$1A(A0) ; csCode = writeDDP
|
||||
move.b #10,$1C(A0) ; socket = 10 (hardcoded)
|
||||
move.b #1,$1D(A0) ; set checksumFlag
|
||||
pea gWDS
|
||||
move.l (SP)+,$1E(A0) ; wdsPointer to our WriteDataStructure
|
||||
dc.w $A404 ; _Control ,async
|
||||
move.l (SP)+,A0
|
||||
|
||||
rts
|
||||
|
||||
DrvrCopyAddrStruct
|
||||
movem.l A0-A1,-(SP)
|
||||
lea gSaveAddr,A0
|
||||
lea gAddr,A1
|
||||
moveq.l #16,D0
|
||||
dc.w $A22E ; _BlockMoveData
|
||||
movem.l (SP)+,A0-A1
|
||||
rts
|
||||
|
||||
DrvrDidSendRead
|
||||
; Called as completion routine: PB/result in A0/D0, must preserve all registers other than A0/A1/D0-D2
|
||||
lea gExpectHdr,A0
|
||||
move.w #$8100,(A0) ; Enable packet reception
|
||||
move.l #kInitialWaitMsec,D0
|
||||
|
||||
DrvrInstallReSendRead
|
||||
; Called from anywhere, D0=waittime, can clobber anything
|
||||
lea tmLink,A0
|
||||
pea DrvrReSendRead
|
||||
move.l (SP)+,tmAddr-tmLink(A0)
|
||||
move.l D0,-(SP)
|
||||
dc.w $A058 ; _InsTime
|
||||
move.l (SP)+,D0
|
||||
dc.w $A05A ; _PrimeTime
|
||||
rts
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
DrvrReSendWrite
|
||||
; Time Manager task
|
||||
lea tmLink,A0
|
||||
dc.w $A059 ; _RmvTime
|
||||
|
||||
move.l gMyDCE,A1 ; get Device Mgr registers
|
||||
move.l 6+2(A1),A0
|
||||
|
||||
; truncate ioActCount
|
||||
sub.l #512,$28(A0)
|
||||
and.l #-32*512,$28(A0) ; truncate ioActCount
|
||||
|
||||
DrvrSendWrite
|
||||
; Called from Prime routine, socket listener or Time Manager:
|
||||
; PB/DCE in A0/A1 must be preserved (but we only require A0)
|
||||
|
||||
; D1 = block index within chunk
|
||||
move.l $28(A0),D1
|
||||
lsr.l #5,D1
|
||||
lsr.l #4,D1
|
||||
move.l D1,D0
|
||||
and.l #32-1,D1
|
||||
|
||||
move.l $28(A0),D2
|
||||
add.l #512,D2
|
||||
cmp.l $24(A0),D2
|
||||
bne.s .notLastBlock
|
||||
bset #7,D1
|
||||
.notLastBlock
|
||||
|
||||
; D0 = first block of chunk
|
||||
and.l #-32,D0
|
||||
add.l $2E(A0),D0
|
||||
|
||||
tst.l D1
|
||||
bne.s .notFirstBlockOfChunk
|
||||
lea gExpectHdr,A2
|
||||
clr.w (A2)+
|
||||
addq.w #1,(A2) ; packet filter sequence word
|
||||
.notFirstBlockOfChunk
|
||||
|
||||
bsr DrvrCopyAddrStruct
|
||||
|
||||
lea gQuery,A2
|
||||
move.b #$82,(A2)+ ; Means a polite request
|
||||
move.b D1,(A2)+ ; nth block of this chunk follows
|
||||
move.w gExpectHdr+2,(A2)+
|
||||
move.l gImage,(A2)+
|
||||
move.l D0,(A2)+ ; first block of this chunk
|
||||
|
||||
lea gWDS,A2
|
||||
clr.w (A2)+ ; WDS+0: reserved field
|
||||
pea gAddr
|
||||
move.l (SP)+,(A2)+ ; WDS+2: pointer to address struct
|
||||
|
||||
move.w #12,(A2)+ ; WDS: push length/ptr of header
|
||||
pea gQuery
|
||||
move.l (SP)+,(A2)+
|
||||
|
||||
move.w #512,(A2)+ ; WDS: push length/ptr of body
|
||||
move.l $20(A0),D0 ; ptr = ioBuffer
|
||||
add.l $28(A0),D0 ; + ioActCount
|
||||
move.l D0,(A2)+
|
||||
|
||||
clr.w (A2)+ ; WDS: end with zero
|
||||
|
||||
move.l A0,-(SP)
|
||||
lea gMyPB,A0
|
||||
bsr DrvrClearBlock
|
||||
pea DrvrDidSendWrite
|
||||
move.l (SP)+,$C(A0) ; ioCompletion
|
||||
move.w #-10,$18(A0) ; ioRefNum = .MPP
|
||||
move.w #246,$1A(A0) ; csCode = writeDDP
|
||||
move.b #10,$1C(A0) ; socket = 10 (hardcoded)
|
||||
move.b #1,$1D(A0) ; set checksumFlag
|
||||
pea gWDS
|
||||
move.l (SP)+,$1E(A0) ; wdsPointer to our WriteDataStructure
|
||||
dc.w $A404 ; _Control ,async
|
||||
move.l (SP)+,A0
|
||||
|
||||
rts
|
||||
|
||||
DrvrDidSendWrite ; completion routine for the above control call..
|
||||
; need to test whether to send another, or switch to wait mode...
|
||||
|
||||
move.l gMyDCE,A0
|
||||
move.l 6+2(A0),A0 ; A3 = dCtlQHdr.qHead = ParamBlk
|
||||
|
||||
movem.l $24(A0),D0/D1 ; D0=ioReqCount, D1=ioActCount
|
||||
add.l #512,D1
|
||||
move.l D1,$28(A0)
|
||||
|
||||
cmp.l D0,D1
|
||||
beq.s DrvrInstallReSendWrite
|
||||
and.l #(32-1)*512,D0
|
||||
beq.s DrvrInstallReSendWrite
|
||||
bra DrvrSendWrite
|
||||
|
||||
DrvrInstallReSendWrite
|
||||
lea tmLink,A0
|
||||
pea DrvrReSendWrite
|
||||
move.l (SP)+,tmAddr-tmLink(A0)
|
||||
dc.w $A058 ; _InsTime
|
||||
move.l #kInitialWaitMsec,D0
|
||||
dc.w $A05A ; _PrimeTime
|
||||
rts
|
||||
|
||||
DrvrClearBlock
|
||||
move.w #$32/2-1,D0
|
||||
.loop clr.w (A0)+
|
||||
dbra D0,.loop
|
||||
lea -$32(A0),A0
|
||||
rts
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
DrvrSockListener
|
||||
; Registers on call to DDP socket listener:
|
||||
; A0 Reserved for internal use by the .MPP driver. You must preserve this register
|
||||
; until after the ReadRest routine has completed execution.
|
||||
; A1 Reserved for internal use by the .MPP driver. You must preserve this register
|
||||
; until after the ReadRest routine has completed execution.
|
||||
; A2 Pointer to the .MPP driver's local variables. Elliot says: the frame and packet
|
||||
; header ("RHA") are at offset 1 from A2 (keeps the 2-byte fields aligned)
|
||||
; A3 Pointer to the first byte in the RHA past the DDP header bytes (the first byte
|
||||
; after the DDP protocol type field).
|
||||
; A4 Pointer to the ReadPacket routine. The ReadRest routine starts 2 bytes after the
|
||||
; start of the ReadPacket routine.
|
||||
; A5 Free for your use before and until your socket listener calls the ReadRest routine.
|
||||
; D0 Lower byte is the destination socket number of the packet.
|
||||
; D1 Word indicating the number of bytes in the DDP packet left to be read (that is,
|
||||
; the number of bytes following the DDP header).
|
||||
; D2 Free for your use.
|
||||
; D3 Free for your use.
|
||||
|
||||
; Registers on entry to the ReadPacket routine
|
||||
; A3 Pointer to a buffer to hold the data you want to read
|
||||
; D3 Number of bytes to read; must be nonzero
|
||||
|
||||
; Registers on exit from the ReadPacket routine
|
||||
; A0 Unchanged
|
||||
; A1 Unchanged
|
||||
; A2 Unchanged
|
||||
; A3 Address of the first byte after the last byte read into buffer
|
||||
; A4 Unchanged
|
||||
; D0 Changed
|
||||
; D1 Number of bytes left to be read
|
||||
; D2 Unchanged
|
||||
; D3 Equals 0 if requested number of bytes were read, nonzero if error
|
||||
|
||||
; Registers on entry to the ReadRest routine
|
||||
; A3 Pointer to a buffer to hold the data you want to read
|
||||
; D3 Size of the buffer (word length); may be 0
|
||||
|
||||
; Registers on exit from the ReadRest routine
|
||||
; A0 Unchanged
|
||||
; A1 Unchanged
|
||||
; A2 Unchanged
|
||||
; A3 Pointer to first byte after the last byte read into buffer
|
||||
; D0 Changed
|
||||
; D1 Changed
|
||||
; D2 Unchanged
|
||||
; D3 Equals 0 if requested number of bytes exactly equaled the size of the buffer;
|
||||
; less than 0 if more data was left than would fit in buffer (extra data equals
|
||||
; -D3 bytes); greater than 0 if less data was left than the size of the buffer
|
||||
; (extra buffer space equals D3 bytes)
|
||||
|
||||
; cmp.b #10,-1(A3) ; DDP protocol type better be ATBOOT
|
||||
; bne.s DrvrTrashPacket
|
||||
|
||||
moveq.l #4,D3
|
||||
jsr (A4) ; Read the nice short packet header
|
||||
bne DrvrTrashPacket
|
||||
move.l -4(A3),D2
|
||||
|
||||
move.l gExpectHdr,D3 ; Check the packet header
|
||||
eor.l D2,D3
|
||||
swap D3
|
||||
clr.b D3
|
||||
bne DrvrTrashPacket
|
||||
|
||||
movem.l A0-A1/D0-D2,-(SP)
|
||||
lea tmLink,A0
|
||||
dc.w $A059 ; _RmvTime
|
||||
movem.l (SP)+,A0-A1/D0-D2
|
||||
|
||||
btst #25,D2
|
||||
bne.s DrvrDidReceiveWrite
|
||||
|
||||
|
||||
DrvrDidReceiveRead
|
||||
swap D2
|
||||
and.l #32-1,D2 ; D2.L = block offset within 32blk chunk
|
||||
|
||||
move.l gMyDCE,A3
|
||||
move.l 6+2(A3),A3 ; A3 = dCtlQHdr.qHead
|
||||
|
||||
move.l $28(A3),D3 ; D3 = .ioActCount...
|
||||
lsr.l #5,D3
|
||||
lsr.l #4,D3 ; ...now in number of blocks
|
||||
and.l #-32,D3 ; ...rounded down to a block chunk
|
||||
add.l D2,D3 ; ...added back the received block
|
||||
asl.l #8,D3
|
||||
add.l D3,D3 ; ... = byte offset within buffer
|
||||
|
||||
move.l $20(A3),A3 ; .ioBuffer
|
||||
add.l D3,A3 ; A3 = ioBuffer + offset
|
||||
move.l #512,D3 ; D3 = size
|
||||
jsr 2(A4) ; ReadRest (A3=dest, D3=length)
|
||||
bne DrvrTrashPacket
|
||||
|
||||
lea gProgress,A1 ; Skip the next step if this packet is a repeat
|
||||
move.l (A1),D1
|
||||
bset.l D2,D1 ; (we saved blkidx in D2 before ReadRest)
|
||||
bne.s DrvrTrashPacket
|
||||
move.l D1,(A1)
|
||||
|
||||
move.l gMyDCE,A0
|
||||
move.l 6+2(A0),A0 ; dCtlQHdr.qHead
|
||||
|
||||
move.l $28(A0),D0 ; Increment ioActCount and cmp with ioReqCount
|
||||
add.l #512,D0
|
||||
move.l D0,$28(A0)
|
||||
cmp.l $24(A0),D0
|
||||
beq.s DrvrIODone
|
||||
|
||||
addq.l #1,D1 ; If bitmap=$FFFFFFFF then get the next chunk of 32
|
||||
beq DrvrSendRead ; A0 must be the PB
|
||||
|
||||
move.l #kSubsequentWaitMsec,D0
|
||||
bra DrvrInstallReSendRead ; Just return to await more packets.
|
||||
|
||||
DrvrDidReceiveWrite
|
||||
moveq.l #0,D3
|
||||
jsr 2(A4) ; ReadRest (D3=0 i.e. discard)
|
||||
|
||||
move.l gMyDCE,A0
|
||||
move.l 6+2(A0),A0 ; A3 = dCtlQHdr.qHead = ParamBlk
|
||||
|
||||
movem.l $24(A0),D0/D1 ; D0=ioReqCount, D1=ioActCount
|
||||
cmp.l D0,D1
|
||||
blo DrvrSendWrite
|
||||
|
||||
bra.s DrvrIODone
|
||||
|
||||
DrvrIODone
|
||||
lea gExpectHdr,A1 ; Disable this socket listener
|
||||
clr.w (A1)
|
||||
|
||||
move.l gMyDCE,A1
|
||||
moveq.l #0,D0
|
||||
move.l $8FC,A0 ; jIODone (D0 = result, A1 = DCE)
|
||||
jmp (A0)
|
||||
|
||||
DrvrTrashPacket
|
||||
moveq.l #0,D3
|
||||
jmp 2(A4) ; ReadRest nothing
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
DrvrControl
|
||||
cmp.w #21,$1A(A0)
|
||||
beq.s control_kDriveIcon
|
||||
cmp.w #22,$1A(A0)
|
||||
beq.s control_kMediaIcon
|
||||
bra.s control_unknown
|
||||
|
||||
control_kDriveIcon
|
||||
control_kMediaIcon
|
||||
lea DrvrIcon,A2
|
||||
move.l A2,$1C(A0)
|
||||
clr.w $10(A0) ; ioResult = noErr
|
||||
bra DrvrFinish
|
||||
|
||||
control_unknown
|
||||
move.w #-17,$10(A0) ; ioResult = controlErr
|
||||
bra DrvrFinish
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
DrvrStatus
|
||||
cmp.w #6,$1A(A0)
|
||||
beq.s status_fmtLstCode
|
||||
cmp.w #8,$1A(A0)
|
||||
beq.s status_drvStsCode
|
||||
bra.s status_unknown
|
||||
|
||||
status_fmtLstCode ; tell them about our size
|
||||
move.l dqDrvSz,D0
|
||||
swap D0
|
||||
lsl.l #5,D0 ; convert from blocks to bytes
|
||||
lsl.l #4,D0
|
||||
|
||||
move.w #1,$1C(A0)
|
||||
move.l $1C+2(A0),A2
|
||||
move.l D0,0(A2)
|
||||
move.l #$40000000,4(A2)
|
||||
|
||||
move.w #0,$10(A0) ; ioResult = noErr
|
||||
bra DrvrFinish
|
||||
|
||||
status_drvStsCode ; tell them about some of our flags
|
||||
move.w #0,$1C(A0) ; csParam[0..1] = track no (0)
|
||||
move.l dqFlags,$1C+2(A0) ; csParam[2..5] = same flags as dqe
|
||||
|
||||
move.w #0,$10(A0) ; ioResult = noErr
|
||||
bra DrvrFinish
|
||||
|
||||
status_unknown
|
||||
move.w #-18,$10(A0) ; ioResult
|
||||
bra DrvrFinish
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
DrvrClose
|
||||
move.w #0,$10(A0) ; ioResult
|
||||
rts
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
DrvrFinish
|
||||
move.w 6(A0),D1 ; iopb.ioTrap
|
||||
btst #9,D1 ; noQueueBit
|
||||
bne.s DrvrNoIoDone
|
||||
move.l $8FC,-(SP) ; jIODone
|
||||
DrvrNoIoDone
|
||||
rts
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
DrvrIcon
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %11111111111111111111111111111111
|
||||
dc.l %10000000000000000000000000000001
|
||||
dc.l %10000000000000001000000000000001
|
||||
dc.l %10010010010010011001001001001001
|
||||
dc.l %10010010010010000001001001001001
|
||||
dc.l %10010010010010000001001001001001
|
||||
dc.l %10010010010010010001001001001001
|
||||
dc.l %10010010010010001001001001001001
|
||||
dc.l %10010010010010010001001001001001
|
||||
dc.l %10010010010010001001001001001001
|
||||
dc.l %10010010010010010001001001001001
|
||||
dc.l %10000000000000000000000000000001
|
||||
dc.l %11111111111111111111111111111111
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
dc.l %00000000000000000000000000000000
|
||||
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.b 22, "AppleTalk NetBoot Disk", 0
|
||||
|
||||
gUserRec ; append user record here later on, no need to waste space on zeros
|
||||
|
||||
DrvrEnd
|
||||
CodeEnd
|
12
Makefile
12
Makefile
@ -82,10 +82,16 @@ testchain: ChainLoader.bin FORCE
|
||||
|
||||
|
||||
|
||||
Client.bin: Client.a
|
||||
vasm-1/vasmm68k_mot -quiet -Fbin -pic -o $@ $<
|
||||
|
||||
Client.bin.summed: Client.bin
|
||||
./snefru_hash.py --align=256 $< $@
|
||||
|
||||
ServerDA.bin: ServerDA.a
|
||||
vasm-1/vasmm68k_mot -quiet -Fbin -pic -o $@ $<
|
||||
|
||||
ServerDRVR.bin: ServerDRVR.a
|
||||
ServerDRVR.bin: ServerDRVR.a Client.bin.summed
|
||||
vasm-1/vasmm68k_mot -quiet -Fbin -pic -o $@ $<
|
||||
|
||||
ServerDA ServerDA.idump ServerDA.rdump: ServerDA.bin ServerDRVR.bin
|
||||
@ -96,11 +102,11 @@ ServerDA ServerDA.idump ServerDA.rdump: ServerDA.bin ServerDRVR.bin
|
||||
echo data "'DRVR'" '(-16000, sysheap, locked) {};' >>ServerDA.rdump
|
||||
rfx cp ServerDRVR.bin ServerDA.rdump//DRVR/-16000
|
||||
|
||||
testda: FORCE ServerDA ServerDA.idump ServerDA.rdump
|
||||
testda: FORCE ServerDA ServerDA.idump ServerDA.rdump BootstrapFloppy.dsk
|
||||
rm -rf /tmp/testda; mkdir -p /tmp/testda/Desktop\ Folder; cp ServerDA ServerDA.idump ServerDA.rdump /tmp/testda/Desktop\ Folder/
|
||||
MakeHFS -s 400k -n TestDA -d now -i /tmp/testda /tmp/testda.dsk
|
||||
rsync Big.dsk /tmp/Big.dsk
|
||||
Mini\ vMac\ Classic.app/Co*/Ma*/* /tmp/Big.dsk /tmp/testda.dsk
|
||||
./twovmacs.bash "BootstrapFloppy.dsk" "/tmp/Big.dsk /tmp/testda.dsk"
|
||||
|
||||
|
||||
|
||||
|
228
ServerDRVR.a
228
ServerDRVR.a
@ -12,11 +12,62 @@ ServerHeader
|
||||
dc.w 0 ; no Control
|
||||
dc.w 0 ; no Status
|
||||
dc.w ServerClose-ServerHeader
|
||||
dc.b 14, '.NetBootServer', 0
|
||||
dc.b 14, '.NetBootServer'
|
||||
even
|
||||
dc.b 0, 0, $20, 1 ; version
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; Inline globals are practical because we are locked in the sysheap
|
||||
|
||||
ATBOOT_BLK_LOG equ 8
|
||||
ATBOOT_BLK_SIZE equ 1<<ATBOOT_BLK_LOG
|
||||
|
||||
gNameTableEntry ; Installed using RegisterName
|
||||
dc.l 0 ; qLink
|
||||
dc.l 0 ; addrblock (our DDP skt at offset 7)
|
||||
odd
|
||||
dc.b 4, '0000' ; object (must match const in PRAM)
|
||||
dc.b 10, 'BootServer'; type (always this)
|
||||
dc.b 1, '*' ; zone
|
||||
even
|
||||
|
||||
gABPBusyFlag ; Lockout flag: only one ABP request can be serviced at a time
|
||||
dc.w 0
|
||||
|
||||
gABPReplyParamBlk ; For writeDDP control calls
|
||||
dcb.b $32
|
||||
|
||||
|
||||
cannedWDS dcb.b 24 ; doesn't need to be this long at all
|
||||
|
||||
odd
|
||||
gABPAddress ; Address of the Apple Boot Protocol client:
|
||||
; set when socket listener gets a packet, used when replying
|
||||
dcb.b 15 ; totally obscure address format
|
||||
dc.b 10 ; ddp type
|
||||
dcb.b 1 ; backup of high byte of dest network
|
||||
; (MUST copy byte 16 to 7 for every use)
|
||||
|
||||
gABPUserReply ; reply to rbMapUser packet
|
||||
dc.b 2 ; rbUserReply
|
||||
dc.b 1 ; protocol version (always 1)
|
||||
dc.w 1 ; osID (always 1)
|
||||
dc.l $FFFFFFFF ; copy client's timestamp here (offset 4)
|
||||
dc.w ATBOOT_BLK_SIZE ; block size
|
||||
dc.w 0 ; imageID (we always use 0)
|
||||
dc.w 0 ; error code
|
||||
dc.l (BlobEnd-Blob)/ATBOOT_BLK_SIZE ; block count
|
||||
dcb.b 568 ; arbitrary bytes
|
||||
|
||||
gABPImageReply ; reply to rbImageRequest packet
|
||||
dc.b 4 ; rbImageData
|
||||
dc.b 1 ; protocol version (always 1)
|
||||
dc.w 0 ; imageID (we always use 0)
|
||||
dc.w 0 ; increment block number here (offset 4)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
ServerOpen
|
||||
dc.w $A9FF
|
||||
movem.l A0/A1,-(SP)
|
||||
|
||||
; Try *once*. AT might come up later but not because of us.
|
||||
@ -90,7 +141,7 @@ Listen
|
||||
move.l (SP)+,$1E(SP) ; listener
|
||||
move.l SP,A0
|
||||
dc.w $A004 ; _Control
|
||||
lea NameTableEntry+7,A0 ; lowest byte of AddrBlock
|
||||
lea gNameTableEntry+7,A0 ; lowest byte of AddrBlock
|
||||
move.b $1C(SP),(A0) ; save socket number there
|
||||
add #$32,SP
|
||||
|
||||
@ -99,7 +150,7 @@ Listen
|
||||
move.w #253,$1A(SP) ; csCode = registerName
|
||||
move.b #7,$1C(SP) ; interval = 7*8 ticks = 1sec
|
||||
move.b #5,$1D(SP) ; count = 5
|
||||
pea NameTableEntry
|
||||
pea gNameTableEntry
|
||||
move.l (SP)+,$1E(SP) ; entityPtr = our NTE
|
||||
move.b #1,$22(SP) ; verifyFlag = do
|
||||
move.l SP,A0
|
||||
@ -108,20 +159,12 @@ Listen
|
||||
|
||||
rts
|
||||
|
||||
NameTableEntry
|
||||
dc.l 0 ; qLink
|
||||
dc.l 0 ; addrblock
|
||||
dc.b 0 ; odd address
|
||||
dc.b 4, '0000' ; object
|
||||
dc.b 10, 'BootServer' ; type
|
||||
dc.b 1, '*' ; zone
|
||||
even
|
||||
|
||||
StopListening
|
||||
sub #$32,SP ; REMOVE NBP SERVICE
|
||||
move.w #-10,$18(SP) ; ioRefNum = MPP
|
||||
move.w #252,$1A(SP) ; csCode = removeName
|
||||
pea NameTableEntry
|
||||
pea gNameTableEntry
|
||||
move.l (SP)+,$1E(SP) ; entityPtr = our NTE
|
||||
move.l SP,A0
|
||||
dc.w $A004 ; _Control
|
||||
@ -130,7 +173,7 @@ StopListening
|
||||
sub #$32,SP ; CLOSE DDP SOCKET
|
||||
move.w #-10,$18(SP) ; ioRefNum = MPP
|
||||
move.w 257,$1A(SP) ; csCode = closeSkt
|
||||
move.b NameTableEntry+7,$1C(SP); socket = auto-assign
|
||||
move.b gNameTableEntry+7,$1C(SP); socket = auto-assign
|
||||
move.l SP,A0
|
||||
dc.w $A004 ; _Control
|
||||
add #$32,SP
|
||||
@ -139,5 +182,158 @@ StopListening
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
SocketListener ; DDP socket listener, very tricky piece of code!
|
||||
dc.w $A9FF
|
||||
nopCompletion
|
||||
lea gABPBusyFlag,A0
|
||||
clr.b (A0)
|
||||
rts
|
||||
|
||||
SocketListener ; Socket listener registers documented in IM
|
||||
lea gABPBusyFlag,A5
|
||||
bset.b #7,(A5)
|
||||
bne .TrashPacketStayingBusy
|
||||
|
||||
lea gABPAddress,A5 ; RETURN ADDRESS SAVE CODE
|
||||
move.b 2(A2),11(A5) ; both types node (ALAP header starts A2+1)
|
||||
move.b -2(A3),13(A5) ; both types socket (DDP header ends A3)
|
||||
moveq #0,D3 ; short packet network = 0
|
||||
cmp.b #1,3(A2)
|
||||
beq.s .short
|
||||
move.w 10(A2),D3 ; long packet network
|
||||
.short move.w D3,7(A5) ; network number as usual
|
||||
asr.w #8,D3
|
||||
move.b D3,16(A5) ; backup high-order byte of network number
|
||||
; otherwise would go to offset 7
|
||||
|
||||
; Always get the command, version and two more bytes
|
||||
moveq #8,D3
|
||||
jsr 2(A4) ; ReadRest command, version and 6 more bytes
|
||||
;bne.s TrashPacket
|
||||
subq #6,A3
|
||||
cmp.b #1,-(A3) ; version must be 1
|
||||
bne.s .TrashPacket
|
||||
move.b -(A3),D2
|
||||
subq.b #1,D2
|
||||
beq.s .rbMapUser ; 1 = user record request
|
||||
subq.b #2,D2 ; 3 = imageRequest
|
||||
beq.s .rbImageRequest
|
||||
|
||||
.TrashPacket
|
||||
lea gABPBusyFlag,A5
|
||||
clr.b (A5)
|
||||
.TrashPacketStayingBusy
|
||||
moveq #0,D3
|
||||
jmp 2(A4) ; ReadRest
|
||||
|
||||
|
||||
.rbMapUser ; "user record request"
|
||||
; +0 (1b) command (already checked)
|
||||
; +1 (1b) version (already checked)
|
||||
; +2 (2b) osID (already slurped and ignored)
|
||||
; +4 (4b) userData (timestamp to be returned in reply)
|
||||
; +8 (32b) userName
|
||||
lea gABPUserReply+4,A0
|
||||
move.l 4(A3),(A0) ; copy "userData" to the reply
|
||||
|
||||
lea cannedWDS+14,A1
|
||||
clr.w -(A1)
|
||||
lea gABPUserReply,A0
|
||||
move.l A0,-(A1)
|
||||
move.w #586,-(A1)
|
||||
lea gABPAddress,A0
|
||||
move.l A0,-(A1)
|
||||
clr.w -(A1)
|
||||
|
||||
move.b 16(A0),7(A0) ; repair the address struct
|
||||
|
||||
lea gABPReplyParamBlk,A0
|
||||
move.l A1,$1E(A0) ; wdsPointer
|
||||
lea nopCompletion,A1
|
||||
move.l A1,$C(A0) ; ioCompletion
|
||||
move.w #-10,$18(A0) ; ioRefNum = .MPP
|
||||
move.w #246,$1A(A0) ; csCode = writeDDP
|
||||
move.b gNameTableEntry+7,$1C(A0); our socket
|
||||
move.b #1,$1D(A0) ; set checksumFlag
|
||||
dc.w $A404 ; _Control ,async
|
||||
rts
|
||||
|
||||
.rbImageRequest ; reply with rbImageReply
|
||||
; +0 (1b) command (already checked)
|
||||
; +1 (1b) version (already checked)
|
||||
; +2 (2b) imageID (already slurped and ignored)
|
||||
; +4 (1b) section, ie which 2MB chunk
|
||||
; +5 (1b) flags
|
||||
; +6 (2b) replyDelay
|
||||
; +8 (<=512b) bitmap
|
||||
|
||||
; IGNORE the janky bitmap because the client implementation is buggy.
|
||||
; It is reasonable just to send every packet instead (our image is tiny).
|
||||
|
||||
lea gABPImageReply+4,A0 ; count of packets to send
|
||||
move.w #(BlobEnd-Blob)/ATBOOT_BLK_SIZE,(A0)
|
||||
|
||||
ImageReplyCompletionRoutine ; fallthru from skt listener OR writeDDP completion
|
||||
lea gABPImageReply+4,A0
|
||||
move.w (A0),D0
|
||||
subq.w #1,D0
|
||||
move.w D0,(A0)
|
||||
blt.s AllDoneCompletionRoutine
|
||||
|
||||
move.w D0,D1 ; D0 = idx, D1 = offset
|
||||
mulu.w #ATBOOT_BLK_SIZE,D1
|
||||
|
||||
lea cannedWDS+20,A1
|
||||
clr.w -(A1)
|
||||
|
||||
lea Blob,A0
|
||||
lea (A0,D1),A0
|
||||
move.l A0,-(A1)
|
||||
move.w #ATBOOT_BLK_SIZE,-(A1)
|
||||
|
||||
lea gABPImageReply,A0
|
||||
move.w D0,4(A0)
|
||||
move.l A0,-(A1)
|
||||
move.w #6,-(A1)
|
||||
|
||||
lea gABPAddress,A0
|
||||
move.l A0,-(A1)
|
||||
clr.w -(A1)
|
||||
|
||||
move.b 16(A0),7(A0) ; repair the address struct
|
||||
|
||||
lea gABPReplyParamBlk,A0
|
||||
move.l A1,$1E(A0) ; wdsPointer
|
||||
lea ImageReplyCompletionRoutine,A1
|
||||
move.l A1,$C(A0) ; ioCompletion
|
||||
move.w #-10,$18(A0) ; ioRefNum = .MPP
|
||||
move.w #246,$1A(A0) ; csCode = writeDDP
|
||||
move.b gNameTableEntry+7,$1C(A0); our socket
|
||||
move.b #1,$1D(A0) ; set checksumFlag
|
||||
dc.w $A404 ; _Control ,async
|
||||
rts
|
||||
|
||||
|
||||
AllDoneCompletionRoutine
|
||||
lea gABPBusyFlag,A0
|
||||
clr.b (A0)
|
||||
rts
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
if 0 ; Cool routine, don't need it because we inline the payload
|
||||
GetMyResourceID ; returns it in D0
|
||||
link A6,#-262
|
||||
lea ServerHeader,A0
|
||||
dc.w $A528 ; _RecoverHandleSys
|
||||
move.l A0,-(SP) ; handle to this resource
|
||||
pea -262(A6) ; place for ID
|
||||
pea -260(A6) ; place for type
|
||||
pea -256(A6) ; place for name
|
||||
dc.w $A9A8 ; _GetResInfo
|
||||
move.w -262(A6),D0
|
||||
unlk A6
|
||||
rts
|
||||
endif
|
||||
|
||||
Blob
|
||||
incbin 'Client.bin.summed' ; built from Client.a
|
||||
BlobEnd
|
||||
|
33
snefru_hash.py
Normal file → Executable file
33
snefru_hash.py
Normal file → Executable file
@ -1,3 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import struct
|
||||
|
||||
SBOXES = [
|
||||
@ -354,25 +356,20 @@ def snefru(inbytes):
|
||||
|
||||
|
||||
def append_snefru(x):
|
||||
if len(x) % 512 == 0:
|
||||
maybe_already = snefru(x[:-64])
|
||||
if x[-16:] == maybe_already: return x
|
||||
|
||||
while len(x) % 512 != 512 - 64: x += b'\0'
|
||||
the_hash = snefru(x)
|
||||
while len(x) % 512 != 512 - 16: x += b'\0'
|
||||
x += the_hash
|
||||
|
||||
return x
|
||||
while len(x) % 64: x += b'\0' # this is a requirement of the hash
|
||||
return x + bytes(48) + snefru(x)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
for p in sys.argv[1:]:
|
||||
try:
|
||||
x = open(p, 'rb').read()
|
||||
y = append_snefru(x)
|
||||
if x != y: open(p, 'wb').write(x)
|
||||
import argparse
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
parser = argparse.ArgumentParser(description='Append snefru hash to file')
|
||||
parser.add_argument('input')
|
||||
parser.add_argument('output')
|
||||
parser.add_argument('--align', type=int, default=64, action='store', help='align the final product to a multiple of this (must be multiple of 64)')
|
||||
|
||||
args = parser.parse_args()
|
||||
x = open(args.input, 'rb').read()
|
||||
while len(x) % args.align != args.align - 64: x += b'\0'
|
||||
x = append_snefru(x)
|
||||
open(args.output, 'wb').write(x)
|
||||
|
34
twovmacs.bash
Executable file
34
twovmacs.bash
Executable file
@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
|
||||
trap 'killall minivmac' SIGINT SIGTERM EXIT
|
||||
|
||||
n=0
|
||||
for args; do
|
||||
Mini\ vMac\ Classic.app/Co*/Ma*/* $args &
|
||||
vmacs="$vmacs $!"
|
||||
done
|
||||
|
||||
vmacs="{`echo $vmacs | tr ' ' ','`}"
|
||||
echo $vmacs
|
||||
|
||||
osascript <<END
|
||||
set n to 0
|
||||
set allpids to $vmacs
|
||||
repeat with mypid in allpids
|
||||
repeat
|
||||
tell application "System Events" to set allmatch to (every process whose unix id is mypid)
|
||||
if (count of allmatch) > 0 then exit repeat
|
||||
delay 0.1
|
||||
end repeat
|
||||
|
||||
tell application "System Events" to tell (first process whose unix id is mypid)
|
||||
tell window 1
|
||||
set position to {(item 1 of (position as list)) + (n - ((count of allpids) - 1) / 2) * 1.02 * (item 1 of (size as list)), (item 2 of (position as list))}
|
||||
end tell
|
||||
set frontmost to true
|
||||
end tell
|
||||
set n to n + 1
|
||||
end repeat
|
||||
END
|
||||
|
||||
wait < <(jobs -p)
|
Loading…
Reference in New Issue
Block a user