Compare commits
No commits in common. "468f7c780af684d74bfe0d20a1d176016eea6651" and "928a8e2fafc06358830e89c174ba98e62d87873e" have entirely different histories.
468f7c780a
...
928a8e2faf
7
Makefile
7
Makefile
|
@ -1,9 +1,8 @@
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
|
|
||||||
all: bin/script-decode bin/sbutil
|
COMMANDS: bin/script-decode
|
||||||
|
|
||||||
bin/script-decode: cmd/script-decode.go script/*.go
|
all: $(COMMANDS)
|
||||||
go build -o $@ $<
|
|
||||||
|
|
||||||
bin/sbutil: cmd/sbutil.go rom/*.go
|
bin/%: cmd/%.go script/*.go
|
||||||
go build -o $@ $<
|
go build -o $@ $<
|
||||||
|
|
|
@ -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.String(scr.Labels))
|
fmt.Fprintln(outfile, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -10,18 +10,17 @@ 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{ // state 1 in firmware.org
|
1: map[byte]decodeFunction{
|
||||||
0x00: decodeMarkDataEnd,
|
0x00: decodeMarkDataEnd,
|
||||||
},
|
},
|
||||||
|
|
||||||
2: map[byte]decodeFunction{ // state 2 in firmware.org
|
2: map[byte]decodeFunction{
|
||||||
0x02: decodeSetWorkRamLoad,
|
0x02: decodeSetWorkRamLoad,
|
||||||
0x03: decodeMarkDataStart,
|
0x03: decodeMarkDataStart,
|
||||||
0x04: decodeMarkDataStart,
|
0x04: decodeMarkDataStart,
|
||||||
|
@ -46,19 +45,18 @@ 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 && stateArg != 0x00 { // stateArg is length here?
|
if page.state == 1 && data[idx+1] != 0x00 {
|
||||||
// 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][stateArg]
|
df, ok := definedPackets[page.state][data[idx+1]]
|
||||||
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, stateArg)
|
page.state, data[idx+1])
|
||||||
}
|
}
|
||||||
packet, page.state, err = df(page, data, idx)
|
packet, page.state, err = df(page, data, idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -151,7 +149,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), // what is this?
|
Reset: (data[idx+2]&0xF0 == 0xF0),
|
||||||
checksum: data[idx+3],
|
checksum: data[idx+3],
|
||||||
address: page.DataOffset + idx,
|
address: page.DataOffset + idx,
|
||||||
}
|
}
|
||||||
|
@ -197,9 +195,6 @@ 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)
|
||||||
|
@ -211,9 +206,8 @@ 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+2+len(packet.Data)]
|
packet.checksum = data[idx+len(packet.Data)+2]
|
||||||
|
|
||||||
// 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{}
|
||||||
|
|
|
@ -13,10 +13,6 @@ 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,
|
||||||
|
@ -24,7 +20,7 @@ func (sb *StudyBox) Export(directory string) error {
|
||||||
Data: []jsonData{},
|
Data: []jsonData{},
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := os.Create(fmt.Sprintf("%s/segment-%02d_packet-0000.txt", directory, pidx))
|
file, err := os.Create(fmt.Sprintf("%s/page%02d_0000.txt", directory, pidx))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -78,13 +74,13 @@ func (sb *StudyBox) Export(directory string) error {
|
||||||
|
|
||||||
switch jData.Type {
|
switch jData.Type {
|
||||||
case "pattern":
|
case "pattern":
|
||||||
jData.File = fmt.Sprintf("%s/segment-%02d_packet-%04d_chrData.chr", directory, pidx, dataStartId)
|
jData.File = fmt.Sprintf("%s/page%02d_%04d_chrData.chr", directory, pidx, dataStartId)
|
||||||
|
|
||||||
case "nametable":
|
case "nametable":
|
||||||
jData.File = fmt.Sprintf("%s/segment-%02d_packet-%04d_ntData.dat", directory, pidx, dataStartId)
|
jData.File = fmt.Sprintf("%s/page%02d_%04d_ntData.dat", directory, pidx, dataStartId)
|
||||||
|
|
||||||
case "script":
|
case "script":
|
||||||
jData.File = fmt.Sprintf("%s/segment-%02d_packet-%04d_scriptData.dat", directory, pidx, dataStartId)
|
jData.File = fmt.Sprintf("%s/page%02d_%04d_scriptData.dat", directory, pidx, dataStartId)
|
||||||
|
|
||||||
//script, err := DissassembleScript(scriptData)
|
//script, err := DissassembleScript(scriptData)
|
||||||
//if err != nil {
|
//if err != nil {
|
||||||
|
@ -122,7 +118,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 segment: %d", p.Asm(), pidx)
|
return fmt.Errorf("Encountered an unknown packet: %s page: %d", p.Asm(), pidx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ func (ph *packetHeader) RawBytes() []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ph *packetHeader) Asm() string {
|
func (ph *packetHeader) Asm() string {
|
||||||
return fmt.Sprintf("header %d [Page %d] ; Checksum: %02X", ph.PageNumber, ph.PageNumber+1, ph.Checksum)
|
return fmt.Sprintf("header %d ; Checksum: %02X", ph.PageNumber, 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 bank:$%02X addr:$%02X00 ; Checksum %02X",
|
return fmt.Sprintf("work_ram_load $%02X $%02X ; 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 // what does this mean? what is special about the value 0xF0?
|
Reset bool
|
||||||
Type uint8
|
Type uint8
|
||||||
|
|
||||||
address int
|
address int
|
||||||
|
@ -251,8 +251,7 @@ 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",
|
return fmt.Sprintf("mark_datatype_end %s ; %s Checksum: %02X", tstr, strings.Join(s, " "), p.checksum)
|
||||||
tstr, strings.Join(s, " "), p.checksum)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *packetMarkDataEnd) Address() int {
|
func (p *packetMarkDataEnd) Address() int {
|
||||||
|
|
|
@ -52,8 +52,6 @@ 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, ""},
|
||||||
|
|
||||||
|
@ -137,14 +135,7 @@ 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, ""},
|
||||||
|
|
||||||
|
@ -162,14 +153,7 @@ 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"},
|
||||||
|
|
|
@ -24,9 +24,7 @@ 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]
|
||||||
|
@ -37,7 +35,6 @@ 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
|
||||||
|
@ -105,7 +102,6 @@ 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,7 +122,6 @@ 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,19 +130,6 @@ 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,4 @@ type Script struct {
|
||||||
|
|
||||||
StartAddress int
|
StartAddress int
|
||||||
StackAddress int
|
StackAddress int
|
||||||
|
|
||||||
Labels map[int]string
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,13 +9,12 @@ 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(labels map[int]string) string {
|
func (t Token) String() string {
|
||||||
suffix := ""
|
suffix := ""
|
||||||
switch t.Raw {
|
switch t.Raw {
|
||||||
case 0x86:
|
case 0x86:
|
||||||
|
@ -23,12 +22,8 @@ func (t Token) String(labels map[int]string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
prefix := ""
|
prefix := ""
|
||||||
if t.IsTarget || t.IsVariable {
|
if t.IsTarget {
|
||||||
if lbl, ok := labels[t.Offset]; ok {
|
prefix = fmt.Sprintf("\nL%04X:\n", t.Offset)
|
||||||
prefix = "\n"+lbl+":\n"
|
|
||||||
} else {
|
|
||||||
prefix = fmt.Sprintf("\nL%04X:\n", t.Offset)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.Raw < 0x80 {
|
if t.Raw < 0x80 {
|
||||||
|
@ -55,26 +50,18 @@ func (t Token) String(labels map[int]string) string {
|
||||||
|
|
||||||
argstr := []string{}
|
argstr := []string{}
|
||||||
for _, a := range t.Inline {
|
for _, a := range t.Inline {
|
||||||
if lbl, ok := labels[a.Int()]; ok {
|
argstr = append(argstr, a.HexString())
|
||||||
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() {
|
||||||
//if lbl, ok := labels[a.Int()]; ok {
|
bytestr = append(bytestr, fmt.Sprintf("%02X", b))
|
||||||
// bytestr = append(bytestr, lbl)
|
|
||||||
//} else {
|
|
||||||
bytestr = append(bytestr, fmt.Sprintf("%02X", b))
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch t.Raw {
|
switch t.Raw {
|
||||||
case 0xBB: // push_data
|
case 0xBB:
|
||||||
bs := []byte{}
|
bs := []byte{}
|
||||||
for _, val := range t.Inline {
|
for _, val := range t.Inline {
|
||||||
bs = append(bs, val.Bytes()...)
|
bs = append(bs, val.Bytes()...)
|
||||||
|
|
Loading…
Reference in New Issue