actually commit minor cleanups
[dcpu16] / as-dcpu16.c
1 #include <stdlib.h>
2 #include <unistd.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <errno.h>
6 #include <sysexits.h>
7 #include <assert.h>
8
9 /* #include dcpu16.h */
10 typedef unsigned short DCPU16_WORD;
11
12 /* quick and dirty assembler for dcpu16 */
13
14 static const char * const src_id_ = "$Id$";
15
16 const char const out_filename_default_[] = "a.out";
17
18 static
19 void usage_(char *prog, unsigned int full) {
20 FILE *f = full ? stdout : stderr;
21 char *x = strrchr(prog, '/');
22
23 if (x && *(x + 1))
24 prog = x + 1;
25
26 if (full)
27 fprintf(f, "%s -- \n\n",
28 prog);
29
30 fprintf(f, "Usage: %s\n",
31 prog);
32
33 if (full) {
34 fprintf(f, "\nOptions:\n"
35 "\t-h -- this screen\n"
36 "\t-o <file> -- output to <file> [default: %s]\n",
37 out_filename_default_);
38
39 fprintf(f, "\n%78s\n",
40 src_id_);
41 }
42 }
43
44 struct operand_ {
45 struct operand_ *next;
46 char *operand;
47 };
48
49 struct instruction_ {
50 struct instruction_ *next;
51 char *label;
52 char *opcode;
53 struct operand_ *operands;
54
55 unsigned int length; /* words */
56 DCPU16_WORD instr_words[];
57 };
58
59 /* buf must be 0-terminated */
60 static
61 int buf_tokenize_(char *buf, struct instruction_ **next_instr) {
62 const char const *sep = " \t\n";
63 struct instruction_ *instr = NULL;
64 char *label = NULL,
65 *opcode = NULL,
66 *operand = NULL;
67
68 char *x,
69 *y,
70 *st;
71
72 assert(buf != NULL);
73 assert(next_instr != NULL);
74
75 *next_instr = NULL;
76
77 /* kill comments */
78 if ((x = strchr(buf, ';')) != NULL)
79 *x = '\0';
80 /* kill leading whitespace */
81 buf += strspn(buf, " \t\n");
82 /* kill trailing newlines */
83 if ((x = strrchr(buf, '\n')) != NULL)
84 *x = '\0';
85
86 /* determine if first token is label, opcode, or we just have a blank line to ignore */
87 x = strtok_r(buf, sep, &st);
88
89 /* empty line? nothing to do here. */
90 if (x == NULL)
91 return 0;
92
93 /* labels end with :, otherwise its an opcode */
94 if ((y = strrchr(x, ':')) != NULL) {
95 *y = '\0';
96 label = x;
97 opcode = strtok_r(NULL, sep, &st);
98 } else {
99 label = NULL;
100 opcode = x;
101 }
102
103 if (opcode) {
104 operand = st;
105 }
106
107 instr = calloc(1, sizeof *instr);
108 if (instr == NULL) {
109 fprintf(stderr, "%s():%s\n", "malloc", strerror(errno));
110 return -1;
111 }
112
113 instr->label = label;
114 instr->opcode = opcode;
115
116 if (operand) {
117
118 }
119
120 *next_instr = instr;
121
122 return 0;
123 }
124
125 /* thish should grow buffer to fit huge linesh, but I jusht don't care right now, hic */
126 static
127 int parse_stream_(FILE *f) {
128 struct instruction_ *instr;
129 char buf[(1<<14)];
130
131 buf[sizeof buf - 1] = '\0';
132
133 while (fgets(buf, sizeof buf, f)) {
134 if (buf[sizeof buf - 1] != '\0') {
135 fprintf(stderr, "input buffer exhausted\n");
136 break;
137 }
138
139 if (buf_tokenize_(buf, &instr)) {
140 fprintf(stderr, "trouble tokenizing input\n");
141 break;
142 }
143
144 if (instr) {
145 struct operand_ *o;
146 if (instr->label) {
147 printf("TRACE: new label '%s'\n", instr->label);
148 }
149 printf("TRACE: tokenized opcode:%s operands:",
150 instr->opcode);
151 for (o = instr->operands; o; o = o->next) {
152 printf("%s%s", o->operand, o->next ? ", " : "");
153 }
154 printf("\n");
155
156
157 /* add to queue of instructions */
158 }
159 }
160 if (ferror(f)) {
161 fprintf(stderr, "%s():%s\n", "fgets", strerror(errno));
162 return -1;
163 }
164 if (! feof(f)) {
165 fprintf(stderr, "parsing aborted\n");
166 return -1;
167 }
168
169 return 0;
170 }
171
172 int main(int argc, char *argv[]) {
173 const char *out_filename = NULL;
174 int c;
175
176 while ( (c = getopt(argc, argv, "ho:")) != EOF ) {
177 switch (c) {
178 case 'o':
179 if (out_filename) {
180 fprintf(stderr, "Sorry, I can only write one file at a time.\n");
181 exit(EX_CANTCREAT);
182 }
183 out_filename = optarg;
184 break;
185
186 case 'h':
187 usage_(argv[0], 1);
188 exit(EX_OK);
189
190 default:
191 usage_(argv[0], 0);
192 exit(EX_USAGE);
193 }
194 }
195
196 if (out_filename == NULL)
197 out_filename = out_filename_default_;
198
199 /* if filenames were specified, parse them instead of stdin */
200 if (argc - optind) {
201 while (argc - optind) {
202 FILE *f = fopen(argv[argc - optind], "r");
203 if (f == NULL) {
204 fprintf(stderr, "%s('%s'):%s\n", "fopen", argv[argc - optind], strerror(errno));
205 optind++;
206 continue;
207 }
208
209 parse_stream_(f);
210
211 fclose(f);
212
213 optind++;
214 }
215 } else {
216 parse_stream_(stdin);
217 }
218
219 exit(EX_OK);
220 }