blob: 5f0a001d5bcc25faed8f15db28eb4b5c6c731e5b [file] [log] [blame]
swissChili97b5d8b2020-08-15 20:00:54 -07001#include "as.h"
2#include "../cpu.h"
3#include "../instructions.h"
4#include "../mnemonics.h"
swissChilica0d2e22020-08-16 15:09:25 -07005#include "map.h"
swissChili97b5d8b2020-08-15 20:00:54 -07006
swissChili97b5d8b2020-08-15 20:00:54 -07007#include <string.h>
8#include <ctype.h>
9#include <stdbool.h>
10
11enum
12{
13 ARG_16, /* Absolute 16 bit argument */
14 ARG_8, /* Absolute 8 bit argument */
15 ARG_8REL, /* Relative 8 bit argument */
16 ARG_REL, /* Relative label */
17 ARG_ABS, /* Absolute label */
18 ARG_IMP, /* Implied argument */
19};
20
21typedef struct
22{
23 uint8_t opcode;
24 uint8_t arg_type;
25 union
26 {
27 char label[32];
28 uint16_t long_arg;
29 uint8_t byte_arg;
30 int8_t rel_arg;
31 };
32} inst_t;
33
34void print_inst(inst_t *arg)
35{
36 char *arg_types =
37 "16 8 8RELREL ABS IMP ";
38
39 printf("\033[33mInst: %.4s $%x ", arg_types + arg->arg_type * 4, arg->opcode);
40
41 switch (arg->arg_type)
42 {
43 case ARG_16:
44 printf("%x", arg->long_arg);
45 break;
46 case ARG_8:
47 printf("%x", arg->byte_arg);
48 break;
49 case ARG_8REL:
50 printf("%d", arg->rel_arg);
51 break;
52 case ARG_REL:
53 case ARG_ABS:
54 printf("%s", arg->label);
55 break;
56 }
57
58 printf("\033[0m\n");
59}
60
61bool is_ident(char c)
62{
swissChilia4f49b52020-08-16 17:35:37 -070063 return c && (isalpha(c) || isdigit(c)
64 || c == '_' || c == '-'
65 || c == '$' || c == '.');
swissChili97b5d8b2020-08-15 20:00:54 -070066}
67
68uint32_t skip_ws(char **code)
69{
70 uint32_t len = 0;
71
swissChilia4f49b52020-08-16 17:35:37 -070072 for (; **code == ' ' || **code == '\t'; (*code)++, len++)
swissChili97b5d8b2020-08-15 20:00:54 -070073 {}
74
75 return len;
76}
77
78uint32_t skip_to_eol(char **code)
79{
80 uint32_t len = 0;
81
82 for (; **code && **code != '\n'; (*code)++, len++)
83 {}
84
85 if (**code)
86 (*code)++;
87
88 return len;
89}
90
91char *parse_label_name(char **code)
92{
93 char *start = *code;
94 for (; is_ident(**code); (*code)++)
95 {}
96
97 if (start == *code)
98 return false;
99
100 **code = 0;
101 return start;
102}
103
104char *parse_label(char **code)
105{
106 char *start = *code;
107
108 for (; is_ident(**code); (*code)++)
109 {}
110
111 skip_ws(code);
112
swissChilia4f49b52020-08-16 17:35:37 -0700113 if (*code != start && **code == ':')
swissChili97b5d8b2020-08-15 20:00:54 -0700114 {
115 **code = 0;
116 (*code)++;
117 return start;
118 }
119
120 *code = start;
121
122 return NULL;
123}
124
125char *parse_inst(char **code)
126{
127 char *start = *code;
128
129 for (; isalpha(**code); (*code)++)
130 {}
131
swissChili97b5d8b2020-08-15 20:00:54 -0700132 if (start == *code)
133 return NULL;
134
swissChilia4f49b52020-08-16 17:35:37 -0700135 // If code is incremented when it points to \0, it will wrap to the next line
136 // returned by strtok(), which causes a bug where instructions followed immediately
137 // by a newline and no arguments causes the next instruction to parse the entire
138 // program as its argument (not good)
139 if (**code)
140 {
141 **code = 0;
142 (*code)++;
143 }
144
swissChili97b5d8b2020-08-15 20:00:54 -0700145 return start;
146}
147
148bool is_eol(char c)
149{
150 return c == ';' ||
151 c == '\n' ||
swissChili97b5d8b2020-08-15 20:00:54 -0700152 c == '\0';
153}
154
155bool skip(char **code, const char *p)
156{
157 for (; *p && *p == **code; p++, (*code)++)
158 {}
159
160 if (!*p)
161 return true;
162 return false;
163}
164
165bool parse_num(char **code, uint64_t *num)
166{
167 char *start = *code;
168 int base = 10;
169 if (**code == '$')
170 {
171 base = 16;
172 (*code)++;
173 }
174
175 skip_ws(code);
176
177 char *endptr = *code;
178 int64_t val = strtol(*code, &endptr, base);
179
180 if (*code == endptr)
181 {
182 *code = start;
183 return false;
184 }
185 *num = val;
186 *code = endptr;
187 return true;
188}
189
190bool parse_num_max(char **code, uint64_t *num, uint64_t max)
191{
192 uint64_t n;
193 if (parse_num(code, &n))
194 {
195 if (n > max)
196 return false;
197
198 *num = n;
199 return true;
200 }
201 else return false;
202}
203
204bool parse_u8(char **code, uint8_t *num)
205{
206 uint64_t n;
207 if (!parse_num_max(code, &n, 0xFF))
208 return false;
209
210 *num = n & 0xFF;
211 return true;
212}
213
214bool parse_u16(char **code, uint16_t *num)
215{
216 uint64_t n;
217 if (!parse_num_max(code, &n, 0xFFFF))
218 return false;
219
220 *num = n & 0xFFFF;
221 return true;
222}
223
224bool ws_end(char **code)
225{
226 skip_ws(code);
227 return is_eol(**code);
228}
229
230bool parse_arg(char *code, int am, inst_t *inst)
231{
232 skip_ws(&code);
233
234 uint16_t num;
235 uint8_t num8;
236 char *lbl;
237
238 switch (am)
239 {
240 case AM_ACC:
241 case AM_IMP:
242 printf("Trying AM_IMP on '%.8s'\n", code);
243 skip_ws(&code);
244 if (is_eol(*code))
245 {
246 inst->arg_type = ARG_IMP;
247 return ws_end(&code);
248 }
249 break;
250
251 case AM_IMM:
252 printf("Trying AM_IMM on '%.8s'\n", code);
253 if (!skip(&code, "#"))
254 return false;
255 skip_ws(&code);
256 case AM_ZP:
257 if (parse_u8(&code, &num8))
258 {
259 inst->arg_type = ARG_8;
260 inst->byte_arg = num8;
261
262 return ws_end(&code);
263 }
264 break;
265
266 case AM_ABS:
267 if (parse_u16(&code, &num))
268 {
269 inst->arg_type = ARG_16;
270 inst->long_arg = num;
271 return true;
272 }
273 else if ((lbl = parse_label_name(&code)))
274 {
275 inst->arg_type = ARG_ABS;
276 strncpy(inst->label, lbl, 32);
277 return true;
278 }
279 break;
280
281 case AM_REL:
282 if (parse_u8(&code, &num8))
283 {
284 inst->arg_type = ARG_8REL;
285 inst->rel_arg = num;
286 return ws_end(&code);
287 }
288 else if ((lbl = parse_label_name(&code)))
289 {
290 inst->arg_type = ARG_REL;
291 strncpy(inst->label, lbl, 32);
292 return ws_end(&code);
293 }
294 break;
295
296 case AM_IND:
297 if (!skip(&code,"("))
298 return false;
299
300 if (!parse_u16(&code, &num))
301 return false;
302
303 if (!skip(&code, ")"))
304 return false;
305
306 inst->arg_type = ARG_16;
307 inst->long_arg = num;
308 return true;
309
310 case AM_AX:
311 case AM_ZPX:
312 case AM_AY:
313 case AM_ZPY:
314 if (am == AM_AX || am == AM_AY)
315 {
316 if (!parse_u16(&code, &num))
317 return false;
318 inst->arg_type = ARG_16;
319 inst->long_arg = num;
320 }
321 else
322 {
323 if (!parse_u8(&code, &num8))
324 return false;
325 inst->arg_type = ARG_8;
326 inst->byte_arg = num8;
327 }
328 if (!skip(&code, ","))
329 return false;
330
331 skip_ws(&code);
332
333 if (tolower(*code) != (am == AM_AY || am == AM_ZPY ? 'y' : 'x'))
334 return false;
335
336 return ws_end(&code);
337
338 case AM_ZIX:
339 if (!skip(&code, "("))
340 break;
341 skip_ws(&code);
342 if (!parse_u8(&code, &num8))
343 break;
344 skip_ws(&code);
345 if (!skip(&code, ","))
346 break;
347 skip_ws(&code);
348 if (tolower(*code) != 'x')
349 return false;
350 skip_ws(&code);
351
352 if (!skip(&code, ")"))
353 break;
354
355 inst->arg_type = ARG_8;
356 inst->byte_arg = num8;
357 return ws_end(&code);
358
359 case AM_ZIY:
360 if (!skip(&code, "("))
361 break;
362 skip_ws(&code);
363 if (!parse_u8(&code, &num8))
364 break;
365 skip_ws(&code);
366 if (!skip(&code, ")"))
367 break;
368 skip_ws(&code);
369 if (!skip(&code, ","))
370 break;
371 skip_ws(&code);
372 if (tolower(*code) != 'x')
373 break;
374
375 inst->arg_type = ARG_8;
376 inst->byte_arg = num8;
377 return ws_end(&code);
378 }
379 return false;
380}
381
382uint32_t assemble(char *code, FILE *out)
383{
swissChilica0d2e22020-08-16 15:09:25 -0700384 uintptr_t num_insts = 0,
385 pc = 0x600;
swissChili97b5d8b2020-08-15 20:00:54 -0700386 uint32_t line_no = 1;
swissChilica0d2e22020-08-16 15:09:25 -0700387 map_t *labels = new_map();
swissChili97b5d8b2020-08-15 20:00:54 -0700388 char *line;
389
390 printf("Assembling File\n");
391 printf("%s\n", code);
392
393 line = strtok(code, "\r\n");
394
395 while (line)
396 {
397 skip_ws(&line);
398
swissChilia4f49b52020-08-16 17:35:37 -0700399 printf("line: \033[36m%.12s\033[0m ", line);
swissChili97b5d8b2020-08-15 20:00:54 -0700400
swissChilia4f49b52020-08-16 17:35:37 -0700401 char *label = parse_label(&line);
402 char *mn = parse_inst(&line);
403 printf(" skipping %d ", skip_ws(&line));
404 //printf("\033[33m%s\033[0m\n", line);
405
406 bool no_argument = false;
407 printf("eol is %c ($%x)\n", *line, *line);
408 if (is_eol(*line))
409 {
410 no_argument = true;
411 printf("... no argument\n");
412 }
swissChili97b5d8b2020-08-15 20:00:54 -0700413 int32_t mnemonic = -1;
414
415 if (label)
416 {
swissChilica0d2e22020-08-16 15:09:25 -0700417 map_set(labels, label, (void *)pc);
swissChilia4f49b52020-08-16 17:35:37 -0700418 printf("Set label %s at $%lx\n", label, pc);
swissChili97b5d8b2020-08-15 20:00:54 -0700419 }
420
421 if (mn)
422 {
423#define MN(a) if (!strcasecmp(mn, #a)) \
swissChilica0d2e22020-08-16 15:09:25 -0700424 { \
swissChili97b5d8b2020-08-15 20:00:54 -0700425 mnemonic = a; \
swissChilica0d2e22020-08-16 15:09:25 -0700426 } \
swissChili97b5d8b2020-08-15 20:00:54 -0700427 else
428
429 MNEMONICS;
430#undef MN
431
432 printf("Got instruction %s %d\n", mn, mnemonic);
433
434 inst_t arg;
435 // printf("Parsing '%s'\n", line);
436#define INST(_mn, am, op, len) \
swissChilia4f49b52020-08-16 17:35:37 -0700437 if ((no_argument && (_mn == AM_IMP || _mn == AM_ACC)) \
438 || (mnemonic == _mn && parse_arg(line, am, &arg))) \
439 { \
440 arg.opcode = op; \
441 pc += len; \
442 print_inst(&arg); \
443 } \
swissChili97b5d8b2020-08-15 20:00:54 -0700444 else
445
446 INSTRUCTIONS
447 {
448 printf("\033[31mCould not be parsed: %s '%s'\033[0m\n", mn, line);
449 }
450#undef INST
451 }
452
swissChili97b5d8b2020-08-15 20:00:54 -0700453 line = strtok(NULL, "\r\n");
454 }
455
456 free_map(labels);
457
458 return num_insts;
459}