Compare commits

...

2 Commits

Author SHA1 Message Date
Zorchenhimer 468f7c780a
Some stuff with the script decoding
I honestly don't remember what these changes are, lol.
2024-09-29 15:03:21 -04:00
Zorchenhimer 3d5790e230
Some cleanup an the decode code
Added a bunch of comments and clarified some variables in the decoding
logic.
2024-09-29 15:00:44 -04:00
9 changed files with 93 additions and 32 deletions

View File

@ -1,8 +1,9 @@
.PHONY: all .PHONY: all
COMMANDS: bin/script-decode all: bin/script-decode bin/sbutil
all: $(COMMANDS) bin/script-decode: cmd/script-decode.go script/*.go
go build -o $@ $<
bin/%: cmd/%.go script/*.go
bin/sbutil: cmd/sbutil.go rom/*.go
go build -o $@ $< go build -o $@ $<

View File

@ -58,7 +58,7 @@ func run(args *Arguments) error {
fmt.Fprintf(outfile, "; Stack address: $%04X\n\n", scr.StackAddress) fmt.Fprintf(outfile, "; Stack address: $%04X\n\n", scr.StackAddress)
for _, token := range scr.Tokens { for _, token := range scr.Tokens {
fmt.Fprintln(outfile, token) fmt.Fprintln(outfile, token.String(scr.Labels))
} }
return nil return nil

View File

@ -10,17 +10,18 @@ import (
// Returns packet, next state, and error (if any) // Returns packet, next state, and error (if any)
type decodeFunction func(page *Page, data []byte, startIdx int) (Packet, int, error) type decodeFunction func(page *Page, data []byte, startIdx int) (Packet, int, error)
// map of states. each state is a map of types // Map of states. Each state is a map of types
var definedPackets = map[int]map[byte]decodeFunction{ var definedPackets = map[int]map[byte]decodeFunction{
0: map[byte]decodeFunction{
0: map[byte]decodeFunction{ // state 3 in firmware.org
0x01: decodeHeader, 0x01: decodeHeader,
}, },
1: map[byte]decodeFunction{ 1: map[byte]decodeFunction{ // state 1 in firmware.org
0x00: decodeMarkDataEnd, 0x00: decodeMarkDataEnd,
}, },
2: map[byte]decodeFunction{ 2: map[byte]decodeFunction{ // state 2 in firmware.org
0x02: decodeSetWorkRamLoad, 0x02: decodeSetWorkRamLoad,
0x03: decodeMarkDataStart, 0x03: decodeMarkDataStart,
0x04: decodeMarkDataStart, 0x04: decodeMarkDataStart,
@ -45,18 +46,19 @@ func (page *Page) decode(data []byte) error {
return nil return nil
} }
stateArg := data[idx+1]
var packet Packet var packet Packet
if page.state == 1 && data[idx+1] != 0x00 { if page.state == 1 && stateArg != 0x00 { // stateArg is length here?
// bulk data // bulk data
packet, page.state, err = decodeBulkData(page, data, idx) packet, page.state, err = decodeBulkData(page, data, idx)
if err != nil { if err != nil {
return err return err
} }
} else { } else {
df, ok := definedPackets[page.state][data[idx+1]] df, ok := definedPackets[page.state][stateArg]
if !ok { if !ok {
return fmt.Errorf("State %d packet with type %02X isn't implemented", return fmt.Errorf("State %d packet with type %02X isn't implemented",
page.state, data[idx+1]) page.state, stateArg)
} }
packet, page.state, err = df(page, data, idx) packet, page.state, err = df(page, data, idx)
if err != nil { if err != nil {
@ -149,7 +151,7 @@ func decodeMarkDataStart(page *Page, data []byte, idx int) (Packet, int, error)
func decodeMarkDataEnd(page *Page, data []byte, idx int) (Packet, int, error) { func decodeMarkDataEnd(page *Page, data []byte, idx int) (Packet, int, error) {
packet := &packetMarkDataEnd{ packet := &packetMarkDataEnd{
Type: data[idx+2], Type: data[idx+2],
Reset: (data[idx+2]&0xF0 == 0xF0), Reset: (data[idx+2]&0xF0 == 0xF0), // what is this?
checksum: data[idx+3], checksum: data[idx+3],
address: page.DataOffset + idx, address: page.DataOffset + idx,
} }
@ -195,6 +197,9 @@ func decodeSetWorkRamLoad(page *Page, data []byte, idx int) (Packet, int, error)
} }
func decodeBulkData(page *Page, data []byte, idx int) (Packet, int, error) { func decodeBulkData(page *Page, data []byte, idx int) (Packet, int, error) {
// idx+1: length of data
// idx+2: data start
if data[idx+1] == 0 { if data[idx+1] == 0 {
return nil, 0, fmt.Errorf("Bulk data packet has a length of zero at offset %08X", return nil, 0, fmt.Errorf("Bulk data packet has a length of zero at offset %08X",
page.DataOffset+idx) page.DataOffset+idx)
@ -206,8 +211,9 @@ func decodeBulkData(page *Page, data []byte, idx int) (Packet, int, error) {
datalen := int(data[idx+1]) datalen := int(data[idx+1])
packet.Data = data[idx+2 : idx+2+datalen] packet.Data = data[idx+2 : idx+2+datalen]
packet.checksum = data[idx+len(packet.Data)+2] packet.checksum = data[idx+2+len(packet.Data)]
// checksum includes the packet ID and data length values
checksum := calcChecksum(data[idx : idx+int(data[idx+1])+2]) checksum := calcChecksum(data[idx : idx+int(data[idx+1])+2])
if checksum != packet.checksum { if checksum != packet.checksum {
data := []string{} data := []string{}

View File

@ -13,6 +13,10 @@ func (sb *StudyBox) Export(directory string) error {
Audio: directory + "/audio" + sb.Audio.ext(), Audio: directory + "/audio" + sb.Audio.ext(),
} }
// A "Page" here does not correspond to the entered "Page" number on the
// title screen. These are really segments. The "Page" that is entered on
// the title screen is stored in the header of a segment. Multiple
// segments can have the same "Page" number.
for pidx, page := range sb.Data.Pages { for pidx, page := range sb.Data.Pages {
jp := jsonPage{ jp := jsonPage{
AudioOffsetLeadIn: page.AudioOffsetLeadIn, AudioOffsetLeadIn: page.AudioOffsetLeadIn,
@ -20,7 +24,7 @@ func (sb *StudyBox) Export(directory string) error {
Data: []jsonData{}, Data: []jsonData{},
} }
file, err := os.Create(fmt.Sprintf("%s/page%02d_0000.txt", directory, pidx)) file, err := os.Create(fmt.Sprintf("%s/segment-%02d_packet-0000.txt", directory, pidx))
if err != nil { if err != nil {
return err return err
} }
@ -74,13 +78,13 @@ func (sb *StudyBox) Export(directory string) error {
switch jData.Type { switch jData.Type {
case "pattern": case "pattern":
jData.File = fmt.Sprintf("%s/page%02d_%04d_chrData.chr", directory, pidx, dataStartId) jData.File = fmt.Sprintf("%s/segment-%02d_packet-%04d_chrData.chr", directory, pidx, dataStartId)
case "nametable": case "nametable":
jData.File = fmt.Sprintf("%s/page%02d_%04d_ntData.dat", directory, pidx, dataStartId) jData.File = fmt.Sprintf("%s/segment-%02d_packet-%04d_ntData.dat", directory, pidx, dataStartId)
case "script": case "script":
jData.File = fmt.Sprintf("%s/page%02d_%04d_scriptData.dat", directory, pidx, dataStartId) jData.File = fmt.Sprintf("%s/segment-%02d_packet-%04d_scriptData.dat", directory, pidx, dataStartId)
//script, err := DissassembleScript(scriptData) //script, err := DissassembleScript(scriptData)
//if err != nil { //if err != nil {
@ -118,7 +122,7 @@ func (sb *StudyBox) Export(directory string) error {
rawData = append(rawData, p.Data...) rawData = append(rawData, p.Data...)
default: default:
return fmt.Errorf("Encountered an unknown packet: %s page: %d", p.Asm(), pidx) return fmt.Errorf("Encountered an unknown packet: %s segment: %d", p.Asm(), pidx)
} }
} }

View File

@ -26,7 +26,7 @@ func (ph *packetHeader) RawBytes() []byte {
} }
func (ph *packetHeader) Asm() string { func (ph *packetHeader) Asm() string {
return fmt.Sprintf("header %d ; Checksum: %02X", ph.PageNumber, ph.Checksum) return fmt.Sprintf("header %d [Page %d] ; Checksum: %02X", ph.PageNumber, ph.PageNumber+1, ph.Checksum)
} }
func (ph *packetHeader) Address() int { func (ph *packetHeader) Address() int {
@ -81,7 +81,7 @@ func newPacketWorkRamLoad(bank, addressHigh uint8) *packetWorkRamLoad {
} }
func (p *packetWorkRamLoad) Asm() string { func (p *packetWorkRamLoad) Asm() string {
return fmt.Sprintf("work_ram_load $%02X $%02X ; Checksum %02X", return fmt.Sprintf("work_ram_load bank:$%02X addr:$%02X00 ; Checksum %02X",
p.bankId, p.loadAddressHigh, p.checksum) p.bankId, p.loadAddressHigh, p.checksum)
} }
@ -192,7 +192,7 @@ func (p *packetMarkDataStart) Address() int {
type packetMarkDataEnd struct { type packetMarkDataEnd struct {
//Arg uint8 //Arg uint8
Reset bool Reset bool // what does this mean? what is special about the value 0xF0?
Type uint8 Type uint8
address int address int
@ -251,7 +251,8 @@ func (p *packetMarkDataEnd) Asm() string {
for _, b := range p.RawBytes() { for _, b := range p.RawBytes() {
s = append(s, fmt.Sprintf("%02X", b)) s = append(s, fmt.Sprintf("%02X", b))
} }
return fmt.Sprintf("mark_datatype_end %s ; %s Checksum: %02X", tstr, strings.Join(s, " "), p.checksum) return fmt.Sprintf("mark_datatype_end %s ; %s Checksum: %02X",
tstr, strings.Join(s, " "), p.checksum)
} }
func (p *packetMarkDataEnd) Address() int { func (p *packetMarkDataEnd) Address() int {

View File

@ -52,6 +52,8 @@ var Instructions []*Instruction = []*Instruction{
&Instruction{ 0x9B, 0, 0, 0, "halt"}, &Instruction{ 0x9B, 0, 0, 0, "halt"},
&Instruction{ 0x9C, 0, 0, 0, "toggle_44FE"}, &Instruction{ 0x9C, 0, 0, 0, "toggle_44FE"},
&Instruction{ 0x9D, 2, 0, 0, "something_tape"}, &Instruction{ 0x9D, 2, 0, 0, "something_tape"},
// Calls 0xEB draw_overlay. Seems to draw a whole screen.
&Instruction{ 0x9E, 2, 0, 0, ""}, &Instruction{ 0x9E, 2, 0, 0, ""},
&Instruction{ 0x9F, 6, 0, 0, ""}, &Instruction{ 0x9F, 6, 0, 0, ""},
@ -135,7 +137,14 @@ var Instructions []*Instruction = []*Instruction{
&Instruction{ 0xDA, 1, 0, 16, "to_int_string"}, &Instruction{ 0xDA, 1, 0, 16, "to_int_string"},
&Instruction{ 0xDB, 3, 0, 0, ""}, &Instruction{ 0xDB, 3, 0, 0, ""},
&Instruction{ 0xDC, 5, 0, 0, ""}, &Instruction{ 0xDC, 5, 0, 0, ""},
&Instruction{ 0xDD, 5, 0, 0, ""},
// ArgA, ArgB: X,Y of corner A
// ArgC, ArgD: X,Y of corner B
// ArgE: fill value. This is an index into
// the table at $B451.
// Fills a box with a tile
&Instruction{ 0xDD, 5, 0, 0, "fill_box"},
&Instruction{ 0xDE, 3, 0, 0, ""}, &Instruction{ 0xDE, 3, 0, 0, ""},
&Instruction{ 0xDF, 3, 0, 0, ""}, &Instruction{ 0xDF, 3, 0, 0, ""},
@ -153,7 +162,14 @@ var Instructions []*Instruction = []*Instruction{
&Instruction{ 0xE8, 1, 0, 0, "setup_tape_nmi"}, &Instruction{ 0xE8, 1, 0, 0, "setup_tape_nmi"},
&Instruction{ 0xE9, 0, 1, 0, "setup_loop"}, &Instruction{ 0xE9, 0, 1, 0, "setup_loop"},
&Instruction{ 0xEA, 0, 0, 0, "string_write_to_table"}, &Instruction{ 0xEA, 0, 0, 0, "string_write_to_table"},
&Instruction{ 0xEB, 4, 0, 0, ""},
// Reads and saves tiles from the PPU, then draws over them.
// This is used to draw dialog boxes, so saving what it overwrites
// so it can re-draw them later makes sense.
// Not sure what the arguments actually mean.
// ArgB and ArgC are probably coordinates.
&Instruction{ 0xEB, 4, 0, 0, "draw_overlay"},
&Instruction{ 0xEC, 2, 0, 0, "scroll"}, &Instruction{ 0xEC, 2, 0, 0, "scroll"},
&Instruction{ 0xED, 1, 0, 0, "disable_sprites"}, &Instruction{ 0xED, 1, 0, 0, "disable_sprites"},
&Instruction{ 0xEE, 1, -3, 0, "call_switch"}, &Instruction{ 0xEE, 1, -3, 0, "call_switch"},

View File

@ -24,7 +24,9 @@ func Parse(rawinput []byte, startAddr int) (*Script, error) {
Warnings: []string{}, Warnings: []string{},
StackAddress: (int(rawinput[1])<<8) | int(rawinput[0]), StackAddress: (int(rawinput[1])<<8) | int(rawinput[0]),
StartAddress: startAddr, StartAddress: startAddr,
Labels: make(map[int]string), // map[location]name
} }
tokenMap := make(map[int]*Token)
for i := 2; i < len(rawinput); i++ { for i := 2; i < len(rawinput); i++ {
raw := rawinput[i] raw := rawinput[i]
@ -35,6 +37,7 @@ func Parse(rawinput []byte, startAddr int) (*Script, error) {
Inline: []InlineVal{}, Inline: []InlineVal{},
} }
script.Tokens = append(script.Tokens, token) script.Tokens = append(script.Tokens, token)
tokenMap[token.Offset] = token
if raw < 0x80 { if raw < 0x80 {
continue continue
@ -102,6 +105,7 @@ func Parse(rawinput []byte, startAddr int) (*Script, error) {
if tok.Offset == addr { if tok.Offset == addr {
tok.IsTarget = true tok.IsTarget = true
found = true found = true
script.Labels[addr] = fmt.Sprintf("L%04X", addr)
break break
} }
} }
@ -122,6 +126,7 @@ func Parse(rawinput []byte, startAddr int) (*Script, error) {
if tok.Offset == addr { if tok.Offset == addr {
tok.IsTarget = true tok.IsTarget = true
found = true found = true
script.Labels[addr] = fmt.Sprintf("L%04X", addr)
break break
} }
} }
@ -130,6 +135,19 @@ func Parse(rawinput []byte, startAddr int) (*Script, error) {
script.Warnings = append(script.Warnings, fmt.Sprintf("Warning: no target found for jump/call switch at offset $%04X; value: $%04X", t.Offset, addr)) script.Warnings = append(script.Warnings, fmt.Sprintf("Warning: no target found for jump/call switch at offset $%04X; value: $%04X", t.Offset, addr))
} }
} }
default:
// if word arg, see if it's something in this script
if t.Instruction == nil {
continue
}
if t.Instruction.OpCount == 2 {
addr := t.Inline[0].Int()
if tok, ok := tokenMap[addr]; ok {
tok.IsVariable = true
script.Labels[addr] = fmt.Sprintf("Var_%04X", addr)
}
}
} }
} }

View File

@ -9,4 +9,6 @@ type Script struct {
StartAddress int StartAddress int
StackAddress int StackAddress int
Labels map[int]string
} }

View File

@ -9,12 +9,13 @@ type Token struct {
Offset int Offset int
Raw byte Raw byte
Inline []InlineVal Inline []InlineVal
IsTarget bool // target of a call/jump? IsTarget bool // target of a call/jump?
IsVariable bool // target of something else
Instruction *Instruction Instruction *Instruction
} }
func (t Token) String() string { func (t Token) String(labels map[int]string) string {
suffix := "" suffix := ""
switch t.Raw { switch t.Raw {
case 0x86: case 0x86:
@ -22,8 +23,12 @@ func (t Token) String() string {
} }
prefix := "" prefix := ""
if t.IsTarget { if t.IsTarget || t.IsVariable {
prefix = fmt.Sprintf("\nL%04X:\n", t.Offset) if lbl, ok := labels[t.Offset]; ok {
prefix = "\n"+lbl+":\n"
} else {
prefix = fmt.Sprintf("\nL%04X:\n", t.Offset)
}
} }
if t.Raw < 0x80 { if t.Raw < 0x80 {
@ -50,18 +55,26 @@ func (t Token) String() string {
argstr := []string{} argstr := []string{}
for _, a := range t.Inline { for _, a := range t.Inline {
argstr = append(argstr, a.HexString()) if lbl, ok := labels[a.Int()]; ok {
argstr = append(argstr, lbl)
} else {
argstr = append(argstr, a.HexString())
}
} }
bytestr := []string{} bytestr := []string{}
for _, a := range t.Inline { for _, a := range t.Inline {
for _, b := range a.Bytes() { for _, b := range a.Bytes() {
bytestr = append(bytestr, fmt.Sprintf("%02X", b)) //if lbl, ok := labels[a.Int()]; ok {
// bytestr = append(bytestr, lbl)
//} else {
bytestr = append(bytestr, fmt.Sprintf("%02X", b))
//}
} }
} }
switch t.Raw { switch t.Raw {
case 0xBB: case 0xBB: // push_data
bs := []byte{} bs := []byte{}
for _, val := range t.Inline { for _, val := range t.Inline {
bs = append(bs, val.Bytes()...) bs = append(bs, val.Bytes()...)