New JSON-based shape extractor and compiler (the PNG-based scheme can be piggybacked...
authorNick Downing <nick@ndcode.org>
Sat, 25 Jun 2022 05:24:09 +0000 (15:24 +1000)
committerNick Downing <nick@ndcode.org>
Tue, 28 Jun 2022 03:21:37 +0000 (13:21 +1000)
.gitignore
disasm/Makefile
disasm/shape_compile.py [new file with mode: 0755]
disasm/shape_extract.py [new file with mode: 0755]
disasm/star_blazer.asm.patch

index fcbdcb0..0ff2e9d 100644 (file)
@@ -9,6 +9,12 @@
 *.rel
 *.rst
 *.seed
+/disasm/shape.json
+/disasm/shape_data.inc
+/disasm/shape_index.inc
+/disasm/shape_data_pixel.inc
+/disasm/shape_index_pixel.inc
+/disasm/shape_pixel.json
 /disasm/star_blazer.asm
 /disasm/star_blazer.asm0
 /disasm/star_blazer.dsk
index 4453351..a548ec8 100644 (file)
@@ -50,14 +50,26 @@ star_blazer.asm \
 ../shape/dhgr_pixel_shape_index.inc \
 ../shape/dhgr_pixel_shape_data_main.inc \
 ../shape/dhgr_pixel_shape_data_aux.inc \
-../shape/pixel_shape_index.inc \
-../shape/pixel_shape_data.inc \
-../shape/shape_index.inc \
-../shape/shape_data.inc \
+shape_index_pixel.inc \
+shape_data_pixel.inc \
+shape_index.inc \
+shape_data.inc \
 ucode_defs.inc \
 ucode_data.inc
        ${AS6500} -3 -l -o $<
 
+shape_index_pixel.inc shape_data_pixel.inc: star_blazer.txt shape_pixel.json
+       ./shape_compile.py $^ shape_index_pixel.inc shape_data_pixel.inc
+
+shape_pixel.json: star_blazer.txt ../loader/star_blazer.ihx
+       ./shape_extract.py --pixel $^ $@
+
+shape_index.inc shape_data.inc: star_blazer.txt shape.json
+       ./shape_compile.py $^ shape_index.inc shape_data.inc
+
+shape.json: star_blazer.txt ../loader/star_blazer.ihx
+       ./shape_extract.py $^ $@
+
 ucode_data.inc: star_blazer.asm
        sed -ne '/^ucode1080_countdown_30_pixel:/,/; 9dec/p' $< |\
 ./ucode_disasm.py >$@
@@ -84,4 +96,10 @@ clean:
 *.rel \
 *.rst \
 star_blazer.asm \
-star_blazer.dsk
+star_blazer.dsk \
+shape_index_pixel.inc \
+shape_data_pixel.inc \
+shape_pixel.json \
+shape_index.inc \
+shape_data.inc \
+shape.json
diff --git a/disasm/shape_compile.py b/disasm/shape_compile.py
new file mode 100755 (executable)
index 0000000..44a0262
--- /dev/null
@@ -0,0 +1,124 @@
+#!/usr/bin/env python3
+
+import json
+import sys
+from intelhex import IntelHex
+
+EXIT_SUCCESS = 0
+EXIT_FAILURE = 1
+
+if len(sys.argv) < 5:
+  print(f'usage: {sys.argv[0]:s} addrs.txt in.json out_index.inc out_data.inc')
+  sys.exit(EXIT_FAILURE)
+addrs_txt = sys.argv[1]
+in_json = sys.argv[2]
+out_index_inc = sys.argv[3]
+out_data_inc = sys.argv[4]
+
+print('reading addrs')
+shape_tables = {}
+with open(addrs_txt, 'r') as fin:
+  def get_line():
+    while True:
+      line = fin.readline()
+      if len(line) == 0:
+        return []
+      i = line.find('#')
+      if i >= 0:
+        line = line[:i]
+      fields = line.strip().split(',')
+      if fields != ['']:
+        #print('fields', fields)
+        return fields
+
+  fields = get_line()
+  while len(fields):
+    assert len(fields) == 1
+    section = fields[0]
+    print(section)
+
+    if section == 'items':
+      fields = get_line()
+      while len(fields) >= 2:
+        assert len(fields) >= 4
+        addr = int(fields[0], 0)
+        name = fields[2]
+        _type = fields[3]
+        if _type == 'byte' and name[:6] == 'shape_':
+          shape_tables[name[6:]] = addr
+        fields = get_line()
+      continue
+
+    # unknown section, skip
+    fields = get_line()
+    while len(fields) >= 2:
+      fields = get_line()
+
+print('reading json')
+with open(in_json) as fin:
+  data = json.load(fin)
+
+print('writing index')
+
+with open(out_index_inc, 'w') as fout:
+  for table, addr in shape_tables.items():
+    fout.write(f'shape_{table:s}:\n')
+
+    prefix = ''
+    if table[-3:] == '_lo':
+      prefix = '<'
+      table = table[:-3]
+    elif table[-3:] == '_hi':
+      prefix = '>'
+      table = table[:-3]
+
+    table_data = [data[i][table] for i in range(0x100)]
+    if table == 'data_ptr' or table == 'size_bytes':
+      for i in range(0x100):
+        name = data[i]['name']
+        if name is not None:
+          size_bytes = int(data[i]['size_bytes'], 0)
+          if table == 'data_ptr':
+            table_data[i] = 'shape_' + name
+          if size_bytes:
+            width_bytes = int(data[i]['width_bytes'], 0)
+            height = int(data[i]['height'], 0)
+            assert size_bytes == width_bytes * height
+            if table == 'data_ptr':
+              table_data[i] += f' + 3 * {width_bytes:d} * {height:d}'
+            else:
+              table_data[i] = f'{width_bytes:d} * {height:d}'
+    fout.write(''.join([f'\t.db\t{prefix:s}{i:s}\n' for i in table_data]))
+
+print('writing data')
+
+# preserve ordering from the original executable, based on data_ptr
+order = sorted(
+  [
+    (int(data[i]['data_ptr'], 0), i)
+    for i in range(0x100)
+    if data[i]['name'] is not None
+  ]
+)
+
+with open(out_data_inc, 'w') as fout:
+  for i in range(len(order)):
+    addr, index = order[i]
+    name = data[index]['name']
+    fout.write(f'shape_{name:s}:\n')
+
+    # check for reuse of same shape data in original executable
+    # if so, don't bother outputting the shape data, use next one
+    if i + 1 < len(order):
+      addr1, index1 = order[i + 1]
+      if addr1 == addr and data[index1]['data'] == data[index]['data']:
+        continue
+
+    fout.write(
+      ''.join(
+        [
+          ''.join(['\t.db\t{0:s}\n'.format(', '.join(j)) for j in i]) + '\n'
+          for i in data[index]['data']
+        ]
+      )
+    )
diff --git a/disasm/shape_extract.py b/disasm/shape_extract.py
new file mode 100755 (executable)
index 0000000..543cd12
--- /dev/null
@@ -0,0 +1,157 @@
+#!/usr/bin/env python3
+
+import json
+import sys
+from intelhex import IntelHex
+
+EXIT_SUCCESS = 0
+EXIT_FAILURE = 1
+
+pixel = False
+if len(sys.argv) >= 2 and sys.argv[1] == '--pixel':
+  pixel = True
+  del sys.argv[1]
+if len(sys.argv) < 4:
+  print(f'usage: {sys.argv[0]:s} [--pixel] addrs.txt in.ihx out.json')
+  sys.exit(EXIT_FAILURE)
+addrs_txt = sys.argv[1]
+in_ihx = sys.argv[2]
+out_json = sys.argv[3]
+
+print('reading addrs')
+shape_tables = {}
+pixel_tables = {}
+shapes = {}
+with open(addrs_txt, 'r') as fin:
+  def get_line():
+    while True:
+      line = fin.readline()
+      if len(line) == 0:
+        return []
+      i = line.find('#')
+      if i >= 0:
+        line = line[:i]
+      fields = line.strip().split(',')
+      if fields != ['']:
+        #print('fields', fields)
+        return fields
+
+  fields = get_line()
+  while len(fields):
+    assert len(fields) == 1
+    section = fields[0]
+    print(section)
+
+    if section == 'items':
+      fields = get_line()
+      while len(fields) >= 2:
+        assert len(fields) >= 4
+        addr = int(fields[0], 0)
+        name = fields[2]
+        _type = fields[3]
+        if _type == 'byte':
+          if name[:6] == 'shape_':
+            shape_tables[name[6:]] = addr
+          elif name[:6] == 'pixel_':
+            pixel_tables[name[6:]] = addr
+        fields = get_line()
+      continue
+
+    if section == 'shapes':
+      fields = get_line()
+      while len(fields) >= 2:
+        assert len(fields) == 2
+        index = int(fields[0], 0)
+        name = fields[1]
+        # shape < 8 is a single pixel masked by the shape number
+        # ignore these lines in addrs file unless --pixel requested
+        if index >= 8 or pixel:
+          shapes[index] = name
+        fields = get_line()
+      continue
+
+    # unknown section, skip
+    fields = get_line()
+    while len(fields) >= 2:
+      fields = get_line()
+
+print('reading ihx')
+intelhex = IntelHex(in_ihx)
+segments = [j for i in intelhex.segments() for j in i]
+for i in range(0, len(segments), 2):
+  print(f'[{segments[i]:04x}, {segments[i + 1]:04x})')
+
+print('extracting')
+data = [{'name': None} for i in range(0x100)]
+
+i = 0
+shape_tables1 = list(shape_tables.items())
+while i < len(shape_tables1):
+  if (
+    i + 2 <= len(shape_tables1) and
+      shape_tables1[i][0][-3:] == '_lo' and
+      shape_tables1[i + 1][0][-3:] == '_hi'
+  ):
+    table, addr_lo = shape_tables1[i]
+    _, addr_hi = shape_tables1[i + 1]
+    table = table[:-3]
+    for j in range(0x100):
+      value = intelhex[addr_lo + j] | (intelhex[addr_hi + j] << 8)
+      data[j][table] = f'0x{value:04x}'
+    i += 2
+  else:
+    table, addr = shape_tables1[i]
+    for j in range(0x100):
+      value = intelhex[addr + j]
+      data[j][table] = str(value)
+    i += 1
+
+# do this here so that item ordering is nicer in the json file
+for i in range(0x100):
+  data[i]['data'] = None
+
+for index, name in shapes.items():
+  data[index]['name'] = f'{index:02x}_{name:s}'
+
+  # shape < 8 is a single pixel masked by the shape number
+  # transform the special data tables for pixel drawing into ordinary data
+  if index < 8:
+    data[index]['data_ptr'] = '0x0000' # make them be compiled first
+    data[index]['width_bytes'] = '2'
+    data[index]['height'] = '1'
+    data[index]['size_bytes'] = '2'
+    data[index]['width'] = '1'
+
+    data_table_left = pixel_tables['data_table_left']
+    data_table_right = pixel_tables['data_table_right']
+    mask_table_left = pixel_tables['mask_table_left']
+    mask_table_right = pixel_tables['mask_table_right']
+    value = [
+      [
+        [
+          f'0x{intelhex[data_table_left + ((i * 2) % 7)] & intelhex[mask_table_left + index]:02x}',
+          f'0x{intelhex[data_table_right + ((i * 2) % 7)] & intelhex[mask_table_right + index]:02x}'
+        ]
+      ]
+      for i in range(7)
+    ]
+  else: 
+    addr = int(data[index]['data_ptr'], 0)
+    height = int(data[index]['height'], 0)
+    width_bytes = int(data[index]['width_bytes'], 0)
+    size_bytes = int(data[index]['size_bytes'], 0)
+    value = [
+      [
+        [
+          f'0x{intelhex[addr + (i - 3) * size_bytes + j * width_bytes + k]:02x}'
+          for k in range(width_bytes)
+        ]
+        for j in range(height)
+      ]
+      for i in range(7 if size_bytes else 1)
+    ]
+  data[index]['data'] = value
+
+print('writing json')
+with open(out_json, 'w') as fout:
+  json.dump(data, fout, indent = 2)
index 6735757..b71aa50 100644 (file)
@@ -1,10 +1,10 @@
---- star_blazer.asm0   2022-06-25 13:01:14.395901396 +1000
-+++ star_blazer.asm    2022-06-25 13:03:26.391899763 +1000
+--- star_blazer.asm0   2022-06-25 14:50:24.743820370 +1000
++++ star_blazer.asm    2022-06-25 15:16:37.251800919 +1000
 @@ -1,3 +1,9 @@
 +UNREACHABLE = 1
 +DHGR = 0
 +PIXEL_SHAPE = 0
-+SHAPE = 0
++SHAPE = 1
 +UCODE = 0
 +
  HIRES_SCREEN = 0x2000                 ; 2000
 +.include "../shape/dhgr_pixel_shape_index.inc"
 +.else
 +.if PIXEL_SHAPE
-+.include "../shape/pixel_shape_index.inc"
++.include "shape_index_pixel.inc"
 +.else
 +.if SHAPE
-+.include "../shape/shape_index.inc"
++.include "shape_index.inc"
 +.else
  shape_data_ptr_lo:
-       .db     <0x5d01                 ; 4000 r
-       .db     <0x5d01                 ; 4001 r
+       .db     <bvar_5d01              ; 4000 r
+       .db     <bvar_5d01              ; 4001 r
 @@ -4625,6 +5097,9 @@
        .db     0x01                    ; 45fd r
        .db     0x01                    ; 45fe r
 +.include "../shape/dhgr_pixel_shape_data_main.inc"
 +.else
 +.if PIXEL_SHAPE
-+.include "../shape/pixel_shape_data.inc"
++.include "shape_data_pixel.inc"
 +.else
 +.if SHAPE
-+.include "../shape/shape_data.inc"
++.include "shape_data.inc"
 +.else
        .db     0x5c                    ; 5c00
        .db     0x02                    ; 5c01
        .db     0x00                    ; 5c02
-@@ -21567,9 +22051,15 @@
+@@ -21696,9 +22180,15 @@
        .db     0xe0                    ; 8c05
        .db     0x83                    ; 8c06
        .db     0x9e                    ; 8c07
  bvar_8e00:
        .db     0x30                    ; 8e00 r
        .db     0x0f                    ; 8e01
-@@ -26140,6 +26630,13 @@
+@@ -26269,6 +26759,13 @@
        .db     0x7c                    ; 9dea
        .db     0x98                    ; 9deb
        .db     0x00                    ; 9dec