Unify the a2_pack.py --(rev|fwd) code slightly, fix a fixup bug in uncompressed
authorNick Downing <nick@ndcode.org>
Wed, 22 Jun 2022 00:17:02 +0000 (10:17 +1000)
committerNick Downing <nick@ndcode.org>
Wed, 22 Jun 2022 00:18:10 +0000 (10:18 +1000)
loader/a2_pack.py

index 483727a..ddeb6d2 100755 (executable)
@@ -28,7 +28,8 @@ MAX_LEN = (1 << LEN_BITS1) + 1 # length codes are 2..MAX_LEN
 # some space lower than the uncompressed data (e.g. if the game loads at
 # 0xa00, there'll be 0x200 bytes after text page 1 for the decompressor)
 
-# forward decompressor: sits above the uncompressed data, overlap like:
+# forward decompressor: sits above the uncompressed data
+# the compressed vs uncompressed data can overlap like this:
 #            CCCCCCDDD (C = compressed data, D = decompressor)
 #           /    /
 #       UUUUUUUUUU     (U = uncompressed data)
@@ -266,6 +267,17 @@ if rev else
   intersect(segments, [0x100, 0x200])
 )
 
+# loader is constructed in execution order (bottom to top)
+# segments also need to be in execution order to construct payload
+# fwd: segments are packed and unpacked from bottom to top
+# rev: segments are packed and unpacked from top to bottom, reverse them
+if rev:
+  segments = [
+    j
+    for i in range(len(segments) - 2, -2, -2)
+    for j in segments[i:i + 2]
+  ]
+
 # fixup is a 4-tuple:
 #   (fixup flags, fixup address, target section, target address)
 # either address can be start relative or end relative
@@ -286,13 +298,6 @@ section_lzss_unpack = Section([], 0, [])
 section_loader = Section([], 0, [])
 section_payload = Section([], 0, [])
 
-# sections are output to the a2bin file from bottom to top as follows:
-sections = (
-  [section_loader, section_lzss_unpack, section_payload]
-if rev else
-  [section_payload, section_loader, section_lzss_unpack]
-)
-
 # report is a 5-tuple:
 #   (report type, ihx start, ihx end, a2bin start, a2bin end)
 # for compressed the compression ratio will be printed
@@ -304,36 +309,50 @@ REPORT_TYPE_UNCOMPRESSED = 1
 REPORT_TYPE_COMPRESSED = 2
 report = []
 
