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