We just released a Feb. 5 '89 prototype of DuckTales for the NES!
If you'd like to support our preservation efforts (and this wasn't cheap), please consider donating or supporting us on Patreon. Thank you!

Notes:Galaxy Force II (Arcade)

From The Cutting Room Floor
Jump to navigation Jump to search

This page contains notes for the game Galaxy Force II (Arcade).

Sound Notes

Only the main CPU can access the Z80, but the main game logic is on the suby CPU. This means the suby CPU has to tell the main CPU what to send to the Z80. However, some of the logic is handled on the main CPU, leading to the main CPU telling the suby CPU some of what to play, and suby sending both its own requests and the main CPU's back to the main CPU. The subx CPU does not appear to issue any sound requests.

Key routine and memory locations:

  • maincpu
    • Address of routine that actually sends sound codes: $1D9A
    • Address maincpu writes sound codes to: byte $82001 (equivalent to byte $F800 in the Z80 address space?)
To do:
it should link to $F801 but the sound code itself gets the value from $F800...
  • suby
    • Routines that play a sound (sound code in d0): $1378E (most of game), $137B4 (sound test, insert coin sound, and jumped to by the other routine)
    • Routine that sends sound codes to the maincpu: $140D0 (this is also the routine that handles codes maincpu sends to suby)
    • Sound test routine start: $753C
    • Sound test sound code array start: $8C54 (one byte per code, in the order given on the sound test screen, with voice sample codes coming immediately after sound effect/music codes)
  • Shared RAM (all three 68000s map to $C0000)
    • Address suby writes sound codes to send to maincpu: byte $C00D0
    • Address suby writes a nonzero value to to tell maincpu that a sound has arrived: byte $C00D1
    • Address maincpu writes its own codes to send to suby: byte $C00D2

maincpu only seems to ask for sound codes $90, $99, $ED, and $EE; $90 is also requested by suby.

Text Encoding

When text is displayed, the game's text routines apply an index to each character, presumably to choose a sprite bank to take the character sprites from. Regardless of the chosen sprite bank, all the strings in the game seem to use the same character format:

Byte Char Byte Char Byte Char Byte Char
$00 0 $01 1 $02 2 $03 3
$04 4 $05 5 $06 6 $07 7
$08 8 $09 9 $0A A $0B B
$0C C $0D D $0E E $0F F
$10 G $11 H $12 I $13 J
$14 K $15 L $16 M $17 N
$18 O $19 P $1A Q $1B R
$1C S $1D T $1E U $1F V
$20 W $21 X $22 Y $23 Z
$24 (space) $25 a $26 b $27 c
$28 d $29 e $2A f $2B g
$2C h $2D i $2E j $2F k
$30 l $31 m $32 n $33 o
$34 p $35 q $36 r $37 s
$38 t $39 u $3A v $3B w
$3C x $3D y $3E z $3F !
$40 / $41 _ $42 . $43 ,

Any higher byte value will show random sprites. Note that for strings, the byte value 0 will actually end the string — custom routines are used to print numbers intead.

The following Go program will read a ROM from standard input, producing an identical ROM on standard input with valid bytes converted to their character equivalents (without the 0 digit character) and all other bytes converted to null bytes (useful for the strings utility on Unix systems).

// pietro gagliardi (andlabs)
// 3 january 2013
package main

import (

var chars [0x100][]byte

func init() {
	// in reality the character at position 0 is the digit 0 but the string print routines cannot show this character (they stop on a 0 byte)
	const base = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz!/_.,"
	for i := 1; i < len(base); i++ {
		chars[i] = []byte{base[i - 1]}

func main() {
	b := make([]byte, 1)
	for {
		n, err := os.Stdin.Read(b)
		if err == io.EOF {
		} else if err != nil {
		} else if n != 1 {
			panic("short read without error")
		if chars[b[0]] != nil {
		} else {		// fill the rest with blank space