-if rev:
-  # prologue
-  section_loader.data.extend(
+# prologue
+if not rev:
+  section_payload.fixups.extend(
     [
-      0xd8,    # cld
-      0xa2, 0xff,      # ldx #0xff
-      0x9a,    # txs
+      (
+        FIXUP_FLAG_LO_BYTE | FIXUP_FLAG_HI_BYTE,
+        len(section_payload.data) + 1,
+        section_loader,
+        0
+      ),
     ]
   )
+  section_payload.data.extend(
+    [
+      0x4c, 0x00, 0x00,        # jmp loader
+    ]
+  )
+section_loader.data.extend(
+  [
+    0xd8,      # cld
+    0xa2, 0xff,        # ldx #0xff
+    0x9a,      # txs
+  ]
+)
 
-  # segments
-  # loader is constructed in order of execution (bottom to top)
-  # payload is constructed in order of unpacking (top to bottom)
-  for i in range(len(segments) - 2, -2, -2):
-    addr0 = segments[i]
-    addr1 = segments[i + 1]
-    data = list(intelhex.tobinstr(addr0, addr1 - 1))
-
-    if len(data) <= 4:
-      report.append(
-        (REPORT_TYPE_DIRECT_POKE, addr0, addr1, 0, 0)
-      )
+# segments
+for i in range(0, len(segments), 2):
+  addr0 = segments[i]
+  addr1 = segments[i + 1]
+  data = list(intelhex.tobinstr(addr0, addr1 - 1))
 
-      # use of zpage version is determined byte by byte
+  if len(data) <= 4:
+    report.append(
+      (REPORT_TYPE_DIRECT_POKE, addr0, addr1, 0, 0)
+    )
+
+    # use of zpage version is determined byte by byte
+    if rev:
       for i in data[::-1]:
         addr1 -= 1
         section_loader.data.extend(
           [
             0xa9, i,                           # lda #data
-            0x85, addr1,                               # sta *addr1
+            0x85, addr1,                       # sta *addr1
           ]
         if addr1 < 0x100 else
           [
@@ -341,39 +360,61 @@ if rev:
             0x8d, addr1 & 0xff, addr1 >> 8,    # sta addr1
           ]
         )
-    elif len(data) <= 0x100:
+    else:
+      for i in data:
+        section_loader.data.extend(
+          [
+            0xa9, i,                           # lda #data
+            0x85, addr0,                               # sta *addr0
+          ]
+        if addr0 < 0x100 else
+          [
+            0xa9, i,                           # lda #data
+            0x8d, addr0 & 0xff, addr0 >> 8,    # sta addr0
+          ]
+        )
+        addr0 += 1
+  elif len(data) <= 0x100:
+    if rev:
       addr3 = -len(section_payload.data)
       section_payload.data.extend(
         data[::-1]
       )
       addr2 = -len(section_payload.data)
-      report.append(
-        (REPORT_TYPE_UNCOMPRESSED, addr0, addr1, addr2, addr3)
+    else:
+      addr2 = len(section_payload.data)
+      section_payload.data.extend(
+        data
       )
+      addr3 = len(section_payload.data)
+    report.append(
+      (REPORT_TYPE_UNCOMPRESSED, addr0, addr1, addr2, addr3)
+    )
 
-      # use of zpage version is determined in advance (if completely fits)
-      zpage = addr1 <= 0x100
+    # use of zpage version is determined in advance (if completely fits)
+    zpage = addr1 <= 0x100
 
-      section_loader.fixups.extend(
-        [
-          (
-            FIXUP_FLAG_TARGET_END_RELATIVE |
-              FIXUP_FLAG_LO_BYTE |
-              FIXUP_FLAG_HI_BYTE,
-            len(section_loader.data) + 3,
-            section_payload,
-            addr2
-          ),
-        ]
-      )
+    if rev:
       if len(data) == 0x100:
         # for the full count we will copy forward (an exception)
+        section_loader.fixups.extend(
+          [
+            (
+              FIXUP_FLAG_TARGET_END_RELATIVE |
+                FIXUP_FLAG_LO_BYTE |
+                FIXUP_FLAG_HI_BYTE,
+              len(section_loader.data) + 3,
+              section_payload,
+              addr2
+            ),
+          ]
+        )
         section_loader.data.extend(
           [
             0xa2, 0x00,                                        # ldx #0
             0xbd, 0x00, 0x00,                          # lda addr2,x
             0x95, addr0 & 0xff,                                # sta *addr0,x
-            0xe8,                                              # inx
+            0xe8,                                      # inx
             0xd0, 0xf8                                 # bne .-6
           ]
         if zpage else
@@ -381,19 +422,31 @@ if rev:
             0xa2, 0x00,                                        # ldx #0
             0xbd, 0x00, 0x00,                          # lda addr2,x
             0x9d, addr0 & 0xff, (addr0 >> 8) & 0xff,   # sta addr0,x
-            0xe8,                                              # inx
+            0xe8,                                      # inx
             0xd0, 0xf7                                 # bne .-7
           ]
         )
       else:
         addr0 -= 1
         addr2 -= 1
+        section_loader.fixups.extend(
+          [
+            (
+              FIXUP_FLAG_TARGET_END_RELATIVE |
+                FIXUP_FLAG_LO_BYTE |
+                FIXUP_FLAG_HI_BYTE,
+              len(section_loader.data) + 3,
+              section_payload,
+              addr2
+            ),
+          ]
+        )
         section_loader.data.extend(
           [
             0xa2, len(data),                           # ldx #count
             0xbd, 0x00, 0x00,                          # lda addr2,x
             0x95, addr0 & 0xff,                                # sta *addr0,x
-            0xca,                                              # dex
+            0xca,                                      # dex
             0xd0, 0xf8                                 # bne .-6
           ]
         if zpage else
@@ -401,138 +454,11 @@ if rev:
             0xa2, len(data),                           # ldx #count
             0xbd, 0x00, 0x00,                          # lda addr2,x
             0x9d, addr0 & 0xff, (addr0 >> 8) & 0xff,   # sta addr0,x
-            0xca,                                              # dex
+            0xca,                                      # dex
             0xd0, 0xf7                                 # bne .-7
           ]
         )
     else:
