Mail has been fixed; you should now be able to confirm your e-mail address, watch pages, and the like.
Please report any issues on Discord.

Notes:688 Attack Sub (Genesis)

From The Cutting Room Floor
Jump to navigation Jump to search

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.