Fix movep size optimising when +o3 is enabled. Show available optimisations in the...
[rmac] / mach.c
1 //
2 // RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System
3 // MACH.C - Code Generation
4 // Copyright (C) 199x Landon Dyer, 2017 Reboot and Friends
5 // RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986
6 // Source utilised with the kind permission of Landon Dyer
7 //
8
9 #include "mach.h"
10 #include "error.h"
11 #include "sect.h"
12 #include "direct.h"
13 #include "token.h"
14 #include "procln.h"
15 #include "riscasm.h"
16 #include "rmac.h"
17
18 #define DEF_KW
19 #include "kwtab.h"
20
21
22 // Fucntion prototypes
23 int m_unimp(WORD, WORD), m_badmode(WORD, WORD), m_bad6mode(WORD, WORD), m_bad6inst(WORD, WORD);
24 int m_self(WORD, WORD);
25 int m_abcd(WORD, WORD);
26 int m_reg(WORD, WORD);
27 int m_imm(WORD, WORD);
28 int m_imm8(WORD, WORD);
29 int m_shi(WORD, WORD);
30 int m_shr(WORD, WORD);
31 int m_bitop(WORD, WORD);
32 int m_exg(WORD, WORD);
33 int m_ea(WORD, WORD);
34 int m_br(WORD, WORD);
35 int m_dbra(WORD, WORD);
36 int m_link(WORD, WORD);
37 int m_adda(WORD, WORD);
38 int m_addq(WORD, WORD);
39 //int m_move(WORD, int);
40 int m_move(WORD, WORD);
41 int m_moveq(WORD, WORD);
42 int m_usp(WORD, WORD);
43 int m_movep(WORD, WORD);
44 int m_trap(WORD, WORD);
45 int m_movem(WORD, WORD);
46 int m_clra(WORD, WORD);
47
48 // Common error messages
49 char range_error[] = "expression out of range";
50 char abs_error[] = "illegal absolute expression";
51 char seg_error[] = "bad (section) expression";
52 char rel_error[] = "illegal relative address";
53 char siz_error[] = "bad size specified";
54 char undef_error[] = "undefined expression";
55 char fwd_error[] = "forward or undefined expression";
56
57 extern int ea0gen(WORD);
58 extern int ea1gen(WORD);
59
60 // Include code tables
61 MNTAB machtab[] = {
62 //   { (WORD)-1, (unsigned long)-1L, (unsigned long)-1L, 0x0000, 0, m_badmode }, // 0
63    { 0xFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000, 0, m_badmode }, // 0
64    #include "68ktab.h"
65    {  0,  0L,  0L, 0x0000, 0, m_unimp   }            // Last entry
66 };
67
68 // Register number << 9
69 WORD reg_9[8] = {
70         0, 1 << 9, 2 << 9, 3 << 9, 4 << 9, 5 << 9, 6 << 9, 7 << 9
71 };
72
73 // SIZB==>00, SIZW==>01, SIZL==>10, SIZN==>01 << 6
74 WORD siz_6[] = {
75         (WORD)-1,                                        // n/a
76         0,                                               // SIZB
77         1<<6, (WORD)-1,                                  // SIZW, n/a
78         2<<6, (WORD)-1, (WORD)-1, (WORD)-1,              // SIZL, n/a, n/a, n/a
79         1<<6                                             // SIZN
80 };
81
82 // Byte/word/long size for MOVE instrs
83 WORD siz_12[] = {
84    (WORD)-1,
85    0x1000,                                           // Byte
86    0x3000, (WORD)-1,                                 // Word
87    0x2000, (WORD)-1, (WORD)-1, (WORD)-1,             // Long
88    0x3000                                            // Word (SIZN)
89 };
90
91 // Word/long size (0=.w, 1=.l) in bit 8
92 WORD lwsiz_8[] = {
93    (WORD)-1,                                         // n/a
94    (WORD)-1,                                         // SIZB
95    0, (WORD)-1,                                      // SIZW, n/a
96    1<<8, (WORD)-1, (WORD)-1, (WORD)-1,               // SIZL, n/a, n/a, n/a
97    0                                                 // SIZN
98 };
99
100 // Addressing mode in bits 6..11 (register/mode fields are reversed)
101 WORD am_6[] = {
102    00000, 01000, 02000, 03000, 04000, 05000, 06000, 07000,
103    00100, 01100, 02100, 03100, 04100, 05100, 06100, 07100,
104    00200, 01200, 02200, 03200, 04200, 05200, 06200, 07200,
105    00300, 01300, 02300, 03300, 04300, 05300, 06300, 07300,
106    00400, 01400, 02400, 03400, 04400, 05400, 06400, 07400,
107    00500, 01500, 02500, 03500, 04500, 05500, 06500, 07500,
108    00600, 01600, 02600, 03600, 04600, 05600, 06600, 07600,
109    00700, 01700, 02700, 03700, 04700, 05700, 06700, 07700
110 };
111
112
113 // Error messages
114 int m_unimp(WORD unused1, WORD unused2)
115 {
116         return (int)error("unimplemented mnemonic");
117 }
118
119
120 //int m_badmode(void)
121 int m_badmode(WORD unused1, WORD unused2)
122 {
123         return (int)error("inappropriate addressing mode");
124 }
125
126
127 int m_self(WORD inst, WORD usused)
128 {
129         D_word(inst);
130         return 0;
131 }
132
133
134 //
135 // Do one EA in bits 0..5
136 //
137 // Bits in `inst' have the following meaning:
138 //
139 // Bit zero specifies which ea (ea0 or ea1) to generate in the lower six bits
140 // of the instr.
141 //
142 // If bit one is set, the OTHER ea (the one that wasn't generated by bit zero)
143 // is generated after the instruction. Regardless of bit 0's value, ea0 is
144 // always deposited in memory before ea1.
145 //
146 // If bit two is set, standard size bits are set in the instr in bits 6 and 7.
147 //
148 // If bit four is set, bit three specifies which eaXreg to place in bits 9..11
149 // of the instr.
150 //
151 int m_ea(WORD inst, WORD siz)
152 {
153         WORD flg = inst;                                        // Save flag bits
154         inst &= ~0x3F;                                          // Clobber flag bits in instr
155
156         // Install "standard" instr size bits
157         if (flg & 4)
158                 inst |= siz_6[siz];
159
160         if (flg & 16)
161         {
162                 // OR-in register number
163                 if (flg & 8)
164                         inst |= reg_9[a1reg];           // ea1reg in bits 9..11
165                 else
166                         inst |= reg_9[a0reg];           // ea0reg in bits 9..11
167         }
168
169         if (flg & 1)
170         {
171                 // Use am1
172                 inst |= am1 | a1reg;                    // Get ea1 into instr
173                 D_word(inst);                                   // Deposit instr
174
175                 // Generate ea0 if requested
176                 if (flg & 2)
177                         ea0gen(siz);
178
179                 ea1gen(siz);                                    // Generate ea1
180         }
181         else
182         {
183                 // Use am0
184                 inst |= am0 | a0reg;                    // Get ea0 into instr
185                 D_word(inst);                                   // Deposit instr
186                 ea0gen(siz);                                    // Generate ea0
187
188                 // Generate ea1 if requested
189                 if (flg & 2)
190                         ea1gen(siz);
191         }
192
193         return 0;
194 }
195
196
197 //
198 // Dx,Dy nnnnXXXnssnnnYYY If bit 0 of `inst' is set, install size bits in bits
199 // 6..7
200 //
201 int m_abcd(WORD inst, WORD siz)
202 {
203         if (inst & 1)
204         {
205                 // Install size bits
206                 inst--;
207                 inst |= siz_6[siz];
208         }
209
210         inst |= a0reg | reg_9[a1reg];
211         D_word(inst);
212
213         return 0;
214 }
215
216
217 //
218 // {adda} ea,AREG
219 //
220 int m_adda(WORD inst, WORD siz)
221 {
222         inst |= am0 | a0reg | lwsiz_8[siz] | reg_9[a1reg];
223         D_word(inst);
224         ea0gen(siz);    // Generate EA
225
226         return 0;
227 }
228
229
230 //
231 // If bit 0 of `inst' is 1, install size bits in bits 6..7 of instr.
232 // If bit 1 of `inst' is 1, install a1reg in bits 9..11 of instr.
233 //
234 int m_reg(WORD inst, WORD siz)
235 {
236         if (inst & 1)
237                 // Install size bits
238                 inst |= siz_6[siz];
239
240         if (inst & 2)
241                 // Install other register (9..11)
242                 inst |= reg_9[a1reg];
243
244         inst &= ~7;                     // Clear off crufty bits
245         inst |= a0reg;          // Install first register
246         D_word(inst);
247
248         return 0;
249 }
250
251
252 //
253 // <op> #expr
254 //
255 int m_imm(WORD inst, WORD siz)
256 {
257         D_word(inst);
258         ea0gen(siz);
259
260         return 0;
261 }
262
263
264 //
265 // <op>.b #expr
266 //
267 int m_imm8(WORD inst, WORD siz)
268 {
269         siz = siz;
270         D_word(inst);
271         ea0gen(SIZB);
272
273         return 0;
274 }
275
276
277 //
278 // <shift> Dn,Dn
279 //
280 int m_shr(WORD inst, WORD siz)
281 {
282         inst |= reg_9[a0reg] | a1reg | siz_6[siz];
283         D_word(inst);
284
285         return 0;
286 }
287
288
289 //
290 // <shift> #n,Dn
291 //
292 int m_shi(WORD inst, WORD siz)
293 {
294         inst |= a1reg | siz_6[siz];
295
296         if (a0exattr & DEFINED)
297         {
298                 if (a0exval > 8)
299                         return error(range_error);
300
301                 inst |= (a0exval & 7) << 9;
302                 D_word(inst);
303         }
304         else
305         {
306                 AddFixup(FU_QUICK, sloc, a0expr);
307                 D_word(inst);
308         }
309
310         return 0;
311 }
312
313
314 //
315 // {bset, btst, bchg, bclr} -- #immed,ea -- Dn,ea
316 //
317 int m_bitop(WORD inst, WORD siz)
318 {
319         // Enforce instruction sizes
320         if (am1 == DREG)
321         {                               // X,Dn must be .n or .l
322                 if (siz & (SIZB | SIZW))
323                         return error(siz_error);
324         }
325         else if (siz & (SIZW | SIZL))   // X,ea must be .n or .b
326                 return error(siz_error);
327
328         // Construct instr and EAs
329         inst |= am1 | a1reg;
330
331         if (am0 == IMMED)
332         {
333                 D_word(inst);
334                 ea0gen(SIZB);                           // Immediate bit number
335         }
336         else
337         {
338                 inst |= reg_9[a0reg];
339                 D_word(inst);
340         }
341
342         // ea to bit-munch
343         ea1gen(SIZB);
344
345         return 0;
346 }
347
348
349 int m_dbra(WORD inst, WORD siz)
350 {
351         VALUE v;
352
353         siz = siz;
354         inst |= a0reg;
355         D_word(inst);
356
357         if (a1exattr & DEFINED)
358         {
359                 if ((a1exattr & TDB) != cursect)
360                         return error(rel_error);
361
362                 v = a1exval - sloc;
363
364                 if (v + 0x8000 > 0x10000)
365                         return error(range_error);
366
367                 D_word(v);
368         }
369         else
370         {
371                 AddFixup(FU_WORD | FU_PCREL | FU_ISBRA, sloc, a1expr);
372                 D_word(0);
373         }
374
375         return 0;
376 }
377
378
379 //
380 // EXG
381 //
382 int m_exg(WORD inst, WORD siz)
383 {
384         int m;
385
386         siz = siz;
387
388         if (am0 == DREG && am1 == DREG)
389                 m = 0x0040;                                           // Dn,Dn
390         else if (am0 == AREG && am1 == AREG)
391                 m = 0x0048;                                           // An,An
392         else
393         {
394                 if (am0 == AREG)
395                 {                                     // Dn,An or An,Dn
396                         m = a1reg;                                         // Get AREG into a1reg
397                         a1reg = a0reg;
398                         a0reg = m;
399                 }
400
401                 m = 0x0088;
402         }
403
404         inst |= m | reg_9[a0reg] | a1reg;
405         D_word(inst);
406
407         return 0;
408 }
409
410
411 //
412 // LINK
413 //
414 int m_link(WORD inst, WORD siz)
415 {
416         siz = siz;
417         inst |= a0reg;
418         D_word(inst);
419         ea1gen(SIZW);
420
421         return 0;
422 }
423
424
425 //
426 // Handle MOVE <C_ALL> <C_ALTDATA>
427 //        MOVE <C_ALL> <M_AREG>
428 //
429 // Optimize MOVE.L #<smalldata>,D0 to a MOVEQ
430 //
431 int m_move(WORD inst, WORD size)
432 {
433         // Cast the passed in value to an int
434         int siz = (int)size;
435
436         // Try to optimize to MOVEQ
437         if (optim_flags[OPT_MOVEL_MOVEQ] && siz == SIZL && am0 == IMMED && am1 == DREG
438                 && (a0exattr & (TDB|DEFINED)) == DEFINED && a0exval + 0x80 < 0x100)
439         {
440                 m_moveq((WORD)0x7000, (WORD)0);
441
442                 if (sbra_flag)
443                         warn("move.l #size,dx converted to moveq");
444         }
445         else
446         {
447                 inst |= siz_12[siz] | am_6[am1] | reg_9[a1reg] | am0 | a0reg;
448
449                 D_word(inst);
450
451                 if (am0 >= ADISP)
452                         ea0gen((WORD)siz);
453
454                 if (am1 >= ADISP)
455                         ea1gen((WORD)siz | 0x8000);   // Tell ea1gen we're move ea,ea
456         }
457
458         return 0;
459 }
460
461
462 //
463 // move USP,An -- move An,USP
464 //
465 int m_usp(WORD inst, WORD siz)
466 {
467         siz = siz;
468
469         if (am0 == AM_USP)
470                 inst |= a1reg;          // USP, An
471         else
472                 inst |= a0reg;          // An, USP
473
474         D_word(inst);
475
476         return 0;
477 }
478
479
480 //
481 // moveq
482 //
483 int m_moveq(WORD inst, WORD siz)
484 {
485         siz = siz;
486
487         // Arrange for future fixup
488         if (!(a0exattr & DEFINED))
489         {
490                 AddFixup(FU_BYTE | FU_SEXT, sloc + 1, a0expr);
491                 a0exval = 0;
492         }
493         else if (a0exval + 0x100 >= 0x200)
494                 return error(range_error);
495
496         inst |= reg_9[a1reg] | (a0exval & 0xFF);
497         D_word(inst);
498
499         return 0;
500 }
501
502 int movep = 0;          // Global flag to indicate we're generating a movep instruction
503 //
504 // movep Dn, disp(An) -- movep disp(An), Dn
505 //
506 int m_movep(WORD inst, WORD siz)
507 {
508     movep = 1;          // Tell ea0gen to lay off the 0(a0) optimisations on this one
509         if (siz == SIZL)
510                 inst |= 0x0040;
511
512         if (am0 == DREG)
513         {
514                 inst |= reg_9[a0reg] | a1reg;
515                 D_word(inst);
516
517                 if (am1 == AIND)
518                         D_word(0)
519                 else
520                         ea1gen(siz);
521         }
522         else
523         {
524                 inst |= reg_9[a1reg] | a0reg;
525                 D_word(inst);
526
527                 if (am0 == AIND)
528                         D_word(0)
529                 else
530                         ea0gen(siz);
531         }
532
533     movep = 0;
534         return 0;
535 }
536
537
538 //
539 // Bcc -- BSR
540 //
541 int m_br(WORD inst, WORD siz)
542 {
543         VALUE v;
544
545         if (a0exattr & DEFINED)
546         {
547                 if ((a0exattr & TDB) != cursect)
548 //{
549 //printf("m_br(): a0exattr = %X, cursect = %X, a0exval = %X, sloc = %X\n", a0exattr, cursect, a0exval, sloc);
550                         return error(rel_error);
551 //}
552
553                 v = a0exval - (sloc + 2);
554
555                 // Optimize branch instr. size
556                 if (siz == SIZN)
557                 {
558                         if (optim_flags[OPT_BSR_BCC_S] && v != 0 && v + 0x80 < 0x100)
559                         {
560                                 // Fits in .B
561                                 inst |= v & 0xFF;
562                                 D_word(inst);
563                                 if (sbra_flag)
564                                         warn("Bcc.w/BSR.w converted to .s");
565                                 return 0;
566                         }
567                         else
568                         {
569                                 // Fits in .W
570                                 if (v + 0x8000 > 0x10000)
571                                         return error(range_error);
572
573                                 D_word(inst);
574                                 D_word(v);
575                                 return 0;
576                         }
577                 }
578
579                 if (siz == SIZB)
580                 {
581                         if (v + 0x80 >= 0x100)
582                                 return error(range_error);
583
584                         inst |= v & 0xFF;
585                         D_word(inst);
586                 }
587                 else
588                 {
589                         if (v + 0x8000 >= 0x10000)
590                                 return error(range_error);
591
592                         D_word(inst);
593                         D_word(v);
594                 }
595
596                 return 0;
597         }
598         else if (siz == SIZN)
599                 siz = SIZW;
600
601         if (siz == SIZB)
602         {
603                 // .B
604                 AddFixup(FU_BBRA | FU_PCREL | FU_SEXT, sloc, a0expr);
605                 D_word(inst);
606                 return 0;
607         }
608         else
609         {
610                 // .W
611                 D_word(inst);
612                 AddFixup(FU_WORD | FU_PCREL | FU_LBRA | FU_ISBRA, sloc, a0expr);
613                 D_word(0);
614         }
615
616         return 0;
617 }
618
619
620 //
621 // ADDQ -- SUBQ
622 //
623 int m_addq(WORD inst, WORD siz)
624 {
625         inst |= siz_6[siz] | am1 | a1reg;
626
627         if (a0exattr & DEFINED)
628         {
629                 if (a0exval > 8 ||      a0exval == 0)                       // Range in 1..8
630                         return error(range_error);
631
632                 inst |= (a0exval & 7) << 9;
633                 D_word(inst);
634         }
635         else
636         {
637                 AddFixup(FU_QUICK, sloc, a0expr);
638                 D_word(inst);
639         }
640
641         ea1gen(siz);
642
643         return 0;
644 }
645
646
647 //
648 // trap #n
649 //
650 int m_trap(WORD inst, WORD siz)
651 {
652         siz = siz;
653
654         if (a0exattr & DEFINED)
655         {
656                 if (a0exattr & TDB)
657                         return error(abs_error);
658
659                 if (a0exval >= 16)
660                         return error(range_error);
661
662                 inst |= a0exval;
663                 D_word(inst);
664         }
665         else
666                 return error(undef_error);
667
668         return 0;
669 }
670
671
672 //
673 // movem <rlist>,ea -- movem ea,<rlist>
674 //
675 int m_movem(WORD inst, WORD siz)
676 {
677         VALUE eval;
678         WORD i;
679         WORD w;
680         WORD rmask;
681
682         if (siz & SIZB)
683                 return error("bad size suffix");
684
685         if (siz == SIZL)
686                 inst |= 0x0040;
687
688         if (*tok == '#')
689         {
690                 // Handle #<expr>, ea
691                 tok++;
692
693                 if (abs_expr(&eval) != OK)
694                         return 0;
695
696                 if (eval >= 0x10000L)
697                         return error(range_error);
698
699                 rmask = (WORD)eval;
700                 goto immed1;
701         }
702
703         if (*tok >= KW_D0 && *tok <= KW_A7)
704         {
705                 // <rlist>, ea
706                 if (reglist(&rmask) < 0)
707                         return 0;
708
709 immed1:
710                 if (*tok++ != ',')
711                         return error("missing comma");
712
713                 if (amode(0) < 0)
714                         return 0;
715
716                 inst |= am0 | a0reg;
717
718                 if (!(amsktab[am0] & (C_ALTCTRL | M_APREDEC)))
719                         return error("invalid addressing mode");
720
721                 // If APREDEC, reverse register mask
722                 if (am0 == APREDEC)
723                 {
724                         w = rmask;
725                         rmask = 0;
726
727                         for(i=0x8000; i; i>>=1, w>>=1)
728                                 rmask = (WORD)((rmask << 1) | w & 1);
729                 }
730         }
731         else
732         {
733                 // ea, <rlist>
734                 if (amode(0) < 0)
735                         return 0;
736
737                 inst |= 0x0400 | am0 | a0reg;
738
739                 if (*tok++ != ',')
740                         return error("missing comma");
741
742                 if (*tok == EOL)
743                         return error("missing register list");
744
745                 if (*tok == '#')
746                 {
747                         // ea, #<expr>
748                         tok++;
749
750                         if (abs_expr(&eval) != OK)
751                                 return 0;
752
753                         if (eval >= 0x10000)
754                                 return error(range_error);
755
756                         rmask = (WORD)eval;
757                 }
758                 else if (reglist(&rmask) < 0)
759                         return 0;
760
761                 if (!(amsktab[am0] & (C_CTRL | M_APOSTINC)))
762                         return error("invalid addressing mode");
763         }
764
765         D_word(inst);
766         D_word(rmask);
767         ea0gen(siz);
768
769         return 0;
770 }
771
772
773 //
774 // CLR.x An ==> SUBA.x An,An
775 //
776 int m_clra(WORD inst, WORD siz)
777 {
778         inst |= a0reg | reg_9[a0reg] | lwsiz_8[siz];
779         D_word(inst);
780
781         return 0;
782 }
783