]> Shamusworld >> Repos - rmac/blob - op.c
Version bump for last commit. :-)
[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 // The main Object Processor assembler. Basically just calls the sub functions
42 // to generate the appropriate code.
43 //
44 int GenerateOPCode(int state)
45 {
46         if (!robjproc)
47                 return error("opcode only valid in OP mode");
48
49         // It's OK to call this before validating state. If the state is invalid, an
50         // error will be generated and no object file will be produced, so it
51         // doesn't matter if the line number symbols are a little off.
52         GENLINENOSYM();
53
54         switch (state)
55         {
56         case MO_BITMAP:
57                 return HandleBitmap();
58         case MO_SCBITMAP:
59                 return HandleScaledBitmap();
60         case MO_GPUOBJ:
61                 return HandleGPUObject();
62         case MO_BRANCH:
63                 return HandleBranch();
64         case MO_STOP:
65                 return HandleStop();
66         case MO_NOP:
67                 return HandleNOP();
68         case MO_JUMP:
69                 return HandleJump();
70         }
71
72         return error("unknown OP opcode");
73 }
74
75 static inline void GetSymbolUCFromTokenStream(char * s)
76 {
77         strcpy(s, string[tok[1]]);
78         strtoupper(s);
79         tok += 2;
80 }
81
82 static inline uint64_t CheckFlags(char * s)
83 {
84         GetSymbolUCFromTokenStream(s);
85
86         if (strcmp(scratchbuf, "REFLECT") == 0)
87                 return 0x01;
88         else if (strcmp(scratchbuf, "RMW") == 0)
89                 return 0x02;
90         else if (strcmp(scratchbuf, "TRANS") == 0)
91                 return 0x04;
92         else if (strcmp(scratchbuf, "RELEASE") == 0)
93                 return 0x08;
94
95         return 0;
96 }
97
98 //
99 // Define a bitmap object
100 // Form: bitmap <data>, <xloc>, <yloc>, <dwidth>, <iwidth>, <iheight>, <bpp>,
101 //              <pallete idx>, <flags>, <firstpix>, <pitch>
102 //
103 static int HandleBitmap(void)
104 {
105         uint64_t xpos = 0;
106         uint64_t ypos = 0;
107         uint64_t iheight = 0;
108         uint64_t dwidth = 0;
109         uint64_t iwidth = 0;
110         uint64_t bpp = 0;
111         uint64_t index = 0;
112         uint64_t flags = 0;
113         uint64_t firstpix = 0;
114         uint64_t pitch = 1;
115
116         uint64_t eval;
117         uint16_t eattr;
118         SYM * esym = 0;
119
120         if ((orgaddr & 0x0F) != 0)
121         {
122                 warn("bitmap not on double phrase boundary");
123
124                 // Fixup org address (by emitting a NOP)...
125                 HandleNOP();
126
127                 // We don't need to do a fixup here, because we're guaranteed that the
128                 // last object, if it was a scaled bitmap *or* regular bitmap, will
129                 // already be on the correct boundary, so we won't have to fix it up.
130         }
131
132         if (expr(exprbuf, &eval, &eattr, &esym) != OK)
133                 return ERROR;
134
135         if (!(eattr & DEFINED))
136                 AddFixup(FU_QUAD | FU_OBJDATA, sloc, exprbuf);
137         else if (eattr & TDB)
138                 MarkRelocatable(cursect, sloc, (eattr & TDB), MQUAD, NULL);
139
140         uint64_t dataAddr = eval & 0xFFFFF8;
141         uint64_t linkAddr = (orgaddr + 16) & 0x3FFFF8;
142
143         uint64_t * vars[10] = { &xpos, &ypos, &dwidth, &iwidth, &iheight, &bpp, &index, 0, &firstpix, &pitch };
144
145         for(int i=0; i<10; i++)
146         {
147                 // If there are less than 10 arguments after the data address, use
148                 // defaults for the rest of them
149                 // N.B.: Should there be a default minimum # of args? Like the first 5?
150                 if (tok[0] == EOL)
151                         break;
152
153                 CHECK_COMMA;
154
155                 if (i != 7)
156                 {
157                         if (expr(exprbuf, &eval, &eattr, &esym) != OK)
158                                 return ERROR;
159
160                         if (!(eattr & DEFINED))
161                                 return error("bad expression");
162
163                         *vars[i] = eval;
164                 }
165                 else
166                 {
167                         // Handle FLAGs. Can have at most four.
168                         for(int j=0; j<4; j++)
169                         {
170                                 if (tok[0] != SYMBOL)
171                                         return error("missing REFLECT, RMW, TRANS, and/or RELEASE");
172
173                                 flags |= CheckFlags(scratchbuf);
174
175                                 // Break out if no more symbols...
176                                 if (tok[0] != SYMBOL)
177                                         break;
178                         }
179                 }
180         }
181
182         ErrorIfNotAtEOL();
183
184         uint64_t p1 = 0x00 | ((ypos * 2) << 3) | (iheight << 14) | (linkAddr << 21) | (dataAddr << 40);
185         uint64_t p2 = xpos | (bpp << 12) | (pitch << 15) | (dwidth << 18) | (iwidth << 28) | (index << 38) | (flags << 45) | (firstpix << 49);
186
187         lastSloc = sloc;
188         lastObjType = 0;
189         D_quad(p1);
190         D_quad(p2);
191
192         return OK;
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 // Insert GPU object
324 // Form: gpuobj <userdata> (bits 3-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 data");
337
338         ErrorIfNotAtEOL();
339
340         uint64_t p1 = 0x02 | (eval << 3);
341
342         lastObjType = 2;
343         D_quad(p1);
344
345         return OK;
346 }
347
348 //
349 // Insert a branch object
350 // Form: branch VC <condition (<, =, >)> <line #>, <link addr>
351 //       branch OPFLAG, <link addr>
352 //       branch SECHALF, <link addr>
353 //
354 static int HandleBranch(void)
355 {
356         char missingKeyword[] = "missing VC, OPFLAG, or SECHALF in branch";
357         uint32_t cc = 0;
358         uint32_t ypos = 0;
359         uint64_t eval;
360         uint16_t eattr;
361         SYM * esym = 0;
362
363         if (tok[0] != SYMBOL)
364                 return error(missingKeyword);
365
366         GetSymbolUCFromTokenStream(scratchbuf);
367
368         if (strcmp(scratchbuf, "VC") == 0)
369         {
370                 switch (*tok++)
371                 {
372                 case '=': cc = 0; break;
373                 case '<': cc = 1; break;
374                 case '>': cc = 2; break;
375                 default:
376                         return error("missing '<', '>', or '='");
377                 }
378
379                 if (expr(exprbuf, &eval, &eattr, &esym) != OK)
380                         return ERROR;
381
382                 if (!(eattr & DEFINED))
383                         return error("bad expression");
384
385                 ypos = (uint32_t)eval;
386         }
387         else if (strcmp(scratchbuf, "OPFLAG") == 0)
388                 cc = 3;
389         else if (strcmp(scratchbuf, "SECHALF") == 0)
390                 cc = 4;
391         else
392                 return error(missingKeyword);
393
394         CHECK_COMMA;
395
396         if (expr(exprbuf, &eval, &eattr, &esym) != OK)
397                 return ERROR;
398
399         if (!(eattr & DEFINED))
400                 AddFixup(FU_QUAD | FU_OBJLINK, sloc, exprbuf);
401
402         ErrorIfNotAtEOL();
403
404         uint64_t p1 = 0x03 | (cc << 14) | ((ypos * 2) << 3) | ((eval & 0x3FFFF8) << 21);
405
406         lastObjType = 3;
407         D_quad(p1);
408
409         return OK;
410 }
411
412 //
413 // Insert a stop object
414 // Form: stop
415 //
416 static int HandleStop(void)
417 {
418         lastObjType = 4;
419         D_quad(4LL);
420
421         return OK;
422 }
423
424 //
425 // Insert a phrase sized "NOP" in the object list (psuedo-op)
426 // Form: nop
427 //
428 static int HandleNOP(void)
429 {
430         uint64_t eval = (orgaddr + 8) & 0x3FFFF8;
431         // This is "branch if VC > 2047". Branch addr is next phrase, so either way
432         // it will pass by this phrase.
433         uint64_t p1 = 0x03 | (2 << 14) | (0x7FF << 3) | (eval << 21);
434
435         lastObjType = 3;
436         D_quad(p1);
437
438         return OK;
439 }
440
441 //
442 // Insert an unconditional jump in the object list (psuedo-op)
443 // Form: jump <link addr>
444 //
445 static int HandleJump(void)
446 {
447         uint64_t eval;
448         uint16_t eattr;
449         SYM * esym = 0;
450
451         if (expr(exprbuf, &eval, &eattr, &esym) != OK)
452                 return ERROR;
453
454         if (!(eattr & DEFINED))
455                 AddFixup(FU_QUAD | FU_OBJLINK, sloc, exprbuf);
456
457         ErrorIfNotAtEOL();
458
459         // This is "branch if VC < 2047", which pretty much guarantees the branch.
460         uint64_t p1 = 0x03 | (1 << 14) | (0x7FF << 3) | ((eval & 0x3FFFF8) << 21);
461
462         lastObjType = 3;
463         D_quad(p1);
464
465         return OK;
466 }