NetBoot/ServerDRVR.a
2021-04-12 21:03:35 +08:00

319 lines
12 KiB
Plaintext

; I implement a "BootServer" on the AppleTalk network
; The Server Desk Accessory installs me in the system heap and opens me
ServerHeader
dc.w $4000 ; dNeedLock
dc.w 0 ; delay
dc.w 0 ; evt mask
dc.w 0 ; menu
dc.w ServerOpen-ServerHeader
dc.w 0 ; no Prime
dc.w 0 ; no Control
dc.w 0 ; no Status
dc.w ServerClose-ServerHeader
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_SIZE equ 256 ; multiple of 64, must match Makefile
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
gABPWriteDataStruct ; For writeDDP control calls (gathers buffers into one pkt)
dcb.b 20 ; 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
; we could put an arbitrary 568b "user record" here
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
movem.l A0/A1,-(SP)
; Try *once*. AT might come up later but not because of us.
bsr TryOpenMPP
bne.s .couldNotOpenAppleTalk
bsr Listen
.couldNotOpenAppleTalk
; Be ready for the LAP Manager to open/close .MPP
bsr RegisterForTransitions
movem.l (SP)+,A0/A1
clr.w $10(A0) ; return noErr
rts
ServerClose
movem.l A0/A1,-(SP)
bsr StopListening
movem.l (SP)+,A0/A1
clr.w D0 ; return noErr
rts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TryOpenMPP
; Checking whether the "B" printer port is configured for AppleTalk is an
; application responsibility on 64K ROM machines (as documented in IM),
; but 128K ROM and later machines move this check to AppleTalk itself,
; for example to allow other AppleTalk interfaces.
tst.w $28E ; ROM85 positive?
bgt.s .returnYes ; it is always safe to try on 128K machines
move.b $1FB,D0 ; SPConfig & 0xF == useFree (0) or useATalk (1)?
and.b #$F,D0
sub.b #1,D0
ble.s .returnYes
move.b $291,D0 ; PortBUse negative or useATalk (1)?
blt.s .returnYes
cmp.b #1,D0
beq.s .returnYes
.returnNo moveq #1,D0
rts
.returnYes
sub #$32,SP
lea .MPP,A0
move.l A0,$12(SP) ; IOFileName
clr.b $1B(SP) ; IOPermssn
move.l SP,A0
dc.w $A000 ; _Open
tst.w $10(A0) ; IOResult: check it
add #$32,SP
rts
.MPP dc.b 4, '.MPP', 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
RegisterForTransitions rts ; register for the LAP Manager queue if possible
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Listen
sub #$32,SP ; OPEN DDP SOCKET
move.w #-10,$18(SP) ; ioRefNum = MPP
move.w #248,$1A(SP) ; csCode = openSkt
clr.b $1C(SP) ; socket = auto-assign
pea SocketListener
move.l (SP)+,$1E(SP) ; listener
move.l SP,A0
dc.w $A004 ; _Control
lea gNameTableEntry+7,A0 ; lowest byte of AddrBlock
move.b $1C(SP),(A0) ; save socket number there
add #$32,SP
sub #$32,SP ; ADVERTISE NBP SERVICE
move.w #-10,$18(SP) ; ioRefNum = MPP
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 gNameTableEntry
move.l (SP)+,$1E(SP) ; entityPtr = our NTE
move.b #1,$22(SP) ; verifyFlag = do
move.l SP,A0
dc.w $A004 ; _Control
add #$32,SP
rts
StopListening
sub #$32,SP ; REMOVE NBP SERVICE
move.w #-10,$18(SP) ; ioRefNum = MPP
move.w #252,$1A(SP) ; csCode = removeName
pea gNameTableEntry
move.l (SP)+,$1E(SP) ; entityPtr = our NTE
move.l SP,A0
dc.w $A004 ; _Control
add #$32,SP
sub #$32,SP ; CLOSE DDP SOCKET
move.w #-10,$18(SP) ; ioRefNum = MPP
move.w 257,$1A(SP) ; csCode = closeSkt
move.b gNameTableEntry+7,$1C(SP); socket = auto-assign
move.l SP,A0
dc.w $A004 ; _Control
add #$32,SP
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 gABPWriteDataStruct+14,A1
clr.w -(A1)
lea gABPUserReply,A0
move.l A0,-(A1)
move.w #18,-(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 AllDoneCompletionRoutine,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 gABPWriteDataStruct+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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Blob
incbin 'Client.bin.summed' ; built from Client.a
BlobEnd