-      addr3 = -len(section_payload.data)
-      section_payload.data.extend(
-        lzss_pack(addr1 - 1, data, True)[::-1]
-      )
-      addr2 = -len(section_payload.data)
-      report.append(
-        (REPORT_TYPE_COMPRESSED, addr0, addr1, addr2, addr3)
-      )
-
-      if len(section_lzss_unpack.data) == 0:
-        section_lzss_unpack.data.extend(
-          lzss_unpack_rev_or_fwd
-        )
-
-      addr3 -= 5 + 1
-      section_loader.fixups.extend(
-        [
-          (
-            FIXUP_FLAG_TARGET_END_RELATIVE | FIXUP_FLAG_LO_BYTE,
-            len(section_loader.data) + 1,
-            section_payload,
-            addr3
-          ),
-          (
-            FIXUP_FLAG_TARGET_END_RELATIVE | FIXUP_FLAG_HI_BYTE,
-            len(section_loader.data) + 3,
-            section_payload,
-            addr3
-          ),
-          (
-            FIXUP_FLAG_LO_BYTE | FIXUP_FLAG_HI_BYTE,
-            len(section_loader.data) + 5,
-            section_lzss_unpack,
-            0
-          ),
-        ]
-      )
-      section_loader.data.extend(
-        [
-          0xa9, 0x00,          # lda #<addr3
-          0xa0, 0x00,          # ldy #>addr3
-          0x20, 0x00, 0x00,    # jsr lzss_unpack_rev
-        ]
-      )
-
-  # epilogue
-  section_loader.data.extend(
-    [
-      0x4c, entry_point & 0xff, entry_point >> 8,      # jmp entry_point
-    ]
-  )
-
-  # sections that were constructed in reverse can now be made normal
-  section_payload.data = section_payload.data[::-1]
-
-  # relocate from bottom up
-  load_addr = load_or_end_addr
-  end_addr = load_addr
-  for section in sections:
-    section.load_addr = end_addr
-    end_addr += len(section.data)
-  load_size = end_addr - load_addr
-else:
-  # prologue
-  section_payload.fixups.extend(
-    [
-      (
-        FIXUP_FLAG_LO_BYTE | FIXUP_FLAG_HI_BYTE,
-        len(section_payload.data) + 1,
-        section_loader,
-        0
-      ),
-    ]
-  )
-  section_payload.data.extend(
-    [
-      0x4c, 0x00, 0x00,        # jmp loader
-    ]
-  )
-  section_loader.data.extend(
-    [
-      0xd8,    # cld
-      0xa2, 0xff,      # ldx #0xff
-      0x9a,    # txs
-    ]
-  )
-
-  # segments
-  # loader is constructed in order of execution (bottom to top)
-  # payload is constructed in order of unpacking (bottom to top)
-  for i in range(0, len(segments), 2):
-    addr0 = segments[i]
-    addr1 = segments[i + 1]
-    data = list(intelhex.tobinstr(addr0, addr1 - 1))
-
-    if len(data) <= 4:
-      report.append(
-        (REPORT_TYPE_DIRECT_POKE, addr0, addr1, 0, 0)
-      )
-
-      # use of zpage version is determined byte by byte
-      for i in data:
-        section_loader.data.extend(
-          [
-            0xa9, i,                           # lda #data
-            0x85, addr0,                               # sta *addr0
-          ]
-        if addr0 < 0x100 else
-          [
-            0xa9, i,                           # lda #data
-            0x8d, addr0 & 0xff, addr0 >> 8,    # sta addr0
-          ]
-        )
-        addr0 += 1
-    elif len(data) <= 0x100:
-      addr2 = len(section_payload.data)
-      section_payload.data.extend(
-        data
-      )
-      addr3 = len(section_payload.data)
-      report.append(
-        (REPORT_TYPE_UNCOMPRESSED, addr0, addr1, addr2, addr3)
-      )
-
-      # use of zpage version is determined in advance (if completely fits)
-      zpage = addr1 <= 0x100
-
       addr1 -= 0x100
       addr3 -= 0x100
       section_loader.fixups.extend(
@@ -562,21 +488,53 @@ else:
           0xd0, 0xf7                                   # bne .-7
         ]
       )
