2024-04-27 12:19:05 -07:00
|
|
|
package script
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
)
|
|
|
|
|
|
|
|
func ParseFile(filename string, startAddr int) (*Script, error) {
|
|
|
|
rawfile, err := os.ReadFile(filename)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("unable to read file: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return Parse(rawfile, startAddr)
|
|
|
|
}
|
|
|
|
|
|
|
|
func Parse(rawinput []byte, startAddr int) (*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,
|
2024-09-29 12:03:21 -07:00
|
|
|
Labels: make(map[int]string), // map[location]name
|
2024-04-27 12:19:05 -07:00
|
|
|
}
|
2024-09-29 12:03:21 -07:00
|
|
|
tokenMap := make(map[int]*Token)
|
2024-04-27 12:19:05 -07:00
|
|
|
|
|
|
|
for i := 2; i < len(rawinput); i++ {
|
|
|
|
raw := rawinput[i]
|
|
|
|
|
|
|
|
token := &Token{
|
|
|
|
Offset: startAddr+i,
|
|
|
|
Raw: raw,
|
|
|
|
Inline: []InlineVal{},
|
|
|
|
}
|
|
|
|
script.Tokens = append(script.Tokens, token)
|
2024-09-29 12:03:21 -07:00
|
|
|
tokenMap[token.Offset] = token
|
2024-04-27 12:19:05 -07:00
|
|
|
|
|
|
|
if raw < 0x80 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
op, ok := InstrMap[raw]
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("OP %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++ {
|
|
|
|
args = append(args, WordVal([2]byte{rawinput[i], rawinput[i+1]}))
|
|
|
|
i+=2
|
|
|
|
}
|
|
|
|
|
|
|
|
case -3: // count then count+1 words (extra is default case)
|
|
|
|
i++
|
|
|
|
l := int(rawinput[i])
|
|
|
|
args = append(args, ByteVal(l))
|
|
|
|
i++
|
|
|
|
for c := 0; c < l+1; c++ {
|
|
|
|
args = append(args, WordVal([2]byte{rawinput[i], rawinput[i+1]}))
|
|
|
|
i+=2
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, t := range script.Tokens {
|
|
|
|
switch t.Raw {
|
|
|
|
case 0x84, 0x85, 0xBF, 0xC0: // jmp/call
|
|
|
|
if len(t.Inline) == 0 {
|
|
|
|
return nil, fmt.Errorf("jump/call missing address")
|
|
|
|
}
|
|
|
|
|
|
|
|
addr := t.Inline[0].Int()
|
|
|
|
found := false
|
|
|
|
for _, tok := range script.Tokens {
|
|
|
|
if tok.Offset == addr {
|
|
|
|
tok.IsTarget = true
|
|
|
|
found = true
|
2024-09-29 12:03:21 -07:00
|
|
|
script.Labels[addr] = fmt.Sprintf("L%04X", addr)
|
2024-04-27 12:19:05 -07:00
|
|
|
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))
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0xC1, 0xEE: // switches
|
|
|
|
if len(t.Inline) < 2 {
|
|
|
|
return nil, fmt.Errorf("jump/call switch missing addresses")
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, v := range t.Inline[1:] {
|
|
|
|
addr := v.Int()
|
|
|
|
found := false
|
|
|
|
for _, tok := range script.Tokens {
|
|
|
|
if tok.Offset == addr {
|
|
|
|
tok.IsTarget = true
|
|
|
|
found = true
|
2024-09-29 12:03:21 -07:00
|
|
|
script.Labels[addr] = fmt.Sprintf("L%04X", addr)
|
2024-04-27 12:19:05 -07:00
|
|
|
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))
|
|
|
|
}
|
|
|
|
}
|
2024-09-29 12:03:21 -07:00
|
|
|
|
|
|
|
default:
|
|
|
|
// if word arg, see if it's something in this script
|
|
|
|
if t.Instruction == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if t.Instruction.OpCount == 2 {
|
|
|
|
addr := t.Inline[0].Int()
|
|
|
|
if tok, ok := tokenMap[addr]; ok {
|
|
|
|
tok.IsVariable = true
|
|
|
|
script.Labels[addr] = fmt.Sprintf("Var_%04X", addr)
|
|
|
|
}
|
|
|
|
}
|
2024-04-27 12:19:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return script, nil
|
|
|
|
}
|