From 00aee2ee81223387a2fd1ca30b1bb143d0db101b Mon Sep 17 00:00:00 2001 From: Ivan Kosarev Date: Sat, 23 May 2020 12:16:51 +0300 Subject: [PATCH] [#304] Support custom code emitters. --- outfmt/__init__.py | 4 ++++ outfmt/binary.py | 25 +++++++++++++++++++++++++ outfmt/codeemitter.py | 19 +++++++++++++++++++ outfmt/tzx.py | 20 +++++++++++++++++++- zxb/__init__.py | 1 + zxb/zxb.py | 5 +++-- zxbasm/asmparse.py | 36 +++++++++++++++++++----------------- 7 files changed, 90 insertions(+), 20 deletions(-) create mode 100644 outfmt/binary.py create mode 100644 outfmt/codeemitter.py diff --git a/outfmt/__init__.py b/outfmt/__init__.py index f390900f5..afa2b9814 100644 --- a/outfmt/__init__.py +++ b/outfmt/__init__.py @@ -1,11 +1,15 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +from .binary import BinaryEmitter +from .codeemitter import CodeEmitter from .tzx import TZX from .tap import TAP __all__ = [ + 'BinaryEmitter', + 'CodeEmitter', 'TZX', 'TAP', ] diff --git a/outfmt/binary.py b/outfmt/binary.py new file mode 100644 index 000000000..84115197e --- /dev/null +++ b/outfmt/binary.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# -------------------------------------------- +# KopyLeft (K) 2008 +# by Jose M. Rodriguez de la Rosa +# +# This program is licensed under the +# GNU Public License v.3.0 +# +# The code emission interface. +# -------------------------------------------- + +from .codeemitter import CodeEmitter + + +class BinaryEmitter(CodeEmitter): + """ Writes compiled code as raw binary data. + """ + def emit(self, output_filename, program_name, loader_bytes, entry_point, + program_bytes, aux_bin_blocks, aux_headless_bin_blocks): + """ Emits resulting binary file. + """ + with open(output_filename, 'wb') as f: + f.write(bytearray(program_bytes)) diff --git a/outfmt/codeemitter.py b/outfmt/codeemitter.py new file mode 100644 index 000000000..105e125fc --- /dev/null +++ b/outfmt/codeemitter.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# -------------------------------------------- +# KopyLeft (K) 2008 +# by Jose M. Rodriguez de la Rosa +# +# This program is licensed under the +# GNU Public License v.3.0 +# +# The code emission interface. +# -------------------------------------------- + + +class CodeEmitter(object): + """ The base code emission interface. + """ + + pass diff --git a/outfmt/tzx.py b/outfmt/tzx.py index e78fb5141..5d142f7a2 100755 --- a/outfmt/tzx.py +++ b/outfmt/tzx.py @@ -13,7 +13,10 @@ # -------------------------------------------- -class TZX(object): +from .codeemitter import CodeEmitter + + +class TZX(CodeEmitter): """ Class to represent tzx data """ VERSION_MAJOR = 1 @@ -131,6 +134,21 @@ def save_program(self, title, bytes, line=32768): bytes = [self.BLOCK_TYPE_DATA] + [(int(x) & 0xFF) for x in bytes] # & 0xFF truncates to bytes self.standard_block(bytes) + def emit(self, output_filename, program_name, loader_bytes, entry_point, + program_bytes, aux_bin_blocks, aux_headless_bin_blocks): + """ Emits resulting tape file. + """ + if loader_bytes is not None: + self.save_program('loader', loader_bytes, line=1) # Put line 0 to protect against MERGE + + self.save_code(program_name, entry_point, program_bytes) + for name, block in aux_bin_blocks: + self.save_code(name, 0, block) + for block in aux_headless_bin_blocks: + self.standard_block(block) + + self.dump(output_filename) + if __name__ == '__main__': """ Sample test if invoked from command line diff --git a/zxb/__init__.py b/zxb/__init__.py index f00018858..d1a7c5edc 100644 --- a/zxb/__init__.py +++ b/zxb/__init__.py @@ -9,4 +9,5 @@ # the GNU General License # ---------------------------------------------------------------------- +from outfmt import CodeEmitter # noqa from .zxb import main # noqa diff --git a/zxb/zxb.py b/zxb/zxb.py index ee5cb4927..80ee6806c 100755 --- a/zxb/zxb.py +++ b/zxb/zxb.py @@ -70,7 +70,7 @@ def output(memory, ofile=None): ofile.write('%s\n' % m) -def main(args=None): +def main(args=None, emitter=None): """ Entry point when executed from command line. You can use zxb.py as a module with import, and this function won't be executed. @@ -352,7 +352,8 @@ def main(args=None): fout.close() asmparse.generate_binary(OPTIONS.outputFileName.value, OPTIONS.output_file_type.value, binary_files=options.append_binary, - headless_binary_files=options.append_headless_binary) + headless_binary_files=options.append_headless_binary, + emitter=emitter) if gl.has_errors: return 5 # Error in assembly diff --git a/zxbasm/asmparse.py b/zxbasm/asmparse.py index 7e8606cc9..4161c0402 100755 --- a/zxbasm/asmparse.py +++ b/zxbasm/asmparse.py @@ -1467,7 +1467,8 @@ def assemble(input_): return gl.has_errors -def generate_binary(outputfname, format_, progname='', binary_files=None, headless_binary_files=None): +def generate_binary(outputfname, format_, progname='', binary_files=None, headless_binary_files=None, + emitter=None): """ Outputs the memory binary to the output filename using one of the given formats: tap, tzx or bin @@ -1512,23 +1513,24 @@ def generate_binary(outputfname, format_, progname='', binary_files=None, headle else: program.add_line([['REM'], ['RANDOMIZE', program.token('USR'), AUTORUN_ADDR]]) - if format_ in ('tap', 'tzx'): - t = {'tap': outfmt.TAP, 'tzx': outfmt.TZX}[format_]() - - if OPTIONS.use_loader.value: - t.save_program('loader', program.bytes, line=1) # Put line 0 to protect against MERGE - - t.save_code(progname, org, binary) - for name, block in bin_blocks: - t.save_code(name, 0, block) - for block in headless_bin_blocks: - t.standard_block(block) - - t.dump(outputfname) + if emitter is None: + if format_ in ('tap', 'tzx'): + emitter = {'tap': outfmt.TAP, 'tzx': outfmt.TZX}[format_]() + else: + emitter = outfmt.BinaryEmitter() - else: - with open(outputfname, 'wb') as f: - f.write(bytearray(binary)) + loader_bytes = None + if OPTIONS.use_loader.value: + loader_bytes = program.bytes + + assert isinstance(emitter, outfmt.CodeEmitter) + emitter.emit(output_filename=outputfname, + program_name=progname, + loader_bytes=loader_bytes, + entry_point=AUTORUN_ADDR, + program_bytes=binary, + aux_bin_blocks=bin_blocks, + aux_headless_bin_blocks=headless_bin_blocks) def main(argv):