diff --git a/IM-D ParamBlock diagram b/IM-D ParamBlock diagram
new file mode 100644
index 0000000..2e203c3
--- /dev/null
+++ b/IM-D ParamBlock diagram
@@ -0,0 +1,8 @@
+
+
+
+
+ URL
+ http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/Devices/Devices-13.html
+
+
diff --git a/NetBoot.py b/NetBoot.py
index afe2eaf..7e71f2d 100755
--- a/NetBoot.py
+++ b/NetBoot.py
@@ -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]; // 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)
diff --git a/doeverything.bash b/doeverything.bash
new file mode 100755
index 0000000..82e53cb
--- /dev/null
+++ b/doeverything.bash
@@ -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
diff --git a/systools607.dsk b/systools607.dsk
new file mode 100644
index 0000000..3512076
Binary files /dev/null and b/systools607.dsk differ
diff --git a/vasm-1/obj/m68k_mot_atom.o b/vasm-1/obj/m68k_mot_atom.o
new file mode 100644
index 0000000..6449d0a
Binary files /dev/null and b/vasm-1/obj/m68k_mot_atom.o differ
diff --git a/vasm-1/obj/m68k_mot_cond.o b/vasm-1/obj/m68k_mot_cond.o
new file mode 100644
index 0000000..0527562
Binary files /dev/null and b/vasm-1/obj/m68k_mot_cond.o differ
diff --git a/vasm-1/obj/m68k_mot_cpu.o b/vasm-1/obj/m68k_mot_cpu.o
new file mode 100644
index 0000000..6366c1a
Binary files /dev/null and b/vasm-1/obj/m68k_mot_cpu.o differ
diff --git a/vasm-1/obj/m68k_mot_dwarf.o b/vasm-1/obj/m68k_mot_dwarf.o
new file mode 100644
index 0000000..5164271
Binary files /dev/null and b/vasm-1/obj/m68k_mot_dwarf.o differ
diff --git a/vasm-1/obj/m68k_mot_error.o b/vasm-1/obj/m68k_mot_error.o
new file mode 100644
index 0000000..755708e
Binary files /dev/null and b/vasm-1/obj/m68k_mot_error.o differ
diff --git a/vasm-1/obj/m68k_mot_expr.o b/vasm-1/obj/m68k_mot_expr.o
new file mode 100644
index 0000000..5e9a63a
Binary files /dev/null and b/vasm-1/obj/m68k_mot_expr.o differ
diff --git a/vasm-1/obj/m68k_mot_hugeint.o b/vasm-1/obj/m68k_mot_hugeint.o
new file mode 100644
index 0000000..2728077
Binary files /dev/null and b/vasm-1/obj/m68k_mot_hugeint.o differ
diff --git a/vasm-1/obj/m68k_mot_osdep.o b/vasm-1/obj/m68k_mot_osdep.o
new file mode 100644
index 0000000..4cd60c8
Binary files /dev/null and b/vasm-1/obj/m68k_mot_osdep.o differ
diff --git a/vasm-1/obj/m68k_mot_output_aout.o b/vasm-1/obj/m68k_mot_output_aout.o
new file mode 100644
index 0000000..ced516a
Binary files /dev/null and b/vasm-1/obj/m68k_mot_output_aout.o differ
diff --git a/vasm-1/obj/m68k_mot_output_bin.o b/vasm-1/obj/m68k_mot_output_bin.o
new file mode 100644
index 0000000..a029fcd
Binary files /dev/null and b/vasm-1/obj/m68k_mot_output_bin.o differ
diff --git a/vasm-1/obj/m68k_mot_output_elf.o b/vasm-1/obj/m68k_mot_output_elf.o
new file mode 100644
index 0000000..d0e9b44
Binary files /dev/null and b/vasm-1/obj/m68k_mot_output_elf.o differ
diff --git a/vasm-1/obj/m68k_mot_output_hunk.o b/vasm-1/obj/m68k_mot_output_hunk.o
new file mode 100644
index 0000000..d6de9bc
Binary files /dev/null and b/vasm-1/obj/m68k_mot_output_hunk.o differ
diff --git a/vasm-1/obj/m68k_mot_output_srec.o b/vasm-1/obj/m68k_mot_output_srec.o
new file mode 100644
index 0000000..8bc5574
Binary files /dev/null and b/vasm-1/obj/m68k_mot_output_srec.o differ
diff --git a/vasm-1/obj/m68k_mot_output_test.o b/vasm-1/obj/m68k_mot_output_test.o
new file mode 100644
index 0000000..0743a34
Binary files /dev/null and b/vasm-1/obj/m68k_mot_output_test.o differ
diff --git a/vasm-1/obj/m68k_mot_output_tos.o b/vasm-1/obj/m68k_mot_output_tos.o
new file mode 100644
index 0000000..130af0d
Binary files /dev/null and b/vasm-1/obj/m68k_mot_output_tos.o differ
diff --git a/vasm-1/obj/m68k_mot_output_vobj.o b/vasm-1/obj/m68k_mot_output_vobj.o
new file mode 100644
index 0000000..cd5535d
Binary files /dev/null and b/vasm-1/obj/m68k_mot_output_vobj.o differ
diff --git a/vasm-1/obj/m68k_mot_output_xfile.o b/vasm-1/obj/m68k_mot_output_xfile.o
new file mode 100644
index 0000000..bc559c4
Binary files /dev/null and b/vasm-1/obj/m68k_mot_output_xfile.o differ
diff --git a/vasm-1/obj/m68k_mot_parse.o b/vasm-1/obj/m68k_mot_parse.o
new file mode 100644
index 0000000..3267bb6
Binary files /dev/null and b/vasm-1/obj/m68k_mot_parse.o differ
diff --git a/vasm-1/obj/m68k_mot_reloc.o b/vasm-1/obj/m68k_mot_reloc.o
new file mode 100644
index 0000000..33b3c0a
Binary files /dev/null and b/vasm-1/obj/m68k_mot_reloc.o differ
diff --git a/vasm-1/obj/m68k_mot_supp.o b/vasm-1/obj/m68k_mot_supp.o
new file mode 100644
index 0000000..3b880ad
Binary files /dev/null and b/vasm-1/obj/m68k_mot_supp.o differ
diff --git a/vasm-1/obj/m68k_mot_symbol.o b/vasm-1/obj/m68k_mot_symbol.o
new file mode 100644
index 0000000..eb755a8
Binary files /dev/null and b/vasm-1/obj/m68k_mot_symbol.o differ
diff --git a/vasm-1/obj/m68k_mot_symtab.o b/vasm-1/obj/m68k_mot_symtab.o
new file mode 100644
index 0000000..2d7470e
Binary files /dev/null and b/vasm-1/obj/m68k_mot_symtab.o differ
diff --git a/vasm-1/obj/m68k_mot_syntax.o b/vasm-1/obj/m68k_mot_syntax.o
new file mode 100644
index 0000000..e6a8433
Binary files /dev/null and b/vasm-1/obj/m68k_mot_syntax.o differ
diff --git a/vasm-1/obj/m68k_mot_vasm.o b/vasm-1/obj/m68k_mot_vasm.o
new file mode 100644
index 0000000..980dbf0
Binary files /dev/null and b/vasm-1/obj/m68k_mot_vasm.o differ
diff --git a/vasm-1/vasmm68k_mot b/vasm-1/vasmm68k_mot
new file mode 100755
index 0000000..617d85c
Binary files /dev/null and b/vasm-1/vasmm68k_mot differ
diff --git a/xo-rom-enable-netboot.py b/xo-rom-enable-netboot.py
index f1839e3..5f8faa2 100755
--- a/xo-rom-enable-netboot.py
+++ b/xo-rom-enable-netboot.py
@@ -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'))