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,11 +133,9 @@ 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, "", "    ")
 | 
				
			||||||
| 
						 | 
					@ -145,5 +143,5 @@ func (sb *StudyBox) Export(directory string, includeAudio bool) error {
 | 
				
			||||||
		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,218 +13,197 @@ 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 {
 | 
				
			||||||
	Opcode    byte
 | 
						Opcode   byte
 | 
				
			||||||
	ArgCount  int  // stack arguments
 | 
						ArgCount int // stack arguments
 | 
				
			||||||
	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
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (i Instruction) String() string {
 | 
					func (i Instruction) String() 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