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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
|
package main
import (
"fmt"
"io"
"os"
"strings"
)
type (
Labels map[string]uint
Definitions map[string]any
)
type ProgramState struct {
Labels Labels
Defs Definitions
IsMacro bool
}
func parseFile(input_file_name string, input []byte, offset uint) ([]byte, error) {
state := ProgramState{
Labels: make(map[string]uint),
Defs: make(map[string]any),
IsMacro: false,
}
_, err := firstPass(input_file_name, input, offset, &state)
if err != nil {
return nil, err
}
return secondPass(input_file_name, input, offset, state)
}
func firstPass(
input_file_name string,
input []byte,
offset uint,
state *ProgramState,
) ([]byte, error) {
lines := strings.Split(string(input), "\n")
line_nb := 0
result := []byte{}
for line_nb < len(lines) {
line := lines[line_nb]
line_parts := strings.Split(line, ";")
line = line_parts[0]
is_label_defined := strings.Contains(line, ":")
if is_label_defined {
parts := strings.Split(line, ":")
for _, label := range parts[:len(parts)-1] {
label = strings.TrimSpace(strings.ToUpper(label))
if _, ok := state.Labels[label]; ok {
return nil, fmt.Errorf(
"File %s, line %d:\nLabel %s is already defined",
input_file_name,
line_nb+1,
label,
)
}
if label[0] == '$' && !state.IsMacro {
return nil, fmt.Errorf("Labels starting with $ can only be used inside macros")
}
if label[0] != '$' && state.IsMacro {
return nil, fmt.Errorf("Labels inside a macro must start with $")
}
state.Labels[label] = uint(len(result)) + offset
}
line = parts[len(parts)-1]
}
line = strings.TrimSpace(line)
// nil sets all the labels and defintion to 0 & thus, to not crash JR, the currentAddress should also be 0
if strings.HasPrefix(line, ".") {
err := MacroParse(line, lines, &result, state, &line_nb, true, offset)
if err != nil {
return nil, fmt.Errorf(
"File %s, line %d (1st pass|macro):\n%w",
input_file_name,
line_nb+1,
err,
)
}
} else {
next_instruction, err := Instructions.Parse(nil, &state.Defs, state.IsMacro, 0, line)
if err != nil {
return nil, fmt.Errorf(
"File %s, line %d (1st pass):\n%w",
input_file_name,
line_nb+1,
err,
)
}
result = append(result, next_instruction...)
}
line_nb += 1
}
return result, nil
}
func secondPass(
input_file_name string,
input []byte,
offset uint,
state ProgramState,
) ([]byte, error) {
lines := strings.Split(string(input), "\n")
line_nb := 0
result := []byte{}
for line_nb < len(lines) {
line := lines[line_nb]
line_parts := strings.Split(line, ";")
line = line_parts[0]
is_label_defined := strings.Contains(line, ":")
if is_label_defined {
parts := strings.Split(line, ":")
line = parts[len(parts)-1]
}
line = strings.TrimSpace(line)
if strings.HasPrefix(line, ".") {
err := MacroParse(line, lines, &result, &state, &line_nb, false, offset)
if err != nil {
return nil, fmt.Errorf(
"File %s, line %d (2nd pass|macro):\n%w",
input_file_name,
line_nb+1,
err,
)
}
} else {
next_instruction, err := Instructions.Parse(&state.Labels, &state.Defs, state.IsMacro, uint16(uint(len(result))+offset), line)
if err != nil {
return nil, fmt.Errorf(
"File %s, line %d (2nd pass): %w",
input_file_name,
line_nb+1,
err,
)
}
result = append(result, next_instruction...)
}
line_nb += 1
}
return result, nil
}
func main() {
if len(os.Args) != 3 {
fmt.Fprintf(os.Stderr, "Usage: gbasm [input_file] [output_file]\n")
os.Exit(1)
}
input_file_name := os.Args[1]
output_file_name := os.Args[2]
input_file, err := os.Open(input_file_name)
if err != nil {
fmt.Fprintf(os.Stderr, "Error while opening input file: %s\n", err.Error())
os.Exit(1)
}
input, err := io.ReadAll(input_file)
if err != nil {
fmt.Fprintf(os.Stderr, "Error while reading input file: %s\n", err.Error())
os.Exit(1)
}
result, err := parseFile(input_file_name, input, 0)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error())
os.Exit(1)
}
output_file, err := os.Create(output_file_name)
if err != nil {
fmt.Fprintf(os.Stderr, "Error while opening output file: %s\n", err.Error())
os.Exit(1)
}
_, err = output_file.Write(result)
if err != nil {
fmt.Fprintf(os.Stderr, "Error while writing to output file: %s\n", err.Error())
os.Exit(1)
}
}
|