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