Compare commits
No commits in common. "045f204a6bc2dcde5ab14cc4b90a72468635c009" and "468f7c780af684d74bfe0d20a1d176016eea6651" have entirely different histories.
045f204a6b
...
468f7c780a
5
Makefile
5
Makefile
|
@ -1,12 +1,9 @@
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
|
|
||||||
all: bin/script-decode bin/sbutil bin/just-stats
|
all: bin/script-decode bin/sbutil
|
||||||
|
|
||||||
bin/script-decode: cmd/script-decode.go script/*.go
|
bin/script-decode: cmd/script-decode.go script/*.go
|
||||||
go build -o $@ $<
|
go build -o $@ $<
|
||||||
|
|
||||||
bin/sbutil: cmd/sbutil.go rom/*.go
|
bin/sbutil: cmd/sbutil.go rom/*.go
|
||||||
go build -o $@ $<
|
go build -o $@ $<
|
||||||
|
|
||||||
bin/just-stats: cmd/just-stats.go script/*.go
|
|
||||||
go build -o $@ $<
|
|
||||||
|
|
|
@ -1,85 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"io/fs"
|
|
||||||
|
|
||||||
"github.com/alexflint/go-arg"
|
|
||||||
|
|
||||||
"git.zorchenhimer.com/Zorchenhimer/go-studybox/script"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Arguments struct {
|
|
||||||
BaseDir string `arg:"positional,required"`
|
|
||||||
Output string `arg:"positional,required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Walker struct {
|
|
||||||
Found []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Walker) WalkFunc(path string, info fs.DirEntry, err error) error {
|
|
||||||
if info.IsDir() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasSuffix(path, "_scriptData.dat") {
|
|
||||||
w.Found = append(w.Found, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func run(args *Arguments) error {
|
|
||||||
w := &Walker{Found: []string{}}
|
|
||||||
err := filepath.WalkDir(args.BaseDir, w.WalkFunc)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("found %d scripts\n", len(w.Found))
|
|
||||||
|
|
||||||
stats := make(script.Stats)
|
|
||||||
|
|
||||||
for _, file := range w.Found {
|
|
||||||
fmt.Println(file)
|
|
||||||
scr, err := script.ParseFile(file, 0x0000)
|
|
||||||
if err != nil {
|
|
||||||
if scr != nil {
|
|
||||||
for _, token := range scr.Tokens {
|
|
||||||
fmt.Println(token.String(scr.Labels))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
stats.Add(scr.Stats())
|
|
||||||
}
|
|
||||||
|
|
||||||
outfile, err := os.Create(args.Output)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer outfile.Close()
|
|
||||||
|
|
||||||
_, err = stats.WriteTo(outfile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
args := &Arguments{}
|
|
||||||
arg.MustParse(args)
|
|
||||||
|
|
||||||
err := run(args)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
198
cmd/sbutil.go
198
cmd/sbutil.go
|
@ -1,198 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/alexflint/go-arg"
|
|
||||||
|
|
||||||
"git.zorchenhimer.com/Zorchenhimer/go-studybox/rom"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Arguments struct {
|
|
||||||
Pack *ArgPack `arg:"subcommand:pack"`
|
|
||||||
UnPack *ArgUnPack `arg:"subcommand:unpack"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ArgPack struct {
|
|
||||||
Input string `arg:"positional,required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ArgUnPack struct {
|
|
||||||
Input string `arg:"positional,required" help:".json metadata file"`
|
|
||||||
NoAudio bool `arg:"--no-audio" help:"Do not unpack the audio portion"`
|
|
||||||
OutDir string `arg:"--dir" help:"Base directory to unpack into (json file will be here)"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
args := &Arguments{}
|
|
||||||
arg.MustParse(args)
|
|
||||||
var err error
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case args.Pack != nil:
|
|
||||||
err = pack(args.Pack)
|
|
||||||
case args.UnPack != nil:
|
|
||||||
err = unpack(args.UnPack)
|
|
||||||
default:
|
|
||||||
fmt.Fprintln(os.Stderr, "Missing command")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func pack(args *ArgPack) error {
|
|
||||||
if !strings.HasSuffix(args.Input, ".json") {
|
|
||||||
return fmt.Errorf("Pack needs a json file as input")
|
|
||||||
}
|
|
||||||
|
|
||||||
//fmt.Println("-- Processing " + args.Input)
|
|
||||||
sb, err := rom.Import(args.Input)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
//outDir := filepath.Base(args.Input)
|
|
||||||
//outDir = strings.ReplaceAll(outDir, ".json", "_output")
|
|
||||||
|
|
||||||
//err = os.MkdirAll(outDir, 0777)
|
|
||||||
//if err != nil {
|
|
||||||
// return err
|
|
||||||
//}
|
|
||||||
|
|
||||||
//err = sb.Export(outDir)
|
|
||||||
//if err != nil {
|
|
||||||
// return err
|
|
||||||
//}
|
|
||||||
|
|
||||||
// TODO: put this in the json file?
|
|
||||||
|
|
||||||
outname := args.Input[:len(args.Input)-len(".json")]+".studybox"
|
|
||||||
fmt.Println(outname)
|
|
||||||
err = sb.Write(outname)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unpack(args *ArgUnPack) error {
|
|
||||||
//fmt.Println("-- Processing " + file)
|
|
||||||
if !strings.HasSuffix(args.Input, ".studybox") {
|
|
||||||
return fmt.Errorf("Input needs to be a .studybox file.")
|
|
||||||
}
|
|
||||||
|
|
||||||
//outDir := filepath.Base(args.Input)
|
|
||||||
outbase := filepath.Base(args.Input[:len(args.Input)-len(".studybox")])
|
|
||||||
outdir := filepath.Dir(args.Input)
|
|
||||||
if args.OutDir != "" {
|
|
||||||
outdir = args.OutDir
|
|
||||||
}
|
|
||||||
outname := filepath.Join(outdir, outbase)
|
|
||||||
fmt.Println(outname)
|
|
||||||
//outDir = strings.ReplaceAll(outDir, ".studybox", "")
|
|
||||||
|
|
||||||
err := os.MkdirAll(outname, 0777)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
sb, err := rom.ReadFile(args.Input)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = sb.Export(outname, !args.NoAudio)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//func main_old() {
|
|
||||||
// if len(os.Args) < 3 {
|
|
||||||
// fmt.Println("Missing command")
|
|
||||||
// os.Exit(1)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// matches := []string{}
|
|
||||||
// for _, glob := range os.Args[2:len(os.Args)] {
|
|
||||||
// m, err := filepath.Glob(glob)
|
|
||||||
// if err != nil {
|
|
||||||
// fmt.Println(err)
|
|
||||||
// os.Exit(1)
|
|
||||||
// }
|
|
||||||
// matches = append(matches, m...)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if len(matches) == 0 {
|
|
||||||
// fmt.Println("No files found!")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// switch strings.ToLower(os.Args[1]) {
|
|
||||||
// case "unpack":
|
|
||||||
// for _, file := range matches {
|
|
||||||
// fmt.Println("-- Processing " + file)
|
|
||||||
// outDir := filepath.Base(file)
|
|
||||||
// outDir = strings.ReplaceAll(outDir, ".studybox", "")
|
|
||||||
//
|
|
||||||
// err := os.MkdirAll(outDir, 0777)
|
|
||||||
// if err != nil {
|
|
||||||
// fmt.Println(err)
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// sb, err := rom.ReadFile(file)
|
|
||||||
// if err != nil {
|
|
||||||
// fmt.Println(err)
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// err = sb.Export(outDir)
|
|
||||||
// if err != nil {
|
|
||||||
// fmt.Println(err)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// case "pack":
|
|
||||||
// for _, file := range matches {
|
|
||||||
// fmt.Println("-- Processing " + file)
|
|
||||||
// sb, err := rom.Import(file)
|
|
||||||
// if err != nil {
|
|
||||||
// fmt.Println(err)
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// outDir := filepath.Base(file)
|
|
||||||
// outDir = strings.ReplaceAll(outDir, ".json", "_output")
|
|
||||||
//
|
|
||||||
// err = os.MkdirAll(outDir, 0777)
|
|
||||||
// if err != nil {
|
|
||||||
// fmt.Println(err)
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// err = sb.Export(outDir)
|
|
||||||
// if err != nil {
|
|
||||||
// fmt.Println(err)
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // TODO: put this in the json file?
|
|
||||||
//
|
|
||||||
// err = sb.Write(outDir + ".studybox")
|
|
||||||
// if err != nil {
|
|
||||||
// fmt.Println(err)
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
|
@ -5,11 +5,8 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"strconv"
|
"strconv"
|
||||||
"bufio"
|
|
||||||
"slices"
|
|
||||||
|
|
||||||
"github.com/alexflint/go-arg"
|
"github.com/alexflint/go-arg"
|
||||||
|
|
||||||
"git.zorchenhimer.com/Zorchenhimer/go-studybox/script"
|
"git.zorchenhimer.com/Zorchenhimer/go-studybox/script"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,9 +14,6 @@ type Arguments struct {
|
||||||
Input string `arg:"positional,required"`
|
Input string `arg:"positional,required"`
|
||||||
Output string `arg:"positional"`
|
Output string `arg:"positional"`
|
||||||
StartAddr string `arg:"--start" default:"0x6000" help:"base address for the start of the script"`
|
StartAddr string `arg:"--start" default:"0x6000" help:"base address for the start of the script"`
|
||||||
StatsFile string `arg:"--stats" help:"file to write some statistics to"`
|
|
||||||
LabelFile string `arg:"--labels" help:"file containing address/label pairs"`
|
|
||||||
|
|
||||||
start int
|
start int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,18 +38,6 @@ func run(args *Arguments) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.LabelFile != "" {
|
|
||||||
labels, err := parseLabelFile(args.LabelFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, label := range labels {
|
|
||||||
//fmt.Printf("%#v\n", label)
|
|
||||||
scr.Labels[label.Address] = label
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
outfile := os.Stdout
|
outfile := os.Stdout
|
||||||
if args.Output != "" {
|
if args.Output != "" {
|
||||||
outfile, err = os.Create(args.Output)
|
outfile, err = os.Create(args.Output)
|
||||||
|
@ -79,79 +61,9 @@ func run(args *Arguments) error {
|
||||||
fmt.Fprintln(outfile, token.String(scr.Labels))
|
fmt.Fprintln(outfile, token.String(scr.Labels))
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.StatsFile != "" {
|
|
||||||
statfile, err := os.Create(args.StatsFile)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Unable to create stats file: %w", err)
|
|
||||||
}
|
|
||||||
defer statfile.Close()
|
|
||||||
|
|
||||||
//err = scr.WriteStats(statfile)
|
|
||||||
_, err = scr.Stats().WriteTo(statfile)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error writing stats: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseLabelFile(filename string) ([]*script.Label, error) {
|
|
||||||
file, err := os.Open(filename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
labels := []*script.Label{}
|
|
||||||
scanner := bufio.NewScanner(file)
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := strings.TrimSpace(scanner.Text())
|
|
||||||
if line == "" || strings.HasPrefix(line, "#") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
line = strings.ReplaceAll(line, "\t", " ")
|
|
||||||
parts := strings.Split(line, " ")
|
|
||||||
|
|
||||||
parts = slices.DeleteFunc(parts, func(str string) bool {
|
|
||||||
return str == ""
|
|
||||||
})
|
|
||||||
|
|
||||||
if len(parts) < 2 {
|
|
||||||
fmt.Println("Ignoring", line)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(parts[0], "$") {
|
|
||||||
parts[0] = "0x"+parts[0][1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
addr, err := strconv.ParseInt(parts[0], 0, 32)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Address parse error for %q: %s\n", line, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
lbl := &script.Label{
|
|
||||||
Name: parts[1],
|
|
||||||
Address: int(addr),
|
|
||||||
}
|
|
||||||
|
|
||||||
if lbl.Name == "$" {
|
|
||||||
lbl.Name = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(parts) > 2 {
|
|
||||||
lbl.Comment = strings.Join(parts[2:], " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
labels = append(labels, lbl)
|
|
||||||
}
|
|
||||||
|
|
||||||
return labels, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
args := &Arguments{}
|
args := &Arguments{}
|
||||||
arg.MustParse(args)
|
arg.MustParse(args)
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (sb *StudyBox) Export(directory string, includeAudio bool) error {
|
func (sb *StudyBox) Export(directory string) error {
|
||||||
sbj := StudyBoxJson{
|
sbj := StudyBoxJson{
|
||||||
Version: 1,
|
Version: 1,
|
||||||
Pages: []jsonPage{},
|
Pages: []jsonPage{},
|
||||||
|
@ -106,7 +106,7 @@ func (sb *StudyBox) Export(directory string, includeAudio bool) error {
|
||||||
return fmt.Errorf("[WARN] unknown end data type: %s\n", jData.Type)
|
return fmt.Errorf("[WARN] unknown end data type: %s\n", jData.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.WriteFile(jData.File, rawData, 0666)
|
err = os.WriteFile(jData.File, rawData, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Unable to write data to file [%q]: %v", jData.File, err)
|
return fmt.Errorf("Unable to write data to file [%q]: %v", jData.File, err)
|
||||||
}
|
}
|
||||||
|
@ -133,17 +133,15 @@ func (sb *StudyBox) Export(directory string, includeAudio bool) error {
|
||||||
return fmt.Errorf("Missing audio!")
|
return fmt.Errorf("Missing audio!")
|
||||||
}
|
}
|
||||||
|
|
||||||
if includeAudio {
|
|
||||||
err := sb.Audio.WriteToFile(directory + "/audio")
|
err := sb.Audio.WriteToFile(directory + "/audio")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error writing audio file: %v", err)
|
return fmt.Errorf("Error writing audio file: %v", err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
rawJson, err := json.MarshalIndent(sbj, "", " ")
|
rawJson, err := json.MarshalIndent(sbj, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return os.WriteFile(directory+".json", rawJson, 0666)
|
return os.WriteFile(directory+".json", rawJson, 0777)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +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",
|
return fmt.Sprintf("header %d [Page %d] ; Checksum: %02X", ph.PageNumber, ph.PageNumber+1, ph.Checksum)
|
||||||
ph.PageNumber, ph.PageNumber+1, ph.Checksum)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ph *packetHeader) Address() int {
|
func (ph *packetHeader) Address() int {
|
||||||
|
@ -128,8 +127,7 @@ func (p *packetBulkData) Asm() string {
|
||||||
// data = append(data, fmt.Sprintf("$%02X", b))
|
// data = append(data, fmt.Sprintf("$%02X", b))
|
||||||
//}
|
//}
|
||||||
//return fmt.Sprintf("[%08X] data %s ; Length %d Checksum: %02X", p.address, strings.Join(data, ", "), len(p.Data), p.checksum)
|
//return fmt.Sprintf("[%08X] data %s ; Length %d Checksum: %02X", p.address, strings.Join(data, ", "), len(p.Data), p.checksum)
|
||||||
return fmt.Sprintf("data $%02X, [...], $%02X ; Length:%d Checksum:%02X",
|
return fmt.Sprintf("data $%02X, [...], $%02X ; Length %d Checksum: %02X", p.Data[0], p.Data[len(p.Data)-1], len(p.Data), p.checksum)
|
||||||
p.Data[0], p.Data[len(p.Data)-1], len(p.Data), p.checksum)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *packetBulkData) RawBytes() []byte {
|
func (p *packetBulkData) RawBytes() []byte {
|
||||||
|
@ -180,7 +178,7 @@ func (p *packetMarkDataStart) dataType() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *packetMarkDataStart) Asm() string {
|
func (p *packetMarkDataStart) Asm() string {
|
||||||
return fmt.Sprintf("mark_datatype_start Type:%s ArgA:$%02X ArgB:$%02X ; Checksum:%02X",
|
return fmt.Sprintf("mark_datatype_start %s $%02X $%02X ; Checksum: %02X",
|
||||||
p.dataType(), p.ArgA, p.ArgB, p.checksum)
|
p.dataType(), p.ArgA, p.ArgB, p.checksum)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,31 +229,30 @@ func (p *packetMarkDataEnd) RawBytes() []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *packetMarkDataEnd) Asm() string {
|
func (p *packetMarkDataEnd) Asm() string {
|
||||||
var typeStr string
|
var tstr string
|
||||||
switch p.Type & 0x0F {
|
switch p.Type & 0x0F {
|
||||||
case 2:
|
case 2:
|
||||||
typeStr = "script"
|
tstr = "script"
|
||||||
case 3:
|
case 3:
|
||||||
typeStr = "nametable"
|
tstr = "nametable"
|
||||||
case 4:
|
case 4:
|
||||||
typeStr = "pattern"
|
tstr = "pattern"
|
||||||
case 5:
|
case 5:
|
||||||
typeStr = "delay"
|
tstr = "delay"
|
||||||
default:
|
default:
|
||||||
typeStr = fmt.Sprintf("unknown $%02X", p.Type)
|
tstr = fmt.Sprintf("unknown $%02X", p.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.Reset {
|
if p.Reset {
|
||||||
typeStr += " reset_state"
|
tstr += " reset_state"
|
||||||
}
|
}
|
||||||
|
|
||||||
s := []string{}
|
s := []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 ; Raw:[%s] Checksum:%02X",
|
tstr, strings.Join(s, " "), p.checksum)
|
||||||
typeStr, strings.Join(s, " "), p.checksum)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *packetMarkDataEnd) Address() int {
|
func (p *packetMarkDataEnd) Address() int {
|
||||||
|
|
|
@ -123,7 +123,7 @@ func (ta TapeAudio) String() string {
|
||||||
|
|
||||||
func (ta *TapeAudio) WriteToFile(basename string) error {
|
func (ta *TapeAudio) WriteToFile(basename string) error {
|
||||||
ext := "." + strings.ToLower(string(ta.Format))
|
ext := "." + strings.ToLower(string(ta.Format))
|
||||||
return os.WriteFile(basename+ext, ta.Data, 0666)
|
return os.WriteFile(basename+ext, ta.Data, 0777)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ta *TapeAudio) ext() string {
|
func (ta *TapeAudio) ext() string {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package script
|
package script
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var InstrMap map[byte]*Instruction
|
var InstrMap map[byte]*Instruction
|
||||||
|
@ -14,206 +13,186 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
var Instructions []*Instruction = []*Instruction{
|
var Instructions []*Instruction = []*Instruction{
|
||||||
&Instruction{ 0x80, 0, 0, 0, false, "play_beep"},
|
&Instruction{ 0x80, 0, 0, 0, "play_beep"},
|
||||||
&Instruction{ 0x81, 0, 0, 0, false, "halt"},
|
&Instruction{ 0x81, 0, 0, 0, "halt"},
|
||||||
&Instruction{ 0x82, 0, 0, 0, false, "tape_nmi_shenanigans"},
|
&Instruction{ 0x82, 0, 0, 0, "tape_nmi_shenanigans"},
|
||||||
&Instruction{ 0x83, 0, 0, 0, false, "tape_wait"},
|
&Instruction{ 0x83, 0, 0, 0, "tape_wait"},
|
||||||
|
|
||||||
// Jump to the inline word, in the VM
|
// Jump to the inline word, in the VM
|
||||||
&Instruction{ 0x84, 0, 2, 0, false, "jump_abs"},
|
&Instruction{ 0x84, 0, 2, 0, "jump_abs"},
|
||||||
|
|
||||||
// Call a routine at the inline word, in the VM
|
// Call a routine at the inline word, in the VM
|
||||||
&Instruction{ 0x85, 0, 2, 0, false, "call_abs"},
|
&Instruction{ 0x85, 0, 2, 0, "call_abs"},
|
||||||
|
|
||||||
// Return from a previous call
|
// Return from a previous call
|
||||||
&Instruction{ 0x86, 0, 0, 0, false, "return"},
|
&Instruction{ 0x86, 0, 0, 0, "return"},
|
||||||
|
&Instruction{ 0x87, 0, 0, 0, "loop"},
|
||||||
|
&Instruction{ 0x88, 0, 0, 0, "play_sound"},
|
||||||
|
&Instruction{ 0x89, 3, 0, 0, ""},
|
||||||
|
&Instruction{ 0x8A, 0, 2, 0, "pop_string_to_addr"},
|
||||||
|
&Instruction{ 0x8B, 1, 0, 0, ""},
|
||||||
|
&Instruction{ 0x8C, 0, 0, 1, "string_length"},
|
||||||
|
&Instruction{ 0x8D, 0, 0, 1, "string_to_int"},
|
||||||
|
&Instruction{ 0x8E, 0, 0, 16, "string_concat"},
|
||||||
|
&Instruction{ 0x8F, 0, 0, 1, "strings_equal"},
|
||||||
|
|
||||||
&Instruction{ 0x87, 0, 0, 0, false, "loop"},
|
&Instruction{ 0x90, 0, 0, 1, "strings_not_equal"},
|
||||||
&Instruction{ 0x88, 0, 0, 0, false, "play_sound"},
|
&Instruction{ 0x91, 0, 0, 1, "string_less_than"},
|
||||||
&Instruction{ 0x89, 3, 0, 0, false, ""},
|
&Instruction{ 0x92, 0, 0, 1, "string_less_than_equal"},
|
||||||
&Instruction{ 0x8A, 0, 2, 0, false, "pop_string_to_addr"},
|
&Instruction{ 0x93, 0, 0, 1, "string_greater_than_equal"},
|
||||||
&Instruction{ 0x8B, 1, 0, 0, false, ""},
|
&Instruction{ 0x94, 0, 0, 1, "string_greater_than"},
|
||||||
&Instruction{ 0x8C, 0, 0, 1, false, "string_length"},
|
|
||||||
&Instruction{ 0x8D, 0, 0, 1, false, "string_to_int"},
|
|
||||||
&Instruction{ 0x8E, 0, 0, 16, false, "string_concat"},
|
|
||||||
&Instruction{ 0x8F, 0, 0, 1, false, "strings_equal"},
|
|
||||||
|
|
||||||
&Instruction{ 0x90, 0, 0, 1, false, "strings_not_equal"},
|
|
||||||
&Instruction{ 0x91, 0, 0, 1, false, "string_less_than"},
|
|
||||||
&Instruction{ 0x92, 0, 0, 1, false, "string_less_than_equal"},
|
|
||||||
&Instruction{ 0x93, 0, 0, 1, false, "string_greater_than_equal"},
|
|
||||||
&Instruction{ 0x94, 0, 0, 1, false, "string_greater_than"},
|
|
||||||
|
|
||||||
// Sets some tape NMI stuff if the byte at $0740 is not zero.
|
// Sets some tape NMI stuff if the byte at $0740 is not zero.
|
||||||
// Will call 0x82 tape_nmi_shenanigans if $0740 != 0
|
&Instruction{ 0x95, 1, 0, 0, "tape_nmi_shenigans_set"},
|
||||||
&Instruction{ 0x95, 1, 0, 0, false, "tape_nmi_shenigans_set"},
|
&Instruction{ 0x96, 0, 2, 0, "set_word_4E"},
|
||||||
|
&Instruction{ 0x97, 2, 0, 0, ""},
|
||||||
|
&Instruction{ 0x98, 1, 0, 0, ""},
|
||||||
|
&Instruction{ 0x99, 1, 0, 0, ""},
|
||||||
|
&Instruction{ 0x9A, 0, 0, 0, ""},
|
||||||
|
&Instruction{ 0x9B, 0, 0, 0, "halt"},
|
||||||
|
&Instruction{ 0x9C, 0, 0, 0, "toggle_44FE"},
|
||||||
|
&Instruction{ 0x9D, 2, 0, 0, "something_tape"},
|
||||||
|
|
||||||
&Instruction{ 0x96, 0, 2, 0, true, "set_word_4E"},
|
// Calls 0xEB draw_overlay. Seems to draw a whole screen.
|
||||||
&Instruction{ 0x97, 2, 0, 0, false, ""},
|
&Instruction{ 0x9E, 2, 0, 0, ""},
|
||||||
&Instruction{ 0x98, 1, 0, 0, false, ""},
|
&Instruction{ 0x9F, 6, 0, 0, ""},
|
||||||
&Instruction{ 0x99, 1, 0, 0, false, ""},
|
|
||||||
&Instruction{ 0x9A, 0, 0, 0, false, ""},
|
|
||||||
&Instruction{ 0x9B, 0, 0, 0, false, "halt"},
|
|
||||||
&Instruction{ 0x9C, 0, 0, 0, false, "toggle_44FE"},
|
|
||||||
&Instruction{ 0x9D, 2, 0, 0, false, "something_tape"},
|
|
||||||
|
|
||||||
// Calls 0xEB draw_overlay. Draws the whole screen from data previously
|
&Instruction{ 0xA0, 2, 0, 1, ""},
|
||||||
// loaded from the tape.
|
&Instruction{ 0xA1, 1, 0, 0, ""},
|
||||||
&Instruction{ 0x9E, 2, 0, 0, false, "draw_and_show_screen"},
|
&Instruction{ 0xA2, 1, 0, 0, "buffer_palette"},
|
||||||
|
|
||||||
&Instruction{ 0x9F, 6, 0, 0, false, ""},
|
|
||||||
|
|
||||||
&Instruction{ 0xA0, 2, 0, 1, false, ""},
|
|
||||||
&Instruction{ 0xA1, 1, 0, 0, false, ""},
|
|
||||||
&Instruction{ 0xA2, 1, 0, 0, false, "buffer_palette"},
|
|
||||||
|
|
||||||
// Possibly a sprite setup routine. loads up some CHR data and some palette
|
// Possibly a sprite setup routine. loads up some CHR data and some palette
|
||||||
// data.
|
// data.
|
||||||
&Instruction{ 0xA3, 1, 0, 0, false, "sprite_setup"},
|
&Instruction{ 0xA3, 1, 0, 0, "sprite_setup"},
|
||||||
&Instruction{ 0xA4, 3, 0, 0, false, ""},
|
&Instruction{ 0xA4, 3, 0, 0, ""},
|
||||||
&Instruction{ 0xA5, 1, 0, 0, false, "set_470A"},
|
&Instruction{ 0xA5, 1, 0, 0, "set_470A"},
|
||||||
&Instruction{ 0xA6, 1, 0, 0, false, "set_470B"},
|
&Instruction{ 0xA6, 1, 0, 0, "set_470B"},
|
||||||
|
|
||||||
// jump to the inline address, in assembly, not in the VM
|
// jump to the inline address, in assembly, not in the VM
|
||||||
// (built-in ACE, lmao)
|
// (built-in ACE, lmao)
|
||||||
// Will not jump to anything at or above $8000 or below $5000.
|
&Instruction{ 0xA7, 0, 0, 0, "call_asm"},
|
||||||
// Addresses in $5000-$5FFF use $470A as the bank ID
|
&Instruction{ 0xA8, 5, 0, 0, ""},
|
||||||
// Addresses in $6000-$7FFF use $470B as the bank ID
|
&Instruction{ 0xA9, 1, 0, 0, ""},
|
||||||
&Instruction{ 0xA7, 0, 0, 0, false, "call_asm"},
|
&Instruction{ 0xAA, 1, 0, 0, ""},
|
||||||
|
&Instruction{ 0xAB, 1, 0, 0, "long_call"},
|
||||||
|
&Instruction{ 0xAC, 0, 0, 0, "long_return"},
|
||||||
|
&Instruction{ 0xAD, 1, 0, 1, "absolute"},
|
||||||
|
&Instruction{ 0xAE, 1, 0, 1, "compare"},
|
||||||
|
&Instruction{ 0xAF, 0, 0, 1, ""},
|
||||||
|
|
||||||
&Instruction{ 0xA8, 5, 0, 0, false, ""},
|
&Instruction{ 0xB0, 1, 0, 16, ""},
|
||||||
&Instruction{ 0xA9, 1, 0, 0, false, ""},
|
&Instruction{ 0xB1, 1, 0, 16, "to_hex_string"},
|
||||||
&Instruction{ 0xAA, 1, 0, 0, false, ""},
|
&Instruction{ 0xB2, 0, 0, 1, ""},
|
||||||
&Instruction{ 0xAB, 1, 0, 0, false, "long_call"},
|
&Instruction{ 0xB3, 7, 0, 0, ""}, // possible 16-bit inline?
|
||||||
&Instruction{ 0xAC, 0, 0, 0, false, "long_return"},
|
&Instruction{ 0xB4, 0, 0, 0, ""},
|
||||||
&Instruction{ 0xAD, 1, 0, 1, false, "absolute"},
|
&Instruction{ 0xB5, 0, 0, 0, ""},
|
||||||
&Instruction{ 0xAE, 1, 0, 1, false, "compare"},
|
&Instruction{ 0xB6, 0, 0, 0, ""},
|
||||||
&Instruction{ 0xAF, 0, 0, 1, false, ""},
|
|
||||||
|
|
||||||
&Instruction{ 0xB0, 1, 0, 16, false, ""},
|
|
||||||
&Instruction{ 0xB1, 1, 0, 16, false, "to_hex_string"},
|
|
||||||
&Instruction{ 0xB2, 0, 0, 1, false, ""},
|
|
||||||
&Instruction{ 0xB3, 7, 0, 0, false, ""}, // possible 16-bit inline?
|
|
||||||
&Instruction{ 0xB4, 0, 0, 0, false, ""},
|
|
||||||
&Instruction{ 0xB5, 0, 0, 0, false, ""},
|
|
||||||
&Instruction{ 0xB6, 0, 0, 0, false, ""},
|
|
||||||
|
|
||||||
// Uses the inline word as a pointer, and pushes the byte value at that
|
// Uses the inline word as a pointer, and pushes the byte value at that
|
||||||
// address to the stack.
|
// address to the stack.
|
||||||
&Instruction{ 0xB7, 0, 2, 0, false, "deref_ptr_inline"},
|
&Instruction{ 0xB7, 0, 2, 0, "deref_ptr_inline"},
|
||||||
|
|
||||||
// Pushes the inline word to the stack
|
// Pushes the inline word to the stack
|
||||||
&Instruction{ 0xB8, 0, 2, 0, true, "push_word"},
|
&Instruction{ 0xB8, 0, 2, 0, "push_word"},
|
||||||
&Instruction{ 0xB9, 0, 2, 0, false, "push_word_indexed"},
|
&Instruction{ 0xB9, 0, 2, 0, "push_word_indexed"},
|
||||||
&Instruction{ 0xBA, 0, 2, 0, false, "push"},
|
&Instruction{ 0xBA, 0, 2, 0, "push"},
|
||||||
&Instruction{ 0xBB, 0, -1, 0, false, "push_data"},
|
&Instruction{ 0xBB, 0, -1, 0, "push_data"},
|
||||||
&Instruction{ 0xBC, 0, 2, 0, false, "push_string_from_table"},
|
&Instruction{ 0xBC, 0, 2, 0, "push_string_from_table"},
|
||||||
|
|
||||||
// Pops a byte off the stack and stores it at the inline address.
|
// Pops a byte off the stack and stores it at the inline address.
|
||||||
&Instruction{ 0xBD, 0, 2, 0, false, "pop_into"},
|
&Instruction{ 0xBD, 0, 2, 0, "pop"},
|
||||||
|
&Instruction{ 0xBE, 0, 2, 0, "write_to_table"},
|
||||||
&Instruction{ 0xBE, 0, 2, 0, false, "write_to_table"},
|
&Instruction{ 0xBF, 0, 2, 0, "jump_not_zero"},
|
||||||
&Instruction{ 0xBF, 0, 2, 0, false, "jump_not_zero"},
|
|
||||||
|
|
||||||
// One byte off stack; jumps to inline if byte is zero
|
// One byte off stack; jumps to inline if byte is zero
|
||||||
&Instruction{ 0xC0, 1, 2, 0, false, "jump_zero"},
|
&Instruction{ 0xC0, 1, 2, 0, "jump_zero"},
|
||||||
&Instruction{ 0xC1, 1, -2, 0, false, "jump_switch"},
|
&Instruction{ 0xC1, 1, -2, 0, "jump_switch"},
|
||||||
&Instruction{ 0xC2, 1, 0, 1, false, "equals_zero"},
|
&Instruction{ 0xC2, 1, 0, 1, "equals_zero"},
|
||||||
&Instruction{ 0xC3, 2, 0, 1, false, "and_a_b"},
|
&Instruction{ 0xC3, 2, 0, 1, "and_a_b"},
|
||||||
&Instruction{ 0xC4, 2, 0, 1, false, "or_a_b"},
|
&Instruction{ 0xC4, 2, 0, 1, "or_a_b"},
|
||||||
&Instruction{ 0xC5, 2, 0, 1, false, "equal"},
|
&Instruction{ 0xC5, 2, 0, 1, "equal"},
|
||||||
|
|
||||||
// Two bytes off stack; result pushed back; 1 if A == B, 0 if A != B
|
// Two bytes off stack; result pushed back; 1 if A == B, 0 if A != B
|
||||||
&Instruction{ 0xC6, 2, 0, 1, false, "not_equal"},
|
&Instruction{ 0xC6, 2, 0, 1, "not_equal"},
|
||||||
|
&Instruction{ 0xC7, 2, 0, 1, "less_than"},
|
||||||
|
&Instruction{ 0xC8, 2, 0, 1, "less_than_equal"},
|
||||||
|
&Instruction{ 0xC9, 2, 0, 1, "greater_than"},
|
||||||
|
&Instruction{ 0xCA, 2, 0, 1, "greater_than_equal"},
|
||||||
|
&Instruction{ 0xCB, 2, 0, 1, "sum"},
|
||||||
|
&Instruction{ 0xCC, 2, 0, 1, "subtract"},
|
||||||
|
&Instruction{ 0xCD, 2, 0, 1, "multiply"},
|
||||||
|
&Instruction{ 0xCE, 2, 0, 1, "signed_divide"},
|
||||||
|
&Instruction{ 0xCF, 1, 0, 1, "negate"},
|
||||||
|
|
||||||
&Instruction{ 0xC7, 2, 0, 1, false, "less_than"},
|
&Instruction{ 0xD0, 1, 0, 1, "modulus"},
|
||||||
&Instruction{ 0xC8, 2, 0, 1, false, "less_than_equal"},
|
&Instruction{ 0xD1, 2, 0, 1, "expansion_controller"},
|
||||||
&Instruction{ 0xC9, 2, 0, 1, false, "greater_than"},
|
&Instruction{ 0xD2, 2, 0, 1, ""},
|
||||||
&Instruction{ 0xCA, 2, 0, 1, false, "greater_than_equal"},
|
&Instruction{ 0xD3, 2, 0, 16, ""},
|
||||||
&Instruction{ 0xCB, 2, 0, 1, false, "sum"},
|
&Instruction{ 0xD4, 3, 0, 0, ""},
|
||||||
&Instruction{ 0xCC, 2, 0, 1, false, "subtract"},
|
&Instruction{ 0xD5, 1, 0, 0, "wait_for_tape"},
|
||||||
&Instruction{ 0xCD, 2, 0, 1, false, "multiply"},
|
&Instruction{ 0xD6, 1, 0, 16, "truncate_string"},
|
||||||
&Instruction{ 0xCE, 2, 0, 1, false, "signed_divide"},
|
&Instruction{ 0xD7, 1, 0, 16, "trim_string"},
|
||||||
&Instruction{ 0xCF, 1, 0, 1, false, "negate"},
|
&Instruction{ 0xD8, 1, 0, 16, "trim_string_start"},
|
||||||
|
&Instruction{ 0xD9, 2, 0, 16, "trim_string_start"},
|
||||||
&Instruction{ 0xD0, 1, 0, 1, false, "modulus"},
|
&Instruction{ 0xDA, 1, 0, 16, "to_int_string"},
|
||||||
&Instruction{ 0xD1, 2, 0, 1, false, "expansion_controller"},
|
&Instruction{ 0xDB, 3, 0, 0, ""},
|
||||||
&Instruction{ 0xD2, 2, 0, 1, false, ""},
|
&Instruction{ 0xDC, 5, 0, 0, ""},
|
||||||
&Instruction{ 0xD3, 2, 0, 16, false, ""},
|
|
||||||
&Instruction{ 0xD4, 3, 0, 0, false, "set_cursor_location"},
|
|
||||||
|
|
||||||
// Wait for ArgA itterations. "itterations" is undefined as of now. (data from tape?)
|
|
||||||
&Instruction{ 0xD5, 1, 0, 0, false, "wait_for_tape"},
|
|
||||||
|
|
||||||
&Instruction{ 0xD6, 1, 0, 16, false, "truncate_string"},
|
|
||||||
&Instruction{ 0xD7, 1, 0, 16, false, "trim_string"},
|
|
||||||
&Instruction{ 0xD8, 1, 0, 16, false, "trim_string_start"},
|
|
||||||
&Instruction{ 0xD9, 2, 0, 16, false, "trim_string_start"},
|
|
||||||
&Instruction{ 0xDA, 1, 0, 16, false, "to_int_string"},
|
|
||||||
&Instruction{ 0xDB, 3, 0, 0, false, ""},
|
|
||||||
&Instruction{ 0xDC, 5, 0, 0, false, ""},
|
|
||||||
|
|
||||||
// ArgA, ArgB: X,Y of corner A
|
// ArgA, ArgB: X,Y of corner A
|
||||||
// ArgC, ArgD: X,Y of corner B
|
// ArgC, ArgD: X,Y of corner B
|
||||||
// ArgE: fill value. This is an index into
|
// ArgE: fill value. This is an index into
|
||||||
// the table at $B451.
|
// the table at $B451.
|
||||||
// Fills a box with a tile
|
// Fills a box with a tile
|
||||||
&Instruction{ 0xDD, 5, 0, 0, false, "fill_box"},
|
&Instruction{ 0xDD, 5, 0, 0, "fill_box"},
|
||||||
|
|
||||||
&Instruction{ 0xDE, 3, 0, 0, false, ""},
|
&Instruction{ 0xDE, 3, 0, 0, ""},
|
||||||
&Instruction{ 0xDF, 3, 0, 0, false, ""},
|
&Instruction{ 0xDF, 3, 0, 0, ""},
|
||||||
|
|
||||||
// Divide and return remainder
|
&Instruction{ 0xE0, 2, 0, 1, "signed_divide"},
|
||||||
&Instruction{ 0xE0, 2, 0, 1, false, "modulo"},
|
&Instruction{ 0xE1, 4, 0, 0, ""},
|
||||||
|
&Instruction{ 0xE2, 7, 0, 0, "setup_sprite"},
|
||||||
&Instruction{ 0xE1, 4, 0, 0, false, ""},
|
|
||||||
&Instruction{ 0xE2, 7, 0, 0, false, "setup_sprite"},
|
|
||||||
|
|
||||||
// Pops a word off the stack, uses it as a pointer, and pushes the byte
|
// Pops a word off the stack, uses it as a pointer, and pushes the byte
|
||||||
// value at that address to the stack.
|
// value at that address to the stack.
|
||||||
&Instruction{ 0xE3, 1, 0, 1, false, "deref_ptr_stack"},
|
&Instruction{ 0xE3, 1, 0, 1, "deref_ptr_stack"},
|
||||||
&Instruction{ 0xE4, 2, 0, 0, false, "swap_ram_bank"},
|
&Instruction{ 0xE4, 2, 0, 0, "swap_ram_bank"},
|
||||||
&Instruction{ 0xE5, 1, 0, 0, false, "disable_sprite"},
|
&Instruction{ 0xE5, 1, 0, 0, "disable_sprite"},
|
||||||
|
&Instruction{ 0xE6, 1, 0, 0, "tape_nmi_setup"},
|
||||||
// Will call 0x82 tape_nmi_shenanigans if $0740 != 0
|
&Instruction{ 0xE7, 7, 0, 0, ""},
|
||||||
&Instruction{ 0xE6, 1, 0, 0, false, "tape_nmi_setup"},
|
&Instruction{ 0xE8, 1, 0, 0, "setup_tape_nmi"},
|
||||||
|
&Instruction{ 0xE9, 0, 1, 0, "setup_loop"},
|
||||||
&Instruction{ 0xE7, 7, 0, 0, false, "draw_metasprite"},
|
&Instruction{ 0xEA, 0, 0, 0, "string_write_to_table"},
|
||||||
&Instruction{ 0xE8, 1, 0, 0, false, "setup_tape_nmi"},
|
|
||||||
&Instruction{ 0xE9, 0, 1, 0, false, "setup_loop"},
|
|
||||||
&Instruction{ 0xEA, 0, 0, 0, false, "string_write_to_table"},
|
|
||||||
|
|
||||||
// Reads and saves tiles from the PPU, then draws over them.
|
// Reads and saves tiles from the PPU, then draws over them.
|
||||||
// This is used to draw dialog boxes, so saving what it overwrites
|
// This is used to draw dialog boxes, so saving what it overwrites
|
||||||
// so it can re-draw them later makes sense.
|
// so it can re-draw them later makes sense.
|
||||||
// Not sure what the arguments actually mean.
|
// Not sure what the arguments actually mean.
|
||||||
// ArgB and ArgC are probably coordinates.
|
// ArgB and ArgC are probably coordinates.
|
||||||
&Instruction{ 0xEB, 4, 0, 0, false, "draw_overlay"},
|
&Instruction{ 0xEB, 4, 0, 0, "draw_overlay"},
|
||||||
|
|
||||||
&Instruction{ 0xEC, 2, 0, 0, false, "scroll"},
|
&Instruction{ 0xEC, 2, 0, 0, "scroll"},
|
||||||
&Instruction{ 0xED, 1, 0, 0, false, "disable_sprites"},
|
&Instruction{ 0xED, 1, 0, 0, "disable_sprites"},
|
||||||
|
&Instruction{ 0xEE, 1, -3, 0, "call_switch"},
|
||||||
|
&Instruction{ 0xEF, 6, 0, 0, ""},
|
||||||
|
|
||||||
&Instruction{ 0xEE, 1, -3, 0, false, "call_switch"},
|
&Instruction{ 0xF0, 0, 0, 0, "disable_sprites"},
|
||||||
&Instruction{ 0xEF, 6, 0, 0, false, ""},
|
&Instruction{ 0xF1, 4, 0, 0, ""},
|
||||||
|
&Instruction{ 0xF2, 0, 0, 0, "halt"},
|
||||||
&Instruction{ 0xF0, 0, 0, 0, false, "disable_sprites"},
|
&Instruction{ 0xF3, 0, 0, 0, "halt"},
|
||||||
&Instruction{ 0xF1, 4, 0, 0, false, ""},
|
&Instruction{ 0xF4, 0, 0, 16, "halt"},
|
||||||
&Instruction{ 0xF2, 0, 0, 0, false, "halt"},
|
&Instruction{ 0xF5, 1, 0, 1, "halt"},
|
||||||
&Instruction{ 0xF3, 0, 0, 0, false, "halt"},
|
&Instruction{ 0xF6, 1, 0, 0, "halt"},
|
||||||
&Instruction{ 0xF4, 0, 0, 16, false, "halt"},
|
&Instruction{ 0xF7, 0, 0, 0, "halt"},
|
||||||
&Instruction{ 0xF5, 1, 0, 1, false, "halt"},
|
&Instruction{ 0xF8, 2, 0, 0, "halt"},
|
||||||
&Instruction{ 0xF6, 1, 0, 0, false, "halt"},
|
&Instruction{ 0xF9, 0, 0, 1, ""},
|
||||||
&Instruction{ 0xF7, 0, 0, 0, false, "halt"},
|
&Instruction{ 0xFA, 0, 0, 1, ""},
|
||||||
&Instruction{ 0xF8, 2, 0, 0, false, "halt"},
|
&Instruction{ 0xFB, 1, 0, 0, "jump_arg_a"},
|
||||||
&Instruction{ 0xF9, 0, 0, 1, false, ""},
|
&Instruction{ 0xFC, 2, 0, 1, ""},
|
||||||
&Instruction{ 0xFA, 0, 0, 1, false, ""},
|
&Instruction{ 0xFD, 0, 0, 16, "halt"},
|
||||||
&Instruction{ 0xFB, 1, 0, 0, false, "jump_arg_a"},
|
&Instruction{ 0xFE, 4, 0, 0, ""},
|
||||||
&Instruction{ 0xFC, 2, 0, 1, false, ""},
|
|
||||||
&Instruction{ 0xFD, 0, 0, 16, false, "halt"},
|
|
||||||
&Instruction{ 0xFE, 4, 2, 0, false, "draw_rom_char"},
|
|
||||||
|
|
||||||
// code handler is $FFFF
|
// code handler is $FFFF
|
||||||
&Instruction{ 0xFF, 0, 0, 0, false, "break_engine"},
|
&Instruction{ 0xFF, 0, 0, 0, "break_engine"},
|
||||||
}
|
}
|
||||||
|
|
||||||
type Instruction struct {
|
type Instruction struct {
|
||||||
|
@ -222,9 +201,8 @@ type Instruction struct {
|
||||||
OpCount int // inline operands. length in bytes.
|
OpCount int // inline operands. length in bytes.
|
||||||
// -1: nul-terminated
|
// -1: nul-terminated
|
||||||
// -2: first byte is count, followed by that number of words
|
// -2: first byte is count, followed by that number of words
|
||||||
// -3: like -2, but with no default. code continues after list on OOB
|
// -3: like -2, but with one additional word
|
||||||
RetCount int // return count
|
RetCount int // return count
|
||||||
InlineImmediate bool // don't turn the inline value into a variable
|
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +212,7 @@ func (i Instruction) String() string {
|
||||||
return i.Name
|
return i.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("unknown_0x%02X", i.Opcode)
|
//return fmt.Sprintf("$%02X_unknown", i.Opcode)
|
||||||
//return "unknown"
|
return "unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,42 +5,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Label struct {
|
|
||||||
Address int
|
|
||||||
Name string
|
|
||||||
Comment string
|
|
||||||
FarLabel bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func AutoLabel(address int) *Label {
|
|
||||||
return &Label{
|
|
||||||
Address: address,
|
|
||||||
Name: fmt.Sprintf("L%04X", address),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func AutoLabelVar(address int) *Label {
|
|
||||||
return &Label{
|
|
||||||
Address: address,
|
|
||||||
Name: fmt.Sprintf("Var_%04X", address),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func AutoLabelFar(address int) *Label {
|
|
||||||
return &Label{
|
|
||||||
Address: address,
|
|
||||||
Name: fmt.Sprintf("F%04X", address),
|
|
||||||
FarLabel: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLabel(address int, name string) *Label {
|
|
||||||
return &Label{
|
|
||||||
Address: address,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseFile(filename string, startAddr int) (*Script, error) {
|
func ParseFile(filename string, startAddr int) (*Script, error) {
|
||||||
rawfile, err := os.ReadFile(filename)
|
rawfile, err := os.ReadFile(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -60,7 +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]*Label), // map[location]name
|
Labels: make(map[int]string), // map[location]name
|
||||||
}
|
}
|
||||||
tokenMap := make(map[int]*Token)
|
tokenMap := make(map[int]*Token)
|
||||||
|
|
||||||
|
@ -81,7 +45,7 @@ func Parse(rawinput []byte, startAddr int) (*Script, error) {
|
||||||
|
|
||||||
op, ok := InstrMap[raw]
|
op, ok := InstrMap[raw]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("OP 0x%02X not in instruction map", raw)
|
return nil, fmt.Errorf("OP %02X not in instruction map", raw)
|
||||||
}
|
}
|
||||||
token.Instruction = op
|
token.Instruction = op
|
||||||
|
|
||||||
|
@ -102,25 +66,19 @@ func Parse(rawinput []byte, startAddr int) (*Script, error) {
|
||||||
args = append(args, ByteVal(l))
|
args = append(args, ByteVal(l))
|
||||||
i++
|
i++
|
||||||
for c := 0; c < l; c++ {
|
for c := 0; c < l; c++ {
|
||||||
if len(rawinput) <= i+1 {
|
|
||||||
return script, fmt.Errorf("OP early end at offset 0x%X (%d) {%d} %#v", i, i, l, op)
|
|
||||||
}
|
|
||||||
|
|
||||||
args = append(args, WordVal([2]byte{rawinput[i], rawinput[i+1]}))
|
args = append(args, WordVal([2]byte{rawinput[i], rawinput[i+1]}))
|
||||||
i+=2
|
i+=2
|
||||||
}
|
}
|
||||||
i--
|
|
||||||
|
|
||||||
case -3: // count then count words. "default" is no call (skip Code_Pointer to after args)
|
case -3: // count then count+1 words (extra is default case)
|
||||||
i++
|
i++
|
||||||
l := int(rawinput[i])
|
l := int(rawinput[i])
|
||||||
args = append(args, ByteVal(l))
|
args = append(args, ByteVal(l))
|
||||||
i++
|
i++
|
||||||
for c := 0; c < l; c++ {
|
for c := 0; c < l+1; c++ {
|
||||||
args = append(args, WordVal([2]byte{rawinput[i], rawinput[i+1]}))
|
args = append(args, WordVal([2]byte{rawinput[i], rawinput[i+1]}))
|
||||||
i+=2
|
i+=2
|
||||||
}
|
}
|
||||||
i--
|
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
args = append(args, WordVal([2]byte{rawinput[i+1], rawinput[i+2]}))
|
args = append(args, WordVal([2]byte{rawinput[i+1], rawinput[i+2]}))
|
||||||
|
@ -134,7 +92,6 @@ func Parse(rawinput []byte, startAddr int) (*Script, error) {
|
||||||
token.Inline = args
|
token.Inline = args
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find and mark labels for a few instructions
|
|
||||||
for _, t := range script.Tokens {
|
for _, t := range script.Tokens {
|
||||||
switch t.Raw {
|
switch t.Raw {
|
||||||
case 0x84, 0x85, 0xBF, 0xC0: // jmp/call
|
case 0x84, 0x85, 0xBF, 0xC0: // jmp/call
|
||||||
|
@ -148,7 +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] = AutoLabel(addr) //fmt.Sprintf("L%04X", addr)
|
script.Labels[addr] = fmt.Sprintf("L%04X", addr)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,7 +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] = AutoLabel(addr) //fmt.Sprintf("L%04X", addr)
|
script.Labels[addr] = fmt.Sprintf("L%04X", addr)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,12 +141,11 @@ func Parse(rawinput []byte, startAddr int) (*Script, error) {
|
||||||
if t.Instruction == nil {
|
if t.Instruction == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if t.Instruction.OpCount == 2 {
|
||||||
if t.Instruction.OpCount == 2 && !t.Instruction.InlineImmediate {
|
|
||||||
addr := t.Inline[0].Int()
|
addr := t.Inline[0].Int()
|
||||||
if tok, ok := tokenMap[addr]; ok {
|
if tok, ok := tokenMap[addr]; ok {
|
||||||
tok.IsVariable = true
|
tok.IsVariable = true
|
||||||
script.Labels[addr] = AutoLabelVar(addr) //fmt.Sprintf("Var_%04X", addr)
|
script.Labels[addr] = fmt.Sprintf("Var_%04X", addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package script
|
package script
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Script struct {
|
type Script struct {
|
||||||
|
@ -11,35 +10,5 @@ type Script struct {
|
||||||
StartAddress int
|
StartAddress int
|
||||||
StackAddress int
|
StackAddress int
|
||||||
|
|
||||||
Labels map[int]*Label
|
Labels map[int]string
|
||||||
}
|
|
||||||
|
|
||||||
type InstrStat struct {
|
|
||||||
Instr *Instruction
|
|
||||||
Count int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (is InstrStat) String() string {
|
|
||||||
return fmt.Sprintf("0x%02X %3d %s", is.Instr.Opcode, is.Count, is.Instr.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Script) Stats() Stats {
|
|
||||||
st := make(Stats)
|
|
||||||
|
|
||||||
for _, t := range s.Tokens {
|
|
||||||
if t.Instruction == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
op := t.Instruction.Opcode
|
|
||||||
if _, ok := st[op]; !ok {
|
|
||||||
st[op] = &InstrStat{
|
|
||||||
Instr: t.Instruction,
|
|
||||||
Count: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
st[op].Count++
|
|
||||||
}
|
|
||||||
|
|
||||||
return st
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
package script
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"slices"
|
|
||||||
"maps"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Stats map[byte]*InstrStat
|
|
||||||
|
|
||||||
func (this Stats) Add(that Stats) {
|
|
||||||
for _, st := range that {
|
|
||||||
op := st.Instr.Opcode
|
|
||||||
if _, ok := this[op]; !ok {
|
|
||||||
this[op] = st
|
|
||||||
} else {
|
|
||||||
this[op].Count += that[op].Count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s Stats) WriteTo(w io.Writer) (int64, error) {
|
|
||||||
count := int64(0)
|
|
||||||
keys := slices.Sorted(maps.Keys(s))
|
|
||||||
|
|
||||||
unknownInstr := 0
|
|
||||||
unknownUses := 0
|
|
||||||
|
|
||||||
for _, key := range keys {
|
|
||||||
n, err := fmt.Fprintln(w, s[key])
|
|
||||||
count += int64(n)
|
|
||||||
if err != nil {
|
|
||||||
return count, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if s[key].Instr.Name == "" {
|
|
||||||
unknownInstr++
|
|
||||||
unknownUses += s[key].Count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err := fmt.Fprintln(w, "\nUnknown uses:", unknownUses)
|
|
||||||
count += int64(n)
|
|
||||||
if err != nil {
|
|
||||||
return count, err
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err = fmt.Fprintln(w, "Unknown instructions:", unknownInstr)
|
|
||||||
count += int64(n)
|
|
||||||
if err != nil {
|
|
||||||
return count, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return count, nil
|
|
||||||
}
|
|
|
@ -15,7 +15,7 @@ type Token struct {
|
||||||
Instruction *Instruction
|
Instruction *Instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t Token) String(labels map[int]*Label) string {
|
func (t Token) String(labels map[int]string) string {
|
||||||
suffix := ""
|
suffix := ""
|
||||||
switch t.Raw {
|
switch t.Raw {
|
||||||
case 0x86:
|
case 0x86:
|
||||||
|
@ -25,18 +25,10 @@ func (t Token) String(labels map[int]*Label) string {
|
||||||
prefix := ""
|
prefix := ""
|
||||||
if t.IsTarget || t.IsVariable {
|
if t.IsTarget || t.IsVariable {
|
||||||
if lbl, ok := labels[t.Offset]; ok {
|
if lbl, ok := labels[t.Offset]; ok {
|
||||||
comment := ""
|
prefix = "\n"+lbl+":\n"
|
||||||
if lbl.Comment != "" {
|
|
||||||
comment = "; "+lbl.Comment+"\n"
|
|
||||||
}
|
|
||||||
prefix = "\n"+comment+lbl.Name+":\n"
|
|
||||||
} else {
|
} else {
|
||||||
prefix = fmt.Sprintf("\nL%04X:\n", t.Offset)
|
prefix = fmt.Sprintf("\nL%04X:\n", t.Offset)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if lbl, ok := labels[t.Offset]; ok && lbl.Comment != "" {
|
|
||||||
suffix = " ; "+lbl.Comment+suffix
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.Raw < 0x80 {
|
if t.Raw < 0x80 {
|
||||||
|
@ -64,7 +56,7 @@ func (t Token) String(labels map[int]*Label) string {
|
||||||
argstr := []string{}
|
argstr := []string{}
|
||||||
for _, a := range t.Inline {
|
for _, a := range t.Inline {
|
||||||
if lbl, ok := labels[a.Int()]; ok {
|
if lbl, ok := labels[a.Int()]; ok {
|
||||||
argstr = append(argstr, lbl.Name)
|
argstr = append(argstr, lbl)
|
||||||
} else {
|
} else {
|
||||||
argstr = append(argstr, a.HexString())
|
argstr = append(argstr, a.HexString())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue