s502 assembler
A very simple assembler for the 6502 line of processors written in C
Typedefs | Functions | Variables
directive.c File Reference

Implement pass 1 and pass 3 (compile) processing for directive tokens. More...

#include "debugmalloc.h"
#include <string.h>
#include <stdlib.h>
#include "state.h"
#include "token_t.h"
#include "tokenFunc.h"
#include "logging.h"
#include "directive.h"
#include "util.h"
#include "number.h"
#include "loadfile.h"
#include "istack.h"

Go to the source code of this file.

Typedefs

typedef enum DIRCommand(* tokenprocessor) (State *, TokensListElement *ptr)
 Token processor function. More...
 

Functions

enum DIRCommand process_define (State *s, TokensListElement *ptr)
 Process an ifbeq directive. More...
 
enum DIRCommand process_ifbeq (State *s, TokensListElement *ptr)
 Process an ifbeq directive. More...
 
enum DIRCommand process_endif (State *s, TokensListElement *ptr)
 "Process" an endif directive More...
 
enum DIRCommand process_print (State *s, TokensListElement *ptr)
 Process a print directive. More...
 
enum DIRCommand process_printc (State *s, TokensListElement *ptr)
 Process a printc directive. More...
 
enum DIRCommand process_include (State *s, TokensListElement *ptr)
 Process an include directive. More...
 
enum DIRCommand process_ifdef (State *s, TokensListElement *ptr)
 Process an ifdef directive. More...
 
enum DIRCommand process_ifndef (State *s, TokensListElement *ptr)
 Process an ifndef directive. More...
 
enum DIRCommand process_org (State *s, TokensListElement *ptr)
 Process an org directive. More...
 
enum DIRCommand process_data (State *s, TokensListElement *ptr)
 Process a data directive. More...
 
enum DIRCommand process_pad (State *s, TokensListElement *ptr)
 Process a pad directive. More...
 
enum DIRCommand do_directive_token (State *s, TokensListElement *ptr, int skip)
 process a directive token More...
 
int compile_data (State *s, Token *t, char **dataptr)
 Compile a .data directive into binary data. More...
 
int compile_pad (State *s, Token *t, char **dataptr)
 Compile a .pad directive into binary data. More...
 
int directive_compile (State *s, Token *t, char **dataptr)
 Compile a directive into binary data. More...
 

Variables

