New state-machine-based boot somewhat works

(For requests under 32b)
This commit is contained in:
Elliot Nunn 2021-02-16 22:05:18 +08:00
parent 286ca935bf
commit 4a44f288c0
3 changed files with 201 additions and 141 deletions

View File

@ -200,7 +200,7 @@ while 1:
# // while others are stored on disk by the boot server
# typedef struct
# {
# char serverName[serverNameLength]; // server name to boot off of
# char serverName[serverNameLength]; // server name to boot off of
# char serverZone[zoneNameLength]; // and the zone it lives in
# char serverVol[volNameLength]; // the volume name
# short serverAuthMeth; // authentication method to use (none, clear txt, rand)
@ -279,7 +279,7 @@ while 1:
# break # wait for another request, you mofo!
elif boot_type == 128:
boot_seq, boot_blkoffset, boot_blkcnt, boot_imgname = struct.unpack_from('>HLL32p', data)
boot_seq, boot_blkoffset, boot_blkcnt = struct.unpack_from('>HLL', data); boot_imgname = b'A608.dsk'
boot_imgname = boot_imgname.decode('mac_roman')
for blk in range(boot_blkoffset, boot_blkoffset + boot_blkcnt):
thisblk = image2dict[boot_imgname][blk*512:blk*512+512]
@ -287,7 +287,7 @@ while 1:
dest_node=llap_src_node, dest_socket=ddp_src_socket,
src_node=99, src_socket=99,
proto_type=10,
data=struct.pack('>BBHL', 129, 1, boot_seq, blk-boot_blkoffset) + thisblk
data=struct.pack('>BBH', 129, blk-boot_blkoffset, boot_seq) + thisblk
),
(MCAST_ADDR, MCAST_PORT))

View File

