Compare commits

...

3 Commits

Author SHA1 Message Date
Zorchenhimer bfc7beb382
[script] Improve CDL logic
- CDL data can now be output to a file.  If --cdl-output isn't given,
  but --cdl is given, the input file is overwritten with the new data.
  If no CDL file is given no data is written.
- Count arguments as "Code"
- The SmartParse function no longer creates a new CDL.  It will use and
  update what it was given.
- The just-stats command does not write CDL data even though it
  generates it.

Variable length arguments are not properly handled yet.  Currently, just
the OP code and first argument (length) are set at Code.  The rest are
ignored.
2025-09-14 18:06:46 -04:00
Zorchenhimer 11cfad1e1a
[script] Add CDL; Add "smart" parsing
- Added a CDL implementation.  It is still very incomplete and is not
  really used yet.
- Added "smart" parsing.  This will disassemble the code and follow any
  jumps and calls.  Anything not found by this is currently excluded
  from output.
2025-09-14 16:52:06 -04:00
Zorchenhimer d8cc126c04
[script] Move InstrStat to script/stats.go 2025-09-13 14:03:45 -04:00
8 changed files with 702 additions and 151 deletions

View File

@ -6,6 +6,7 @@ import (
"path/filepath"
"strings"
"io/fs"
"slices"
"github.com/alexflint/go-arg"
@ -19,6 +20,7 @@ type Arguments struct {
type Walker struct {
Found []string
CDLs []string
}
func (w *Walker) WalkFunc(path string, info fs.DirEntry, err error) error {
@ -30,6 +32,10 @@ func (w *Walker) WalkFunc(path string, info fs.DirEntry, err error) error {
w.Found = append(w.Found, path)
}
if strings.HasSuffix(path, "_scriptData.cdl.json") {
w.CDLs = append(w.CDLs, path)
}
return nil
}
@ -46,17 +52,31 @@ func run(args *Arguments) error {
for _, file := range w.Found {
fmt.Println(file)
scr, err := script.ParseFile(file, 0x0000)
if err != nil {
if scr != nil {
for _, token := range scr.Tokens {
fmt.Println(token.String(scr.Labels))
}
var cdl *script.CodeDataLog
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
}
return err
}
stats.Add(scr.Stats())
scr, err := script.SmartParseFile(file, 0x6000, cdl)
if err != nil {
//if scr != nil {
// for _, token := range scr.Tokens {
// fmt.Println(token.String(scr.Labels))
// }
//}
fmt.Println(err)
//return err
}
if scr != nil {
stats.Add(scr.Stats())
}
}
outfile, err := os.Create(args.Output)

View File

@ -7,6 +7,7 @@ import (
"strconv"
"bufio"
"slices"
"errors"
"github.com/alexflint/go-arg"
@ -19,6 +20,9 @@ type Arguments struct {
StartAddr string `arg:"--start" default:"0x6000" help:"base address for the start of the script"`
StatsFile string `arg:"--stats" help:"file to write some statistics to"`
LabelFile string `arg:"--labels" help:"file containing address/label pairs"`
CDL string `arg:"--cdl" help:"CodeDataLog json file"`
CDLOutput string `arg:"--cdl-output"`
Smart bool `arg:"--smart"`
start int
}
@ -39,19 +43,38 @@ func run(args *Arguments) error {
args.start = int(val)
scr, err := script.ParseFile(args.Input, args.start)
var cdl *script.CodeDataLog
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 {
return err
if errors.Is(err, script.ErrEarlyEOF) || errors.Is(err, script.ErrNavigation) {
fmt.Println(err)
} else {
return fmt.Errorf("Script parse error: %w", err)
}
}
if args.LabelFile != "" {
labels, err := parseLabelFile(args.LabelFile)
if err != nil {
return err
return fmt.Errorf("Labels parse error: %w", err)
}
for _, label := range labels {
//fmt.Printf("%#v\n", label)
scr.Labels[label.Address] = label
}
}
@ -66,7 +89,7 @@ func run(args *Arguments) error {
}
for _, w := range scr.Warnings {
fmt.Fprintln(os.Stderr, w)
//fmt.Fprintln(os.Stderr, w)
if args.Output != "" {
fmt.Fprintln(outfile, "; "+w)
}
@ -75,6 +98,12 @@ func run(args *Arguments) error {
fmt.Fprintf(outfile, "; Start address: $%04X\n", scr.StartAddress)
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 {
fmt.Fprintln(outfile, token.String(scr.Labels))
}
@ -86,13 +115,28 @@ func run(args *Arguments) error {
}
defer statfile.Close()
//err = scr.WriteStats(statfile)
_, err = scr.Stats().WriteTo(statfile)
if err != nil {
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
}

245
script/cdl.go Normal file
View File

@ -0,0 +1,245 @@
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
}

42
script/labels.go Normal file
View File

@ -0,0 +1,42 @@
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,
}
}

View File

@ -3,185 +3,300 @@ package script
import (
"fmt"
"os"
"errors"
)
type Label struct {
Address int
Name string
Comment string
FarLabel bool
var (
ErrEarlyEOF = errors.New("Unexpected EOF when reading OP arguments")
ErrInvalidInstruction = errors.New("Invalid instruction")
ErrNavigation = errors.New("SmartParse navigation error")
)
type Parser struct {
rawinput []byte
current int
startAddr int
script *Script
cdl *CodeDataLog
}
func AutoLabel(address int) *Label {
return &Label{
Address: address,
Name: fmt.Sprintf("L%04X", address),
}
}
func AutoLabelVar(address int) *Label {
return &Label{
Address: address,
Name: fmt.Sprintf("Var_%04X", address),
}
}
func AutoLabelFar(address int) *Label {
return &Label{
Address: address,
Name: fmt.Sprintf("F%04X", address),
FarLabel: true,
}
}
func NewLabel(address int, name string) *Label {
return &Label{
Address: address,
Name: name,
}
}
func ParseFile(filename string, startAddr int) (*Script, error) {
func ParseFile(filename string, startAddr int, cdl *CodeDataLog) (*Script, error) {
rawfile, err := os.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("unable to read file: %w", err)
}
return Parse(rawfile, startAddr)
return Parse(rawfile, startAddr, cdl)
}
func Parse(rawinput []byte, startAddr int) (*Script, error) {
func SmartParseFile(filename string, startAddr int, cdl *CodeDataLog) (*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 {
return nil, fmt.Errorf("not enough bytes for script")
}
script := &Script{
Tokens: []*Token{},
Warnings: []string{},
StackAddress: (int(rawinput[1])<<8) | int(rawinput[0]),
StartAddress: startAddr,
Labels: make(map[int]*Label), // map[location]name
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,
}
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)
for i := 2; i < len(rawinput); i++ {
raw := rawinput[i]
if p.script.CDL == nil {
p.script.CDL = &CodeDataLog{}
}
//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{
Offset: startAddr+i,
Offset: startAddr+p.current,
Raw: raw,
Inline: []InlineVal{},
}
script.Tokens = append(script.Tokens, token)
p.script.Tokens = append(p.script.Tokens, token)
tokenMap[token.Offset] = token
if raw < 0x80 {
if raw < 0x80 || p.script.CDL.IsData(p.current+startAddr) { // || p.current >= earliestVar {
if p.script.CDL.IsData(p.current+startAddr) {
token.IsData = true
//fmt.Print(".")
//fmt.Printf("%#v\n", token)
}
continue
}
op, ok := InstrMap[raw]
if !ok {
return nil, fmt.Errorf("OP 0x%02X not in instruction map", raw)
err := p.parseToken(token, raw)
if err != nil {
return p.script, err
}
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
for _, t := range script.Tokens {
for _, t := range p.script.Tokens {
if t.Instruction == nil {
continue
}
switch t.Raw {
case 0x84, 0x85, 0xBF, 0xC0: // jmp/call
if len(t.Inline) == 0 {
return nil, fmt.Errorf("jump/call missing address")
//return nil, fmt.Errorf("jump/call missing address ($%04X)", t.Offset)
p.script.Warnings = append(p.script.Warnings,
fmt.Sprintf("jump/call missing addresses ($%04X)", t.Offset))
continue
}
addr := t.Inline[0].Int()
found := false
for _, tok := range script.Tokens {
for _, tok := range p.script.Tokens {
if tok.Offset == addr {
tok.IsTarget = true
found = true
script.Labels[addr] = AutoLabel(addr) //fmt.Sprintf("L%04X", addr)
p.script.Labels[addr] = AutoLabel(addr) //fmt.Sprintf("L%04X", addr)
break
}
}
if !found {
script.Warnings = append(script.Warnings, fmt.Sprintf("Warning: no target found for jump/call at offset $%04X; value $%04X", t.Offset, addr))
p.script.Warnings = append(p.script.Warnings, fmt.Sprintf("Warning: no target found for jump/call at offset $%04X; value $%04X", t.Offset, addr))
}
case 0xC1, 0xEE: // switches
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:] {
addr := v.Int()
found := false
for _, tok := range script.Tokens {
for _, tok := range p.script.Tokens {
if tok.Offset == addr {
tok.IsTarget = true
found = true
script.Labels[addr] = AutoLabel(addr) //fmt.Sprintf("L%04X", addr)
p.script.Labels[addr] = AutoLabel(addr) //fmt.Sprintf("L%04X", addr)
break
}
}
if !found {
script.Warnings = append(script.Warnings, fmt.Sprintf("Warning: no target found for jump/call switch at offset $%04X; value: $%04X", t.Offset, addr))
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))
}
}
default:
// if word arg, see if it's something in this script
if t.Instruction == nil {
//if t.IsData {
// fmt.Print(",")
//}
continue
}
@ -189,11 +304,83 @@ func Parse(rawinput []byte, startAddr int) (*Script, error) {
addr := t.Inline[0].Int()
if tok, ok := tokenMap[addr]; ok {
tok.IsVariable = true
script.Labels[addr] = AutoLabelVar(addr) //fmt.Sprintf("Var_%04X", addr)
p.script.Labels[addr] = AutoLabelVar(addr) //fmt.Sprintf("Var_%04X", addr)
}
}
}
}
return script, nil
return p.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
}

View File

@ -1,7 +1,6 @@
package script
import (
"fmt"
)
type Script struct {
@ -12,15 +11,7 @@ type Script struct {
StackAddress int
Labels map[int]*Label
}
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())
CDL *CodeDataLog
}
func (s *Script) Stats() Stats {

View File

@ -9,6 +9,15 @@ import (
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) {
for _, st := range that {
op := st.Instr.Opcode
@ -40,7 +49,23 @@ func (s Stats) WriteTo(w io.Writer) (int64, error) {
}
}
n, err := fmt.Fprintln(w, "\nUnknown uses:", unknownUses)
n, err := fmt.Fprintln(w, "\nUnused OpCodes:")
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)
if err != nil {
return count, err

View File

@ -6,11 +6,12 @@ import (
)
type Token struct {
Offset int
Offset int // in CPU space
Raw byte
Inline []InlineVal
IsTarget bool // target of a call/jump?
IsVariable bool // target of something else
IsData bool // from CDL
Instruction *Instruction
}
@ -18,28 +19,24 @@ type Token struct {
func (t Token) String(labels map[int]*Label) string {
suffix := ""
switch t.Raw {
case 0x86:
case 0x86: // Newline after return
suffix = "\n"
}
prefix := ""
if t.IsTarget || t.IsVariable {
if lbl, ok := labels[t.Offset]; ok {
comment := ""
if lbl.Comment != "" {
comment = "; "+lbl.Comment+"\n"
}
prefix = "\n"+comment+lbl.Name+":\n"
} else {
prefix = fmt.Sprintf("\nL%04X:\n", t.Offset)
if lbl, ok := labels[t.Offset]; ok {
comment := ""
if lbl.Comment != "" {
comment = "; "+lbl.Comment+"\n"
}
} else {
if lbl, ok := labels[t.Offset]; ok && lbl.Comment != "" {
suffix = " ; "+lbl.Comment+suffix
name := ""
if lbl.Name != "" {
name = lbl.Name+":\n"
}
prefix = "\n"+comment+name
}
if t.Raw < 0x80 {
if t.Instruction == nil {
return fmt.Sprintf("%s[%04X] %02X %-5s : %d%s",
prefix,
t.Offset,