mirror of
https://github.com/elliotnunn/tbxi-patches.git
synced 2024-06-14 18:29:28 +00:00
Key realisations: 1. The driver entry point only needs to be called once per disk, not once per bootable partition. Bootability is decided elsewhere. 2. As a result, my shim code does not need to access the disk at all. 3. None of the ROM-based ATADisk drivers support "slave" drives. 4. Apple drivers (contrary to the Monster Disk Driver Technote) only care about D5 (the drive ID and some flags) on entry.
90 lines
2.8 KiB
Python
Executable File
90 lines
2.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import patch_common
|
|
|
|
from os import path
|
|
import os
|
|
import struct
|
|
import shutil
|
|
import re
|
|
import binascii
|
|
|
|
|
|
src, cleanup = patch_common.get_src(desc='''
|
|
Support ATA startup disks without an Apple_Driver_ATA partition. This allows Mac
|
|
OS to be installed on a disk without repartitioning it. Accomplished by
|
|
shoehorning the Drive Setup 2.1 ATA driver into the ROM ATALoad driver, to
|
|
replace missing driver partitions.
|
|
''')
|
|
|
|
|
|
def find_InitDevice(code):
|
|
for i in range(0, len(code), 2):
|
|
# checking Driver Descriptor Map magic number (there are two but this one always first)
|
|
if code[i:i+2] == b'ER':
|
|
for j in reversed(range(0, i, 2)):
|
|
if code[j:j+2] == b'NV': # LINK A6,#$xxxx
|
|
return j
|
|
|
|
raise ValueError('Function InitDevice not found')
|
|
|
|
|
|
def patch_ataload(code):
|
|
code = bytearray(code)
|
|
cut1 = len(code) # boundary between original and glue
|
|
|
|
# Parse the DumpObj'd file
|
|
with open(path.join(path.dirname(__file__), 'ATALoad.dmp')) as f:
|
|
for l in f:
|
|
m = re.match(r'^[0-9A-F]{8}: ([0-9A-F ]+)', l) or re.match('^ {13}([0-9A-F][0-9A-F ]*)', l)
|
|
if m:
|
|
code.extend(binascii.unhexlify(m.group(1).replace(' ', '')))
|
|
|
|
cut2 = len(code) # boundary between glue and driver
|
|
|
|
with open(path.join(path.dirname(__file__), 'AppleATADisk'), 'rb') as f:
|
|
code.extend(f.read())
|
|
|
|
InitDevice = find_InitDevice(code[:cut1])
|
|
|
|
print('ATALoad patch: InitDevice=0x%X, glue=0x%X, driver=0x%X' % (InitDevice, cut1, cut2))
|
|
|
|
# "Link" the new code into the old
|
|
for i in range(cut1, len(code), 2):
|
|
if code[i:i+4] == b'Nsrt':
|
|
code[i:i+4] = code[InitDevice:InitDevice+4] # copy the first LINK from the original function
|
|
code[InitDevice:InitDevice+2] = b'\x60\x00' # BRA.W opcode, to...
|
|
code[InitDevice+2:InitDevice+4] = struct.pack('>h', cut1 - (InitDevice+2)) # ...the start of the new code
|
|
|
|
if code[i:i+2] == b'At':
|
|
code[i:i+2] = struct.pack('>h', cut2 - i)
|
|
|
|
if code[i:i+4] == b'Size':
|
|
code[i:i+4] = struct.pack('>l', cut2 - cut1)
|
|
|
|
if code[i:i+2] == b'ID': # reference to original InitDevice, skipping the mangled 4-byte LINK
|
|
code[i:i+2] = struct.pack('>h', (InitDevice+4) - i)
|
|
|
|
return bytes(code)
|
|
|
|
|
|
found_drvr = False
|
|
|
|
for (parent, folders, files) in os.walk(src):
|
|
folders.sort(); files.sort() # make it kinda deterministic
|
|
for filename in files:
|
|
full = path.join(src, parent, filename)
|
|
|
|
if filename == 'DRVR_-20175_ATALoad':
|
|
code = open(full, 'rb').read()
|
|
code = patch_ataload(code)
|
|
open(full, 'wb').write(code)
|
|
found_drvr = True
|
|
|
|
|
|
if not found_drvr:
|
|
raise ValueError('ATALoad DRVR not found')
|
|
|
|
|
|
cleanup()
|