@ -1,6 +1,8 @@
myUnitNum equ 52
myDRefNum equ ~myUnitNum
serverBufSize equ 32
Code
cmp.l #1,4(SP)
@ -41,14 +43,14 @@ getBootBlocks
move.w D0,7(A0) ; network
; Patch the disk image name and size into the DRVR code
bsr BootPicker ; A0 = pstring ptr
lea gNumBlks-DrvrBase(A2),A1
move.l -4(A0),(A1) ; the size is underneath the pstring ptr
move.l -4(A0),D6 ; D6 = block count, needed for DQE
lea gQFilename-DrvrBase(A2),A1 ; A1 = dest
moveq.l #1,D0
add.b (A0),D0
dc.w $A22E ; _BlockMoveData
; bsr BootPicker ; A0 = pstring ptr
; lea gNumBlks-DrvrBase(A2),A1
; move.l -4(A0),(A1) ; the size is underneath the pstring ptr
; move.l -4(A0),D6 ; D6 = block count, needed for DQE
; lea gQFilename-DrvrBase(A2),A1 ; A1 = dest
; moveq.l #1,D0
; add.b (A0),D0
; dc.w $A22E ; _BlockMoveData
; Install the driver in the unit table
move.l #myDRefNum,D0
@ -262,14 +264,10 @@ DrvrBase
g
gNumBlks dc.l 0 ; the source of all truth
gMyDCE dc.l 0
gQuery
gQTypeVer dc.w 0 ; part of gQuery
gQSeqNum dc.w 0 ; part of gQuery
gQOffset dc.l 0 ; part of gQuery: block offset
gQLen dc.l 0 ; part of gQuery: block count
gQFilename dcb.b 32 ; part of gQuery ; set by the init code
gQueryEnd
gWDS dcb.b 2+4+2+4+2
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
@ -304,55 +302,72 @@ DrvrPrime
ror.l #5,D0
move.l D0,$2E(A0) ; ioPosOffset = D0 = byte_offset/512
; Increment the protocol sequence counter.
lea gQSeqNum,A2
addq.w #1,(A2)
; Kick the state machine.
movem.l A0-A1,-(SP)
bsr.s DrvrTransmitRequest ; takes A0 as PB argument
movem.l (SP)+,A0-A1
; Return with a "pending" ioResult.
move.w #1,$10(A0) ; ioResult = pending. We will return without an answer.
rts
; 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"
DrvrTransmitRequest ; On entry, A0=pb. Trashes A0-A4/D0 so be careful. Uses our globals extensively.
lea g,A2
move.b gSaveAddr-g(A2),gAddr-g(A2) ; reasonable way to copy the address struct, I guess
move.l gSaveAddr-g+1(A2),gAddr-g+1(A2)
move.l gSaveAddr-g+5(A2),gAddr-g+5(A2)
move.l gSaveAddr-g+9(A2),gAddr-g+9(A2)
move.w gSaveAddr-g+13(A2),gAddr-g+13(A2)
move.b gSaveAddr-g+15(A2),gAddr-g+15(A2)
move.w #$8001,gQTypeVer-g(A2) ; Means a polite request, v1
; Some wrappers for the routines below...
DrvrSendRead
; Called from Prime routine: PB/DCE in A0/A1 must be preserved
lea gExpectHdr+2,A2
addq.w #1,(A2) ; packet filter sequence word
lea gProgress,A2
clr.l (A2)
lea gSaveAddr,A2
lea gAddr,A3
move.b (A2)+,(A3)+ ; copy the address struct, dang it!
move.l (A2)+,(A3)+
move.l (A2)+,(A3)+
move.l (A2)+,(A3)+
move.w (A2)+,(A3)+
move.b (A2)+,(A3)+
lea gQuery,A2
move.w #$8000,(A2)+ ; Means a polite request
move.w gExpectHdr+2,(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,gQOffset-g(A2) ; -> "offset" field of request
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,gQLen-g(A2) ; -> "length" field of request
move.l D0,(A2)+ ; -> "length" field of request
lea gAddr,A3
clr.w gWDS-g(A2) ; WDS+0 reserved field
move.l A3,gWDS-g+2(A2) ; WDS+2 pointer to address struct
lea gQuery,A3
move.w #gQueryEnd-gQuery,gWDS-g+6(A2) ; WDS+6 length of second entry
move.l A3,gWDS-g+8(A2) ; WDS+8 pointer to second entry
clr.w gWDS-g+12(A2) ; WDS+10 zero to end the list
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 pointer/length
pea gQuery
move.l (SP)+,(A2)+
clr.w (A2)+ ; WDS: end with zero
move.l A0,-(SP)
lea gMyPB,A0
.retry
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)
@ -360,25 +375,40 @@ DrvrTransmitRequest ; On entry, A0=pb. Trashes A0-A4/D0 so be careful. Uses our
pea gWDS
move.l (SP)+,$1E(A0) ; wdsPointer to our WriteDataStructure
dc.w $A404 ; _Control ,async
cmp.w #-91,$10(A0) ; This happens when the RAM MPP replaces the ROM one
bne.s .noNeedToRetry ; so we need to reopen socket #10, then retry...
bsr DrvrClearBlock
move.w #-10,$18(A0) ; ioRefNum = .MPP
move.w #248,$1A(A0) ; csCode = openSkt
move.b #10,$1C(A0) ; socket = 10 (hardcoded)
pea DrvrSockListener
move.l (SP)+,$1E(A0) ; listener
dc.w $A004 ; _Control (synchronous, better hope it doesn't deadlock!)
bra.s .retry
.noNeedToRetry
move.w $10(A0),D0 ; for our caller to figure out what went wrong
move.l (SP)+,A0
rts
DrvrDidSendRead
; Called as completion routine: PB/result in A0/D0, must preserve all registers other than A0/A1/D0-D2
lea gExpectHdr,A2
move.w #$8100,(A2) ; Enable packet reception
rts ; TODO: set up a timeout/retry task
DrvrSendFirstWrite
; yech...
; fall through...
DrvrSendWrite
nop
DrvrDidSendWrite
nop
DrvrClearBlock
move.w #$32/2-1,D0
.loop clr.w (A0)+
@ -386,87 +416,123 @@ DrvrClearBlock
lea -$32(A0),A0
rts
DrvrSockListener ; works closely with the Prime routine
; 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.
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
move.l -4(A3),D0
if 0
lea gExpectHdr,A5 ; Check the packet header
move.l (A5),D1
eor.l D0,D1
swap D1
clr.b D1
bne.s DrvrTrashPacket
endif
btst #25,D0
bne.s DrvrDidReceiveWrite
DrvrDidReceiveRead
swap D0 ; Confirm that this packet is not a dupe
and.w #32-1,D0
lea gProgress,A5
move.l (A5),D2
bset.l D0,D2
bne.s DrvrTrashPacket
move.l D2,(A5)
move.l gMyDCE,A5
btst.b #7,5(A5) ; Check drvrActive flag (is there an outstanding request?)
beq .trashpacket
cmp.b #10,-1(A3) ; DDP protocol type better be ATBOOT
bne.s .trashpacket
moveq.l #8,D3
jsr (A4) ; Read 8 bytes
cmp.w #$8101,-8(A3) ; Check protocol and version
bne.s .trashpacket
lea gQSeqNum,A5 ; Check packet sequence number
move.w (A5),D2
cmp.w -6(A3),D2
bne.s .trashpacket
move.l gMyDCE,A5 ; Get current param block
move.l 6+2(A5),A5 ; dCtlQHdr.qHead
move.l $28(A5),D2 ; keep this in D2 because we will use it in a sec
move.l -4(A3),D3
lsl.l #4,D3
lsl.l #5,D3
cmp.l D3,D2 ; ioActCount = this offset?
bne.s .outoforder
move.l $28(A5),D2 ; increment ioActCount, check ioReqCount
add.l #512,D2
move.l D2,$28(A5)
sub.l $24(A5),D2 ; D2 gets saved across the coming ReadRest call
move.l $20(A5),A3 ; ioBuffer
add.l D2,A3 ; A3 = ioBuffer + ioReqDone
move.l #512,D3 ; D3 = bytes to read = 512
add.l D3,D2 ; D2 = new ioReqDone = old ioReqDone + 512
jsr 2(A4) ; ReadRest
; From this point on, use A1 instead of A5
asl.w #5,D0
asl.w #4,D0
add.w D0,A3 ; A3 = ioBuffer + thisPackOffset
move.l #512,D3 ; D3 = size
jsr 2(A4) ; ReadRest!
move.l gMyDCE,A1 ; Get current param block
move.l 6+2(A1),A1 ; dCtlQHdr.qHead
tst.l D2
beq.s .ioDone
rts
move.l D2,$28(A1) ; update ioActCount
cmp.l $24(A1),D2 ; does it equal ioReqCount?
bne.s .rts
lea gMyDCE,A1
move.l (A1),A1
.ioDone lea gExpectHdr,A1
clr.w (A1)
move.l gMyDCE,A1
moveq.l #0,D0
move.l $8FC,A0 ; jIODone (D0 = result, A1 = DCE)
jmp (A0)
.outoforder ; apparent dropped packet, so please resend
moveq.l #0,D3
jsr 2(A4) ; ReadRest nothing
movem.l A0-A4,-(SP)
move.l gMyDCE,A0 ; Get current param block
move.l 6+2(A0),A0 ; dCtlQHdr.qHead
bsr DrvrTransmitRequest
movem.l (SP)+,A0-A4
DrvrDidReceiveWrite
bsr DrvrDidReceiveWrite
rts
.trashpacket
DrvrTrashPacket
moveq.l #0,D3
jmp 2(A4) ; ReadRest nothing
.rts
rts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DrvrControl
@ -503,7 +569,7 @@ status_fmtLstCode ; tell them about our size
status_drvStsCode ; tell them about some of our flags
move.w #0,$1C(A0) ; csParam[0..1] = track no (0)
move.l #$80080000,$1C+2(A0) ; csParam[2..5] = same flags as dqe
move.w #0,$10(A0) ; ioResult = noErr
bra DrvrFinish

View File

@ -90,26 +90,20 @@ while 1:
print('malformed short packet %r' % data.hex())
continue
boot_type, boot_vers = struct.unpack_from('>BB', data)
data = data[2:]
if boot_type == 1:
print(' ATBOOT "syn"', data.hex())
print(' ATBOOT "syn"', data[2:].hex())
elif boot_type == 2:
print(' ATBOOT "ack"', data.hex())
print(' ATBOOT "ack"', data[2:].hex())
elif boot_type == 3:
print(' ATBOOT image request', data.hex())
print(' ATBOOT image request', data[2:].hex())
elif boot_type == 4:
print(' ATBOOT image reply', data.hex())
elif boot_type == 128:
a, b, c, d = struct.unpack_from('>HLL32p', data)
d = d.decode('mac_roman')
print(f' ATBOOT Elliot block seq={hex(a)} blkIdx={hex(b)} byteLen={hex(c)} img={repr(d)}')
elif boot_type == 129:
a, b = struct.unpack_from('>HL', data)
print(f' ATBOOT Elliot reply seq={hex(a)} relByteIdx={hex(b)}')
print(' ATBOOT image reply', data[2:].hex())
elif boot_type >= 0x80:
print(' ATBOOT Elliot', data.hex())
else:
print(' ATBOOT', boot_type, boot_vers, data.hex())
print(' ATBOOT ???', boot_type, boot_vers, data[2:].hex())
else:
print('Totally unknown DDP type', ddp_proto_type)
print('Totally unknown DDP type', ddp_proto_type)
print(' ' + data.hex())