struct {
tokenprocessor p
 
char * name
 
processors []
 The list of all processor functions and their tokens. More...
 
struct {
enum DIRCommand ret
 
char * name
 
skipProcessors []
 List of tokens to "process" when skipping tokens due to a falsy if. More...
 

Detailed Description

Implement pass 1 and pass 3 (compile) processing for directive tokens.

Pass 1 processing is complex processing, exact method depens on the current directive.
For this, I used an array to link directive names to different processing functions.
(also another array for disabled compilation)

Directives control conditional compilation and can modify the list of currently loaded tokens.
For this reason the common interface for processing functions receives state and full list along the current token, and returns a DIRCommand

Definition in file directive.c.

Typedef Documentation

◆ tokenprocessor

typedef enum DIRCommand(* tokenprocessor) (State *, TokensListElement *ptr)

Token processor function.

Should process one token and return a DIRCommand
The reason why it takes a TokensListElemnt* and not just a Token* is that we need to do an insert to it's position (in require)

Definition at line 1 of file directive.c.

Function Documentation

◆ compile_data()

int compile_data ( State s,
Token t,
char **  dataptr 
)

Compile a .data directive into binary data.

Parameters
dataptrreturn buffer for data
sstate object
ttoken to compile
Returns
number of bytes in buffer or -1 on error

Definition at line 396 of file directive.c.

396  {
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 }

References Token::binSize, ERROR, LOG, number_get_number(), NUMBER_LABEL_NODEF, Token::stripped, Token::token_print(), and util_split_string().

Referenced by directive_compile().

◆ compile_pad()

int compile_pad ( State s,
Token t,
char **  dataptr 
)

Compile a .pad directive into binary data.

Parameters
dataptrreturn buffer for data
sstate object
ttoken to compile
Returns
number of bytes in buffer or -1 on error

Definition at line 469 of file directive.c.

469  {
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 }

References Token::binSize, ERROR, number_get_number(), Token::stripped, Token::token_print(), and util_split_string().

Referenced by directive_compile().

◆ directive_compile()

int directive_compile ( State s,
Token t,
char **  dataptr 
)

Compile a directive into binary data.

Parameters
dataptrreturn buffer for data
sassembler state
ttoken to compile
Returns
number of bytes in buffer or -1 on error

Simply relays it to compile_pad or compile_data

Definition at line 498 of file directive.c.

498  {
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 }

References compile_data(), compile_pad(), Token::stripped, and util_match_string().

Referenced by token_compile().

◆ do_directive_token()

enum DIRCommand do_directive_token ( State s,
TokensListElement ptr,
int  skip 
)

process a directive token

Parameters
sstate of the compiler
ptrptr to the current token in the list
skipdisable compilation flag (1=disabled)
Returns
DIRCommand to pass 1

Identifies directive type, and runs a processor function on it.

Definition at line 1 of file directive.c.

362  {
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 }

Referenced by pass_one().

◆ process_data()

enum DIRCommand process_data ( State s,
TokensListElement ptr 
)

Process a data directive.

Returns
DIR_NOP

Only counts size, generating binary will be done if all labels are defined (pass 3)

Definition at line 1 of file directive.c.

275  {
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 }

◆ process_define()

enum DIRCommand process_define ( State s,
TokensListElement ptr 
)

Process an ifbeq directive.

Returns
DIR_NOP or DIR_STOP
Parameters
scurrent state
ptrpointer to element to process

Definition at line 1 of file directive.c.

49  {
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 }

◆ process_endif()

enum DIRCommand process_endif ( State s,
TokensListElement ptr 
)

"Process" an endif directive

Returns
DIR_ENDIF

Definition at line 1 of file directive.c.

125  {
126  return DIR_ENDIF;
127 }

◆ process_ifbeq()

enum DIRCommand process_ifbeq ( State s,
TokensListElement ptr 
)

Process an ifbeq directive.

Returns
DIR_IF_TRUE or DIR_IF_FALSE or DIR_STOP

Definition at line 1 of file directive.c.

90  {
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 }

◆ process_ifdef()

enum DIRCommand process_ifdef ( State s,
TokensListElement ptr 
)

Process an ifdef directive.

Returns
DIR_STOP, DIR_IF_TRUE or DIR_IF_FALSE

Definition at line 1 of file directive.c.

205  {
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 }

◆ process_ifndef()

enum DIRCommand process_ifndef ( State s,
TokensListElement ptr 
)

Process an ifndef directive.

Returns
DIR_STOP, DIR_IF_TRUE or DIR_IF_FALSE

Uses process_ifdef internally

Definition at line 1 of file directive.c.

227  {
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 }

◆ process_include()

enum DIRCommand process_include ( State s,
TokensListElement ptr 
)

Process an include directive.

Returns
DIR_NOP or DIR_STOP

Reads another file and inserts the tokens into the list.
This modifies the tokenslist directly!

Definition at line 1 of file directive.c.

180  {
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 }

◆ process_org()

enum DIRCommand process_org ( State s,
TokensListElement ptr 
)

Process an org directive.

Returns
DIR_STOP or DIR_NOP

Modifies state PC

Definition at line 1 of file directive.c.

242  {
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 }

◆ process_pad()

enum DIRCommand process_pad ( State s,
TokensListElement ptr 
)

Process a pad directive.

Returns
DIR_NOP

Definition at line 1 of file directive.c.

298  {
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 }

◆ process_print()

enum DIRCommand process_print ( State s,
TokensListElement ptr 
)

Process a print directive.

Returns
DIR_NOP or DIR_STOP

Definition at line 1 of file directive.c.

133  {
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 }

◆ process_printc()

enum DIRCommand process_printc ( State s,
TokensListElement ptr 
)

Process a printc directive.

Returns
DIR_NOP or DIR_STOP

Definition at line 1 of file directive.c.

150  {
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 }

Variable Documentation

◆ processors

struct { ... } processors[]
Initial value:
= {
{ process_define, "define" },
{ process_ifdef, "ifdef" },
{ process_printc, "printc" },
{ process_print, "print" },
{ process_include, "include" },
{ process_endif, "endif" },
{ process_ifndef, "ifndef" },
{ process_ifbeq, "ifbeq" },
{ process_org, "org" },
{ process_data, "data" },
{ process_pad, "pad" },
}

The list of all processor functions and their tokens.

Their order DOES matter as comparision can not check end of strings as tokens do not end after directive names.
(namely printc must come before print or will get falsely reconized as a print)

◆ skipProcessors

struct { ... } skipProcessors[]
Initial value:
= {
{ DIR_ENDIF, "endif" },
{ DIR_IF_TRUE, "ifdef" },
{ DIR_IF_TRUE, "ifndef"},
{ DIR_IF_TRUE, "ifbeq" },
}

List of tokens to "process" when skipping tokens due to a falsy if.

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
LOG
#define LOG(LVL,...)
logging macro - works like printf
Definition: logging.h:28
State::tokens
TokensList * tokens
tokens
Definition: state.h:38
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
State::defines
Map * defines
defined constants
Definition: state.h:34
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
compile_data
int compile_data(State *s, Token *t, char **dataptr)
Compile a .data directive into binary data.
Definition: directive.c:396
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
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
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
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
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
process_endif
enum DIRCommand process_endif(State *s, TokensListElement *ptr)
"Process" an endif directive
Definition: directive.c:125
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
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