git @ Cat's Eye Technologies SixtyPical / master src / sixtypical / outputter.py
master

Tree @master (Download .tar.gz)

outputter.py @masterraw · history · blame

# Copyright (c) 2014-2024, Chris Pressey, Cat's Eye Technologies.
# This file is distributed under a 2-clause BSD license.  See LICENSES/ dir.
# SPDX-License-Identifier: LicenseRef-BSD-2-Clause-X-SixtyPical

"""Executable file writer."""

from sixtypical.emitter import Emitter, Byte, Word


class Outputter(object):
    def __init__(self, fh, start_addr=None):
        self.start_addr = self.__class__.start_addr
        if start_addr is not None:
            self.start_addr = start_addr
        self.prelude = self.__class__.prelude
        self.fh = fh
        self.emitter = Emitter(self.start_addr)

    def write_header(self):
        pass

    def write_prelude(self):
        self.write_header()
        for byte in self.prelude:
            self.emitter.emit(Byte(byte))

    def write_postlude(self):
        pass


class RawOutputter(Outputter):
    start_addr = 0x0000
    prelude = []


class PrgOutputter(Outputter):
    start_addr = 0xc000
    prelude = []

    def write_header(self):
        # If we are outputting a .PRG, we output the load address first.
        # We don't use the Emitter for this b/c not part of addr space.
        self.fh.write(bytearray(Word(self.start_addr).serialize(0)))


class C64BasicPrgOutputter(PrgOutputter):
    start_addr = 0x0801
    prelude = [0x10, 0x08, 0xc9, 0x07, 0x9e, 0x32,
               0x30, 0x36, 0x31, 0x00, 0x00, 0x00]


class Vic20BasicPrgOutputter(PrgOutputter):
    start_addr = 0x1001
    prelude = [0x0b, 0x10, 0xc9, 0x07, 0x9e, 0x34,
               0x31, 0x30, 0x39, 0x00, 0x00, 0x00]


class Atari2600CartOutputter(Outputter):
    start_addr = 0xf000
    prelude = [0x78, 0xd8, 0xa2, 0xff, 0x9a, 0xa9,
               0x00, 0x95, 0x00, 0xca, 0xd0, 0xfb]

    def write_postlude(self):
        # If we are outputting a cartridge with boot and BRK address
        # at the end, pad to ROM size minus 4 bytes, and emit addresses.
        self.emitter.pad_to_size(4096 - 4)
        self.emitter.emit(Word(self.start_addr))
        self.emitter.emit(Word(self.start_addr))


def outputter_class_for(output_format):
    return {
        'raw': RawOutputter,
        'prg': PrgOutputter,
        'c64-basic-prg': C64BasicPrgOutputter,
        'vic20-basic-prg': Vic20BasicPrgOutputter,
        'atari2600-cart': Atari2600CartOutputter,
    }[output_format]