#!/usr/bin/env python3 import math pageSize = 128 sectorsPerTrack = 256 blockSize = 16384 pagesPerBlock = 128 maxDirs = 128 dirBlocks = 1 def addBootloader(name): loader = open(name, 'rb') b = loader.read() if len(b) > pageSize: raise RuntimeError("Bootloader binary cannot be larger than one page") out.seek(0) out.write(b) def addOS(cpmName, biosName): cpm = open(cpmName, 'rb') bios = open(biosName, 'rb') b = cpm.read() out.seek(pageSize) out.write(b) b = bios.read() out.seek(pageSize + 0x1600) out.write(b) def seekBlock(i): # Blocks start at track 1, sector 0 out.seek(pageSize*sectorsPerTrack + i*blockSize) def initDirs(): seekBlock(0) out.write(bytearray(([0xe5] + [0x00] * 31) * maxDirs)) fileCounter = 0 allocationCounter = 1 def addFile(filename, n, t): global fileCounter global allocationCounter if fileCounter > maxDirs: raise RuntimeError("Max dir entries has been reached") if len(n) > 8: raise RuntimeError("Filename cannot be longer than 8") if len(t) > 3: raise RuntimeError("Filetype cannot be longer than 3") f = open(filename, 'rb') b = f.read() total_records = math.ceil(len(b)/pageSize) records = (total_records - 1) % pagesPerBlock + 1 extends = math.ceil(total_records/pagesPerBlock) if extends >= 8: RuntimeError("File is to big to be stored in one entry, multple entries are not implemented") seekBlock(0) out.seek(32*fileCounter, 1) # Write user (assume 0 for now) out.write(bytearray(1)) # Write the name out.write(n.upper().encode("ascii")) out.write((" " * (8-len(n))).encode("ascii")) # Write the type out.write(t.upper().encode("ascii")) out.write((" " * (3-len(t))).encode("ascii")) # Write extend # @todo This is actually wrong, as the order is EX S1 S2 with S1 being unused, however we never deal with files that big out.write((extends-1).to_bytes(1, byteorder='little')) # Reserved byte out.write(bytearray(2)) # Number of records out.write(records.to_bytes(1, byteorder='little')) allocationCounterOld = allocationCounter # We are assuming one block per file for now, so we can use fileCounter for i in range(extends): out.write(allocationCounter.to_bytes(2, byteorder='little')) allocationCounter += 1 seekBlock(allocationCounterOld) out.write(b) fileCounter += 1 def main(): # @todo Load the config from a file that we specify instead of hardcoding everything # We can probably keep the os part hardcoded as that is not something we really need to change between different projects global out out = open('disk.img', 'wb') addBootloader('../../software/loader/.build/loader.bin') addOS('../../software/cpm/.build/cpm22.bin', '../../software/cpm/.build/bios.bin') initDirs() addFile("../../software/monitor/.build/MONITOR.COM", "MONITOR", "COM") addFile("../../software/tetris/.build/tetris.com", "TETRIS", "COM") addFile("bin/STAT.COM", "STAT", "COM") addFile("bin/MBASIC.COM", "MBASIC", "COM") if __name__ == "__main__": main()