This page contains notes for the game 688 Attack Sub (Genesis).
Compressed Data#
For those who are wondering, these are the locations of all compressed blobs in the game. If you look, they'll appear to be PowerPacker-compressed blobs. But they're not quite PowerPacker format... and each blob has a size field stored elsewhere.
Address |
Size Field |
|---|---|
000146cc |
$9C8 + 8 |
000150bc |
$330 + 8 |
0002020e |
$C04 + 8 |
00020e3a |
$364 + 8 |
000211b4 |
$9FC + 8 |
00021bd8 |
$318 + 8 |
00021f06 |
$1D8 + 8 |
000220f4 |
$59C + 8 |
000226b8 |
$20C + 8 |
000228da |
$B4 + 8 |
00023328 |
$CC4 + 8 |
00024014 |
$2BC + 8 |
000242e6 |
$E4C + 8 |
0002515a |
$304 + 8 |
00025472 |
$2C0 |
00025732 |
$274 |
0002652c |
$1A4C + 8 |
00027fa0 |
$4FC + 8 |
000284b2 |
$1210 + 8 |
000296ca |
$304 + 8 |
000299e4 |
$E2C + 8 |
0002a818 |
$2C4 + 8 |
0002aaf2 |
$FE0 + 8 |
0002bada |
$2A8 + 8 |
0002bd98 |
$12C0 + 8 |
0002d060 |
$2B0 + 8 |
000318d0 |
$9DC + 8 |
000322d4 |
$310 + 8 |
000325fa |
$91C + 8 |
00032f3e |
$2F8 + 8 |
0003324c |
$7DC + 8 |
00033a50 |
$2A0 + 8 |
00033d06 |
$910 + 8 |
0003463e |
$2D4 + 8 |
00034928 |
$29C + 8 |
00034bec |
$160 + 8 |
00034d62 |
$45C + 8 |
000351e6 |
$188 + 8 |
000353d6 |
$49C |
00035872 |
$460 |
00035cd2 |
$65C |
0003632e |
$57C |
000368aa |
$614 |
00036ebe |
$57C |
0003743a |
$6C8 |
00037b02 |
$680 |
00038182 |
$704 |
00038886 |
$590 |
00038e16 |
$4E8 |
000392fe |
$5F8 |
000398f6 |
$5D0 |
00039ec6 |
$654 |
0003a51a |
$6BC |
0003abd6 |
$718 |
0003b2fe |
$804 + 8 |
0003bb2a |
$2D0 + 8 |
0003be10 |
$8F8 + 8 |
0003c730 |
$2E0 + 8 |
0007e0d6 |
$844 |
0007e91a |
$6BC |
0007efd6 |
$7BC |
0007f792 |
$8A4 |
00080036 |
$6A4 |
000806da |
$6BC |
00080d96 |
$4F4 |
0008128a |
$5B0 |
0008183a |
$8E4 |
0008211e |
$554 |
00082672 |
$59C |
00082c0e |
$828 |
00083436 |
$79C |
00083bd2 |
$504 |
000840d6 |
$46C |
00084542 |
$4AC |
00084d32 |
$1C34 + 8 |
000869ae |
$564 + 8 |
00086f28 |
$355C + 8 |
0008a48c |
$858 + 8 |
00095e9a |
$8D0 + 8 |
00096792 |
$2D0 + 8 |
00096a78 |
$8C8 + 8 |
00097368 |
$2EC + 8 |
0009766a |
$358 + 8 |
000979ca |
$104 + 8 |
00097ae4 |
$1CC + 8 |
00097cb8 |
$8C + 8 |
00097d5a |
$170 + 8 |
00097ed2 |
$78 + 8 |
Looks like there's no unused text or staff rolls here. :( Have the decompression anyway (input is the above table, columns separated by tabs).
// 26 june 2015
package main
import (
"fmt"
"os"
"io/ioutil"
"bufio"
"encoding/binary"
"strings"
"strconv"
)
// utilities
func readbyte(from []byte, addr uint32) byte {
return from[addr]
}
func readword(from []byte, addr uint32) uint16 {
return binary.BigEndian.Uint16(from[addr:])
}
func readlong(from []byte, addr uint32) uint32 {
return binary.BigEndian.Uint32(from[addr:])
}
func writebyte(to []byte, addr uint32, b byte) {
to[addr] = b
}
func writeword(to []byte, addr uint32, w uint16) {
binary.BigEndian.PutUint16(to[addr:], w)
}
func writelong(to []byte, addr uint32, l uint32) {
binary.BigEndian.PutUint32(to[addr:], l)
}
func move_b(dest uint32, src uint32) uint32 {
dest &= 0xFFFFFF00
dest |= src & 0xFF
return dest
}
func sub_b(dest uint32, src uint32) uint32 {
b := byte(dest)
b -= byte(src)
return move_b(dest, uint32(b))
}
func add_w(dest uint32, src uint32) uint32 {
w := uint16(dest)
w += uint16(src)
return move_w(dest, uint32(w))
}
func move_w(dest uint32, src uint32) uint32 {
dest &= 0xFFFF0000
dest |= src & 0xFFFF
return dest
}
func dbf(reg *uint32) bool {
w := uint16(*reg)
w--
*reg = move_w(*reg, uint32(w))
return w != 0xFFFF
}
// note the parameter order here
func carryset(src uint32, dest uint32) bool {
return dest < src
}
func aX_dX_w(aX uint32, dX uint32) uint32 {
// sign-extend dX as a 16-bit value to 32-bits
ddX := uint32(int32(int16(uint16(dX))))
return aX + ddX
}
func addq_w(dest uint32, src uint32) uint32 {
return add_w(dest, src)
}
func subq_w(dest uint32, src uint32) uint32 {
return sub_w(dest, src)
}
var extend uint32 = 0
func roxl_l(dest uint32, src uint32) uint32 {
for {
highbit := src & 0x80000000
dest <<= 1
dest |= extend
extend = highbit >> 31
src--
if src == 0 {
break
}
}
return dest
}
func subq_b(dest uint32, src uint32) uint32 {
return sub_b(dest, src)
}
func sub_w(dest uint32, src uint32) uint32 {
w := uint16(dest)
w -= uint16(src)
return move_w(dest, uint32(w))
}
// the decompressor itself
var ROM []byte
var RAM = make([]byte, 0x10000)
var d0, d1, d2, d3, d4, d5, d6, d7 uint32
var a0, a1, a2, a3, a4, a5, a6 uint32
var assumedEnd uint32
// (a0:source(ROM) a1:destination(RAM) d0:length -- )
func decomp() {
a5 = 0x45F6 // RAM
writelong(RAM, a5, readlong(ROM, a0 + 4))
a0 += d0
a2 = a1 // RAM
a0 -= 4; d5 = readlong(ROM, a0)
d1 = 0
d1 = move_b(d1, d5)
d5 >>= 8
a1 += d5
assumedEnd = a1
a0 -= 4; d5 = readlong(ROM, a0)
d5 >>= d1
d7 = move_b(d7, 0x20)
d7 = sub_b(d7, d1)
loc_FAF6:
sub_FB62()
if (d1 & 0xFF) != 0 {
goto loc_FB1C
}
d2 = 0
loc_FAFE:
d0 = 2
sub_FB64()
d2 = add_w(d2, d1)
if (d1 & 0xFFFF) == 3 {
goto loc_FAFE
}
loc_FB0A:
d0 = move_w(d0, 8)
sub_FB64()
a1--; writebyte(RAM, a1, byte(d1))
if dbf(&d2) {
goto loc_FB0A
}
if carryset(a1, a2) {
goto loc_FB1C
}
return
loc_FB1C:
d0 = 2
sub_FB64()
d0 = 0
d0 = move_b(d0, uint32(readbyte(RAM, aX_dX_w(a5, d1))))
d4 = d0
d2 = move_w(d2, d1)
d2 = addq_w(d2, 1)
if (d2 & 0xFFF) != 4 {
goto loc_FB4E
}
sub_FB62()
d0 = d4
if (d1 & 0xFF) != 0 {
goto loc_FB3C
}
d0 = 7
loc_FB3C:
sub_FB64()
d3 = move_w(d3, d1)
loc_FB40:
d0 = 3
sub_FB64()
d2 = add_w(d2, d1)
if (d1 & 0xFFFF) == 7 {
goto loc_FB40
}
goto loc_FB52
loc_FB4E:
sub_FB64()
d3 = move_w(d3, d1)
loc_FB52:
d0 = move_b(d0, uint32(readbyte(RAM, aX_dX_w(a1, d3))))
a1--; writebyte(RAM, a1, byte(d0))
if dbf(&d2) {
goto loc_FB52
}
if carryset(a1, a2) {
goto loc_FAF6
}
return
}
func sub_FB62() {
d0 = 1
sub_FB64()
}
func sub_FB64() {
d1 = 0
d0 = subq_w(d0, 1)
loc_FB68:
extend = d5 & 1
d5 >>= 1
d1 = roxl_l(d1, 1)
d7 = subq_b(d7, 1)
if (d7 & 0xFF) != 0 {
goto loc_FB76
}
d7 = move_b(d7, 0x20)
a0 -= 4; d5 = readlong(ROM, a0)
loc_FB76:
if dbf(&d0) {
goto loc_FB68
}
return
}
// the program
func main() {
var err error
if len(os.Args) != 3 {
fmt.Fprintf(os.Stderr, "usage: %s ROM locations\n", os.Args[0])
os.Exit(1)
}
ROM, err = ioutil.ReadFile(os.Args[1])
if err != nil {
fmt.Fprintf(os.Stderr, "error reading ROM: %v", err)
os.Exit(1)
}
f, err := os.Open(os.Args[2])
if err != nil {
fmt.Fprintf(os.Stderr, "error opening location list: %v", err)
os.Exit(1)
}
defer f.Close()
r := bufio.NewScanner(f)
for r.Scan() {
line := r.Text()
parts := strings.Split(line, "\t")
add8 := strings.Contains(parts[1], "+ 8")
parts[1] = strings.Replace(parts[1], "+ 8", "", -1)
parts[1] = strings.Replace(parts[1], "$", "", -1)
parts[0] = strings.TrimSpace(parts[0])
parts[1] = strings.TrimSpace(parts[1])
offset, err := strconv.ParseUint(parts[0], 16, 32)
if err != nil {
panic(err)
}
size, err := strconv.ParseUint(parts[1], 16, 32)
if err != nil {
panic(err)
}
if add8 {
size += 8
}
a0 = uint32(offset)
a1 = 0x8000
d0 = uint32(size)
decomp()
err = ioutil.WriteFile(parts[0], RAM[0x8000:assumedEnd], 0644)
if err != nil {
fmt.Fprintf(os.Stderr, "error writing file %s: %v", parts[0], err)
os.Exit(1)
}
}
if err := r.Err(); err != nil {
fmt.Fprintf(os.Stderr, "error reading location list: %v", err)
os.Exit(1)
}
}
Ed Fletcher#
It turns out that the string I was trying so hard to find was encoded differently. It's stored at $146A4, with each byte stored $20 lower than its actual ASCII value, and ending with an $FF byte.