s502 assembler
A very simple assembler for the 6502 line of processors written in C
directive.c
Go to the documentation of this file.
1 #include "debugmalloc.h"
2 
3 #include <string.h>
4 #include <stdlib.h>
5 
6 #include "state.h"
7 #include "token_t.h"
8 #include "tokenFunc.h"
9 #include "logging.h"
10 #include "directive.h"
11 #include "util.h"
12 #include "number.h"
13 #include "loadfile.h"
14 #include "istack.h"
15 
29  // START OF INDIVIDUAL TOKEN PROCESSOR FUNCTIONS //
32 
33 
40 typedef enum DIRCommand(*tokenprocessor)(State*, TokensListElement* ptr);
41 
42 
50 
51  int n;
52  char** line = util_split_string(ptr->token->stripped, &n);
53 
54  if (n != 3) {
55  ERROR("Mismatched number of arguments for '%s'\n", line[0]); // define
56  goto ERR;
57  }
58 
59  int as_num = number_get_number(s, line[2]);
60  if (as_num < 0) {
61  if (as_num == NUMBER_LABEL_NODEF)
62  ERROR("Can not use undefined labels with define!\n");
63  FAIL("Define argument is not valid!\n");
64  goto ERR;
65  }
66 
67  if (strlen(line[1]) > MAP_MAX_KEY_LEN - 1) {
68  ERROR("Too long constant name! (max is %d chars)\n", MAP_MAX_KEY_LEN - 1);
69  goto ERR;
70  }
71 
72  char def[MAP_MAX_KEY_LEN];
73  strncpy(def, line[1], MAP_MAX_KEY_LEN);
74 
75  if (map_set(s->defines, def, as_num) < 0) goto ERR;
76  free(line);
77  return DIR_NOP;
78 
79 ERR:
80  FAIL("process_define() failed!\n");
81  token_print(ptr->token);
82  free(line);
83  return DIR_STOP;
84 }
85 
91  int n;
92  char** line = util_split_string(ptr->token->stripped, &n);
93 
94  if (n != 3) {
95  ERROR("Mismatched number of arguments for '%s'\n", line[0]); // ifbeq
96  goto ERR;
97  }
98 
99  int first_as_num = number_get_number(s, line[1]);
100  int second_as_num = number_get_number(s, line[2]);
101 
102  if (first_as_num < 0 || second_as_num < 0) {
103  if (first_as_num == NUMBER_LABEL_NODEF || second_as_num == NUMBER_LABEL_NODEF) {
104  ERROR("Can not forward-ref labels with ifbeq!\n");
105  } else {
106  FAIL("ifbeq argument not defined!\n");
107  }
108  goto ERR;
109  }
110 
111  free(line);
112  return first_as_num > second_as_num ? DIR_IF_TRUE : DIR_IF_FALSE;
113 
114 ERR:
115  token_print(ptr->token);
116  FAIL("process_ifbeq() failed!\n");
117  free(line);
118  return DIR_STOP;
119 }
120 
126  return DIR_ENDIF;
127 }
128 
134  char* str = &(ptr->token->stripped[1]);
135  str = util_find_string_segment(str) + 1;
136  if (*(str - 1) == 0) {
137  ERROR("Print with empty message!\n");
138  token_print(ptr->token);
139  return DIR_STOP;
140  }
141  printf("\e[44mMESSAGE\e[49m\t%s\n", str);
142  return DIR_NOP;
143 }
144 
145 
151  int n;
152  char** line = util_split_string(ptr->token->stripped, &n);
153 
154  if (n != 2) {
155  ERROR("Mismatched number of arguments for '%s'\n", line[0]); // printc
156  token_print(ptr->token);
157  free(line);
158  return DIR_STOP;
159  }
160 
161  int v = map_get(s->defines, line[1]);
162  printf("\e[44mDEFINE\e[49m:\t%s = ", line[1]);
163 
164  if (v < 0)
165  printf("\e[31mNOT DEFINED\e[39m\n");
166  else
167  printf("%d\n", v);
168 
169  free(line);
170  return DIR_NOP;
171 }
172 
181  int n;
182  char** line = util_split_string(ptr->token->stripped, &n);
183  if (n != 2) {
184  ERROR("Mismatched number of arguments for '%s'\n", line[0]); // include
185  free(line);
186  return DIR_STOP;
187  }
188  TokensList* f = load_file(line[1]);
189  if (f == NULL) {
190  FAIL("Could not include file!\n");
191  token_print(ptr->token);
192  free(line);
193  return DIR_STOP;
194  }
195  tokenslist_insert(s->tokens, ptr, f);
196  tokenslist_free(f);
197  free(line);
198  return DIR_NOP;
199 }
200 
206  int n;
207  char** line = util_split_string(ptr->token->stripped, &n);
208 
209  if (n != 2) {
210  ERROR("Mismatched number of arguments for '%s'\n", line[0]); // ifdef / ifndef
211  token_print(ptr->token);
212  free(line);
213  return DIR_STOP;
214  }
215  int num = map_get(s->defines, line[1]);
216  free(line);
217 
218  return num < 0 ? DIR_IF_FALSE : DIR_IF_TRUE;
219 }
220 
228  enum DIRCommand ret = process_ifdef(s, ptr);
229  if (ret == DIR_IF_FALSE)
230  return DIR_IF_TRUE;
231  if (ret == DIR_IF_TRUE)
232  return DIR_IF_FALSE;
233  return DIR_STOP;
234 }
235 
243  int n;
244  char** line = util_split_string(ptr->token->stripped, &n);
245  if (n != 2) {
246  ERROR("Mismatched number of arguments for '%s'\n", line[0]); // org
247  goto ERR;
248  }
249  int num = number_get_number(s, line[1]);
250  if (num < 0) {
251  if (num == NUMBER_LABEL_NODEF) {
252  ERROR("Can not use undefined labels with org!\n");
253  }
254  FAIL("Invalid number with .org!\n");
255  goto ERR;
256  }
257  s->PC = num;
258  free(line);
259  LOG(3, "PC = %d\n", num);
260  return DIR_NOP;
261 
262 ERR:
263  free(line);
264  FAIL("process_org() failed!\n");
265  token_print(ptr->token);
266  return DIR_STOP;
267 }
268 
276  // collect size
277  int size = 0;
278  int n;
279  char** arr = util_split_string(ptr->token->stripped, &n);
280  for (int i = 1; i < n; i++) {
281  if (arr[i][0] == 'w')
282  size += 2;
283  else if (arr[i][0] == '"')
284  size += strlen(arr[i]) - 2;
285  else
286  size += 1;
287  }
288  ptr->token->binSize = size;
289  free(arr);
290  return DIR_NOP;
291 }
292 
299  int n;
300  char** line = util_split_string(ptr->token->stripped, &n);
301 
302  if (2 > n || 3 < n) {
303  ERROR("Mismatched number of arguments for '%s'\n", line[0]); // pad
304  goto ERR;
305  }
306 
307  int target = number_get_number(s, line[1]);
308  if (target < 0) {
309  ERROR("Invalid argument in .pad!\n");
310  goto ERR;
311  }
312  int size = target - s->PC;
313  if (size < 0) {
314  ERROR("Negative padding! PC=%x, target=%x\n", s->PC, target);
315  goto ERR;
316  }
317  ptr->token->binSize = size;
318  free(line);
319  return DIR_NOP;
320 
321 ERR:
322  token_print(ptr->token);
323  free(line);
324  return DIR_STOP;
325 }
326 
328 // END OF TOKEN PROCESSOR FUNCTIONS LIST //
330 
337 struct { tokenprocessor p; char* name; } processors[] = {
338  { process_define, "define" },
339  { process_ifdef, "ifdef" },
340  { process_printc, "printc" },
341  { process_print, "print" },
342  { process_include, "include" },
343  { process_endif, "endif" },
344  { process_ifndef, "ifndef" },
345  { process_ifbeq, "ifbeq" },
346  { process_org, "org" },
347  { process_data, "data" },
348  { process_pad, "pad" },
349 };
350 
354 struct { enum DIRCommand ret; char* name; } skipProcessors[] = {
355  { DIR_ENDIF, "endif" },
356  { DIR_IF_TRUE, "ifdef" },
357  { DIR_IF_TRUE, "ifndef"},
358  { DIR_IF_TRUE, "ifbeq" },
359 };
360 
361 
362 enum DIRCommand do_directive_token(State* s, TokensListElement* ptr, int skip) {
363  char* directive = ptr->token->stripped + 1;
364 
365  if (skip) {
366  for (int i = 0; i < sizeof(skipProcessors) / sizeof(skipProcessors[0]); i++) {
367  if (util_match_string(directive, skipProcessors[i].name, strlen(skipProcessors[i].name)) == 0) {
368  return skipProcessors[i].ret;
369  }
370  }
371  return DIR_NOP;
372  }
373 
374  for (int i = 0; i < sizeof(processors) / sizeof(processors[0]); i++) {
375  if (util_match_string(directive, processors[i].name, strlen(processors[i].name)) == 0) {
376  return processors[i].p(s, ptr);
377  }
378  }
379 
380  ERROR("Unknown directive: %s\n", directive);
381  token_print(ptr->token);
382  return DIR_STOP;
383 }
384 
386 // PASS 3 DIRECTIVE COMPILATION //
388 
396 int compile_data(State* s, Token* t, char** dataptr) {
397  // binsize is already calculated in pass 1 processing
398  char* buff = malloc(t->binSize);
399  int bufferIdx = 0; // p is current index in buffer
400 
401  // split string
402  int n;
403  char** arr = util_split_string(t->stripped, &n);
404 
405  for (int i = 1; i < n; i++) {
406  LOG(4, ".data entry: '%s'\n", arr[i]);
407 
408  if (arr[i][0] == 'w') {
409  // word (16 bit)
410  // get word
411  int num = number_get_number(s, &arr[i][2]);
412  if (num < 0) {
413  if (num == NUMBER_LABEL_NODEF)
414  ERROR("Label '%s' is not defined!\n", &arr[i][2]);
415  ERROR("Invalid word in .data!\n");
416  goto ERR;
417  }
418 
419  // write
420  buff[bufferIdx++] = num & 0xff;
421  buff[bufferIdx++] = (num >> 8) & 0xff;
422 
423  } else if (arr[i][0] == '"') {
424  // strings
425 
426  if (arr[i][strlen(arr[i]) - 1] != '"') {
427  ERROR("Malformed string in .data! (no whitespaces allowed even in quotes)\n");
428  goto ERR;
429  }
430 
431  // write
432  for (int j = 1; j < strlen(arr[i]) - 1; j++)
433  buff[bufferIdx++] = arr[i][j];
434 
435  } else {
436  // byte
437 
438  int num = number_get_number(s, arr[i]);
439  if (num < 0 || num >> 8) {
440  if (num == NUMBER_LABEL_NODEF)
441  ERROR("Label '%s' is not defined!\n", arr[i]);
442  ERROR("Invalid byte in .data!\n");
443  goto ERR;
444  }
445 
446  // write
447  buff[bufferIdx++] = num & 0xff;
448  }
449  }
450  *dataptr = buff;
451  free(arr);
452  return t->binSize;
453 
454 ERR:
455  token_print(t);
456  free(arr);
457  free(buff);
458  *dataptr = NULL;
459  return -1;
460 }
461 
469 int compile_pad(State* s, Token* t, char** dataptr) {
470  int padwith = 0;
471 
472  // split string
473  int n;
474  char** arr = util_split_string(t->stripped, &n);
475 
476 
477  if (n == 3) {
478  padwith = number_get_number(s, arr[2]);
479  if (padwith < 0 || padwith >> 8) {
480  ERROR("Can not pad with invalid value!\n");
481  token_print(t);
482  *dataptr = NULL;
483  free(arr);
484  return -1;
485  }
486  }
487 
488  // allocate buffer and fill it
489  char* buff = malloc(t->binSize);
490  memset(buff, padwith, t->binSize);
491 
492  // return
493  *dataptr = buff;
494  free(arr);
495  return t->binSize;
496 }
497 
498 int directive_compile(State* s, Token* t, char** dataptr) {
499  // data or pad
500  if (util_match_string(t->stripped, ".data", strlen(".data")) == 0)
501  return compile_data(s, t, dataptr);
502  if (util_match_string(t->stripped, ".pad", strlen(".pad")) == 0)
503  return compile_pad(s, t, dataptr);
504  return -1;
505 }
TokensList::tokenslist_free
void tokenslist_free(TokensList *list)
free ALL memory associated with the TokensList object
Definition: tokenslist.c:78
Token::stripped
char stripped[TOKEN_BUFFER_SIZE]
stripped text from source file
Definition: token_t.h:55
load_file
TokensList * load_file(char *name)
load and parse one file
Definition: loadfile.c:166
DIR_STOP
@ DIR_STOP
An error occured, stop compilation.
Definition: directive.h:21
util_split_string
char ** util_split_string(char *str, int *n)
split a string into segments on spaces
Definition: util.c:39
DIR_NOP
@ DIR_NOP
Nothing special, do nothing.
Definition: directive.h:23
Map::map_set
int map_set(Map *map, char *name, int value)
Sets a key in the map.
Definition: map.c:43
process_ifdef
enum DIRCommand process_ifdef(State *s, TokensListElement *ptr)
Process an ifdef directive.
Definition: directive.c:205
TokensListElement::token
Token * token
Token value.
Definition: tokenslist.h:18
loadfile.h
File reading step.
LOG
#define LOG(LVL,...)
logging macro - works like printf
Definition: logging.h:28
State::tokens
TokensList * tokens
tokens
Definition: state.h:38
State
Compiler pseudo-global state.
Definition: state.h:32
process_print
enum DIRCommand process_print(State *s, TokensListElement *ptr)
Process a print directive.
Definition: directive.c:133
DIR_ENDIF
@ DIR_ENDIF
An endif was encountered, should pop from state stack.
Definition: directive.h:29
process_printc
enum DIRCommand process_printc(State *s, TokensListElement *ptr)
Process a printc directive.
Definition: directive.c:150
process_define
enum DIRCommand process_define(State *s, TokensListElement *ptr)
Process an ifbeq directive.
Definition: directive.c:49
skipProcessors
struct @1 skipProcessors[]
List of tokens to "process" when skipping tokens due to a falsy if.
processors
struct @0 processors[]
The list of all processor functions and their tokens.
process_org
enum DIRCommand process_org(State *s, TokensListElement *ptr)
Process an org directive.
Definition: directive.c:242
util_find_string_segment
char * util_find_string_segment(char *ptr)
find pointer to next space or null in string
Definition: util.c:15
directive.h
step 1 and 3 processing for directive tokens
State::defines
Map * defines
defined constants
Definition: state.h:34
Token
Token type to store token information.
Definition: token_t.h:37
Token::binSize
int binSize
number of bytes this token will generate
Definition: token_t.h:39
process_include
enum DIRCommand process_include(State *s, TokensListElement *ptr)
Process an include directive.
Definition: directive.c:180
process_pad
enum DIRCommand process_pad(State *s, TokensListElement *ptr)
Process a pad directive.
Definition: directive.c:298
util_match_string
int util_match_string(char *first, char *second, int count)
case-insensitive memcmp
Definition: util.c:32
TokensListElement
An element of a TokensList.
Definition: tokenslist.h:16
compile_data
int compile_data(State *s, Token *t, char **dataptr)
Compile a .data directive into binary data.
Definition: directive.c:396
do_directive_token
enum DIRCommand do_directive_token(State *s, TokensListElement *ptr, int skip)
process a directive token
Definition: directive.c:362
DIRCommand
DIRCommand
Internal command type for directives.
Definition: directive.h:19
DIR_IF_FALSE
@ DIR_IF_FALSE
A conditional evaluated to false.
Definition: directive.h:27
token_t.h
token type struct
tokenFunc.h
Token type member methods.
TokensList
A doubly-linked list for storing Tokens.
Definition: tokenslist.h:29
process_data
enum DIRCommand process_data(State *s, TokensListElement *ptr)
Process a data directive.
Definition: directive.c:275
Token::token_print
void token_print(Token *token)
Pretty-print one token, with its source and length.
Definition: tokenFunc.c:20
directive_compile
int directive_compile(State *s, Token *t, char **dataptr)
Compile a directive into binary data.
Definition: directive.c:498
DIR_IF_TRUE
@ DIR_IF_TRUE
A conditional evaluated to true.
Definition: directive.h:25
Map::map_get
int map_get(Map *map, char *name)
Get the value of a key.
Definition: map.c:75
process_ifndef
enum DIRCommand process_ifndef(State *s, TokensListElement *ptr)
Process an ifndef directive.
Definition: directive.c:227
logging.h
logging and fancy-printing
compile_pad
int compile_pad(State *s, Token *t, char **dataptr)
Compile a .pad directive into binary data.
Definition: directive.c:469
MAP_MAX_KEY_LEN
@ MAP_MAX_KEY_LEN
Key buffer size for Map.
Definition: map.h:14
State::PC
int PC
PC (starts at 0)
Definition: state.h:42
istack.h
iStack class for storing non-negative integers
TokensList::tokenslist_insert
void tokenslist_insert(TokensList *list, TokensListElement *target, TokensList *src)
Insert the contents of SRC into another list after an element.
Definition: tokenslist.c:97
state.h
implement State class
process_endif
enum DIRCommand process_endif(State *s, TokensListElement *ptr)
"Process" an endif directive
Definition: directive.c:125
number.h
Number module to parse numbers.
util.h
various utility functions
number_get_number
int number_get_number(State *s, char *str)
interpret a string as a constant, label or number
Definition: number.c:75
process_ifbeq
enum DIRCommand process_ifbeq(State *s, TokensListElement *ptr)
Process an ifbeq directive.
Definition: directive.c:90
tokenprocessor
enum DIRCommand(* tokenprocessor)(State *, TokensListElement *ptr)
Token processor function.
Definition: directive.c:40
FAIL
#define FAIL(...)
Fancy-print a fail (failed step). Works like printf.
Definition: logging.h:45
ERROR
#define ERROR(...)
Fancy-print an error (cause of faliure). Works like printf.
Definition: logging.h:40
NUMBER_LABEL_NODEF
@ NUMBER_LABEL_NODEF
Undefined label.
Definition: number.h:17