Up to the point where I get stuck in AccessBT

This commit is contained in:
Elliot Nunn 2020-07-04 13:53:21 +08:00
parent 9d4022b833
commit ebe262e429
30 changed files with 707 additions and 15 deletions

8
IM-D ParamBlock diagram Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>URL</key>
<string>http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/Devices/Devices-13.html</string>
</dict>
</plist>

View File

@ -4,8 +4,464 @@
# Adapted from: http://chaos.weblogs.us/archives/164
import socket
import os
from os import path
import struct
ANY = "0.0.0.0"
import time
# ; the below code might come in handy later on, when we support executable boot blocks.
# CodeToUncorruptTheBootBlocks:
# move.l (SP),A1
# sub.l #8,A1 ; What will be the start address of the boot blocks??
# lea BigDiskImage,A0
# move.l #1024,D0
# dc.w 0xA02E ; BlockMove
# sub.l #6,(SP) ; Re-run the code that called us; it will be real boot blocks now!
# rts
my_unique_ltoudp_id = b'El' + (os.getpid() & 0xFFFF).to_bytes(2, 'big')
disk_image = open(path.join(path.dirname(path.abspath(__file__)), 'systools607.dsk'), 'rb').read()
def assemble(the_code):
with open('/tmp/vasm.bootblocks', 'w') as f:
f.write(the_code)
assembler = path.join(path.dirname(path.abspath(__file__)), 'vasm-1/vasmm68k_mot')
os.system(f'{assembler} -quiet -Fbin -pic -o /tmp/vasm.bootblocks.bin /tmp/vasm.bootblocks')
with open('/tmp/vasm.bootblocks.bin', 'rb') as f:
return f.read()
# typedef short (*j_code)( short command, # SP+4 (sign-extend to long)
# DGlobals *g, # SP+8
# int **var1, # SP+12
# int **var2); # SP+16
# We return our OSErr in d0, and leave the 16 bytes of arguments on the stack for the caller to clean
image = assemble(f'''
myUnitNum equ 52
myDRefNum equ ~myUnitNum
cmp.l #1,4(SP)
beq getBootBlocks
cmp.l #2,4(SP)
beq getSysVol
cmp.l #3,4(SP)
beq mountSysVol
move.l #-1,d0
rts
getBootBlocks
lea DiskImage,A0
move.l 8(SP),A1 ; Inside the global struct...
add.l #$BA,A1 ; ...is an element for the structured part of the boot blocks
move.l #138,D0 ; ...of this length
dc.w $A02E ; BlockMove
bra return
getSysVol
; Our register conventions:
; A2 = fake dqe; A3 = dce; A4 = copied driver
link A6,#-$32
movem.l A2-A4/D3,-(SP)
; Now I copy all this stuff under BufPtr (because this current location will disappear)
sub.l #(BufPtrCopyEnd-BufPtrCopy),$10C ; BufPtr
move.l $10C,A4 ; ... into A4
; Copy the driver and image as one chunk
lea BufPtrCopy,A0
move.l A4,A1
move.l #(BufPtrCopyEnd-BufPtrCopy),D0
dc.w $A02E ; BlockMove
; Install the driver in the unit table
move.l #myDRefNum,D0
dc.w $A43D ; _DrvrInstall ReserveMem
bne error
; Get DCE handle of installed driver
move.l $11C,A0 ; UTableBase
add.l #myUnitNum*4,A0
move.l (A0),A3
; Lock it down
move.l A3,A0
dc.w $A029 ; _HLock
; Populate the empty DCE that DrvrInstall left us
move.l (A3),A0 ; A0 = dce ptr
move.l A4,0(A0) ; dCtlDriver is pointer (not hdl)
move.w 0(A4),D0 ; drvrFlags
and.w #~$0040,D0 ; Clear dRAMBased bit (to treat dCtlDriver as a pointer)
move.w D0,4(A0) ; dCtlFlags
; Copy these other values that apparently the Device Mgr forgets
move.w 2(A4),$22(A0) ; drvrDelay to dCtlDelay
move.w 4(A4),$24(A0) ; drvrEMask to dCtlEMask
move.w 6(A4),$26(A0) ; drvrMenu to dCtlMenu
; Open the driver
lea -$32(A6),A0
bsr clearblock
lea DrvrNameString,A1
move.l A1,$12(A0) ; IOFileName
dc.w $A000 ; _Open
bne error
; Attempt to read from the driver
; lea -$32(A6),A0
; bsr clearblock
; move.w #myDRefNum,$18(A0) ; ioRefNum
; move.l #$200,$24(A0) ; ioByteCount
; dc.w $A002
; bne error
; Create a DQE
move.l #$16,D0
dc.w $A71E ; _NewPtr ,Sys,Clear
add.l #4,A0 ; has some cheeky flags at negative offset
move.l A0,A2
; Point our caller to the fake dqe
move.l 4+12(A6),A1
move.l A2,(A1)
; Find a free drive number (nicked this code from BootUtils.a:AddMyDrive)
LEA $308,A0 ; [DrvQHdr]
MOVEQ #4,D3 ; start with drive number 4
CheckDrvNum
MOVE.L 2(A0),A1 ; [qHead] start with first drive
CheckDrv
CMP.W 6(A1),D3 ; [dqDrive] does this drive already have our number?
BEQ.S NextDrvNum ; yep, bump the number and try again.
CMP.L 6(A0),A1 ; [qTail] no, are we at the end of the queue?
BEQ.S GotDrvNum ; if yes, our number's unique! Go use it.
MOVE.L 0(A1),A1 ; [qLink] point to next queue element
BRA.S CheckDrv ; go check it.
NextDrvNum
; this drive number is taken, pick another
ADDQ.W #1,D3 ; bump to next possible drive number
BRA.S CheckDrvNum ; try the new number
GotDrvNum
; Populate the DQE
move.l #$80080000,-4(A2) ; secret flags, see http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/Files/Files-112.html
move.w #1,4(A2) ; qType
move.w #(DiskImageEnd-DiskImage)&$FFFF,$C(A2) ; dQDrvSz
move.w #(DiskImageEnd-DiskImage)>>16,$E(A2) ; dQDrvSz2
move.w #0,$A(A2) ; dQFSID should be for a native fs
; Into the drive queue (which will further populate the DQE)
move.l A2,A0 ; A0 = DQE ptr
move.w D3,D0
swap.w D0 ; D0.H = drive number
move.w #myDRefNum,D0 ; D0.L = driver refnum
dc.w $A04E ; _AddDrive
bne error
; Save this most precious knowledge for later
lea gDriveNum,A0
move.w D3,(A0)
; Clean up our stack frame
movem.l (SP)+,A2-A4/D3
unlk A6
bra return
mountSysVol
link A6,#-$32
movem.l A2-A4/D3,-(SP)
lea gDriveNum,A0
move.w (A0),D3
; Make our call able to work! (Fuck, this is ugly)
lea NaughtyFSQHSave,A0
move.l $3E2,(A0)
lea NaughtyFSQHKiller,A0
move.l A0,$3E2 ; Disable FSQueueSync
; Try to mount the volume!
lea -$32(A6),A0
bsr clearblock
; FSQueue ourself!
; move.w #$A00F,D1
; move.l $2AE,A1 ; ROMBase
; add.l #$42E8,A1
; jsr (A1)
; MountVol and pray
move.w #7,$16(A0) ; ioVRefNum = ioDrvNum = the drive number
dc.w $A00F ; _MountVol
; Tell Elliot that we made it through
move.w #$1234,D0
dc.w $a9c9
movem.l (SP)+,A2-A4/D3
unlk A6
bra return
return
move.l #0,d0
rts
error
move.w #$DDDD,D0
dc.w $A9C9
clearblock
clr.w $0(A0)
clr.w $2(A0)
clr.w $4(A0)
clr.w $6(A0)
clr.w $8(A0)
clr.w $a(A0)
clr.w $c(A0)
clr.w $e(A0)
clr.w $10(A0)
clr.w $12(A0)
clr.w $14(A0)
clr.w $16(A0)
clr.w $18(A0)
clr.w $1a(A0)
clr.w $1c(A0)
clr.w $1e(A0)
clr.w $20(A0)
clr.w $22(A0)
clr.w $24(A0)
clr.w $26(A0)
clr.w $28(A0)
clr.w $2a(A0)
clr.w $2c(A0)
clr.w $2e(A0)
clr.w $30(A0)
rts
NaughtyFSQHSave
dc.l 0
NaughtyFSQHKiller
move.l A0,-(SP)
lea NaughtyFSQHSave,A0
move.l (A0),$3E2
move.l (SP)+,A0
add.l #4,SP
rts
DrvrNameString
dc.b 11, ".netRamDisk"
gDriveNum
dc.w 0
; code on this side is for start only, and stays in the netBOOT driver globals until released
BufPtrCopy
; code on this side gets copied beneath BufPtr (is that the best place??)
; Shall we start with a driver?
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
dc.b 11, ".netRamDisk"
; a0=iopb, a1=dce on entry to all of these...
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DrvrOpen
move.w #0,$10(A0) ; ioResult
rts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DrvrPrime
movem.l A0-A4/D0-D4,-(SP)
move.l A0,A2 ; ParamBlk
move.l A1,A3 ; DCE
cmp.b #2,$7(A2) ; ioTrap == aRdCmd
bne.s notRead
; D0 = number of bytes to read
move.l $24(A2),D0 ; ioReqCount
move.l D0,$28(A2) ; -> ioActCount
; D1 = source offset
clr.l D1
cmp.b #1,$2C(A2) ; ? ioPosMode == fsFromStart
beq.s dontAddMark
add.l $10(A3),D1 ; add dCtlPosition
dontAddMark
cmp.b #3,$2C(A2) ; ? ioPosMode == fsFromMark
bne.s dontAddOffset
add.l $2E(A2),D1 ; add ioPosOffset
dontAddOffset
; Advance the pointer
move.l D0,D2
add.l D1,D2 ; calculate new position
move.l D2,$10(A3) ; -> dCtlPosition
move.l D2,$2E(A2) ; -> ioPosOffset
; Do the dirty
lea DiskImage,A0
add.l D1,A0
move.l $20(A2),A1 ; ioBuffer
dc.w $A02E ; BlockMove
move.w #0,$10(A2) ; ioResult
bra primeFinish
notRead
move.w #$2222,D0
dc.w $A9C9
cmp.b #3,7(A0) ; ioTrap == aRdCmd
primeFinish
movem.l (SP)+,A0-A4/D0-D4
bra DrvrFinish
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DrvrControl
move.w #$2222,D0
dc.w $A9C9
move.w #0,$10(A0) ; ioResult
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 A2,-(SP)
move.w #1,$1C(A0)
move.w $1C+4(A0),A2
move.l #(DiskImageEnd-DiskImage),0(A2)
move.l #$40000000,4(A2)
move.l (SP)+,A2
move.w #0,$10(A0) ; ioResult = statusErr
bra DrvrFinish
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
status_unknown
move.w $1A(A0),D0 ; dodgy, for debugging
add.w #$3000,D0
dc.w $A9C9
move.w #-18,$10(A0) ; ioResult
bra DrvrFinish
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DrvrClose
move.w #$4444,D0
dc.w $A9C9
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
DiskImage {chr(10).join(' dc.b ' + str(x) for x in disk_image)}
DiskImageEnd
BufPtrCopyEnd
''')
image = image.ljust(512*20)
ANY = "0.0.0.0"
MCAST_ADDR = "239.192.76.84"
MCAST_PORT = 1954
@ -25,15 +481,223 @@ status = sock.setsockopt(socket.IPPROTO_IP,
socket.IP_ADD_MEMBERSHIP,
socket.inet_aton(MCAST_ADDR) + socket.inet_aton(ANY))
# setblocking(0) is equiv to settimeout(0.0) which means we poll the socket.
# But this will raise an error if recv() or send() can't immediately find or send data.
# sock.setblocking(0)
# Wrap up in all sorts of crap...
def mk_ddp(dest_node, dest_socket, src_node, src_socket, proto_type, data):
# Wrap into DDP datagram
data = struct.pack('>HBBB', len(data) + 5, dest_socket, src_socket, proto_type) + data
# Wrap into LLAP packet
data = struct.pack('>BBB', dest_node, src_node, 1) + data
# Wrap the novel LToUDP header
data = my_unique_ltoudp_id + data
return data
def pstring(x):
try:
x = x.encode('mac_roman')
except AttributeError:
pass
return bytes([len(x)]) + x
while 1:
try:
data, addr = sock.recvfrom(1024)
except socket.error as e:
pass
data, addr = sock.recvfrom(1024)
# Okay... LToUDP parsing here. Let's start with the LLAP packet.
# Man, chaining protocols is hard. This will inevitably require a rewrite.
# Be careful to keep this as one cascading thing...
if len(data) < 4: continue
if data.startswith(my_unique_ltoudp_id): continue
data = data[4:] # Trim down to LLAP packet (not "frame")
if len(data) < 3: continue
llap_dest_node = data[0]
llap_src_node = data[1]
llap_proto_type = data[2]
data = data[3:] # Trim down to LLAP payload
# Try to extract a DDP header, which is all we want!
if llap_proto_type == 1:
# ddp, short
if len(data) < 5: continue
ddp_len, ddp_dest_socket, ddp_src_socket, ddp_proto_type = struct.unpack_from('>HBBB', data)
data = data[5:ddp_len]
elif llap_proto_type == 2:
# ddp, long (what should we do with this extra information?)
if len(data) < 13: continue
ddp_len, ddp_cksum, ddp_dest_net, ddp_src_net, ddp_dest_node, ddp_src_node, ddp_dest_socket, ddp_src_socket = struct.unpack_from('>4H5B', data)
ddp_hop_count = (ddp_len >> 10) & 0xF
ddp_len &= 0x3FF
data = data[13:ddp_len]
else:
print("From: ", addr)
print("Data: ", data)
# llap control packet -- can probably ignore!
continue
print(f'datagram {llap_src_node}:{ddp_src_socket}->{llap_dest_node}:{ddp_dest_socket}', end=' ')
if ddp_proto_type == 2:
# Name Binding Protocol
if len(data) < 2: continue
nbp_func = data[0] >> 4
nbp_tuple_cnt = data[0] & 0xF
nbp_id = data[1]
data = data[2:]
nbp_tuples = []
while data and len(nbp_tuples) < nbp_tuple_cnt:
if len(data) < 5: break
this_tuple = list(struct.unpack_from('>HBBB', data))
data = data[5:]
for i in range(3):
# This should be coded more defensively, perhaps using exceptions
this_tuple.append(data[1:1+data[0]].decode('mac_roman'))
data = data[1+data[0]:]
nbp_tuples.append(tuple(this_tuple))
print('nbp func', nbp_func, 'id', nbp_id)
for t in nbp_tuples:
print(f' net:node:sock={t[0]}:{t[1]}:{t[2]} enum={t[3]} object:type@zone={t[4]}:{t[5]}@{t[6]}')
if nbp_func == 2:
# NBP LkUp
if len(nbp_tuples) == 1 and nbp_tuples[0][5] == 'BootServer': # Looking for us
sock.sendto(mk_ddp(
dest_node=llap_src_node, dest_socket=ddp_src_socket,
src_node=99, src_socket=99,
proto_type=2,
data=bytes([0x31, nbp_id]) + # 3=LkUp-Reply, 1=number-of-tuples
struct.pack('>HBBB', # Pack the first tuple
0, # (My) Network number
99, # (My) Node ID
99, # (My) Socket number
0, # (My) Enumerator (i.e. which of many possible names for 99/99 is this?)
) +
pstring(nbp_tuples[0][5]) + pstring('BootServer') + pstring('*')
),
(MCAST_ADDR, MCAST_PORT))
elif ddp_proto_type == 10:
# Mysterious ATBOOT protocol
if len(data) < 2: continue
boot_type, boot_vers = struct.unpack_from('>BB', data)
data = data[2:]
# rbNullCommand EQU 0 ; ignore this one
# rbMapUser EQU 1 ; user record request
# rbUserReply EQU 2 ; user record reply
# rbImageRequest EQU 3 ; image request & bitmap
# rbImageData EQU 4 ; image data
# rbImageDone EQU 5 ; server done with current image
# rbUserRecordUpdate EQU 6 ; new user info to server
# rbUserRecordAck EQU 7 ; info received from server
if boot_type == 1:
# It might be neater to trim off the "type" and "version" fields a bit earlier
boot_machine_id, boot_timestamp, boot_username = struct.unpack_from('>HL 34p', data)
print(f'atboot type={boot_type}, vers={boot_vers}, machineID={boot_machine_id}, userName={boot_username}')
# // This defines a user record.
# // Some of these fields can be determined on the fly by the boot server,
# // while others are stored on disk by the boot server
# typedef struct
# {
# 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)
# unsigned long sharedSysDirID; // dir id of shared system folder
# unsigned long userDirID; // dir id of the user's private system folder
# unsigned long finderInfo[8]; // blessed folder dir id, startup folder dir id etc...
# unsigned char bootBlocks[138]; // see Inside Mac V-135
# unsigned short bootFlag; // server based flags
# unsigned char pad[306-18]; // <pd 5>pad to ddpMaxData
# }userRecord;
# // This defines the user record reply sent to workstations.
# typedef struct
# {
# unsigned char Command; /* record or image request command word */
# unsigned char pversion; /* version of boot protocol spoken by requestor */
# unsigned short osID; /* type and version of os */
# unsigned int userData; /* time stamp goes here */
# unsigned short blockSize; /* size of blocks we will send */
# unsigned short imageID; /* image ID */
# short result; /* error codes */
# unsigned int imageSize; /* size of image in blocks */
# userRecord userRec; /* tell user where to go */
# }BootPktRply;
# Ignore the silly user record. Just make the fucker work!
sock.sendto(mk_ddp(
dest_node=llap_src_node, dest_socket=ddp_src_socket,
src_node=99, src_socket=99,
proto_type=10,
data=struct.pack('>BBHLHHhL', 2, 1, boot_machine_id, boot_timestamp, 512, 0, 0, len(image) // 512).ljust(586, b'\0')
),
(MCAST_ADDR, MCAST_PORT))
elif boot_type == 3:
# It seems to want part of the boot image!
# print('it wants data', data)
# typedef struct
# {
# unsigned short imageID; /* */
# unsigned char section; /* "section" of the image the bitmap refers to */
# unsigned char flags; /* ??? */
# unsigned short replyDelay;
# unsigned char bitmap[512]; /* bitmap of the section of the image requested */
# }bir; // Boot Image Req
print('Boot Image Req')
if len(data) < 6: continue
boot_image_id, boot_section, boot_flags, boot_reply_delay = struct.unpack_from('>HBBH', data)
data = data[6:]
# Okay, pretty much just send the bits that were requested!
print('Sending blocks')
for blocknum in range(len(image) // 512):
if len(data) > blocknum // 8 and (data[blocknum // 8] >> (blocknum % 8)) & 1:
# typedef struct {
# unsigned char packetType; /* The command number */
# unsigned char packetVersion; /* Protocol version number */
# unsigned short packetImage; /* The image this block belongs to */
# unsigned short packetBlockNo; /* The block of the image (starts with 1) */
# unsigned char packetData[512];/* the data portion of the packet */
# } BootBlock,*BootBlockPtr;
# print(f' {blocknum}', end='', flush=True)
sock.sendto(mk_ddp(
dest_node=llap_src_node, dest_socket=ddp_src_socket,
src_node=99, src_socket=99,
proto_type=10,
data=struct.pack('>BBHH', 4, 1, boot_image_id, blocknum) + image[blocknum*512:blocknum*512+512]
),
(MCAST_ADDR, MCAST_PORT))
# time.sleep(0.5)
# break # wait for another request, you mofo!
elif boot_type > 4:
print(f'protocol10 type={boot_type} vers={boot_vers} contents={repr(data)}')
else:
print('unknown ddp payload', ddp_proto_type)

5
doeverything.bash Executable file
View File

@ -0,0 +1,5 @@
#!/bin/bash
trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
./NetBoot.py & cp xo.rom edit.rom && ./xo-rom-enable-netboot.py edit.rom && Mini\ vMac\ Classic.app/Contents/MacOS/minivmac edit.rom && kill %1

BIN
systools607.dsk Normal file

Binary file not shown.

BIN
vasm-1/obj/m68k_mot_atom.o Normal file

Binary file not shown.

BIN
vasm-1/obj/m68k_mot_cond.o Normal file

Binary file not shown.

BIN
vasm-1/obj/m68k_mot_cpu.o Normal file

Binary file not shown.

BIN
vasm-1/obj/m68k_mot_dwarf.o Normal file

Binary file not shown.

BIN
vasm-1/obj/m68k_mot_error.o Normal file

Binary file not shown.

BIN
vasm-1/obj/m68k_mot_expr.o Normal file

Binary file not shown.

Binary file not shown.

BIN
vasm-1/obj/m68k_mot_osdep.o Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
vasm-1/obj/m68k_mot_parse.o Normal file

Binary file not shown.

BIN
vasm-1/obj/m68k_mot_reloc.o Normal file

Binary file not shown.

BIN
vasm-1/obj/m68k_mot_supp.o Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
vasm-1/obj/m68k_mot_vasm.o Normal file

Binary file not shown.

BIN
vasm-1/vasmm68k_mot Executable file

Binary file not shown.

View File

@ -34,7 +34,7 @@ with open(sys.argv[1], 'r+b') as f:
garbage_location = 0x67046 # The MacsBug copyright string in the ROM Disk
# Make out own structure, as adapted from NetBoot.h
the_netboot_structure = struct.pack('>BBBB BB 8s 32s 8s H',
the_netboot_structure = struct.pack('>BBBB BB 16s 32s 8s H', # Note that "int"s are 4 bytes, somehow!
# First 4 bytes at PRAM+4
1, # char osType; /* preferred os to boot from */
1, # char protocol; /* preferred protocol to boot from */
@ -44,12 +44,16 @@ with open(sys.argv[1], 'r+b') as f:
# Now, the AppleTalk-protocol-specific part
0, # unsigned char nbpVars; /* address of last server that we booted off of */
0, # unsigned char timeout; /* seconds to wait for bootserver response */
b'', # unsigned int signature[4]; /* image signature */
b'', # unsigned int signature[4]; /* image signature */ Elliot notes: zeroes match anything!
b'Elliot', # char userName[31]; /* an array of char, no length byte */
b'volapuk', # char password[8]; /* '' */
0x7777, # short serverNum; /* the server number */
0xBABE, # short serverNum; /* the server number */
).ljust(72, b'\0')
# the_netboot_structure = bytearray(the_netboot_structure)
# for i, j in enumerate(range(54, len(the_netboot_structure))):
# the_netboot_structure[j] = i
# This is the guts of the NetBoot ReadPRAM procedure
f.seek(0x1DF08)
write_asm(f, f'''
@ -64,6 +68,17 @@ with open(sys.argv[1], 'r+b') as f:
f.seek(garbage_location)
f.write(the_netboot_structure)
# Do the dodgy... cancel signature validation!
f.seek(0x21A84)
while f.tell() < 0x21A98:
f.write(b'Nq') # nop
# Work around Mini vMac's cutesy many-drives hack (it steals out preferred drivenum of 4 from us)
f.seek(0x1DF51) # AddMyDrive: moveq #4,d9
f.write(b'\x10')
f.seek(0x1DFDF) # myExtFSFilter: cmp.w #4,d0
f.write(b'\x10')
# Enable this to make a SysError, for rudimentaly debug output
# f.seek(0x1DD4E)
# f.write(b'\xA9\xC9')
f.seek(0x878C)
f.write(assemble('move.w #0xFFFF,D0 \n .2byte 0xA9C9'))