[script] Rework CDL & Label stuff
Labels are now in a json file instead of a dumb custom format. This should make things easier to maintain, both in code and data. Address values are in hexadecimal format inside strings. The CDL handling has been cleaned up a bit. Internally, all the values are indexed by CPU address instead of by file offset. This is done because it seems that *all* of the tapes put *all* of their scripts starting at $6000. The only difference between locations is the bank ID used. An offset of +/-0x6000 has been added in a bunch of places to keep the index values in CPU address space. Additionally, setting CDL data for Data has been fixed. This includes creating tokens for Unknown and Data bytes so they can be output in the source. They will have a label of either "UNKN" or "DATA" in the bytecode column. Some smaller things: - EntryPoints array is now in the CDL, allowing to add more starting points for SmartParse(). - script-decode.go now accepts a --no-addr-prefix to suppress the address output on the beginning of each line of script. This enables diffing scripts and having the output mean something. - `push_data` string has been tweaked. Instead of using %q for everything, we build a string from scratch. If there are no bytes outside of the ASCII printable character range just print the ASCII. Otherwise, print out the hex value for every byte. If an individual byte is printable ASCII, print that charater in curly brackets. - Label files will be updated with auto-generated labels. - Provding a non-existent filename for labels or CDL will print a warning, but will write out a file if no other errors occur. - Added DebugCDL() that will write the raw CDL values to a file. This enables comparing the CDL values directly to the script binary. - Added newlines after `long_return` and `long_jump`
This commit is contained in:
parent
c43d4dd29c
commit
193ce21297
|
@ -5,7 +5,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"strconv"
|
"strconv"
|
||||||
"bufio"
|
|
||||||
"slices"
|
"slices"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
@ -23,6 +22,7 @@ type Arguments struct {
|
||||||
CDL string `arg:"--cdl" help:"CodeDataLog json file"`
|
CDL string `arg:"--cdl" help:"CodeDataLog json file"`
|
||||||
CDLOutput string `arg:"--cdl-output"`
|
CDLOutput string `arg:"--cdl-output"`
|
||||||
Smart bool `arg:"--smart"`
|
Smart bool `arg:"--smart"`
|
||||||
|
NoAddrPrefix bool `arg:"--no-addr-prefix"`
|
||||||
|
|
||||||
start int
|
start int
|
||||||
}
|
}
|
||||||
|
@ -45,10 +45,13 @@ func run(args *Arguments) error {
|
||||||
|
|
||||||
var cdl *script.CodeDataLog
|
var cdl *script.CodeDataLog
|
||||||
if args.CDL != "" {
|
if args.CDL != "" {
|
||||||
//fmt.Println(" CDL:", args.CDL)
|
|
||||||
cdl, err = script.CdlFromJsonFile(args.CDL)
|
cdl, err = script.CdlFromJsonFile(args.CDL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//return fmt.Errorf("CDL Parse error: %w", err)
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
fmt.Println("WARN: CDL file doesn't exist")
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("CDL Parse error: %w", err)
|
||||||
|
}
|
||||||
cdl = nil
|
cdl = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,14 +72,24 @@ func run(args *Arguments) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.LabelFile != "" {
|
if args.LabelFile != "" {
|
||||||
labels, err := parseLabelFile(args.LabelFile)
|
err = scr.LabelsFromJsonFile(args.LabelFile)
|
||||||
|
//labels, err := parseLabelFile(args.LabelFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
fmt.Println("WARN: Label file doesn't exist")
|
||||||
|
} else {
|
||||||
return fmt.Errorf("Labels parse error: %w", err)
|
return fmt.Errorf("Labels parse error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, label := range labels {
|
|
||||||
scr.Labels[label.Address] = label
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = scr.WriteLabelsToFile(args.LabelFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Labels write error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//for _, label := range labels {
|
||||||
|
// scr.Labels[label.Address] = label
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
outfile := os.Stdout
|
outfile := os.Stdout
|
||||||
|
@ -105,7 +118,7 @@ func run(args *Arguments) error {
|
||||||
})
|
})
|
||||||
|
|
||||||
for _, token := range scr.Tokens {
|
for _, token := range scr.Tokens {
|
||||||
fmt.Fprintln(outfile, token.String(scr.Labels))
|
fmt.Fprintln(outfile, token.String(scr.Labels, args.NoAddrPrefix))
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.StatsFile != "" {
|
if args.StatsFile != "" {
|
||||||
|
@ -135,67 +148,16 @@ func run(args *Arguments) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error writing CDL file: %w", err)
|
return fmt.Errorf("Error writing CDL file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = scr.DebugCDL(cdlout+".dbg")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error writing CDL debug file: %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)
|
||||||
|
|
|
@ -13,8 +13,10 @@ type CodeDataLog struct {
|
||||||
Code []CdlRange
|
Code []CdlRange
|
||||||
Data []CdlRange
|
Data []CdlRange
|
||||||
|
|
||||||
|
EntryPoints []string
|
||||||
|
|
||||||
|
entries []int
|
||||||
cache map[int]cdlBit
|
cache map[int]cdlBit
|
||||||
offset int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type CdlRange struct {
|
type CdlRange struct {
|
||||||
|
@ -32,6 +34,26 @@ var (
|
||||||
//cdlOpCode cdlBit = 0x04
|
//cdlOpCode cdlBit = 0x04
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (c cdlBit) String() string {
|
||||||
|
switch c {
|
||||||
|
case cdlUnknown:
|
||||||
|
return "UNKN"
|
||||||
|
case cdlCode:
|
||||||
|
return "CODE"
|
||||||
|
case cdlData:
|
||||||
|
return "DATA"
|
||||||
|
default:
|
||||||
|
return "????"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCDL() *CodeDataLog {
|
||||||
|
return &CodeDataLog{
|
||||||
|
entries: []int{},
|
||||||
|
cache: make(map[int]cdlBit),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (cdl *CodeDataLog) WriteToFile(filename string) error {
|
func (cdl *CodeDataLog) WriteToFile(filename string) error {
|
||||||
file, err := os.Create(filename)
|
file, err := os.Create(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -47,6 +69,10 @@ func (cdl *CodeDataLog) WriteToFile(filename string) error {
|
||||||
return werr
|
return werr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cdl *CodeDataLog) getEntries() []int {
|
||||||
|
return cdl.entries
|
||||||
|
}
|
||||||
|
|
||||||
func getRanges(list []int) []CdlRange {
|
func getRanges(list []int) []CdlRange {
|
||||||
//fmt.Printf("getRanges(%v)\n", list)
|
//fmt.Printf("getRanges(%v)\n", list)
|
||||||
data := []CdlRange{}
|
data := []CdlRange{}
|
||||||
|
@ -102,6 +128,10 @@ func (cdl *CodeDataLog) WriteTo(w io.Writer) (int64, error) {
|
||||||
data := []int{}
|
data := []int{}
|
||||||
|
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
|
if k < 0x6000 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
b := cdl.cache[k]
|
b := cdl.cache[k]
|
||||||
if b & cdlCode == cdlCode {
|
if b & cdlCode == cdlCode {
|
||||||
code = append(code, k)
|
code = append(code, k)
|
||||||
|
@ -115,6 +145,10 @@ func (cdl *CodeDataLog) WriteTo(w io.Writer) (int64, error) {
|
||||||
clean.Code = getRanges(code)
|
clean.Code = getRanges(code)
|
||||||
clean.Data = getRanges(data)
|
clean.Data = getRanges(data)
|
||||||
|
|
||||||
|
for _, ent := range cdl.entries {
|
||||||
|
clean.EntryPoints = append(clean.EntryPoints, fmt.Sprintf("0x%X", ent))
|
||||||
|
}
|
||||||
|
|
||||||
raw, err := json.MarshalIndent(clean, "", "\t")
|
raw, err := json.MarshalIndent(clean, "", "\t")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
@ -124,26 +158,12 @@ func (cdl *CodeDataLog) WriteTo(w io.Writer) (int64, error) {
|
||||||
return int64(n), err
|
return int64(n), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cdl *CodeDataLog) setData(scriptOffset int) {
|
func (cdl *CodeDataLog) setData(addr int) {
|
||||||
if cdl.cache == nil {
|
cdl.cache[addr] |= cdlData
|
||||||
err := cdl.doCache()
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("CDL data error: %w", err))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cdl.cache[scriptOffset+cdl.offset] |= cdlData
|
func (cdl *CodeDataLog) setCode(addr int) {
|
||||||
}
|
cdl.cache[addr] |= cdlCode
|
||||||
|
|
||||||
func (cdl *CodeDataLog) setCode(scriptOffset int) {
|
|
||||||
if cdl.cache == nil {
|
|
||||||
err := cdl.doCache()
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("CDL data error: %w", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cdl.cache[scriptOffset+cdl.offset] |= cdlCode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cdl *CodeDataLog) doCache() error {
|
func (cdl *CodeDataLog) doCache() error {
|
||||||
|
@ -161,10 +181,6 @@ func (cdl *CodeDataLog) doCache() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := int(start); i <= int(end); i++ {
|
for i := int(start); i <= int(end); i++ {
|
||||||
if _, ok := cdl.cache[i]; !ok {
|
|
||||||
cdl.cache[i] = cdlUnknown
|
|
||||||
}
|
|
||||||
|
|
||||||
cdl.cache[i] |= cdlCode
|
cdl.cache[i] |= cdlCode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,25 +197,34 @@ func (cdl *CodeDataLog) doCache() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := int(start); i <= int(end); i++ {
|
for i := int(start); i <= int(end); i++ {
|
||||||
if _, ok := cdl.cache[i]; !ok {
|
|
||||||
cdl.cache[i] = cdlUnknown
|
|
||||||
}
|
|
||||||
|
|
||||||
cdl.cache[i] |= cdlData
|
cdl.cache[i] |= cdlData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cdl.entries = []int{}
|
||||||
|
for _, ent := range cdl.EntryPoints {
|
||||||
|
addr, err := strconv.ParseInt(ent, 0, 32)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Invalid entry point: %q", ent)
|
||||||
|
}
|
||||||
|
|
||||||
|
cdl.entries = append(cdl.entries, int(addr))
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CdlFromJson(r io.Reader) (*CodeDataLog, error) {
|
func CdlFromJson(r io.Reader) (*CodeDataLog, error) {
|
||||||
cdl := &CodeDataLog{}
|
cdl := NewCDL()
|
||||||
dec := json.NewDecoder(r)
|
dec := json.NewDecoder(r)
|
||||||
err := dec.Decode(cdl)
|
err := dec.Decode(cdl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//cdl.Data = []CdlRange{}
|
||||||
|
cdl.doCache()
|
||||||
|
|
||||||
return cdl, nil
|
return cdl, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,13 +238,6 @@ func CdlFromJsonFile(filename string) (*CodeDataLog, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cdl *CodeDataLog) IsData(addr int) bool {
|
func (cdl *CodeDataLog) IsData(addr int) bool {
|
||||||
if cdl.cache == nil {
|
|
||||||
err := cdl.doCache()
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("CDL data error: %w", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val, ok := cdl.cache[addr]
|
val, ok := cdl.cache[addr]
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
|
@ -229,13 +247,6 @@ func (cdl *CodeDataLog) IsData(addr int) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cdl *CodeDataLog) IsCode(addr int) bool {
|
func (cdl *CodeDataLog) IsCode(addr int) bool {
|
||||||
if cdl.cache == nil {
|
|
||||||
err := cdl.doCache()
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("CDL data error: %w", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val, ok := cdl.cache[addr]
|
val, ok := cdl.cache[addr]
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -2,6 +2,10 @@ package script
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Label struct {
|
type Label struct {
|
||||||
|
@ -11,6 +15,36 @@ type Label struct {
|
||||||
FarLabel bool
|
FarLabel bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type JsonLabel struct {
|
||||||
|
Address string
|
||||||
|
Name string
|
||||||
|
Comment string
|
||||||
|
FarLabel bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l Label) JsonLabel() JsonLabel {
|
||||||
|
return JsonLabel{
|
||||||
|
Address: fmt.Sprintf("0x%X", l.Address),
|
||||||
|
Name: l.Name,
|
||||||
|
Comment: l.Comment,
|
||||||
|
FarLabel: l.FarLabel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l JsonLabel) Label() (*Label, error) {
|
||||||
|
addr, err := strconv.ParseInt(l.Address, 0, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Invalid address: %q", l.Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Label{
|
||||||
|
Address: int(addr),
|
||||||
|
Name: l.Name,
|
||||||
|
Comment: l.Comment,
|
||||||
|
FarLabel: l.FarLabel,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func AutoLabel(address int) *Label {
|
func AutoLabel(address int) *Label {
|
||||||
return &Label{
|
return &Label{
|
||||||
Address: address,
|
Address: address,
|
||||||
|
@ -40,3 +74,61 @@ func NewLabel(address int, name string) *Label {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Script) LabelsFromJsonFile(filename string) error {
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
return s.LabelsFromJson(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Script) LabelsFromJson(r io.Reader) error {
|
||||||
|
lbls := []JsonLabel{}
|
||||||
|
dec := json.NewDecoder(r)
|
||||||
|
err := dec.Decode(&lbls)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Labels == nil {
|
||||||
|
s.Labels = make(map[int]*Label)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, lbl := range lbls {
|
||||||
|
l, err := lbl.Label()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Labels[l.Address] = l
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Script) WriteLabelsToFile(filename string) error {
|
||||||
|
file, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
return s.WriteLabels(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Script) WriteLabels(w io.Writer) error {
|
||||||
|
slice := []JsonLabel{}
|
||||||
|
for _, lbl := range s.Labels {
|
||||||
|
slice = append(slice, lbl.JsonLabel())
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := json.MarshalIndent(slice, "", "\t")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = w.Write(raw)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
|
@ -54,6 +54,7 @@ func SmartParse(rawinput []byte, startAddr int, cdl *CodeDataLog) (*Script, erro
|
||||||
Labels: make(map[int]*Label), // map[location]name
|
Labels: make(map[int]*Label), // map[location]name
|
||||||
|
|
||||||
CDL: cdl,
|
CDL: cdl,
|
||||||
|
origSize: len(rawinput),
|
||||||
},
|
},
|
||||||
|
|
||||||
rawinput: rawinput,
|
rawinput: rawinput,
|
||||||
|
@ -61,17 +62,28 @@ func SmartParse(rawinput []byte, startAddr int, cdl *CodeDataLog) (*Script, erro
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.script.CDL == nil {
|
if p.script.CDL == nil {
|
||||||
p.script.CDL = &CodeDataLog{}
|
p.script.CDL = NewCDL()
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenMap := make(map[int]*Token)
|
tokenMap := make(map[int]*Token)
|
||||||
|
|
||||||
// starting point is the third byte in the script.
|
// starting point is the third byte in the script.
|
||||||
branches := []int{ 2 }
|
branches := []int{ 2 }
|
||||||
|
for _, ent := range p.script.CDL.getEntries() {
|
||||||
|
addr := ent-startAddr
|
||||||
|
if addr > 0 {
|
||||||
|
branches = append(branches, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
visited := make([]bool, len(p.rawinput))
|
visited := make([]bool, len(p.rawinput))
|
||||||
|
|
||||||
for len(branches) > 0 {
|
for len(branches) > 0 {
|
||||||
//fmt.Printf("start @ $%04X\n", branches[0]+startAddr)
|
st := branches[0]+startAddr
|
||||||
|
//fmt.Printf("start @ $%04X\n", st)
|
||||||
|
p.script.Labels[st] = AutoLabel(st)
|
||||||
|
|
||||||
INNER:
|
INNER:
|
||||||
for p.current = branches[0]; p.current < len(p.rawinput); p.current++ {
|
for p.current = branches[0]; p.current < len(p.rawinput); p.current++ {
|
||||||
//branches = branches[1:]
|
//branches = branches[1:]
|
||||||
|
@ -99,7 +111,7 @@ INNER:
|
||||||
|
|
||||||
//fmt.Printf("{$%04X} %s\n", token.Offset, token.String(map[int]*Label{}))
|
//fmt.Printf("{$%04X} %s\n", token.Offset, token.String(map[int]*Label{}))
|
||||||
|
|
||||||
p.script.CDL.setCode(p.current)
|
p.script.CDL.setCode(p.current+p.startAddr)
|
||||||
if raw < 0x80 {
|
if raw < 0x80 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -112,7 +124,7 @@ INNER:
|
||||||
//fmt.Println(token.String(map[int]*Label{}))
|
//fmt.Println(token.String(map[int]*Label{}))
|
||||||
|
|
||||||
switch raw {
|
switch raw {
|
||||||
case 0x86, 0xAC, 0xFF, 0x81, 0x9B, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xFD: // return, long_return, break_engine & halts
|
case 0x86, 0xAC, 0xAA, 0xFF, 0x81, 0x9B, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xFD: // return, long_return, long_jump, break_engine & halts
|
||||||
//fmt.Printf("[$%04X] %s\n",
|
//fmt.Printf("[$%04X] %s\n",
|
||||||
// token.Offset, token.Instruction.Name)
|
// token.Offset, token.Instruction.Name)
|
||||||
break INNER
|
break INNER
|
||||||
|
@ -162,11 +174,13 @@ INNER:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if token.Instruction.OpCount == 2 {
|
if token.Instruction.OpCount == 2 && !token.Instruction.InlineImmediate {
|
||||||
val := token.Inline[0].Int()
|
val := token.Inline[0].Int()
|
||||||
if _, ok := p.script.Labels[val]; !ok {
|
if _, ok := p.script.Labels[val]; !ok {//&& val >= startAddr {
|
||||||
p.script.Labels[val] = AutoLabelVar(val)
|
p.script.Labels[val] = AutoLabelVar(val)
|
||||||
}
|
}
|
||||||
|
p.script.CDL.setData(val)
|
||||||
|
p.script.CDL.setData(val+1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,6 +190,40 @@ INNER:
|
||||||
branches = branches[1:]
|
branches = branches[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add data tokens
|
||||||
|
for addr, bit := range p.script.CDL.cache {
|
||||||
|
if addr < 0x6002 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore code bytes
|
||||||
|
if bit & cdlCode == cdlCode {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore labels outside the script's address range
|
||||||
|
if addr > len(rawinput)+0x6000 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := p.script.Labels[addr]; ok {
|
||||||
|
p.script.Tokens = append(p.script.Tokens, &Token{
|
||||||
|
Offset: addr,
|
||||||
|
Inline: []InlineVal{NewWordVal([]byte{rawinput[addr-0x6000], rawinput[addr+1-0x6000]})},
|
||||||
|
IsVariable: true,
|
||||||
|
IsData: true,
|
||||||
|
cdl: bit.String(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
p.script.Tokens = append(p.script.Tokens, &Token{
|
||||||
|
Offset: addr,
|
||||||
|
Raw: rawinput[addr-0x6000],
|
||||||
|
IsData: true,
|
||||||
|
cdl: bit.String(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return p.script, nil
|
return p.script, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,6 +240,7 @@ func Parse(rawinput []byte, startAddr int, cdl *CodeDataLog) (*Script, error) {
|
||||||
StartAddress: startAddr,
|
StartAddress: startAddr,
|
||||||
Labels: make(map[int]*Label), // map[location]name
|
Labels: make(map[int]*Label), // map[location]name
|
||||||
CDL: cdl,
|
CDL: cdl,
|
||||||
|
origSize: len(rawinput),
|
||||||
},
|
},
|
||||||
rawinput: rawinput,
|
rawinput: rawinput,
|
||||||
startAddr: startAddr,
|
startAddr: startAddr,
|
||||||
|
@ -199,7 +248,7 @@ func Parse(rawinput []byte, startAddr int, cdl *CodeDataLog) (*Script, error) {
|
||||||
tokenMap := make(map[int]*Token)
|
tokenMap := make(map[int]*Token)
|
||||||
|
|
||||||
if p.script.CDL == nil {
|
if p.script.CDL == nil {
|
||||||
p.script.CDL = &CodeDataLog{}
|
p.script.CDL = NewCDL()
|
||||||
}
|
}
|
||||||
|
|
||||||
//earliestVar := len(p.rawinput)-2
|
//earliestVar := len(p.rawinput)-2
|
||||||
|
@ -302,8 +351,8 @@ func Parse(rawinput []byte, startAddr int, cdl *CodeDataLog) (*Script, error) {
|
||||||
|
|
||||||
if t.Instruction.OpCount == 2 && !t.Instruction.InlineImmediate {
|
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 _, ok := tokenMap[addr]; ok {
|
||||||
tok.IsVariable = true
|
//tok.IsVariable = true
|
||||||
p.script.Labels[addr] = AutoLabelVar(addr) //fmt.Sprintf("Var_%04X", addr)
|
p.script.Labels[addr] = AutoLabelVar(addr) //fmt.Sprintf("Var_%04X", addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -325,7 +374,7 @@ func (p *Parser) parseToken(token *Token, raw byte) error {
|
||||||
switch op.OpCount {
|
switch op.OpCount {
|
||||||
case -1: // null terminated
|
case -1: // null terminated
|
||||||
for ; p.current < len(p.rawinput); p.current++ {
|
for ; p.current < len(p.rawinput); p.current++ {
|
||||||
p.script.CDL.setCode(p.current)
|
p.script.CDL.setCode(p.current+p.startAddr)
|
||||||
val := ByteVal(p.rawinput[p.current])
|
val := ByteVal(p.rawinput[p.current])
|
||||||
args = append(args, val)
|
args = append(args, val)
|
||||||
if p.rawinput[p.current] == 0x00 {
|
if p.rawinput[p.current] == 0x00 {
|
||||||
|
@ -336,10 +385,12 @@ func (p *Parser) parseToken(token *Token, raw byte) error {
|
||||||
case -2: // count then count words
|
case -2: // count then count words
|
||||||
// FIXME: wtf makes this different from -3??
|
// FIXME: wtf makes this different from -3??
|
||||||
p.current++
|
p.current++
|
||||||
|
|
||||||
l := int(p.rawinput[p.current])
|
l := int(p.rawinput[p.current])
|
||||||
p.script.CDL.setCode(p.current)
|
p.script.CDL.setCode(p.current+p.startAddr)
|
||||||
args = append(args, ByteVal(l))
|
args = append(args, ByteVal(l))
|
||||||
p.current++
|
p.current++
|
||||||
|
|
||||||
for c := 0; c < l; c++ {
|
for c := 0; c < l; c++ {
|
||||||
if len(p.rawinput) <= p.current+1 {
|
if len(p.rawinput) <= p.current+1 {
|
||||||
return errors.Join(ErrEarlyEOF,
|
return errors.Join(ErrEarlyEOF,
|
||||||
|
@ -347,26 +398,37 @@ func (p *Parser) parseToken(token *Token, raw byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
args = append(args, WordVal([2]byte{p.rawinput[p.current], p.rawinput[p.current+1]}))
|
args = append(args, WordVal([2]byte{p.rawinput[p.current], p.rawinput[p.current+1]}))
|
||||||
|
p.script.CDL.setCode(p.current+p.startAddr)
|
||||||
|
p.script.CDL.setCode(p.current+p.startAddr+1)
|
||||||
p.current+=2
|
p.current+=2
|
||||||
}
|
}
|
||||||
p.current--
|
p.current--
|
||||||
|
|
||||||
case -3: // count then count words. "default" is no call (skip Code_Pointer to after args)
|
case -3: // count then count words. "default" is no call (skip Code_Pointer to after args)
|
||||||
p.current++
|
p.current++
|
||||||
|
|
||||||
l := int(p.rawinput[p.current])
|
l := int(p.rawinput[p.current])
|
||||||
args = append(args, ByteVal(l))
|
args = append(args, ByteVal(l))
|
||||||
p.script.CDL.setCode(p.current)
|
p.script.CDL.setCode(p.current+p.startAddr)
|
||||||
p.current++
|
p.current++
|
||||||
|
|
||||||
for c := 0; c < l; c++ {
|
for c := 0; c < l; c++ {
|
||||||
|
if len(p.rawinput) <= p.current+1 {
|
||||||
|
return errors.Join(ErrEarlyEOF,
|
||||||
|
fmt.Errorf("OP early end at offset 0x%X (%d) {%d} %#v", p.current, p.current, l, op))
|
||||||
|
}
|
||||||
|
|
||||||
args = append(args, WordVal([2]byte{p.rawinput[p.current], p.rawinput[p.current+1]}))
|
args = append(args, WordVal([2]byte{p.rawinput[p.current], p.rawinput[p.current+1]}))
|
||||||
|
p.script.CDL.setCode(p.current+p.startAddr)
|
||||||
|
p.script.CDL.setCode(p.current+p.startAddr+1)
|
||||||
p.current+=2
|
p.current+=2
|
||||||
}
|
}
|
||||||
p.current--
|
p.current--
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
args = append(args, WordVal([2]byte{p.rawinput[p.current+1], p.rawinput[p.current+2]}))
|
args = append(args, WordVal([2]byte{p.rawinput[p.current+1], p.rawinput[p.current+2]}))
|
||||||
p.script.CDL.setCode(p.current+1)
|
p.script.CDL.setCode(p.current+p.startAddr+1)
|
||||||
p.script.CDL.setCode(p.current+2)
|
p.script.CDL.setCode(p.current+p.startAddr+2)
|
||||||
p.current+=2
|
p.current+=2
|
||||||
|
|
||||||
//fmt.Printf("var at $%04X\n", val.Int())
|
//fmt.Printf("var at $%04X\n", val.Int())
|
||||||
|
@ -377,7 +439,7 @@ func (p *Parser) parseToken(token *Token, raw byte) error {
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
p.current++
|
p.current++
|
||||||
p.script.CDL.setCode(p.current)
|
p.script.CDL.setCode(p.current+p.startAddr)
|
||||||
args = append(args, ByteVal(p.rawinput[p.current]))
|
args = append(args, ByteVal(p.rawinput[p.current]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package script
|
package script
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Script struct {
|
type Script struct {
|
||||||
|
@ -12,6 +14,8 @@ type Script struct {
|
||||||
|
|
||||||
Labels map[int]*Label
|
Labels map[int]*Label
|
||||||
CDL *CodeDataLog
|
CDL *CodeDataLog
|
||||||
|
|
||||||
|
origSize int // size of the binary input
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Script) Stats() Stats {
|
func (s *Script) Stats() Stats {
|
||||||
|
@ -34,3 +38,30 @@ func (s *Script) Stats() Stats {
|
||||||
|
|
||||||
return st
|
return st
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Script) DebugCDL(filename string) error {
|
||||||
|
if s.origSize == 0 {
|
||||||
|
return fmt.Errorf("origSize == 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.CDL.cache == nil {
|
||||||
|
err := s.CDL.doCache()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("doCache() error: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dat := make([]byte, s.origSize)
|
||||||
|
for i := 2; i < len(dat); i++ {
|
||||||
|
if val, ok := s.CDL.cache[i+0x6000]; ok {
|
||||||
|
dat[i] = byte(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := os.WriteFile(filename, dat, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("WriteFile() error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
108
script/tokens.go
108
script/tokens.go
|
@ -13,16 +13,23 @@ type Token struct {
|
||||||
IsVariable bool // target of something else
|
IsVariable bool // target of something else
|
||||||
IsData bool // from CDL
|
IsData bool // from CDL
|
||||||
|
|
||||||
|
cdl string // CDL string type
|
||||||
|
|
||||||
Instruction *Instruction
|
Instruction *Instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t Token) String(labels map[int]*Label) string {
|
func (t Token) String(labels map[int]*Label, suppAddr bool) string {
|
||||||
suffix := ""
|
suffix := ""
|
||||||
switch t.Raw {
|
switch t.Raw {
|
||||||
case 0x86: // Newline after return
|
case 0x86, 0xAC, 0xAA: // Newline after return, long_return, & long_jump
|
||||||
suffix = "\n"
|
suffix = "\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offset := ""
|
||||||
|
if !suppAddr {
|
||||||
|
offset = fmt.Sprintf("[%04X] ", t.Offset)
|
||||||
|
}
|
||||||
|
|
||||||
prefix := ""
|
prefix := ""
|
||||||
if lbl, ok := labels[t.Offset]; ok {
|
if lbl, ok := labels[t.Offset]; ok {
|
||||||
comment := ""
|
comment := ""
|
||||||
|
@ -36,21 +43,44 @@ func (t Token) String(labels map[int]*Label) string {
|
||||||
prefix = "\n"+comment+name
|
prefix = "\n"+comment+name
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.Instruction == nil {
|
if t.IsVariable {
|
||||||
return fmt.Sprintf("%s[%04X] %02X %-5s : %d%s",
|
return fmt.Sprintf("%s%s%02X %-5s : %d %s%s",
|
||||||
prefix,
|
prefix,
|
||||||
t.Offset,
|
offset,
|
||||||
|
t.Raw,
|
||||||
|
"",
|
||||||
|
t.Inline[0].Int(),
|
||||||
|
t.Inline[0].HexString(),
|
||||||
|
suffix,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.Instruction == nil {
|
||||||
|
if t.IsData == false {
|
||||||
|
return fmt.Sprintf("%s%s%02X %-5s : %d%s",
|
||||||
|
prefix,
|
||||||
|
offset,
|
||||||
t.Raw,
|
t.Raw,
|
||||||
"",
|
"",
|
||||||
int(t.Raw),
|
int(t.Raw),
|
||||||
suffix,
|
suffix,
|
||||||
)
|
)
|
||||||
|
} else if t.IsData {
|
||||||
|
return fmt.Sprintf("%s%s%02X %-5s : %d%s",
|
||||||
|
prefix,
|
||||||
|
offset,
|
||||||
|
t.Raw,
|
||||||
|
t.cdl,
|
||||||
|
int(t.Raw),
|
||||||
|
suffix,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(t.Inline) == 0 {
|
if len(t.Inline) == 0 {
|
||||||
return fmt.Sprintf("%s[%04X] %02X %-5s : %s%s",
|
return fmt.Sprintf("%s%s%02X %-5s : %s%s",
|
||||||
prefix,
|
prefix,
|
||||||
t.Offset,
|
offset,
|
||||||
t.Raw,
|
t.Raw,
|
||||||
"",
|
"",
|
||||||
t.Instruction.String(),
|
t.Instruction.String(),
|
||||||
|
@ -60,7 +90,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 && !t.Instruction.InlineImmediate {
|
||||||
argstr = append(argstr, lbl.Name)
|
argstr = append(argstr, lbl.Name)
|
||||||
} else {
|
} else {
|
||||||
argstr = append(argstr, a.HexString())
|
argstr = append(argstr, a.HexString())
|
||||||
|
@ -80,18 +110,56 @@ func (t Token) String(labels map[int]*Label) string {
|
||||||
|
|
||||||
switch t.Raw {
|
switch t.Raw {
|
||||||
case 0xBB: // push_data
|
case 0xBB: // push_data
|
||||||
bs := []byte{}
|
raw := []byte{}
|
||||||
for _, val := range t.Inline {
|
|
||||||
bs = append(bs, val.Bytes()...)
|
ascii := true
|
||||||
|
for _, val := range t.Inline[1:len(t.Inline)-1] {
|
||||||
|
for _, b := range val.Bytes() {
|
||||||
|
raw = append(raw, b)
|
||||||
|
if b < 0x20 || b > 0x7E {
|
||||||
|
ascii = false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("%s[%04X] %02X (...) : %s %q%s",
|
bs := ""
|
||||||
|
if ascii {
|
||||||
|
bs = fmt.Sprintf("%q", string(raw))
|
||||||
|
} else {
|
||||||
|
vals := []string{}
|
||||||
|
for _, b := range raw {
|
||||||
|
if b >= 0x20 && b <= 0x7E {
|
||||||
|
vals = append(vals, fmt.Sprintf("0x%02X{%c}", b, b))
|
||||||
|
} else {
|
||||||
|
vals = append(vals, fmt.Sprintf("0x%02X", b))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bs = "["+strings.Join(vals, " ")+"]"
|
||||||
|
}
|
||||||
|
|
||||||
|
//for _, val := range t.Inline {
|
||||||
|
// //bs = append(bs, val.Bytes()...)
|
||||||
|
// for _, b := range val.Bytes() {
|
||||||
|
// // These strings are strictly binary or ascii. If there's
|
||||||
|
// // non-ascii, don't try and read it as unicode if we find
|
||||||
|
// // some "valid" code points. Eg, 0xD?, 0xB? (%110?_????, %10??_????)
|
||||||
|
// if b < 0x20 || b > 0x7E {
|
||||||
|
// bs = append(bs, fmt.Sprintf("\\x%02x", b))
|
||||||
|
// } else {
|
||||||
|
// bs = append(bs, string(b))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s%s%02X (...) : %s %s%s",
|
||||||
prefix,
|
prefix,
|
||||||
t.Offset,
|
offset,
|
||||||
t.Raw,
|
t.Raw,
|
||||||
t.Instruction.String(),
|
t.Instruction.String(),
|
||||||
string(bs[1:len(bs)-1]),
|
//string(bs[1:len(bs)-1]),
|
||||||
|
//strings.Join(bs[1:len(bs)-1], ""),
|
||||||
//strings.Join(argstr[1:], " "),
|
//strings.Join(argstr[1:], " "),
|
||||||
|
bs,
|
||||||
suffix,
|
suffix,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -99,9 +167,9 @@ func (t Token) String(labels map[int]*Label) string {
|
||||||
|
|
||||||
|
|
||||||
case 0xC1, 0xEE: // switches
|
case 0xC1, 0xEE: // switches
|
||||||
return fmt.Sprintf("%s[%04X] %02X %-5s : %s %s%s",
|
return fmt.Sprintf("%s%s%02X %-5s : %s %s%s",
|
||||||
prefix,
|
prefix,
|
||||||
t.Offset,
|
offset,
|
||||||
t.Raw,
|
t.Raw,
|
||||||
"",
|
"",
|
||||||
t.Instruction.String(),
|
t.Instruction.String(),
|
||||||
|
@ -110,9 +178,9 @@ func (t Token) String(labels map[int]*Label) string {
|
||||||
)
|
)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return fmt.Sprintf("%s[%04X] %02X %-5s : %s %s%s",
|
return fmt.Sprintf("%s%s%02X %-5s : %s %s%s",
|
||||||
prefix,
|
prefix,
|
||||||
t.Offset,
|
offset,
|
||||||
t.Raw,
|
t.Raw,
|
||||||
strings.Join(bytestr, " "),
|
strings.Join(bytestr, " "),
|
||||||
t.Instruction.String(),
|
t.Instruction.String(),
|
||||||
|
@ -122,9 +190,9 @@ func (t Token) String(labels map[int]*Label) string {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("%s%04X: %s %s%s",
|
return fmt.Sprintf("%s%s%s %s%s",
|
||||||
prefix,
|
prefix,
|
||||||
t.Offset,
|
offset,
|
||||||
t.Instruction.String(),
|
t.Instruction.String(),
|
||||||
strings.Join(argstr, " "),
|
strings.Join(argstr, " "),
|
||||||
suffix,
|
suffix,
|
||||||
|
|
Loading…
Reference in New Issue