Version bump for last patch; now at v1.13.4.
[rmac] / op.c
1 //
2 // Jaguar Object Processor assembler
3 //
4 // by James Hammons
5 // (C) 2018 Underground Software
6 //
7
8 #include "op.h"
9 #include "direct.h"
10 #include "error.h"
11 #include "expr.h"
12 #include "fltpoint.h"
13 #include "mark.h"
14 #include "procln.h"
15 #include "riscasm.h"
16 #include "rmac.h"
17 #include "sect.h"
18 #include "token.h"
19
20 // Macros to help define things (though largely unnecessary for this assembler)
21 #define BITMAP    3100
22 #define SCBITMAP  3101
23 #define GPUOBJ    3102
24 #define BRANCH    3103
25 #define STOP      3104
26 #define NOP       3105
27 #define JUMP      3106
28
29 // Function prototypes
30 int HandleBitmap(void);
31 int HandleScaledBitmap(void);
32 int HandleGPUObject(void);
33 int HandleBranch(void);
34 int HandleStop(void);
35 int HandleNOP(void);
36 int HandleJump(void);
37
38 // OP assembler vars.
39 static uint8_t lastObjType;
40 static uint32_t lastSloc;
41 static char scratchbuf[4096];
42 static TOKEN fixupExpr[4] = { CONST, 0, 0, ENDEXPR };
43 //static PTR fixupPtr = { .tk = (fixupExpr + 1) };              // C99 \o/
44 static PTR fixupPtr = { (uint8_t *)(fixupExpr + 1) };   // meh, it works
45
46
47 //
48 // The main Object Processor assembler. Basically just calls the sub functions
49 // to generate the appropriate code.
50 //
51 int GenerateOPCode(int state)
52 {
53         if (!robjproc)
54                 return error("opcode only valid in OP mode");
55
56         switch (state)
57         {
58         case BITMAP:
59                 return HandleBitmap();
60         case SCBITMAP:
61                 return HandleScaledBitmap();
62         case GPUOBJ:
63                 return HandleGPUObject();
64         case BRANCH:
65                 return HandleBranch();
66         case STOP:
67                 return HandleStop();
68         case NOP:
69                 return HandleNOP();
70         case JUMP:
71                 return HandleJump();
72         }
73
74         return error("unknown OP opcode");
75 }
76
77
78 static inline void GetSymbolUCFromTokenStream(char * s)
79 {
80         strcpy(s, string[tok[1]]);
81         strtoupper(s);
82         tok += 2;
83 }
84
85
86 static inline uint64_t CheckFlags(char * s)
87 {
88         GetSymbolUCFromTokenStream(s);
89
90         if (strcmp(scratchbuf, "REFLECT") == 0)
91                 return 0x01;
92         else if (strcmp(scratchbuf, "RMW") == 0)
93                 return 0x02;
94         else if (strcmp(scratchbuf, "TRANS") == 0)
95                 return 0x04;
96         else if (strcmp(scratchbuf, "RELEASE") == 0)
97                 return 0x08;
98         return 0;
99 }
100
101
102 //
103 // Define a bitmap object
104 // Form: bitmap <data>, <xloc>, <yloc>, <dwidth>, <iwidth>, <iheight>, <bpp>,
105 //              <pallete idx>, <flags>, <firstpix>, <pitch>
106 //
107 int HandleBitmap(void)
108 {
109         uint64_t xpos = 0;
110         uint64_t ypos = 0;
111         uint64_t iheight = 0;
112         uint64_t dwidth = 0;
113         uint64_t iwidth = 0;
114         uint64_t bpp = 0;
115         uint64_t index = 0;
116         uint64_t flags = 0;
117         uint64_t firstpix = 0;
118         uint64_t pitch = 1;
119
120         uint64_t eval;
121         uint16_t eattr;
122         SYM * esym = 0;
123
124         if ((orgaddr & 0x0F) != 0)
125         {
126                 warn("bitmap not on double phrase boundary");
127
128                 // Fixup org address (by emitting a NOP)...
129                 HandleNOP();
130
131                 // We don't need to do a fixup here, because we're guaranteed that the
132                 // last object, if it was a scaled bitmap *or* regular bitmap, will
133                 // already be on the correct boundary, so we won't have to fix it up.
134         }
135
136         if (expr(exprbuf, &eval, &eattr, &esym) != OK)
137                 return ERROR;
138
139         if (!(eattr & DEFINED))
140                 AddFixup(FU_QUAD | FU_OBJDATA, sloc, exprbuf);
141         else if (eattr & TDB)
142                 MarkRelocatable(cursect, sloc, (eattr & TDB), MQUAD, NULL);
143
144         uint64_t dataAddr = eval & 0xFFFFF8;
145         uint64_t linkAddr = (orgaddr + 16) & 0x3FFFF8;
146
147         uint64_t * vars[10] = { &xpos, &ypos, &dwidth, &iwidth, &iheight, &bpp, &index, 0, &firstpix, &pitch };
148
149         for(int i=0; i<10; i++)
150         {
151                 // If there are less than 10 arguments after the data address, use
152                 // defaults for the rest of them
153                 // N.B.: Should there be a default minimum # of args? Like the first 5?
154                 if (tok[0] == EOL)
155                         break;
156
157                 CHECK_COMMA;
158
159                 if (i != 7)
160                 {
161                         if (expr(exprbuf, &eval, &eattr, &esym) != OK)
162                                 return ERROR;
163
164                         if (!(eattr & DEFINED))
165                                 return error("bad expression");
166
167                         *vars[i] = eval;
168                 }
169                 else
170                 {
171                         // Handle FLAGs. Can have at most four.
172                         for(int j=0; j<4; j++)
173                         {
174                                 if (tok[0] != SYMBOL)
175                                         return error("missing REFLECT, RMW, TRANS, and/or RELEASE");
176
177                                 flags |= CheckFlags(scratchbuf);
178
179                                 // Break out if no more symbols...
180                                 if (tok[0] != SYMBOL)
181                                         break;
182                         }
183                 }
184         }
185
186         at_eol();
187
188         uint64_t p1 = 0x00 | ((ypos * 2) << 3) | (iheight << 14) | (linkAddr << 21) | (dataAddr << 40);
189         uint64_t p2 = xpos | (bpp << 12) | (pitch << 15) | (dwidth << 18) | (iwidth << 28) | (index << 38) | (flags << 45) | (firstpix << 49);
190
191         lastSloc = sloc;
192         lastObjType = 0;
193         D_quad(p1);
194         D_quad(p2);
195
196         return OK;
197 }
198
199
200 //
201 // Define a scaled bitmap object
202 // Form: scbitmap <data>, <xloc>, <yloc>, <dwidth>, <iwidth>, <iheight>,
203 //                <xscale>, <yscale>, <remainder>, <bpp>, <pallete idx>,
204 //                <flags>, <firstpix>, <pitch>
205 //
206 int HandleScaledBitmap(void)
207 {
208         uint64_t xpos = 0;
209         uint64_t ypos = 0;
210         uint64_t iheight = 0;
211         uint64_t dwidth = 0;
212         uint64_t iwidth = 0;
213         uint64_t bpp = 0;
214         uint64_t index = 0;
215         uint64_t flags = 0;
216         uint64_t firstpix = 0;
217         uint64_t pitch = 1;
218         uint64_t xscale = 0;
219         uint64_t yscale = 0;
220         uint64_t remainder = 0;
221
222         uint64_t eval;
223         uint16_t eattr;
224         SYM * esym = 0;
225
226         if ((orgaddr & 0x1F) != 0)
227         {
228                 warn("scaled bitmap not on quad phrase boundary");
229
230                 // We only have to do a fixup here if the previous object was a bitmap,
231                 // as it can live on a 16-byte boundary while scaled bitmaps can't. If
232                 // the previous object was a scaled bitmap, it is guaranteed to have
233                 // been aligned, therefore no fixup is necessary.
234                 if (lastObjType == 0)
235                 {
236                         *fixupPtr.u64 = (orgaddr + 0x18) & 0xFFFFFFE0;
237                         AddFixup(FU_QUAD | FU_OBJLINK, lastSloc, fixupExpr);
238                 }
239
240                 switch (orgaddr & 0x1F)
241                 {
242                 case 0x08: HandleNOP(); // Emit 3 NOPs
243                 case 0x10: HandleNOP(); // Emit 2 NOPs
244                 case 0x18: HandleNOP(); // Emit 1 NOP
245                 }
246         }
247
248         if (expr(exprbuf, &eval, &eattr, &esym) != OK)
249                 return ERROR;
250
251         if (!(eattr & DEFINED))
252                 AddFixup(FU_QUAD | FU_OBJDATA, sloc, exprbuf);
253         else if (eattr & TDB)
254                 MarkRelocatable(cursect, sloc, (eattr & TDB), MQUAD, NULL);
255
256         uint64_t dataAddr = eval & 0xFFFFF8;
257         uint64_t linkAddr = (orgaddr + 32) & 0x3FFFF8;
258
259         uint64_t * vars[13] = { &xpos, &ypos, &dwidth, &iwidth, &iheight, &xscale, &yscale, &remainder, &bpp, &index, 0, &firstpix, &pitch };
260
261         for(int i=0; i<13; i++)
262         {
263                 // If there are less than 13 arguments after the data address, use
264                 // defaults for the rest of them
265                 // N.B.: Should there be a default minimum # of args? Like the first 5?
266                 if (tok[0] == EOL)
267                         break;
268
269                 CHECK_COMMA;
270
271                 if (i != 10)
272                 {
273                         if (expr(exprbuf, &eval, &eattr, &esym) != OK)
274                                 return ERROR;
275
276                         if (!(eattr & DEFINED))
277                                 return error("bad expression");
278
279                         // Handle 3.5 fixed point nums (if any)...
280                         if ((i >= 5) && (i <= 7))
281                         {
282                                 if (eattr & FLOAT)                      // Handle floats
283                                 {
284 //                                      PTR p = { .u64 = &eval };               // C99 \o/
285                                         PTR p = { (uint8_t *)&eval };   // Meh, it works
286                                         eval = DoubleToFixedPoint(*p.dp, 3, 5);
287                                 }
288                                 else
289                                         eval <<= 5;                             // Otherwise, it's just an int...
290                         }
291
292                         *vars[i] = eval;
293                 }
294                 else
295                 {
296                         // Handle FLAGs. Can have at most four.
297                         for(int j=0; j<4; j++)
298                         {
299                                 if (tok[0] != SYMBOL)
300                                         return error("missing REFLECT, RMW, TRANS, and/or RELEASE");
301
302                                 flags |= CheckFlags(scratchbuf);
303
304                                 // Break out if no more symbols...
305                                 if (tok[0] != SYMBOL)
306                                         break;
307                         }
308                 }
309         }
310
311         at_eol();
312
313         uint64_t p1 = 0x01 | ((ypos * 2) << 3) | (iheight << 14) | (linkAddr << 21) | (dataAddr << 40);
314         uint64_t p2 = xpos | (bpp << 12) | (pitch << 15) | (dwidth << 18) | (iwidth << 28) | (index << 38) | (flags << 45) | (firstpix << 49);
315         uint64_t p3 = (xscale & 0xFF) | (yscale & 0xFF) << 8 | (remainder & 0xFF) << 16;
316
317         lastSloc = sloc;
318         lastObjType = 1;
319         D_quad(p1);
320         D_quad(p2);
321         D_quad(p3);
322         D_quad(0LL);
323
324         return OK;
325 }
326
327
328 //
329 // Insert GPU object
330 // Form: gpuobj <line #>, <userdata> (bits 14-63 of this object)
331 //
332 int HandleGPUObject(void)
333 {
334         uint64_t eval;
335         uint16_t eattr;
336         SYM * esym = 0;
337
338         if (expr(exprbuf, &eval, &eattr, &esym) != OK)
339                 return ERROR;
340
341         if (!(eattr & DEFINED))
342                 return error("bad expression in y position");
343
344         uint64_t ypos = eval;
345
346         CHECK_COMMA;
347
348         if (expr(exprbuf, &eval, &eattr, &esym) != OK)
349                 return ERROR;
350
351         if (!(eattr & DEFINED))
352                 return error("bad expression in data");
353
354         at_eol();
355
356         uint64_t p1 = 0x02 | ((ypos * 2) << 3) | (eval << 14);
357
358         lastObjType = 2;
359         D_quad(p1);
360
361         return OK;
362 }
363
364
365 //
366 // Insert a branch object
367 // Form: branch VC <condition (<, =, >)> <line #>, <link addr>
368 //       branch OPFLAG, <link addr>
369 //       branch SECHALF, <link addr>
370 //
371 int HandleBranch(void)
372 {
373         char missingKeyword[] = "missing VC, OPFLAG, or SECHALF in branch";
374         uint32_t cc = 0;
375         uint32_t ypos = 0;
376         uint64_t eval;
377         uint16_t eattr;
378         SYM * esym = 0;
379
380         if (tok[0] != SYMBOL)
381                 return error(missingKeyword);
382
383         GetSymbolUCFromTokenStream(scratchbuf);
384
385         if (strcmp(scratchbuf, "VC") == 0)
386         {
387                 switch (*tok++)
388                 {
389                 case '=': cc = 0; break;
390                 case '<': cc = 1; break;
391                 case '>': cc = 2; break;
392                 default:
393                         return error("missing '<', '>', or '='");
394                 }
395
396                 if (expr(exprbuf, &eval, &eattr, &esym) != OK)
397                         return ERROR;
398
399                 if (!(eattr & DEFINED))
400                         return error("bad expression");
401
402                 ypos = (uint32_t)eval;
403         }
404         else if (strcmp(scratchbuf, "OPFLAG") == 0)
405                 cc = 3;
406         else if (strcmp(scratchbuf, "SECHALF") == 0)
407                 cc = 4;
408         else
409                 return error(missingKeyword);
410
411         CHECK_COMMA;
412
413         if (expr(exprbuf, &eval, &eattr, &esym) != OK)
414                 return ERROR;
415
416         if (!(eattr & DEFINED))
417                 AddFixup(FU_QUAD | FU_OBJLINK, sloc, exprbuf);
418
419         at_eol();
420
421         uint64_t p1 = 0x03 | (cc << 14) | ((ypos * 2) << 3) | ((eval & 0x3FFFF8) << 21);
422
423         lastObjType = 3;
424         D_quad(p1);
425
426         return OK;
427 }
428
429
430 //
431 // Insert a stop object
432 // Form: stop
433 //
434 int HandleStop(void)
435 {
436         lastObjType = 4;
437         D_quad(4LL);
438
439         return OK;
440 }
441
442
443 //
444 // Insert a phrase sized "NOP" in the object list (psuedo-op)
445 // Form: nop
446 //
447 int HandleNOP(void)
448 {
449         uint64_t eval = (orgaddr + 8) & 0x3FFFF8;
450         // This is "branch if VC > 2047". Branch addr is next phrase, so either way
451         // it will pass by this phrase.
452         uint64_t p1 = 0x03 | (2 << 14) | (0x7FF << 3) | (eval << 21);
453
454         lastObjType = 3;
455         D_quad(p1);
456
457         return OK;
458 }
459
460
461 //
462 // Insert an unconditional jump in the object list (psuedo-op)
463 // Form: jump <link addr>
464 //
465 int HandleJump(void)
466 {
467         uint64_t eval;
468         uint16_t eattr;
469         SYM * esym = 0;
470
471         if (expr(exprbuf, &eval, &eattr, &esym) != OK)
472                 return ERROR;
473
474         if (!(eattr & DEFINED))
475                 AddFixup(FU_QUAD | FU_OBJLINK, sloc, exprbuf);
476
477         at_eol();
478
479         // This is "branch if VC < 2047", which pretty much guarantees the branch.
480         uint64_t p1 = 0x03 | (1 << 14) | (0x7FF << 3) | ((eval & 0x3FFFF8) << 21);
481
482         lastObjType = 3;
483         D_quad(p1);
484
485         return OK;
486 }
487