Compare commits
	
		
			No commits in common. "bfc7beb38234667f2f69a9e415a053161232ce98" and "045f204a6bc2dcde5ab14cc4b90a72468635c009" have entirely different histories.
		
	
	
		
			bfc7beb382
			...
			045f204a6b
		
	
		| 
						 | 
					@ -6,7 +6,6 @@ import (
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"io/fs"
 | 
						"io/fs"
 | 
				
			||||||
	"slices"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/alexflint/go-arg"
 | 
						"github.com/alexflint/go-arg"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,7 +19,6 @@ type Arguments struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Walker struct {
 | 
					type Walker struct {
 | 
				
			||||||
	Found []string
 | 
						Found []string
 | 
				
			||||||
	CDLs  []string
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (w *Walker) WalkFunc(path string, info fs.DirEntry, err error) error {
 | 
					func (w *Walker) WalkFunc(path string, info fs.DirEntry, err error) error {
 | 
				
			||||||
| 
						 | 
					@ -32,10 +30,6 @@ func (w *Walker) WalkFunc(path string, info fs.DirEntry, err error) error {
 | 
				
			||||||
		w.Found = append(w.Found, path)
 | 
							w.Found = append(w.Found, path)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if strings.HasSuffix(path, "_scriptData.cdl.json") {
 | 
					 | 
				
			||||||
		w.CDLs = append(w.CDLs, path)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,31 +46,17 @@ func run(args *Arguments) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, file := range w.Found {
 | 
						for _, file := range w.Found {
 | 
				
			||||||
		fmt.Println(file)
 | 
							fmt.Println(file)
 | 
				
			||||||
		var cdl *script.CodeDataLog
 | 
							scr, err := script.ParseFile(file, 0x0000)
 | 
				
			||||||
		cdlname := file[:len(file)-4]+".cdl.json"
 | 
					 | 
				
			||||||
		if slices.Contains(w.CDLs, cdlname) {
 | 
					 | 
				
			||||||
			fmt.Println("", cdlname)
 | 
					 | 
				
			||||||
			cdl, err = script.CdlFromJsonFile(cdlname)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				fmt.Println(" CDL read error:", err)
 | 
					 | 
				
			||||||
				cdl = nil
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		scr, err := script.SmartParseFile(file, 0x6000, cdl)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			//if scr != nil {
 | 
								if scr != nil {
 | 
				
			||||||
			//	for _, token := range scr.Tokens {
 | 
									for _, token := range scr.Tokens {
 | 
				
			||||||
			//		fmt.Println(token.String(scr.Labels))
 | 
										fmt.Println(token.String(scr.Labels))
 | 
				
			||||||
			//	}
 | 
									}
 | 
				
			||||||
			//}
 | 
								}
 | 
				
			||||||
			fmt.Println(err)
 | 
								return err
 | 
				
			||||||
			//return err
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if scr != nil {
 | 
							stats.Add(scr.Stats())
 | 
				
			||||||
			stats.Add(scr.Stats())
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	outfile, err := os.Create(args.Output)
 | 
						outfile, err := os.Create(args.Output)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,6 @@ import (
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"bufio"
 | 
						"bufio"
 | 
				
			||||||
	"slices"
 | 
						"slices"
 | 
				
			||||||
	"errors"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/alexflint/go-arg"
 | 
						"github.com/alexflint/go-arg"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,9 +19,6 @@ type Arguments struct {
 | 
				
			||||||
	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"`
 | 
						StatsFile string `arg:"--stats" help:"file to write some statistics to"`
 | 
				
			||||||
	LabelFile string `arg:"--labels" help:"file containing address/label pairs"`
 | 
						LabelFile string `arg:"--labels" help:"file containing address/label pairs"`
 | 
				
			||||||
	CDL string `arg:"--cdl" help:"CodeDataLog json file"`
 | 
					 | 
				
			||||||
	CDLOutput string `arg:"--cdl-output"`
 | 
					 | 
				
			||||||
	Smart bool `arg:"--smart"`
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	start int
 | 
						start int
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -43,38 +39,19 @@ func run(args *Arguments) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	args.start = int(val)
 | 
						args.start = int(val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var cdl *script.CodeDataLog
 | 
						scr, err := script.ParseFile(args.Input, args.start)
 | 
				
			||||||
	if args.CDL != "" {
 | 
					 | 
				
			||||||
		//fmt.Println("  CDL:", args.CDL)
 | 
					 | 
				
			||||||
		cdl, err = script.CdlFromJsonFile(args.CDL)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			//return fmt.Errorf("CDL Parse error: %w", err)
 | 
					 | 
				
			||||||
			cdl = nil
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var scr *script.Script
 | 
					 | 
				
			||||||
	if args.Smart {
 | 
					 | 
				
			||||||
		scr, err = script.SmartParseFile(args.Input, args.start, cdl)
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		scr, err = script.ParseFile(args.Input, args.start, cdl)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if errors.Is(err, script.ErrEarlyEOF) || errors.Is(err, script.ErrNavigation) {
 | 
							return err
 | 
				
			||||||
			fmt.Println(err)
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			return fmt.Errorf("Script parse error: %w", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if args.LabelFile != "" {
 | 
						if args.LabelFile != "" {
 | 
				
			||||||
		labels, err := parseLabelFile(args.LabelFile)
 | 
							labels, err := parseLabelFile(args.LabelFile)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return fmt.Errorf("Labels parse error: %w", err)
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for _, label := range labels {
 | 
							for _, label := range labels {
 | 
				
			||||||
 | 
								//fmt.Printf("%#v\n", label)
 | 
				
			||||||
			scr.Labels[label.Address] = label
 | 
								scr.Labels[label.Address] = label
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -89,7 +66,7 @@ func run(args *Arguments) error {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, w := range scr.Warnings {
 | 
						for _, w := range scr.Warnings {
 | 
				
			||||||
		//fmt.Fprintln(os.Stderr, w)
 | 
							fmt.Fprintln(os.Stderr, w)
 | 
				
			||||||
		if args.Output != "" {
 | 
							if args.Output != "" {
 | 
				
			||||||
			fmt.Fprintln(outfile, "; "+w)
 | 
								fmt.Fprintln(outfile, "; "+w)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -98,12 +75,6 @@ func run(args *Arguments) error {
 | 
				
			||||||
	fmt.Fprintf(outfile, "; Start address: $%04X\n", scr.StartAddress)
 | 
						fmt.Fprintf(outfile, "; Start address: $%04X\n", scr.StartAddress)
 | 
				
			||||||
	fmt.Fprintf(outfile, "; Stack address: $%04X\n\n", scr.StackAddress)
 | 
						fmt.Fprintf(outfile, "; Stack address: $%04X\n\n", scr.StackAddress)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	slices.SortFunc(scr.Tokens, func(a, b *script.Token) int {
 | 
					 | 
				
			||||||
		if a.Offset < b.Offset { return -1 }
 | 
					 | 
				
			||||||
		if a.Offset > b.Offset { return 1 }
 | 
					 | 
				
			||||||
		return 0
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, token := range scr.Tokens {
 | 
						for _, token := range scr.Tokens {
 | 
				
			||||||
		fmt.Fprintln(outfile, token.String(scr.Labels))
 | 
							fmt.Fprintln(outfile, token.String(scr.Labels))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -115,28 +86,13 @@ func run(args *Arguments) error {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		defer statfile.Close()
 | 
							defer statfile.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							//err = scr.WriteStats(statfile)
 | 
				
			||||||
		_, err = scr.Stats().WriteTo(statfile)
 | 
							_, err = scr.Stats().WriteTo(statfile)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return fmt.Errorf("Error writing stats: %w", err)
 | 
								return fmt.Errorf("Error writing stats: %w", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if scr.CDL != nil {
 | 
					 | 
				
			||||||
		cdlout := args.CDL
 | 
					 | 
				
			||||||
		if args.CDLOutput != "" {
 | 
					 | 
				
			||||||
			cdlout = args.CDLOutput
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if cdlout == "" {
 | 
					 | 
				
			||||||
			return nil
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		err = scr.CDL.WriteToFile(cdlout)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return fmt.Errorf("Error writing CDL file: %w", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										245
									
								
								script/cdl.go
								
								
								
								
							
							
						
						
									
										245
									
								
								script/cdl.go
								
								
								
								
							| 
						 | 
					@ -1,245 +0,0 @@
 | 
				
			||||||
package script
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"io"
 | 
					 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
	"strconv"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"slices"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type CodeDataLog struct {
 | 
					 | 
				
			||||||
	Code []CdlRange
 | 
					 | 
				
			||||||
	Data []CdlRange
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cache map[int]cdlBit
 | 
					 | 
				
			||||||
	offset int
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type CdlRange struct {
 | 
					 | 
				
			||||||
	// strings cuz json doesn't know wtf hexadecimal is
 | 
					 | 
				
			||||||
	Start string
 | 
					 | 
				
			||||||
	End   string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type cdlBit byte
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var (
 | 
					 | 
				
			||||||
	cdlUnknown cdlBit = 0x00
 | 
					 | 
				
			||||||
	cdlCode    cdlBit = 0x01
 | 
					 | 
				
			||||||
	cdlData    cdlBit = 0x02
 | 
					 | 
				
			||||||
	//cdlOpCode  cdlBit = 0x04
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (cdl *CodeDataLog) WriteToFile(filename string) error {
 | 
					 | 
				
			||||||
	file, err := os.Create(filename)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_, werr := cdl.WriteTo(file)
 | 
					 | 
				
			||||||
	err = file.Close()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return werr
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func getRanges(list []int) []CdlRange {
 | 
					 | 
				
			||||||
	//fmt.Printf("getRanges(%v)\n", list)
 | 
					 | 
				
			||||||
	data := []CdlRange{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	start := -1
 | 
					 | 
				
			||||||
	//end := -1
 | 
					 | 
				
			||||||
	prev := -1
 | 
					 | 
				
			||||||
	for _, addr := range list {
 | 
					 | 
				
			||||||
		if start == -1 {
 | 
					 | 
				
			||||||
			start = addr
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if prev != -1 && prev != addr-1 {
 | 
					 | 
				
			||||||
			data = append(data, CdlRange{
 | 
					 | 
				
			||||||
				Start: fmt.Sprintf("0x%X", start),
 | 
					 | 
				
			||||||
				End: fmt.Sprintf("0x%X", prev),
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			//fmt.Printf("start: 0x%X end: 0x%X\n", start, prev)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			start = addr
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		prev = addr
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if start != -1 && prev != -1 {
 | 
					 | 
				
			||||||
		data = append(data, CdlRange{
 | 
					 | 
				
			||||||
			Start: fmt.Sprintf("0x%X", start),
 | 
					 | 
				
			||||||
			End: fmt.Sprintf("0x%X", prev),
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		//fmt.Println("start:", start, "end:", prev)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return data
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (cdl *CodeDataLog) WriteTo(w io.Writer) (int64, error) {
 | 
					 | 
				
			||||||
	clean := &CodeDataLog{
 | 
					 | 
				
			||||||
		Code: []CdlRange{},
 | 
					 | 
				
			||||||
		Data: []CdlRange{},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	keys := []int{}
 | 
					 | 
				
			||||||
	for k, _ := range cdl.cache {
 | 
					 | 
				
			||||||
		keys = append(keys, k)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	slices.Sort(keys)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	code := []int{}
 | 
					 | 
				
			||||||
	data := []int{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, k := range keys {
 | 
					 | 
				
			||||||
		b := cdl.cache[k]
 | 
					 | 
				
			||||||
		if b & cdlCode == cdlCode {
 | 
					 | 
				
			||||||
			code = append(code, k)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if b & cdlData == cdlData {
 | 
					 | 
				
			||||||
			data = append(data, k)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	clean.Code = getRanges(code)
 | 
					 | 
				
			||||||
	clean.Data = getRanges(data)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	raw, err := json.MarshalIndent(clean, "", "\t")
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return 0, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	n, err := w.Write(raw)
 | 
					 | 
				
			||||||
	return int64(n), err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (cdl *CodeDataLog) setData(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] |= cdlData
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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 {
 | 
					 | 
				
			||||||
	cdl.cache = make(map[int]cdlBit)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, rng := range cdl.Code {
 | 
					 | 
				
			||||||
		start, err := strconv.ParseInt(rng.Start, 0, 32)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return fmt.Errorf("Invalid start: %q", rng.Start)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		end, err := strconv.ParseInt(rng.End, 0, 32)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return fmt.Errorf("Invalid end: %q", rng.End)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for i := int(start); i <= int(end); i++ {
 | 
					 | 
				
			||||||
			if _, ok := cdl.cache[i]; !ok {
 | 
					 | 
				
			||||||
				cdl.cache[i] = cdlUnknown
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			cdl.cache[i] |= cdlCode
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, rng := range cdl.Data {
 | 
					 | 
				
			||||||
		start, err := strconv.ParseInt(rng.Start, 0, 32)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return fmt.Errorf("Invalid start: %q", rng.Start)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		end, err := strconv.ParseInt(rng.End, 0, 32)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return fmt.Errorf("Invalid end: %q", rng.End)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for i := int(start); i <= int(end); i++ {
 | 
					 | 
				
			||||||
			if _, ok := cdl.cache[i]; !ok {
 | 
					 | 
				
			||||||
				cdl.cache[i] = cdlUnknown
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			cdl.cache[i] |= cdlData
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func CdlFromJson(r io.Reader) (*CodeDataLog, error) {
 | 
					 | 
				
			||||||
	cdl := &CodeDataLog{}
 | 
					 | 
				
			||||||
	dec := json.NewDecoder(r)
 | 
					 | 
				
			||||||
	err := dec.Decode(cdl)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return cdl, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func CdlFromJsonFile(filename string) (*CodeDataLog, error) {
 | 
					 | 
				
			||||||
	file, err := os.Open(filename)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer file.Close()
 | 
					 | 
				
			||||||
	return CdlFromJson(file)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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]
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return val & cdlData == cdlData
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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]
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return val & cdlCode == cdlCode
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,42 +0,0 @@
 | 
				
			||||||
package script
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							
								
								
									
										409
									
								
								script/parser.go
								
								
								
								
							
							
						
						
									
										409
									
								
								script/parser.go
								
								
								
								
							| 
						 | 
					@ -3,300 +3,185 @@ package script
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"errors"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					type Label struct {
 | 
				
			||||||
	ErrEarlyEOF = errors.New("Unexpected EOF when reading OP arguments")
 | 
						Address int
 | 
				
			||||||
	ErrInvalidInstruction = errors.New("Invalid instruction")
 | 
						Name string
 | 
				
			||||||
	ErrNavigation = errors.New("SmartParse navigation error")
 | 
						Comment string
 | 
				
			||||||
)
 | 
						FarLabel bool
 | 
				
			||||||
 | 
					 | 
				
			||||||
type Parser struct {
 | 
					 | 
				
			||||||
	rawinput []byte
 | 
					 | 
				
			||||||
	current int
 | 
					 | 
				
			||||||
	startAddr int
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	script *Script
 | 
					 | 
				
			||||||
	cdl    *CodeDataLog
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ParseFile(filename string, startAddr int, cdl *CodeDataLog) (*Script, error) {
 | 
					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) {
 | 
				
			||||||
	rawfile, err := os.ReadFile(filename)
 | 
						rawfile, err := os.ReadFile(filename)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("unable to read file: %w", err)
 | 
							return nil, fmt.Errorf("unable to read file: %w", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return Parse(rawfile, startAddr, cdl)
 | 
						return Parse(rawfile, startAddr)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func SmartParseFile(filename string, startAddr int, cdl *CodeDataLog) (*Script, error) {
 | 
					func Parse(rawinput []byte, startAddr int) (*Script, error) {
 | 
				
			||||||
	rawfile, err := os.ReadFile(filename)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("unable to read file: %w", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return SmartParse(rawfile, startAddr, cdl)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func SmartParse(rawinput []byte, startAddr int, cdl *CodeDataLog) (*Script, error) {
 | 
					 | 
				
			||||||
	if len(rawinput) < 3 {
 | 
						if len(rawinput) < 3 {
 | 
				
			||||||
		return nil, fmt.Errorf("not enough bytes for script")
 | 
							return nil, fmt.Errorf("not enough bytes for script")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	p := &Parser{
 | 
						script := &Script{
 | 
				
			||||||
 | 
							Tokens: []*Token{},
 | 
				
			||||||
		script: &Script{
 | 
							Warnings: []string{},
 | 
				
			||||||
			Tokens: []*Token{},
 | 
							StackAddress: (int(rawinput[1])<<8) | int(rawinput[0]),
 | 
				
			||||||
			Warnings: []string{},
 | 
							StartAddress: startAddr,
 | 
				
			||||||
			StackAddress: (int(rawinput[1])<<8) | int(rawinput[0]),
 | 
							Labels: make(map[int]*Label), // map[location]name
 | 
				
			||||||
			StartAddress: startAddr,
 | 
					 | 
				
			||||||
			Labels: make(map[int]*Label), // map[location]name
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			CDL: cdl,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		rawinput: rawinput,
 | 
					 | 
				
			||||||
		startAddr: startAddr,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if p.script.CDL == nil {
 | 
					 | 
				
			||||||
		p.script.CDL = &CodeDataLog{}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	tokenMap := make(map[int]*Token)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// starting point is the third byte in the script.
 | 
					 | 
				
			||||||
	branches := []int{ 2 }
 | 
					 | 
				
			||||||
	visited := make([]bool, len(p.rawinput))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for len(branches) > 0 {
 | 
					 | 
				
			||||||
		//fmt.Printf("start @ $%04X\n", branches[0]+startAddr)
 | 
					 | 
				
			||||||
INNER:
 | 
					 | 
				
			||||||
		for p.current = branches[0]; p.current < len(p.rawinput); p.current++ {
 | 
					 | 
				
			||||||
			//branches = branches[1:]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if p.current < 0 {
 | 
					 | 
				
			||||||
				return p.script, errors.Join(ErrNavigation,
 | 
					 | 
				
			||||||
					fmt.Errorf("HOW IS CURRENT NEGATIVE?????"))
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if visited[p.current] {
 | 
					 | 
				
			||||||
				//fmt.Printf("found visited at $%04X\n", p.current+startAddr)
 | 
					 | 
				
			||||||
				break
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			visited[p.current] = true
 | 
					 | 
				
			||||||
			raw := p.rawinput[p.current]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			token := &Token{
 | 
					 | 
				
			||||||
				Offset: startAddr+p.current,
 | 
					 | 
				
			||||||
				Raw: raw,
 | 
					 | 
				
			||||||
				Inline: []InlineVal{},
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			p.script.Tokens = append(p.script.Tokens, token)
 | 
					 | 
				
			||||||
			tokenMap[token.Offset] = token
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			//fmt.Printf("{$%04X} %s\n", token.Offset, token.String(map[int]*Label{}))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			p.script.CDL.setCode(p.current)
 | 
					 | 
				
			||||||
			if raw < 0x80 {
 | 
					 | 
				
			||||||
				continue
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			err := p.parseToken(token, raw)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				return p.script, err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			//fmt.Println(token.String(map[int]*Label{}))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			switch raw {
 | 
					 | 
				
			||||||
			case 0x86, 0xAC, 0xFF, 0x81, 0x9B, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xFD: // return, long_return, break_engine & halts
 | 
					 | 
				
			||||||
				//fmt.Printf("[$%04X] %s\n",
 | 
					 | 
				
			||||||
				//	token.Offset, token.Instruction.Name)
 | 
					 | 
				
			||||||
				break INNER
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			case 0x84, 0xBF, 0xC0, 0x85: // jump_abs, jump_not_zero, jump_zero, call_abs
 | 
					 | 
				
			||||||
				if len(token.Inline) < 1 {
 | 
					 | 
				
			||||||
					return p.script, errors.Join(ErrNavigation,
 | 
					 | 
				
			||||||
						fmt.Errorf("jump missing target"))
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				if len(token.Inline) > 1 {
 | 
					 | 
				
			||||||
					return p.script, errors.Join(ErrNavigation,
 | 
					 | 
				
			||||||
						fmt.Errorf("jump has too many targets"))
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				val := token.Inline[0].Int()
 | 
					 | 
				
			||||||
				//fmt.Printf("[$%04X] %s $%04X\n",
 | 
					 | 
				
			||||||
				//	token.Offset, token.Instruction.Name, val)
 | 
					 | 
				
			||||||
				branches = append(branches, val-startAddr)
 | 
					 | 
				
			||||||
				p.script.Labels[val] = AutoLabel(val)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				if raw == 0x84 { // not jump_abs
 | 
					 | 
				
			||||||
					break INNER
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			case 0xC1, 0xEE: // jump_switch, call_switch
 | 
					 | 
				
			||||||
				if len(token.Inline) < 2 {
 | 
					 | 
				
			||||||
					return p.script, errors.Join(ErrNavigation,
 | 
					 | 
				
			||||||
						fmt.Errorf("switch missing targets"))
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				count := token.Inline[0].Int()
 | 
					 | 
				
			||||||
				if len(token.Inline) != count+1 {
 | 
					 | 
				
			||||||
					return p.script, errors.Join(ErrNavigation,
 | 
					 | 
				
			||||||
						fmt.Errorf("switch target missmatch (expected %d, got %d)", count, len(token.Inline)-1))
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				for _, val := range token.Inline[1:] {
 | 
					 | 
				
			||||||
					//fmt.Printf("[$%04X] %s $%04X\n",
 | 
					 | 
				
			||||||
					//	token.Offset, token.Instruction.Name, val.Int())
 | 
					 | 
				
			||||||
					branches = append(branches, val.Int()-startAddr)
 | 
					 | 
				
			||||||
					p.script.Labels[val.Int()] = AutoLabel(val.Int())
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				if raw == 0xC1 { // jump_switch
 | 
					 | 
				
			||||||
					break INNER
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if token.Instruction.OpCount == 2 {
 | 
					 | 
				
			||||||
				val := token.Inline[0].Int()
 | 
					 | 
				
			||||||
				if _, ok := p.script.Labels[val]; !ok {
 | 
					 | 
				
			||||||
					p.script.Labels[val] = AutoLabelVar(val)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if len(branches) == 1 {
 | 
					 | 
				
			||||||
			break
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		branches = branches[1:]
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return p.script, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func Parse(rawinput []byte, startAddr int, cdl *CodeDataLog) (*Script, error) {
 | 
					 | 
				
			||||||
	if len(rawinput) < 3 {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("not enough bytes for script")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	p := &Parser{
 | 
					 | 
				
			||||||
		script: &Script{
 | 
					 | 
				
			||||||
			Tokens: []*Token{},
 | 
					 | 
				
			||||||
			Warnings: []string{},
 | 
					 | 
				
			||||||
			StackAddress: (int(rawinput[1])<<8) | int(rawinput[0]),
 | 
					 | 
				
			||||||
			StartAddress: startAddr,
 | 
					 | 
				
			||||||
			Labels: make(map[int]*Label), // map[location]name
 | 
					 | 
				
			||||||
			CDL: cdl,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		rawinput: rawinput,
 | 
					 | 
				
			||||||
		startAddr: startAddr,
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	tokenMap := make(map[int]*Token)
 | 
						tokenMap := make(map[int]*Token)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if p.script.CDL == nil {
 | 
						for i := 2; i < len(rawinput); i++ {
 | 
				
			||||||
		p.script.CDL = &CodeDataLog{}
 | 
							raw := rawinput[i]
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	//earliestVar := len(p.rawinput)-2
 | 
					 | 
				
			||||||
	//fmt.Printf("var start bounds: $%04X, $%04X\n", startAddr, startAddr+len(p.rawinput))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for p.current = 2; p.current < len(p.rawinput); p.current++ {
 | 
					 | 
				
			||||||
		//if p.current >= earliestVar {
 | 
					 | 
				
			||||||
		//	fmt.Printf("Earliest Variable found at offset %d ($%04X)\n", p.current, startAddr+p.current)
 | 
					 | 
				
			||||||
		//	break
 | 
					 | 
				
			||||||
		//}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		raw := p.rawinput[p.current]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		token := &Token{
 | 
							token := &Token{
 | 
				
			||||||
			Offset: startAddr+p.current,
 | 
								Offset: startAddr+i,
 | 
				
			||||||
			Raw: raw,
 | 
								Raw: raw,
 | 
				
			||||||
			Inline: []InlineVal{},
 | 
								Inline: []InlineVal{},
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		p.script.Tokens = append(p.script.Tokens, token)
 | 
							script.Tokens = append(script.Tokens, token)
 | 
				
			||||||
		tokenMap[token.Offset] = token
 | 
							tokenMap[token.Offset] = token
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if raw < 0x80 || p.script.CDL.IsData(p.current+startAddr) { // || p.current >= earliestVar {
 | 
							if raw < 0x80 {
 | 
				
			||||||
			if p.script.CDL.IsData(p.current+startAddr) {
 | 
					 | 
				
			||||||
				token.IsData = true
 | 
					 | 
				
			||||||
				//fmt.Print(".")
 | 
					 | 
				
			||||||
				//fmt.Printf("%#v\n", token)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		err := p.parseToken(token, raw)
 | 
							op, ok := InstrMap[raw]
 | 
				
			||||||
		if err != nil {
 | 
							if !ok {
 | 
				
			||||||
			return p.script, err
 | 
								return nil, fmt.Errorf("OP 0x%02X not in instruction map", raw)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							token.Instruction = op
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							args := []InlineVal{}
 | 
				
			||||||
 | 
							switch op.OpCount {
 | 
				
			||||||
 | 
							case -1: // null terminated
 | 
				
			||||||
 | 
								for ; i < len(rawinput); i++ {
 | 
				
			||||||
 | 
									val := ByteVal(rawinput[i])
 | 
				
			||||||
 | 
									args = append(args, val)
 | 
				
			||||||
 | 
									if rawinput[i] == 0x00 {
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case -2: // count then count words
 | 
				
			||||||
 | 
								i++
 | 
				
			||||||
 | 
								l :=  int(rawinput[i])
 | 
				
			||||||
 | 
								args = append(args, ByteVal(l))
 | 
				
			||||||
 | 
								i++
 | 
				
			||||||
 | 
								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]}))
 | 
				
			||||||
 | 
									i+=2
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								i--
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case -3: // count then count words.  "default" is no call (skip Code_Pointer to after args)
 | 
				
			||||||
 | 
								i++
 | 
				
			||||||
 | 
								l :=  int(rawinput[i])
 | 
				
			||||||
 | 
								args = append(args, ByteVal(l))
 | 
				
			||||||
 | 
								i++
 | 
				
			||||||
 | 
								for c := 0; c < l; c++ {
 | 
				
			||||||
 | 
									args = append(args, WordVal([2]byte{rawinput[i], rawinput[i+1]}))
 | 
				
			||||||
 | 
									i+=2
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								i--
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case 2:
 | 
				
			||||||
 | 
								args = append(args, WordVal([2]byte{rawinput[i+1], rawinput[i+2]}))
 | 
				
			||||||
 | 
								i+=2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case 1:
 | 
				
			||||||
 | 
								i++
 | 
				
			||||||
 | 
								args = append(args, ByteVal(rawinput[i]))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							token.Inline = args
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Find and mark labels for a few instructions
 | 
						// Find and mark labels for a few instructions
 | 
				
			||||||
	for _, t := range p.script.Tokens {
 | 
						for _, t := range script.Tokens {
 | 
				
			||||||
		if t.Instruction == nil {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		switch t.Raw {
 | 
							switch t.Raw {
 | 
				
			||||||
		case 0x84, 0x85, 0xBF, 0xC0: // jmp/call
 | 
							case 0x84, 0x85, 0xBF, 0xC0: // jmp/call
 | 
				
			||||||
			if len(t.Inline) == 0 {
 | 
								if len(t.Inline) == 0 {
 | 
				
			||||||
				//return nil, fmt.Errorf("jump/call missing address ($%04X)", t.Offset)
 | 
									return nil, fmt.Errorf("jump/call missing address")
 | 
				
			||||||
				p.script.Warnings = append(p.script.Warnings,
 | 
					 | 
				
			||||||
					fmt.Sprintf("jump/call missing addresses ($%04X)", t.Offset))
 | 
					 | 
				
			||||||
				continue
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			addr := t.Inline[0].Int()
 | 
								addr := t.Inline[0].Int()
 | 
				
			||||||
			found := false
 | 
								found := false
 | 
				
			||||||
			for _, tok := range p.script.Tokens {
 | 
								for _, tok := range script.Tokens {
 | 
				
			||||||
				if tok.Offset == addr {
 | 
									if tok.Offset == addr {
 | 
				
			||||||
					tok.IsTarget = true
 | 
										tok.IsTarget = true
 | 
				
			||||||
					found = true
 | 
										found = true
 | 
				
			||||||
					p.script.Labels[addr] = AutoLabel(addr) //fmt.Sprintf("L%04X", addr)
 | 
										script.Labels[addr] = AutoLabel(addr) //fmt.Sprintf("L%04X", addr)
 | 
				
			||||||
					break
 | 
										break
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if !found {
 | 
								if !found {
 | 
				
			||||||
				p.script.Warnings = append(p.script.Warnings, fmt.Sprintf("Warning: no target found for jump/call at offset $%04X; value $%04X", t.Offset, addr))
 | 
									script.Warnings = append(script.Warnings, fmt.Sprintf("Warning: no target found for jump/call at offset $%04X; value $%04X", t.Offset, addr))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		case 0xC1, 0xEE: // switches
 | 
							case 0xC1, 0xEE: // switches
 | 
				
			||||||
			if len(t.Inline) < 2 {
 | 
								if len(t.Inline) < 2 {
 | 
				
			||||||
				//return nil, fmt.Errorf("jump/call switch missing addresses")
 | 
									return nil, fmt.Errorf("jump/call switch missing addresses")
 | 
				
			||||||
				p.script.Warnings = append(p.script.Warnings,
 | 
					 | 
				
			||||||
					fmt.Sprintf("jump/call switch missing addresses ($%04X)", t.Offset))
 | 
					 | 
				
			||||||
				continue
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			for _, v := range t.Inline[1:] {
 | 
								for _, v := range t.Inline[1:] {
 | 
				
			||||||
				addr := v.Int()
 | 
									addr := v.Int()
 | 
				
			||||||
				found := false
 | 
									found := false
 | 
				
			||||||
				for _, tok := range p.script.Tokens {
 | 
									for _, tok := range script.Tokens {
 | 
				
			||||||
					if tok.Offset == addr {
 | 
										if tok.Offset == addr {
 | 
				
			||||||
						tok.IsTarget = true
 | 
											tok.IsTarget = true
 | 
				
			||||||
						found = true
 | 
											found = true
 | 
				
			||||||
						p.script.Labels[addr] = AutoLabel(addr) //fmt.Sprintf("L%04X", addr)
 | 
											script.Labels[addr] = AutoLabel(addr) //fmt.Sprintf("L%04X", addr)
 | 
				
			||||||
						break
 | 
											break
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if !found {
 | 
									if !found {
 | 
				
			||||||
					p.script.Warnings = append(p.script.Warnings, fmt.Sprintf("Warning: no target found for jump/call switch at offset $%04X; value: $%04X", t.Offset, addr))
 | 
										script.Warnings = append(script.Warnings, fmt.Sprintf("Warning: no target found for jump/call switch at offset $%04X; value: $%04X", t.Offset, addr))
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			// if word arg, see if it's something in this script
 | 
								// if word arg, see if it's something in this script
 | 
				
			||||||
			if t.Instruction == nil {
 | 
								if t.Instruction == nil {
 | 
				
			||||||
				//if t.IsData {
 | 
					 | 
				
			||||||
				//	fmt.Print(",")
 | 
					 | 
				
			||||||
				//}
 | 
					 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -304,83 +189,11 @@ func Parse(rawinput []byte, startAddr int, cdl *CodeDataLog) (*Script, error) {
 | 
				
			||||||
				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
 | 
				
			||||||
					p.script.Labels[addr] = AutoLabelVar(addr) //fmt.Sprintf("Var_%04X", addr)
 | 
										script.Labels[addr] = AutoLabelVar(addr) //fmt.Sprintf("Var_%04X", addr)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return p.script, nil
 | 
						return script, nil
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (p *Parser) parseToken(token *Token, raw byte) error {
 | 
					 | 
				
			||||||
	op, ok := InstrMap[raw]
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		return errors.Join(ErrInvalidInstruction,
 | 
					 | 
				
			||||||
			fmt.Errorf("OP 0x%02X not in instruction map", raw))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	token.Instruction = op
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	args := []InlineVal{}
 | 
					 | 
				
			||||||
	switch op.OpCount {
 | 
					 | 
				
			||||||
	case -1: // null terminated
 | 
					 | 
				
			||||||
		for ; p.current < len(p.rawinput); p.current++ {
 | 
					 | 
				
			||||||
			p.script.CDL.setCode(p.current)
 | 
					 | 
				
			||||||
			val := ByteVal(p.rawinput[p.current])
 | 
					 | 
				
			||||||
			args = append(args, val)
 | 
					 | 
				
			||||||
			if p.rawinput[p.current] == 0x00 {
 | 
					 | 
				
			||||||
				break
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	case -2: // count then count words
 | 
					 | 
				
			||||||
		// FIXME: wtf makes this different from -3??
 | 
					 | 
				
			||||||
		p.current++
 | 
					 | 
				
			||||||
		l :=  int(p.rawinput[p.current])
 | 
					 | 
				
			||||||
		p.script.CDL.setCode(p.current)
 | 
					 | 
				
			||||||
		args = append(args, ByteVal(l))
 | 
					 | 
				
			||||||
		p.current++
 | 
					 | 
				
			||||||
		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]}))
 | 
					 | 
				
			||||||
			p.current+=2
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		p.current--
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	case -3: // count then count words.  "default" is no call (skip Code_Pointer to after args)
 | 
					 | 
				
			||||||
		p.current++
 | 
					 | 
				
			||||||
		l :=  int(p.rawinput[p.current])
 | 
					 | 
				
			||||||
		args = append(args, ByteVal(l))
 | 
					 | 
				
			||||||
		p.script.CDL.setCode(p.current)
 | 
					 | 
				
			||||||
		p.current++
 | 
					 | 
				
			||||||
		for c := 0; c < l; c++ {
 | 
					 | 
				
			||||||
			args = append(args, WordVal([2]byte{p.rawinput[p.current], p.rawinput[p.current+1]}))
 | 
					 | 
				
			||||||
			p.current+=2
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		p.current--
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	case 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+2)
 | 
					 | 
				
			||||||
		p.current+=2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		//fmt.Printf("var at $%04X\n", val.Int())
 | 
					 | 
				
			||||||
		//if val.Int() > p.startAddr && val.Int() < p.startAddr+len(p.rawinput) && p.earliestVar > val.Int() {
 | 
					 | 
				
			||||||
		//	fmt.Printf("new earliest: $%04X\n", val.Int())
 | 
					 | 
				
			||||||
		//	p.earliestVar = val.Int()
 | 
					 | 
				
			||||||
		//}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	case 1:
 | 
					 | 
				
			||||||
		p.current++
 | 
					 | 
				
			||||||
		p.script.CDL.setCode(p.current)
 | 
					 | 
				
			||||||
		args = append(args, ByteVal(p.rawinput[p.current]))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	token.Inline = args
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
package script
 | 
					package script
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Script struct {
 | 
					type Script struct {
 | 
				
			||||||
| 
						 | 
					@ -11,7 +12,15 @@ type Script struct {
 | 
				
			||||||
	StackAddress int
 | 
						StackAddress int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Labels map[int]*Label
 | 
						Labels map[int]*Label
 | 
				
			||||||
	CDL *CodeDataLog
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 {
 | 
					func (s *Script) Stats() Stats {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,15 +9,6 @@ import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Stats map[byte]*InstrStat
 | 
					type Stats map[byte]*InstrStat
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type InstrStat struct {
 | 
					 | 
				
			||||||
	Instr *Instruction
 | 
					 | 
				
			||||||
	Count int
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (is InstrStat) String() string {
 | 
					 | 
				
			||||||
	return fmt.Sprintf("0x%02X %6d %s", is.Instr.Opcode, is.Count, is.Instr.String())
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (this Stats) Add(that Stats) {
 | 
					func (this Stats) Add(that Stats) {
 | 
				
			||||||
	for _, st := range that {
 | 
						for _, st := range that {
 | 
				
			||||||
		op := st.Instr.Opcode
 | 
							op := st.Instr.Opcode
 | 
				
			||||||
| 
						 | 
					@ -49,23 +40,7 @@ func (s Stats) WriteTo(w io.Writer) (int64, error) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	n, err := fmt.Fprintln(w, "\nUnused OpCodes:")
 | 
						n, err := fmt.Fprintln(w, "\nUnknown uses:", unknownUses)
 | 
				
			||||||
	count += int64(n)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return count, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i := byte(0x80); i <= 0xFF && i >= 0x80; i++ {
 | 
					 | 
				
			||||||
		if _, ok := s[i]; !ok {
 | 
					 | 
				
			||||||
			n, err = fmt.Fprintf(w, "0x%02X %s\n", i, InstrMap[i].Name)
 | 
					 | 
				
			||||||
			count += int64(n)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				return count, err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	n, err = fmt.Fprintln(w, "\nUnknown uses:", unknownUses)
 | 
					 | 
				
			||||||
	count += int64(n)
 | 
						count += int64(n)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return count, err
 | 
							return count, err
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,12 +6,11 @@ import (
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Token struct {
 | 
					type Token struct {
 | 
				
			||||||
	Offset   int // in CPU space
 | 
						Offset   int
 | 
				
			||||||
	Raw      byte
 | 
						Raw      byte
 | 
				
			||||||
	Inline   []InlineVal
 | 
						Inline   []InlineVal
 | 
				
			||||||
	IsTarget bool   // target of a call/jump?
 | 
						IsTarget bool   // target of a call/jump?
 | 
				
			||||||
	IsVariable bool // target of something else
 | 
						IsVariable bool // target of something else
 | 
				
			||||||
	IsData     bool // from CDL
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Instruction *Instruction
 | 
						Instruction *Instruction
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -19,24 +18,28 @@ type Token struct {
 | 
				
			||||||
func (t Token) String(labels map[int]*Label) string {
 | 
					func (t Token) String(labels map[int]*Label) string {
 | 
				
			||||||
	suffix := ""
 | 
						suffix := ""
 | 
				
			||||||
	switch t.Raw {
 | 
						switch t.Raw {
 | 
				
			||||||
	case 0x86: // Newline after return
 | 
						case 0x86:
 | 
				
			||||||
		suffix = "\n"
 | 
							suffix = "\n"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	prefix := ""
 | 
						prefix := ""
 | 
				
			||||||
	if lbl, ok := labels[t.Offset]; ok {
 | 
						if t.IsTarget || t.IsVariable {
 | 
				
			||||||
		comment := ""
 | 
							if lbl, ok := labels[t.Offset]; ok {
 | 
				
			||||||
		if lbl.Comment != "" {
 | 
								comment := ""
 | 
				
			||||||
			comment = "; "+lbl.Comment+"\n"
 | 
								if lbl.Comment != "" {
 | 
				
			||||||
 | 
									comment = "; "+lbl.Comment+"\n"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								prefix = "\n"+comment+lbl.Name+":\n"
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								prefix = fmt.Sprintf("\nL%04X:\n", t.Offset)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		name := ""
 | 
						} else {
 | 
				
			||||||
		if lbl.Name != "" {
 | 
							if lbl, ok := labels[t.Offset]; ok && lbl.Comment != "" {
 | 
				
			||||||
			name = lbl.Name+":\n"
 | 
								suffix = " ; "+lbl.Comment+suffix
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		prefix = "\n"+comment+name
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if t.Instruction == nil {
 | 
						if t.Raw < 0x80 {
 | 
				
			||||||
		return fmt.Sprintf("%s[%04X] %02X %-5s : %d%s",
 | 
							return fmt.Sprintf("%s[%04X] %02X %-5s : %d%s",
 | 
				
			||||||
			prefix,
 | 
								prefix,
 | 
				
			||||||
			t.Offset,
 | 
								t.Offset,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue