1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
package main
import (
"fmt"
"io"
"os"
"strconv"
"strings"
)
var MacroInstructions = NewInstructionSetMacros()
func NewInstructionSetMacros() InstructionSet {
result := make(InstructionSet)
result[".PADTO"] = []InstructionParams{
{
Types: []ParamType{Raw16},
Assembler: func(currentAddress uint16, args []uint16) ([]byte, error) {
fmt.Printf(
".PADTO 0x%04x, currentAddress: 0x%04x, inserting: 0x%04x\n",
args[0],
currentAddress,
args[0]-currentAddress,
)
return make([]byte, args[0]-currentAddress), nil
},
},
}
result[".DB"] = []InstructionParams{
{
Types: []ParamType{Raw8},
Assembler: func(currentAddress uint16, args []uint16) ([]byte, error) {
result := make([]byte, len(args))
for i := range args {
result[i] = uint8(args[i])
}
return result, nil
},
Wildcard: true,
},
}
return result
}
type (
Indirect8b uint16
Indirect16b uint16
Raw8b uint16
Raw16b uint16
)
func MacroParse(
line string,
result *[]byte,
state *ProgramState,
_ *int, // line_nb
is_first_pass bool,
offset uint,
) error {
words := strings.Split(line, " ")
if len(words) == 0 {
return fmt.Errorf("Macro parsed doesn't accept empty lines")
}
macroName := words[0]
if _, ok := MacroInstructions[macroName]; ok {
new_instruction, err := MacroInstructions.Parse(
&state.Labels,
&state.Defs,
uint16(uint(len(*result))+offset),
line,
)
if err != nil {
return fmt.Errorf("Macro instruction parsing failed %w", err)
}
*result = append(*result, new_instruction...)
return nil
} else if macroName == ".INCLUDE" {
filePath := strings.Trim(strings.TrimSpace(strings.TrimPrefix(line, ".INCLUDE")), "\"'")
input_file, err := os.Open(filePath)
if err != nil {
return fmt.Errorf("Error while opening file %s", filePath)
}
input, err := io.ReadAll(input_file)
if err != nil {
return fmt.Errorf("Error while reading file %s", filePath)
}
fileStartOffset := uint(len(*result)) + offset
if is_first_pass {
included, err := firstPass(filePath, input, fileStartOffset, state)
if err != nil {
return err
}
*result = append(*result, included...)
} else {
included, err := secondPass(filePath, input, fileStartOffset, *state)
if err != nil {
return err
}
*result = append(*result, included...)
}
} else if macroName == ".DEFINE" {
if len(words) != 3 {
return fmt.Errorf(".DEFINE must have 2 arguments (%v)", words)
}
name := strings.ToUpper(words[1])
_, err := strconv.ParseUint(name, 16, 16)
if err == nil {
return fmt.Errorf("Defined variable \"%s\" is also valid hexadecimal", name)
}
var definedValue any
if v, err := Raw8Indirect(&state.Labels, &state.Defs, words[2]); err == nil {
definedValue = Indirect8b(v)
} else if v, err := Raw16Indirect(&state.Labels, &state.Defs, words[2]); err == nil {
definedValue = Indirect16b(v)
} else if v, err := Raw8(&state.Labels, &state.Defs, words[2]); err == nil {
definedValue = Raw8b(v)
} else if v, err := Raw16(&state.Labels, &state.Defs, words[2]); err == nil {
definedValue = Raw16b(v)
} else {
return fmt.Errorf("\"%s\" could not be parsed as a .DEFINE argument", words[2])
}
state.Defs[name] = definedValue
} else {
return fmt.Errorf("Unknown macro \"%s\"", macroName)
}
return nil
}
|