# 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)
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
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
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
[
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
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
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(
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(
[
),
]
)
- 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