+  else:
+    if rev:
+      addr3 = -len(section_payload.data)
+      section_payload.data.extend(
+        lzss_pack(addr1 - 1, data, True)[::-1]
+      )
+      addr2 = -len(section_payload.data)
     else:
       addr2 = len(section_payload.data)
       section_payload.data.extend(
         lzss_pack(addr0, data, False)
       )
       addr3 = len(section_payload.data)
-      report.append(
-        (REPORT_TYPE_COMPRESSED, addr0, addr1, addr2, addr3)
-      )
+    report.append(
+      (REPORT_TYPE_COMPRESSED, addr0, addr1, addr2, addr3)
+    )
 
-      if len(section_lzss_unpack.data) == 0:
-        section_lzss_unpack.data.extend(
-          lzss_unpack_rev_or_fwd
-        )
+    if len(section_lzss_unpack.data) == 0:
+      section_lzss_unpack.data.extend(
+        lzss_unpack_rev_or_fwd
+      )
 
+    if rev:
+      addr3 -= 5 + 1
+      section_loader.fixups.extend(
+        [
+          (
+            FIXUP_FLAG_TARGET_END_RELATIVE | FIXUP_FLAG_LO_BYTE,
+            len(section_loader.data) + 1,
+            section_payload,
+            addr3
+          ),
+          (
+            FIXUP_FLAG_TARGET_END_RELATIVE | FIXUP_FLAG_HI_BYTE,
+            len(section_loader.data) + 3,
+            section_payload,
+            addr3
+          ),
+          (
+            FIXUP_FLAG_LO_BYTE | FIXUP_FLAG_HI_BYTE,
+            len(section_loader.data) + 5,
+            section_lzss_unpack,
+            0
+          ),
+        ]
+      )
+    else:
       addr2 += 5 - 0x100
       section_loader.fixups.extend(
         [
@@ -600,21 +558,37 @@ else:
           ),
         ]
       )
-      section_loader.data.extend(
-        [
-          0xa9, 0x00,          # lda #<addr2
-          0xa0, 0x00,          # ldy #>addr2
-          0x20, 0x00, 0x00,    # jsr lzss_unpack_fwd
-        ]
-      )
+    section_loader.data.extend(
+      [
+        0xa9, 0x00,            # lda #<addr(3|2)
+        0xa0, 0x00,            # ldy #>addr(3|2)
+        0x20, 0x00, 0x00,      # jsr lzss_unpack_(rev|fwd)
+      ]
+    )
 
-  # epilogue
-  section_loader.data.extend(
-    [
-      0x4c, entry_point & 0xff, entry_point >> 8,      # jmp entry_point
-    ]
-  )
+# epilogue
+section_loader.data.extend(
+  [
+    0x4c, entry_point & 0xff, entry_point >> 8,        # jmp entry_point
+  ]
+)
 
+# sections defines section order in the a2bin file, bottom to top:
+sections = [section_payload, section_lzss_unpack, section_loader]
+if rev:
+  sections = sections[::-1]
+
+  # sections that were constructed in reverse can now be made normal
+  section_payload.data = section_payload.data[::-1]
+
+  # relocate from bottom up
+  load_addr = load_or_end_addr
+  end_addr = load_addr
+  for section in sections:
+    section.load_addr = end_addr
+    end_addr += len(section.data)
+  load_size = end_addr - load_addr
+else:
   # relocate from top down
   end_addr = load_or_end_addr
   load_addr = end_addr