SPLIT-i のディスクの中身を眺めたい

SPLIT-i + ALPHA-DOS の中身を眺めたかったのですが、あいにく手元にPC-8801実機がなく、エミュレータを(合法的に)動かすのも結構ハードルが高そう。

仕方がないのでバイナリを直接眺めていたら何となく構造がわかったので、突貫でバラすプログラムを作ってみた。

d88やALPHA-DOSの仕様をちゃんと調べたわけではないので、他では使えないと思います。あと、BASICのプログラムは、シングルクォートのコメント以外非対応です。

Python 3.6以降なら動くと思います。その場にあるSPLIT-i 2014.d88を読んで、勝手にAlphaというフォルダを作ってそこにファイルを作ります。

import os

OUT_DIR = 'Alpha'
D88_HEADER = 0x2B0
SECTOR_INFO = 0x10
SECTOR_BODY = 0x100
SECTOR = SECTOR_INFO + SECTOR_BODY
ALPHA_DOS_INFO = 0x25000
ALPHA_DOS_SYSTEM = 0x3000
ENCODING = 'shift_jis'

with open('SPLIT-i 2014.d88', 'rb') as f:
    d88 = f.read(-1)
d88 = d88[D88_HEADER:]

raw = bytes()
for i in range(0, len(d88), SECTOR):
    raw += d88[i + SECTOR_INFO:i + SECTOR]

offset = ALPHA_DOS_INFO
os.makedirs(OUT_DIR, exist_ok=True)

with open(os.path.join(OUT_DIR, 'ALPHA_DOS.BIN'), 'wb') as f:
    f.write(raw[:ALPHA_DOS_SYSTEM])
current_address = ALPHA_DOS_SYSTEM

while True:
    if len(raw) < offset + 0x10 or raw[offset] == 0xFF:
        break
    base = raw[offset:offset + 6].rstrip().decode(encoding=ENCODING)
    suf = raw[offset + 6:offset + 9].rstrip().decode(encoding=ENCODING)
    name = base + '.' + suf if suf != '' else base
    typ = raw[offset + 9]
    if typ == 0xFF:
        break
    block = raw[offset + 10]
    address = block * 0x800
    if address < current_address:
        print(f'! Overrun detected ({address:X} < {current_address:X})')
    current_address = address
    tmp = raw[address:]
    if typ == 0x01:
        start = int.from_bytes(bytes=tmp[0:2], byteorder='little')
        end = int.from_bytes(bytes=tmp[2:4], byteorder='little')
        size = end - start
        tmp = tmp[4:4 + size]
        print(f'{name}: Binary 0x{start:04X}-0x{end:04X} (size 0x{size:04X})')
        with open(os.path.join(OUT_DIR, name), 'wb') as f:
            f.write(tmp)
        current_address += 4 + size
    elif typ == 0x80:
        print(f'{name}: Basic Text')
        with open(os.path.join(OUT_DIR, name), 'w') as f:
            p = 0
            while True:
                eol = int.from_bytes(bytes=tmp[p:p + 2], byteorder='little')
                p += 2
                if eol == 0:
                    break
                eol -= 2
                if tmp[eol] != 0:
                    print(f'! {name}: Unexpected EOL 0x{tmp[eol]:02X} at {address + eol:X}.')
                    break
                number = int.from_bytes(bytes=tmp[p:p + 2], byteorder='little')
                print(f'{number} ', end='', file=f)
                p += 2
                line = tmp[p:eol]
                if line[:3] == b'\x3A\x8F\xE9':
                    print(f"'{line[3:].decode(encoding=ENCODING)}", file=f)
                else:
                    print('(SOME BASIC STATEMENT)', file=f)  # NOT SUPPORTED
                p = eol + 1
        current_address += p
    else:
        print(f'! {name}: Unknown type 0x{typ:02X}.')
    offset += 0x10

(実は既に良いツールがある、というオチはありそう。)