diff --git a/skoolkit/config.py b/skoolkit/config.py index 33dfbaec..0f86ff14 100644 --- a/skoolkit/config.py +++ b/skoolkit/config.py @@ -84,6 +84,8 @@ 'skool2bin': { 'Banks': (0, 'banks'), 'Data': (0, 'data'), + 'PadLeft': (65536, ''), + 'PadRight': (0, ''), 'Verbose': (0, 'verbose'), 'Warnings': (1, 'warn') }, diff --git a/skoolkit/skool2bin.py b/skoolkit/skool2bin.py index d668a79b..ab258ffd 100644 --- a/skoolkit/skool2bin.py +++ b/skoolkit/skool2bin.py @@ -54,7 +54,8 @@ def __str__(self): return '{0:05} {0:04X} {1} {2:13} {3}'.format(self.real_address, self.marker, self.operation, suffix).rstrip() class BinWriter: - def __init__(self, skoolfile, asm_mode=0, fix_mode=0, banks=False, start=-1, end=65537, data=False, verbose=False, warn=False): + def __init__(self, skoolfile, asm_mode=0, fix_mode=0, banks=False, start=-1, end=65537, + data=False, verbose=False, warn=False, pad_left=65536, pad_right=0): if fix_mode > 2: asm_mode = 3 elif asm_mode > 2: @@ -66,6 +67,8 @@ def __init__(self, skoolfile, asm_mode=0, fix_mode=0, banks=False, start=-1, end self.end = end self.verbose = verbose self.warn = warn + self.pad_left = pad_left + self.pad_right = pad_right self.weights = { 'isub': (0, int(asm_mode > 0)), 'ssub': (0, 2 * int(asm_mode > 1)), @@ -269,15 +272,22 @@ def write(self, binfile): end_address = min(self.end, self.end_address) base_address = min(base_address, end_address) data = self.snapshot[base_address:end_address] + if self.pad_left < base_address: + data = [0] * (base_address - self.pad_left) + data + base_address = self.pad_left + if self.pad_right > end_address: + data += [0] * (self.pad_right - end_address) + end_address = self.pad_right with open_file(binfile, 'wb') as f: f.write(bytearray(data)) if binfile == '-': binfile = 'stdout' info("Wrote {}: start={}, end={}, size={}".format(binfile, base_address, end_address, len(data))) -def run(skoolfile, binfile, options): +def run(skoolfile, binfile, options, config): binwriter = BinWriter(skoolfile, options.asm_mode, options.fix_mode, options.banks, options.start, - options.end, options.data, options.verbose, options.warn) + options.end, options.data, options.verbose, options.warn, + config['PadLeft'], config['PadRight']) binwriter.write(binfile) def main(args): @@ -339,4 +349,4 @@ def main(args): binfile = 'program.bin' else: binfile = basename(skoolfile) + '.bin' - run(skoolfile, binfile, namespace) + run(skoolfile, binfile, namespace, config) diff --git a/sphinx/source/changelog.rst b/sphinx/source/changelog.rst index 0b2e5cb7..73347fee 100644 --- a/sphinx/source/changelog.rst +++ b/sphinx/source/changelog.rst @@ -8,6 +8,8 @@ Changelog * Added the ``--ini`` and ``--show-config`` options to :ref:`skool2bin.py` (for setting the value of a configuration parameter and for showing all configuration parameter values) +* Added support to :ref:`skool2bin.py` for padding the output with zeroes (as + specified by the ``PadLeft`` and ``PadRight`` configuration parameters) * Fixed how the 'ADC A,*', 'SBC A,*', 'ADC HL,rr' and 'SBC HL,rr' instructions affect the half-carry flag * Fixed how 'BIT n,(IX/Y+d)' affects bits 3 and 5 of the flags in the C version diff --git a/sphinx/source/commands.rst b/sphinx/source/commands.rst index a2c956dc..f1e9192e 100644 --- a/sphinx/source/commands.rst +++ b/sphinx/source/commands.rst @@ -526,6 +526,10 @@ configuration parameters are: 128K file (``1``), or don't (``0``, the default) * ``Data`` - process :ref:`defb`, :ref:`defs` and :ref:`defw` directives (``1``), or don't (``0``, the default) +* ``PadLeft`` - address at which to start padding the output on the left with + zeroes; the default value is ``65536``, which produces no padding +* ``PadRight`` - address at which to stop padding the output on the right with + zeroes; the default value is ``0``, which produces no padding * ``Verbose`` - show info on each converted instruction (``1``), or don't (``0``, the default) * ``Warnings`` - show warnings (``1``, the default), or suppress them (``0``) @@ -545,7 +549,8 @@ Configuration parameters may also be set on the command line by using the | Version | Changes | +=========+===================================================================+ | 9.4 | Configuration is read from `skoolkit.ini` if present; added the | -| | ``--ini`` and ``--show-config`` options | +| | ``--ini`` and ``--show-config`` options; added support for | +| | padding the output with zeroes | +---------+-------------------------------------------------------------------+ | 9.1 | Added the ``--banks`` option | +---------+-------------------------------------------------------------------+ diff --git a/sphinx/source/man/skool2bin.py.rst b/sphinx/source/man/skool2bin.py.rst index 8af13b00..157153c3 100644 --- a/sphinx/source/man/skool2bin.py.rst +++ b/sphinx/source/man/skool2bin.py.rst @@ -76,6 +76,10 @@ configuration parameters are: (``1``), or don't (``0``, the default). :Data: Process ``@defb``, ``@defs`` and ``@defw`` directives (``1``), or don't (``0``, the default). + :PadLeft: Address at which to start padding the output on the left with + zeroes. The default value is ``65536``, which produces no padding. + :PadRight: Address at which to stop padding the output on the right with + zeroes. The default value is ``0``, which produces no padding. :Verbose: Show info on each converted instruction (``1``), or don't (``0``, the default). :Warnings: Show warnings (``1``, the default), or suppress them (``0``). diff --git a/tests/test_skool2bin.py b/tests/test_skool2bin.py index 4bc90d81..64487f1f 100644 --- a/tests/test_skool2bin.py +++ b/tests/test_skool2bin.py @@ -9,7 +9,7 @@ def mock_config(name): return {k: v[0] for k, v in COMMANDS[name].items()} class MockBinWriter: - def __init__(self, skoolfile, asm_mode, fix_mode, banks, start, end, data, verbose, warn): + def __init__(self, skoolfile, asm_mode, fix_mode, banks, start, end, data, verbose, warn, pad_left, pad_right): global mock_bin_writer mock_bin_writer = self self.skoolfile = skoolfile @@ -21,13 +21,16 @@ def __init__(self, skoolfile, asm_mode, fix_mode, banks, start, end, data, verbo self.data = data self.verbose = verbose self.warn = warn + self.pad_left = pad_left + self.pad_right = pad_right self.binfile = None def write(self, binfile): self.binfile = binfile class Skool2BinTest(SkoolKitTestCase): - def _check_values(self, skoolfile, binfile, asm_mode=0, fix_mode=0, banks=False, data=False, verbose=False, warn=True, start=-1, end=65537): + def _check_values(self, skoolfile, binfile, asm_mode=0, fix_mode=0, banks=False, data=False, + verbose=False, warn=True, start=-1, end=65537, pad_left=65536, pad_right=0): self.assertEqual(mock_bin_writer.skoolfile, skoolfile) self.assertEqual(mock_bin_writer.binfile, binfile) self.assertEqual(mock_bin_writer.asm_mode, asm_mode) @@ -38,6 +41,8 @@ def _check_values(self, skoolfile, binfile, asm_mode=0, fix_mode=0, banks=False, self.assertIs(mock_bin_writer.warn, warn) self.assertEqual(mock_bin_writer.start, start) self.assertEqual(mock_bin_writer.end, end) + self.assertEqual(mock_bin_writer.pad_left, pad_left) + self.assertEqual(mock_bin_writer.pad_right, pad_right) def test_no_arguments(self): output, error = self.run_skool2bin(catch_exit=2) @@ -86,6 +91,46 @@ def test_output_filename(self): self.assertEqual(len(error), 0) self.assertEqual(mock_bin_writer.binfile, binfile) + @patch.object(skool2bin, 'get_config', mock_config) + @patch.object(skool2bin, 'BinWriter', MockBinWriter) + def test_config_PadLeft_set_on_command_line(self): + skoolfile = 'in.skool' + binfile = 'out.bin' + self.run_skool2bin(f'-I PadLeft=16384 {skoolfile} {binfile}') + self._check_values(skoolfile, binfile, pad_left=16384) + + @patch.object(skool2bin, 'BinWriter', MockBinWriter) + def test_config_PadLeft_read_from_file(self): + ini = """ + [skool2bin] + PadLeft=16384 + """ + self.write_text_file(dedent(ini).strip(), 'skoolkit.ini') + skoolfile = 'in.skool' + binfile = 'out.bin' + self.run_skool2bin(f'{skoolfile} {binfile}') + self._check_values(skoolfile, binfile, pad_left=16384) + + @patch.object(skool2bin, 'get_config', mock_config) + @patch.object(skool2bin, 'BinWriter', MockBinWriter) + def test_config_PadRight_set_on_command_line(self): + skoolfile = 'in.skool' + binfile = 'out.bin' + self.run_skool2bin(f'-I PadRight=65536 {skoolfile} {binfile}') + self._check_values(skoolfile, binfile, pad_right=65536) + + @patch.object(skool2bin, 'BinWriter', MockBinWriter) + def test_config_PadLeft_read_from_file(self): + ini = """ + [skool2bin] + PadRight=65536 + """ + self.write_text_file(dedent(ini).strip(), 'skoolkit.ini') + skoolfile = 'in.skool' + binfile = 'out.bin' + self.run_skool2bin(f'{skoolfile} {binfile}') + self._check_values(skoolfile, binfile, pad_right=65536) + @patch.object(skool2bin, 'BinWriter', MockBinWriter) def test_option_B(self): skoolfile = 'test-B.skool' @@ -201,6 +246,8 @@ def test_option_show_config(self): [skool2bin] Banks=0 Data=0 + PadLeft=65536 + PadRight=0 Verbose=0 Warnings=1 """ @@ -211,6 +258,8 @@ def test_option_show_config_read_from_file(self): [skool2bin] Banks=1 Data=1 + PadLeft=16384 + PadRight=65536 Verbose=1 Warnings=0 """ @@ -221,6 +270,8 @@ def test_option_show_config_read_from_file(self): [skool2bin] Banks=1 Data=1 + PadLeft=16384 + PadRight=65536 Verbose=1 Warnings=0 """ @@ -277,7 +328,8 @@ def test_option_w(self): self._check_values(skoolfile, exp_binfile, warn=False) class BinWriterTestCase(SkoolKitTestCase): - def _test_write(self, skool, base_address, exp_data, *modes, banks=False, data=False, start=-1, end=65537, warn=True, exp_output='', exp_warnings=''): + def _test_write(self, skool, base_address, exp_data, *modes, banks=False, data=False, start=-1, + end=65537, warn=True, pad_left=65536, pad_right=0, exp_output='', exp_warnings=''): if skool is None: skoolfile = '-' binfile = self.write_bin_file(suffix='.bin') @@ -291,7 +343,8 @@ def _test_write(self, skool, base_address, exp_data, *modes, banks=False, data=F asm_mode = {'isub': 1, 'ssub': 2, 'rsub': 3}[mode] elif mode.endswith('fix'): fix_mode = {'ofix': 1, 'bfix': 2, 'rfix': 3}[mode] - bin_writer = skool2bin.BinWriter(skoolfile, asm_mode, fix_mode, banks, start, end, data, bool(exp_output), warn) + bin_writer = skool2bin.BinWriter(skoolfile, asm_mode, fix_mode, banks, start, end, data, + bool(exp_output), warn, pad_left, pad_right) bin_writer.write(binfile) with open(binfile, 'rb') as f: bdata = list(f.read()) @@ -740,6 +793,30 @@ def test_footer_is_ignored(self): exp_data = [1, 2] self._test_write(skool, 32768, exp_data) + def test_pad_left(self): + skool = """ + ; Data + b32770 DEFB 1,2 + """ + exp_data = [0, 0, 1, 2] + self._test_write(skool, 32768, exp_data, pad_left=32768) + + def test_pad_right(self): + skool = """ + ; Data + b32768 DEFB 3,4 + """ + exp_data = [3, 4, 0, 0] + self._test_write(skool, 32768, exp_data, pad_right=32772) + + def test_pad_left_and_right(self): + skool = """ + ; Data + b32769 DEFB 5,6 + """ + exp_data = [0, 5, 6, 0] + self._test_write(skool, 32768, exp_data, pad_left=32768, pad_right=32772) + def test_verbose(self): skool = """ ; Routine