%{
/*
This file is an almost complete machine description file of the MC68000
family for the lburg code generator generator.  What's missing can be
easily filled in for a specific MC68k assembler.  A change must be made to
config.h in order to use this file.  The Xnode struct's kids field must be
enlarged to 4.  A completed version of this file forms the basis for the
MPW version of the lcc compiler targeted for the Apple Macintosh.  The code
emitted by this specification is for the Apple MPW Assembler which has a
syntax similar to Motorola's assembler. A major difference between this
lburg specification and the examples in the lcc distribution is that switch
statement tables are stored in the code segment and are addressed by the pc
relative addressing mode.

Registers A2-A7,D3-D7,FP4-FP7 are preserved across function calls.
Registers D0 and FP0 store function results.  Parameters are passed from
right to left on the stack.  All floating point values are first promoted
to Motorola extended format before being pushed on the stack.

Here are some benchmark results of some compilers available for the Apple
Macintosh.  The programs are similar to those found in the Hennessy suite.
MPW C is the Apple C compiler. Gcc is the MPW port of gcc version
1.37.1r15. SC is the Symantec C compiler (ex-Zortech with m68k backend).
SCG is the Symantec C compiler with all optimizations on.  This compiler
does global optimizations.  LCC is the lcc compiler produced with this
machine description file.  MW is the Metrowerks 68k C compiler from Code
Warrior.  All tests were run on a Macintosh Centris 650 with MC68040 @25MHz
using 020 addressing modes and FPU.

        MPW C   gcc     gcc -O  SC      SCG     LCC     MW opt
Perm     13     13       11     17      19      12        22
Towers   12     15       10     17      16      12        11
Queens    6      7        5     7        8       8         4
Intmm    19     18       16     18      11      19        17
Mm       17     19       16     18      10      20        18
Puzzle   68     86       48     74      35      74        47
Quick     8     10        5     13      11       8        14
Bubble   11     16        7     19      10      12        11
FFT      25     28       16     24      20      25        19

Hopefully this file will prove useful to those wishing to target lcc to
Atari's, Amiga's, Sun's and Next's.  Let me know if you retarget lcc to any
other m68k machines using this file.
Please report any bugs to:

Woodrow Yeung
yeung@reed.edu

*/
/*
Changed FCMP.L FPm.FPx to FCMP
        FBcc.W to FBcc
l359:   reg: LOADD(reg)        "\tFMOVE.X %0,%c\n"   move(a) FROM FMOVE.D
        reg: LOADF(reg)        "\tFMOVE.X %0,%c\n"   move(a) FROM FMOVE.D
*/

enum { D0, D1, D2, D3, D4, D5, D6, D7 };
enum { A0, A1, A2, A3, A4, A5, A6, A7 };
enum { FP0, FP1, FP2, FP3, FP4, FP5, FP6, FP7 };
#if 0
#define realparamsize 12
#endif
#ifndef AMIGA
#include "c.h"
#include <ctype.h>
#else
/* Work around some incompatibilities between system/lcc header files and
   <exec/types.h> */
#define SHORT	_SHORT
#define LONG	_LONG
#define FLOAT	_FLOAT
#define DOUBLE	_DOUBLE
#include "c.h"
#undef SHORT
#undef LONG
#undef FLOAT
#undef DOUBLE
#include <ctype.h>
#ifdef BITSPERBYTE
#undef BITSPERBYTE
#endif
#ifdef MAXINT
#undef MAXINT
#endif
#ifdef MININT
#undef MININT
#endif
#ifdef LONGBITS
#undef LONGBITS
#endif
#include <dos/dos.h>
#ifdef __GNUC__
#include <proto/dos.h>
#else
#include <clib/dos_protos.h>
#endif
#undef STATIC
#undef GLOBAL
#endif
#define NODEPTR_TYPE Node
#define OP_LABEL(p) ((p)->op)
#define LEFT_CHILD(p) ((p)->kids[0])
#define RIGHT_CHILD(p) ((p)->kids[1])
#define STATE_LABEL(p) ((p)->x.state)
#define FILEPART(x) (strrchr((x),'/') ? strrchr((x),'/')+1 : \
		    (strrchr((x),':') ? strrchr((x),':')+1 : (x)))
static void address ARGS((Symbol, Symbol, int));
static void blkfetch    ARGS((int, int, int, int));
static void blkloop ARGS((int, int, int, int, int, int[]));
static void blkstore    ARGS((int, int, int, int));
static void defaddress  ARGS((Symbol));
static void defconst    ARGS((int, Value));
static void defstring   ARGS((int, char *));
static void defsymbol   ARGS((Symbol));
static void doarg   ARGS((Node));
static void emit2   ARGS((Node));
static void export  ARGS((Symbol));
static void clobber ARGS((Node));
static void function    ARGS((Symbol, Symbol [], Symbol [], int));
static void global  ARGS((Symbol));
static void import  ARGS((Symbol));
static void local   ARGS((Symbol));
static void progbeg ARGS((int, char **));
static void progend ARGS((void));
static void segment ARGS((int));
static void space   ARGS((int));
static void target  ARGS((Node));

static int sametree ARGS((Node, Node));
static int memop ARGS((Node));

static void symname ARGS((Symbol p));
static void stabend ARGS((Coordinate *, Symbol, Coordinate **, Symbol *, Symbol *));
static void stabline ARGS((Coordinate *));

static Symbol ireg[32], *dreg = &ireg[0], areg[32], freg[32];
 
static int cseg;
 
%}
%start stmt
%term ADDD=306 ADDF=305 ADDI=309 ADDP=311 ADDU=310 ADDC=307 ADDS=308
%term ADDRFP=279
%term ADDRGP=263
%term ADDRLP=295
%term ARGB=41 ARGD=34 ARGF=33 ARGI=37 ARGP=39
%term ASGNB=57 ASGNC=51 ASGND=50 ASGNF=49 ASGNI=53 ASGNP=55 ASGNS=52 ASGNU=54
%term BANDU=390
%term BCOMU=406
%term BORU=422
%term BXORU=438
%term CALLB=217 CALLD=210 CALLF=209 CALLI=213 CALLV=216
%term CNSTC=19 CNSTD=18 CNSTF=17 CNSTI=21 CNSTP=23 CNSTS=20 CNSTU=22
%term CVCI=85 CVCU=86
%term CVDF=97 CVDI=101
%term CVFD=114
%term CVIC=131 CVID=130 CVIS=132 CVIU=134
%term CVPU=150
%term CVSI=165 CVSU=166
%term CVUC=179 CVUI=181 CVUP=183 CVUS=180
%term DIVD=450 DIVF=449 DIVI=453 DIVU=454
%term EQD=482 EQF=481 EQI=485
%term GED=498 GEF=497 GEI=501 GEU=502
%term GTD=514 GTF=513 GTI=517 GTU=518
%term INDIRB=73 INDIRC=67 INDIRD=66 INDIRF=65 INDIRI=69 INDIRP=71 INDIRS=68
%term JUMPV=584
%term LABELV=600
%term LED=530 LEF=529 LEI=533 LEU=534
%term LOADB=233 LOADC=227 LOADD=226 LOADF=225 LOADI=229 LOADP=231 LOADS=228 LOADU=230
%term LSHI=341 LSHU=342
%term LTD=546 LTF=545 LTI=549 LTU=550
%term MODI=357 MODU=358
%term MULD=466 MULF=465 MULI=469 MULU=470
%term NED=562 NEF=561 NEI=565
%term NEGD=194 NEGF=193 NEGI=197
%term RETD=242 RETF=241 RETI=245
%term RSHI=373 RSHU=374
%term SUBD=322 SUBF=321 SUBI=325 SUBP=327 SUBU=326 SUBC=323 SUBS=324
%term VREGP=615
%%
reg:  INDIRC(VREGP) "# read register\n"
reg:  INDIRD(VREGP) "# read register\n"
reg:  INDIRF(VREGP) "# read register\n"
reg:  INDIRI(VREGP) "# read register\n"
reg:  INDIRP(VREGP) "# read register\n"
reg:  INDIRS(VREGP) "# read register\n"
stmt: ASGNC(VREGP,reg)  "# write register\n"
stmt: ASGND(VREGP,reg)  "# write register\n"
stmt: ASGNF(VREGP,reg)  "# write register\n"
stmt: ASGNI(VREGP,reg)  "# write register\n"
stmt: ASGND(VREGP,reg)  "# write register\n"
stmt: ASGNP(VREGP,reg)  "# write register\n"
stmt: ASGNS(VREGP,reg)  "# write register\n"
con: CNSTC  "%#%a"
con: CNSTI  "%#%a"
con: CNSTP  "%#%a"
con: CNSTS  "%#%a"
con: CNSTU  "%#%a"
stmt: reg  ""
reg: CVIU(reg)  "%0"  notarget(a)
reg: CVUI(reg)  "%0"  notarget(a)
 
disp16: CNSTI   "%a"     range(a, -32768, 32767)
disp8:  CNSTI   "%a"     range(a, -128, 127)
 
base:  ADDP(reg,disp16) "%1(%0"
base:  ADDRFP   "%a(A5"  range(a, -32768, 32767)
base:  ADDRLP   "%a(A5"  range(a, -32768, 32767)
base:  basei    "%0"
basei: reg      "0(%0"
basei: ADDP(reg,disp8)   "%1(%0"
basei: ADDRFP   "%a(A5"  range(a, -128, 127)
basei: ADDRLP   "%a(A5"  range(a, -128, 127)
basei: ADDRFP   "0(%a,A5"  1
basei: ADDRLP   "0(%a,A5"  1

addr:  ADDRGP   "%a"
acon:  ADDRGP   "%a"
table: ADDRGP   "\tLEA %a,%c\n"

index: reg "%0"
index: LSHI(reg,scf)   "%0*%1"

addr: base    "%0)"
 
addr: ADDP(index,basei) "%1,%0)"
case: ADDP(index,table) "0(%1,%0)"
 
avar: acon     "%0"
avar: reg      "(%0)"  1
avar: addr     "%0"    2
 
con0:  CNSTI "%#0"   range(a, 0, 0)
con0:  CNSTP "%#0"   range(a, 0, 0)
con0:  CNSTU "%#0"   range(a, 0, 0)
con3:  CNSTI "%#%a"  range(a, 1, 8)
con3:  CNSTP "%#%a"  range(a, 1, 8)

scf:  CNSTI "2"     range(a, 1, 1)
scf:  CNSTI "4"     range(a, 2, 2)
scf:  CNSTI "8"     range(a, 3, 3)
 
mem: INDIRI(addr)  "%0"
mem: INDIRP(addr)  "%0"
 
pcrel: INDIRP(case) "\tMOVE.L %0,%c\n"
 
ri:   reg  "%0"
ri:   con  "%0"
ri3:  con3 "%0"
ri3:  reg  "%0"
 
ea0: mem  "%0"
ea0: ri   "%0"
ea1: mem  "%0"  1
ea1: ri   "%0"
ea3: mem  "%0"  3
ea3: ri   "%0"
 
reg:  addr    "\tLEA %0,%c\n"    1
reg:  ri      "\tMOVE.L %0,%c\n" 1
reg: INDIRC(addr)  "\tMOVE.B %0,%c\n"  1
reg: INDIRI(addr)  "\tMOVE.L %0,%c\n"  1
reg: INDIRP(addr)  "\tMOVE.L %0,%c\n"  1
reg: INDIRS(addr)  "\tMOVE.W %0,%c\n"  1
 
reg:  LOADC(reg)  "\tMOVE.L %0,%c\n"  move(a)
reg:  LOADI(reg)  "\tMOVE.L %0,%c\n"  move(a)
reg:  LOADP(reg)  "\tMOVE.L %0,%c\n"  move(a)
reg:  LOADS(reg)  "\tMOVE.L %0,%c\n"  move(a)
reg:  LOADU(reg)  "\tMOVE.L %0,%c\n"  move(a)
 
reg:  ADDI(reg,con3)  "?\tMOVE.L %0,%c\n\tADDQ.L %1,%c\n"
reg:  SUBI(reg,con3)  "?\tMOVE.L %0,%c\n\tSUBQ.L %1,%c\n"
 
reg:  ADDI(reg,ea1)  "?\tMOVE.L %0,%c\n\tADD.L %1,%c\n"  1
reg:  ADDP(reg,ea1)  "?\tMOVE.L %0,%c\n\tADDA.L %1,%c\n" 1
reg:  ADDU(reg,ea1)  "?\tMOVE.L %0,%c\n\tADD.L %1,%c\n"  1
reg:  SUBI(reg,ea1)  "?\tMOVE.L %0,%c\n\tSUB.L %1,%c\n"  1
reg:  SUBP(reg,ea1)  "?\tMOVE.L %0,%c\n\tSUBA.L %1,%c\n" 1
reg:  SUBU(reg,ea1)  "?\tMOVE.L %0,%c\n\tSUB.L %1,%c\n"  1
 
stmt: ASGNI(addr,ADDI(mem,ri))  "\tADD.L %2,%1\n"  memop(a)
stmt: ASGNI(addr,ADDU(mem,ri))  "\tADD.L %2,%1\n"  memop(a)
stmt: ASGNI(addr,SUBI(mem,ri))  "\tSUB.L %2,%1\n"  memop(a)
stmt: ASGNI(addr,SUBU(mem,ri))  "\tSUB.L %2,%1\n"  memop(a)
 
reg: BANDU(reg,ea1)  "?\tMOVE.L %0,%c\n\tAND.L %1,%c\n"  1
reg: BORU(reg,ea1)   "?\tMOVE.L %0,%c\n\tOR.L %1,%c\n"   1
reg: BXORU(reg,reg)  "?\tMOVE.L %0,%c\n\tEOR.L %1,%c\n"  1
 
stmt: ASGNI(addr,BANDU(mem,ri))  "\tAND.L %2,%1\n"   memop(a)
stmt: ASGNI(addr,BORU(mem,ri))   "\tOR.L %2,%1\n"    memop(a)
stmt: ASGNI(addr,BXORU(mem,reg))  "\tEOR.L %2,%1\n"   memop(a)
 
reg: BCOMU(reg)  "?\tMOVE.L %0,%c\n\tNOT.L %c\n"  1
reg: NEGI(reg)   "?\tMOVE.L %0,%c\n\tNEG.L %c\n"  1
  
stmt: ASGNI(addr,BCOMU(mem))  "\tNOT.L %1\n"  memop(a)
stmt: ASGNI(addr,NEGI(mem))   "\tNEG.L %1\n"  memop(a)
 
reg: LSHI(reg,ri3)  "?\tMOVE.L %0,%c\n\tASL.L %1,%c\n"  1
reg: LSHU(reg,ri3)  "?\tMOVE.L %0,%c\n\tLSL.L %1,%c\n"  1
reg: RSHI(reg,ri3)  "?\tMOVE.L %0,%c\n\tASR.L %1,%c\n"  1
reg: RSHU(reg,ri3)  "?\tMOVE.L %0,%c\n\tLSR.L %1,%c\n"  1
 
reg: MULI(reg,ea3)  "?\tMOVE.L %0,%c\n\tMULS.L %1,%c\n"   12
reg: MULU(reg,ea3)  "?\tMOVE.L %0,%c\n\tMULU.L %1,%c\n"   12
 
reg: DIVU(reg,ea3)  "?\tMOVE.L %0,%c\n\tDIVU.L %1,%c\n"
reg: DIVI(reg,ea3)  "?\tMOVE.L %0,%c\n\tDIVS.L %1,%c\n"
reg: MODU(reg,ea3)  "\tMOVE.L %0,D1\n\tCLR.L %c\n\tDIVU.L %1,%c:D1\n"
reg: MODI(reg,reg)  "#\tCLR.L %c\n\tDIVS.L %1,%0\n"
 
reg: CVCI(reg)  "\tMOVE.B %0,%c\n\tEXT.W %c\n\tEXT.L %c\n" 2
reg: CVCU(reg)  "\tMOVE.B %0,%c\n\tAND.L #0xFF,%0\n"        3
reg: CVSI(reg)  "\tMOVE.W %0,%c\n\tEXT.L %c\n"             1
reg: CVSU(reg)  "\tMOVE.W %0,%c\n\tAND.L #0xFFFF,%0\n"      3
reg: CVCI(INDIRC(addr))  "\tMOVE.B %0,%c\n\tEXT.W %c\n\tEXT.L %c\n" 2
reg: CVCU(INDIRC(addr))  "\tMOVE.B %0,%c\n\tANDI.B #0x00FF,%c\n"    2
reg: CVSI(INDIRS(addr))  "\tMOVE.W %0,%c\n\tEXT.L %c\n"          2
reg: CVSU(INDIRS(addr))  "\tMOVE.W %0,%c\n\tANDI.W #0xFFFF,%c\n"  2
reg: CVIC(reg)  "%0"  notarget(a)
reg: CVIS(reg)  "%0"  notarget(a)
reg: CVUC(reg)  "%0"  notarget(a)
reg: CVUS(reg)  "%0"  notarget(a)
reg: CVIC(reg)  "\tMOVE.L %0,%c\n"  move(a)
reg: CVIS(reg)  "\tMOVE.L %0,%c\n"  move(a)
reg: CVIU(reg)  "\tMOVE.L %0,%c\n"  move(a)
reg: CVPU(reg)  "\tMOVE.L %0,%c\n"
reg: CVUC(reg)  "\tMOVE.L %0,%c\n"  move(a)
reg: CVUI(reg)  "\tMOVE.L %0,%c\n"  move(a)
reg: CVUP(reg)  "\tMOVE.L %0,%c\n"
reg: CVUS(reg)  "\tMOVE.L %0,%c\n"  move(a)
 
reg: CVDF(reg)  "%0"  notarget(a)
reg: CVFD(reg)  "%0"  notarget(a)
reg: CVDF(reg)  "\tFMOVE.d %0,%c\n" move(a)
reg: CVFD(reg)  "\tFMOVE.d %0,%c\n" move(a)
reg: CVID(ea0)  "\tFMOVE.L %0,%c\n"
reg: CVDI(reg)  "\tFMOVE.L %0,%c\n"
 
stmt: ASGNC(addr,ea1)  "\tMOVE.B %1,%0\n"    1
stmt: ASGNC(addr,con0)  "\tCLR.L %0\n"
stmt: ASGNI(addr,ea1)  "\tMOVE.L %1,%0\n"    1
stmt: ASGNI(addr,con0)  "\tCLR.L %0\n"
stmt: ASGNI(addr,CVDI(reg))  "\tFMOVE.L %1,%0\n"
stmt: ASGNP(addr,ea1)  "\tMOVE.L %1,%0\n"    1
stmt: ASGNP(addr,con0)  "\tCLR.L %0\n"
stmt: ASGNS(addr,ea1)  "\tMOVE.W %1,%0\n"    1
stmt: ASGNS(addr,con0)  "\tCLR.W %0\n"
 
stmt: ARGI(ea0)   "\tMOVE.L %0,-(SP)\n"  1
stmt: ARGI(addr)  "\tPEA.L %0\n"         1
stmt: ARGP(ea0)   "\tMOVE.L %0,-(SP)\n"  1
stmt: ARGP(addr)  "\tPEA.L %0\n"         1
stmt: ASGNB(reg, INDIRB(reg)) "# MOVE (src)+,(dst)+\n" 2
stmt: ARGB(INDIRB(reg))  "# Copy to stack\n" 2
 
reg: LOADD(reg)        "\tFMOVE.X %0,%c\n"   move(a)
reg: LOADF(reg)        "\tFMOVE.X %0,%c\n"   move(a)
 
mem: INDIRD(addr)      "%0"
mem: INDIRF(addr)      "%0"
reg: INDIRD(addr)      "\tFMOVE.D %0,%c\n"   3
reg: INDIRF(addr)      "\tFMOVE.S %0,%c\n"   3
 
stmt: ASGND(addr,reg)  "\tFMOVE.D %1,%0\n"  4
stmt: ASGND(addr,mem)  "\tLEA.L %1,A0\n\tLEA.L %0,A1\n\tMOVE.L (A0)+,(A1)+\n\tMOVE.L (A0),(A1)\n"  3
stmt: ASGNF(addr,reg)  "\tFMOVE.S %1,%0\n"  4
stmt: ASGNF(addr,mem)  "\tMOVE.L %1,%0\n"   1
stmt: ARGD(reg)        "\tFMOVE.D %0,-(SP)\n" 3
stmt: ARGF(reg)        "\tFMOVE.D %0,-(SP)\n" 3
 
reg: ADDD(reg,reg)  "?\tFMOVE.x %0,%c\n\tFADD.X %1,%c\n"
reg: ADDF(reg,reg)  "?\tFMOVE.x %0,%c\n\tFADD.X %1,%c\n"
reg: SUBD(reg,reg)  "?\tFMOVE.x %0,%c\n\tFSUB.X %1,%c\n"
reg: SUBF(reg,reg)  "?\tFMOVE.x %0,%c\n\tFSUB.X %1,%c\n"
reg: MULD(reg,reg)  "?\tFMOVE.x %0,%c\n\tFMUL.X %1,%c\n"
reg: MULF(reg,reg)  "?\tFMOVE.x %0,%c\n\tFMUL.X %1,%c\n"
reg: DIVD(reg,reg)  "?\tFMOVE.x %0,%c\n\tFDIV.X %1,%c\n"
reg: DIVF(reg,reg)  "?\tFMOVE.x %0,%c\n\tFDIV.X %1,%c\n"
 
reg: NEGD(reg)  "\tFNEG.X %c\n"
reg: NEGF(reg)  "\tFNEG.X %c\n"
 
stmt:  JUMPV(acon)    "\tBRA.L %0\n"
stmt:  JUMPV(pcrel)   "\tJMP 0(%0)\n"
stmt:  LABELV         "%a:\n"
 
stmt: EQI(ea0,con0)  "\tTST.L %0\n\tBEQ.L %a\n"   2
stmt: NEI(ea0,con0)  "\tTST.L %0\n\tBNE.L %a\n"   2

stmt: EQI(reg,ea1)  "\tCMP.L %1,%0\n\tBEQ.L %a\n"   3
stmt: GEI(reg,ea1)  "\tCMP.L %1,%0\n\tBGE.L %a\n"   3
stmt: GTI(reg,ea1)  "\tCMP.L %1,%0\n\tBGT.L %a\n"   3
stmt: LEI(reg,ea1)  "\tCMP.L %1,%0\n\tBLE.L %a\n"   3
stmt: LTI(reg,ea1)  "\tCMP.L %1,%0\n\tBLT.L %a\n"   3
stmt: NEI(reg,ea1)  "\tCMP.L %1,%0\n\tBNE.L %a\n"   3
 
stmt: GEU(reg,ea1)  "\tCMP.L %1,%0\n\tBHS.L %a\n"   3
stmt: GTU(reg,ea1)  "\tCMP.L %1,%0\n\tBHI.L %a\n"   3
stmt: LEU(reg,ea1)  "\tCMP.L %1,%0\n\tBLS.L %a\n"   3
stmt: LTU(reg,ea1)  "\tCMP.L %1,%0\n\tBCS.L %a\n"   3
 
stmt: EQD(reg,reg)  "\tFCMP.X %1,%0\n\tFBEQ %a\n"
stmt: GED(reg,reg)  "\tFCMP.X %1,%0\n\tFBGE %a\n"
stmt: GTD(reg,reg)  "\tFCMP.X %1,%0\n\tFBGT %a\n"
stmt: LED(reg,reg)  "\tFCMP.X %1,%0\n\tFBLE %a\n"
stmt: LTD(reg,reg)  "\tFCMP.X %1,%0\n\tFBLT %a\n"
stmt: NED(reg,reg)  "\tFCMP.X %1,%0\n\tFBNE %a\n"
 
stmt: EQF(reg,reg)  "\tFCMP.X %1,%0\n\tFBEQ %a\n"
stmt: GEF(reg,reg)  "\tFCMP.X %1,%0\n\tFBGE %a\n"
stmt: GTF(reg,reg)  "\tFCMP.X %1,%0\n\tFBGT %a\n"
stmt: LEF(reg,reg)  "\tFCMP.X %1,%0\n\tFBLE %a\n"
stmt: LTF(reg,reg)  "\tFCMP.X %1,%0\n\tFBLT %a\n"
stmt: NEF(reg,reg)  "\tFCMP.X %1,%0\n\tFBNE %a\n"
 
reg:  CALLI(avar)   "\tJSR %0\n\tADDA.L %#%a,SP\n"
stmt: CALLV(avar)   "\tJSR %0\n\tADDA.L %#%a,SP\n"
reg:  CALLF(avar)   "\tJSR %0\n\tADDA.L %#%a,SP\n"
reg:  CALLD(avar)   "\tJSR %0\n\tADDA.L %#%a,SP\n"
 
stmt: RETI(reg)    "# RTS\n"
stmt: RETF(reg)    "# RTS\n"
stmt: RETD(reg)    "# RTS\n"
 
%%
static void progend() {
#ifdef IXEMUL
#else
  print( "\tend\n" ) ;
#endif
  }

static void segment(n) {
  switch( n ) {
#ifdef IXEMUL
    case CODE : print( ".text\n" ) ; break ; /* <- .code */
    case BSS  : print( ".bss\n"  ) ; break ;
    case DATA : print( ".data\n" ) ; break ; /* <- .data */
    case LIT  : print( ".text\n" ) ; break ; /* <- .lit  */
#else
    case CODE : print( "\tSECTION text,code\n" ) ; break ;
    case BSS  : print( "\tSECTION udata,bss\n"  ) ; break ;
    case DATA : print( "\tSECTION data,data\n" ) ; break ;
    case LIT  : print( "\tSECTION data,data\n"  ) ; break ;
#endif
    }
}

/* Mark nodes that must be evaluated into a specific register (p. 357)
 */
static void target(p) Node p; {
    assert(p);
    switch (p->op) {
    case ASGNB:
        rtarget(p, 1, areg[A0+8]); /* A0 Source */
        rtarget(p, 0, areg[A1+8]); /* A1 Destination */
        break;
    case ARGB:
            rtarget(p->kids[0], 0, areg[A0+8]); /* A0 Source */
        break;
    case CALLI: case CALLV:
            setreg(p, dreg[D0]);
        break;
    case CALLD: case CALLF:
            setreg(p, freg[FP0]);
            break;
    case RETI:
        rtarget(p, 0, ireg[D0]);
        break;
    case RETF: case RETD:
            rtarget(p, 0, freg[FP0]);
        break;
    }
}
 
/* spills to memory and later reloads all registers destroyed by a
 * given function (p. 357)
 */
static void clobber(p) Node p; {
    assert(p);
    switch (p->op) {
    case MULI: case MULU:
        break;
    case DIVI: case DIVU:
        break;
    case MODI: case MODU:
        spill(1<<D1, IREG, p);
        break;
    case ASGND:
        spill(1<<D0 | 1<<A0+8 | 1<<A1+8, IREG, p);
        break;
    case ASGNB: case ARGB:
        spill(1<<D0 | 1<<A0+8 | 1<<A1+8, IREG, p);
        break;
    case CALLI:
            /* save scratch registers but not return register */
        spill(1<<D1 | 1<<D2 | 1<<A0+8 | 1<<A1+8, IREG, p);
        spill(1<<FP0 | 1<<FP1 | 1<<FP2 | 1<<FP3, FREG, p);
        break;
    case CALLV:
            /* save scratch registers */
        spill(1<<D0 | 1<<D1 | 1<<D2 | 1<<A0+8 | 1<<A1+8, IREG, p);
        spill(1<<FP0 | 1<<FP1 | 1<<FP2 | 1<<FP3, FREG, p);
        break;
            break;
    case CALLD: case CALLF:
            /* save scratch registers but not return register */
        spill(1<<D0 | 1<<D1 | 1<<D2 | 1<<A0+8 | 1<<A1+8, IREG, p);
        spill(1<<FP1 | 1<<FP2 | 1<<FP3, FREG, p);
        break;
    }
}

/* confirms that the destination is the same as the first source operand
 * (p. 507)
 */
static int sametree(p, q) Node p, q; {
    return p == 0 && q == 0
    || p && q && p->op == q->op && p->syms[0] == q->syms[0]
        && sametree(p->kids[0], q->kids[0]) && sametree(p->kids[1], q->kids[1]);

}
 
/* confirms that the tree has the form ASGNa( x, b(INDIR(x), c)) (p. 507)
 */
static int memop(p) Node p; {
    assert(p);
    assert(generic(p->op) == ASGN);
    assert(p->kids[0]);
    assert(p->kids[1]);
    if (generic(p->kids[1]->kids[0]->op) == INDIR) {
        assert(p->kids[1]->kids[0]);
        return sametree(p->kids[0], p->kids[1]->kids[0]->kids[0]) ? 2 : SHRT_MAX;
    } else
        return SHRT_MAX;
}

/* Deals with instructions that cannot be handled with simple templates.
 * (p. 356)
 */
static void emit2(p) Node p; {
    switch(p->op) {
    case ASGNB: {
        int lab1 = genlabel(1), lab2 = genlabel(1);
        int src = getregnum(p->x.kids[1]), dst = getregnum(p->x.kids[0]);
        int size = p->syms[0]->u.c.v.i;
        
        if(size % 2)
            print("\tMOVE.L %#%d,D0\n", size);
        else
            print("\tMOVE.L %#%d,D0\n", size / 2);
        print("\tBRA.S .%d\n", lab2);
        print(".%d:\n", lab1);
        if(size % 2)
            print("\tMOVE.B (A%d)+,(A%d)+\n", src, dst);
        else
            print("\tMOVE.W (A%d)+,(A%d)+\n", src, dst);
        print(".%d:\n", lab2);
        print("\tDBF.W D0,.%d\n", lab1);
        break;
        }
    case ARGB: {
        int lab1 = genlabel(1), lab2 = genlabel(1);
        int size = p->syms[0]->u.c.v.i, src = getregnum(p->x.kids[0]);
        
        if(size % 2) ++size;
        print("\tSUBA.L %#%d,SP\n", size);
        print("\tMOVE.L SP,A1\n");
        print("\tMOVE.L %#%d,D0\n", size / 2);
        print("\tBRA.S .%d\n", lab2);
        print(".%d:\n", lab1);
        print("\tMOVE.W (A%d)+,(A1)+\n", src);
        print(".%d:\n", lab2);
        print("\tDBF.W D0,.%d\n", lab1);
        }
        break;
    case MODI: {
        int lab1 = genlabel(1);
        int rem = getregnum(p), quo = getregnum(p->x.kids[0]);
        int divisor = getregnum(p->x.kids[1]);
	print("\tMOVEQ.L #-1,D%d\n", D1);
	print("\tMOVE.L #31,D%d\n", D2);
	print("\tBTST.L D%d,D%d\n", D2, quo);
	print("\tBNE.S .%d\n", lab1);
	print("\tCLR.L D%d\n", D1);
	print(".%d:\n", lab1);
#if 0
  	print("\tDIVS.L D%d,D%d:D%d\n", divisor, D1, rem);
#else
	print("\tDIVS.L D%d,D%d:D%d\n", divisor, D1, quo);
#endif
	print("\tMOVE.L D%d,D%d\n", D1, rem);
        break;
        }
    }
}


/* Compute the register or stack cell assigned to the next argument (p. 356)
 */
static void doarg(p) Node p; {
    assert(p && p->syms[0]);
        switch(p->op) {
#if 0
        case ARGD: case ARGF:
            mkactual(2,realparamsize); /* pass all floats as extended which are 96-bits */
            break;
#endif
        default:
        mkactual(2, p->syms[0]->u.c.v.i);
    }
}

/* load register tmp with k bytes from off(reg) (p. 356) */
static void blkfetch(k, off, reg, tmp) {
  assert(0) ;
}

/* store k bytes from register tmp  at off(reg) (p. 356) */
static void blkstore(k, off, reg, tmp) {
  assert(0) ;
}

/* emit a loop to copy size bytes from soff(sreg) to doff(dreg) using the
 * 3 temporary registers in tmps (p. 356)
 */
static void blkloop(dreg, doff, sreg, soff, size, tmps)
    int tmps[]; {
  assert(0) ;
}

/* called by the front end to announce a local variable (p. 90) */
static void local(p) Symbol p; {
    if (askregvar(p, rmap[ttob(p->type)]) == 0)
        mkauto(p);
}

/* initialize prolog and epilog code (p. 93) */
static void function(f, caller, callee, n)
Symbol f, callee[], caller[]; {
    int i, count, acount, inseq;
        char reglist[256];
        char temp[4];

    print("%s:\n", f->x.name);
#ifdef AMIGA
    if (glevel > 1) {
        print("\tDEBUG\t%d\n",f->src.y);
    }
#endif

    usedmask[0] = usedmask[1] = 0;
    freemask[0] = freemask[1] = ~(unsigned)0;
    offset = 4 + 4; /* FP and return address */
    for (i = 0; callee[i]; i++) {
        Symbol p = callee[i];
        Symbol q = caller[i];
        assert(q);
        p->x.offset = q->x.offset = offset;
        p->x.name = q->x.name = stringf("%d", p->x.offset);
        p->sclass = q->sclass = AUTO;
#if 0
        if( p->type == floattype || p->type == doubletype)
            offset += roundup(realparamsize, 2);
        else
#endif
            offset += roundup(q->type->size, 2);
    }
    assert(caller[i] == 0);
    offset = maxoffset = 0;
    gencode(caller, callee);
    framesize = roundup(maxoffset, 2);
    
    if (framesize > 0)
        print("\tLINK A5,#%d\n", -framesize);
    else
        print("\tLINK A5,#0\n");
        
    /* format save register mask */
    reglist[0]= 0;
    count = acount = inseq = 0;
    for(i=D3;i<=D7;++i) {       
        if(usedmask[IREG] & (1<<i)) {
            if(count==0) {
                reglist[0] = 'D';
                reglist[1] = i + '0';
                reglist[2] = 0;
            } else if(!inseq) {
                temp[0] = '/';
                temp[1] = 'D';
                temp[2] = i + '0';
                temp[3] = 0;
                strcat(reglist,temp);
            }
            inseq++; count++;
        } else {
            if(inseq>1) {
                temp[0] = '-';
                temp[1] = 'D';
                temp[2] = i - 1 + '0';
                temp[3] = 0;
                strcat(reglist,temp);
            }
            inseq = 0;
        }
    }
    if(inseq>1) {
        temp[0] = '-';
        temp[1] = 'D';
        temp[2] = '7';
        temp[3] = 0;
        strcat(reglist,temp);
    }

    if(usedmask[IREG] & ((1<<A2+8) | (1<<A3+8) | (1<<A4+8))) {
        i = 0;
        if(count)
            temp[i++] = '/';
        temp[i] = 0;
        if(i)
            strcat(reglist, temp);

        acount = inseq = 0;
        for(i=A2;i<=A4;++i) {       
            if(usedmask[IREG] & (1<<i+8)) {
                if(acount==0) {
                    temp[0] = 'A';
                    temp[1] = i + '0';
                    temp[2] = 0;
                    strcat(reglist,temp);
                } else if(!inseq) {
                    temp[0] = '/';
                    temp[1] = 'A';
                    temp[2] = i + '0';
                    temp[3] = 0;
                    strcat(reglist,temp);
                }
                inseq++; acount++;
            } else {
                if(inseq>1) {
                    temp[0] = '-';
                    temp[1] = 'A';
                    temp[2] = i - 1 + '0';
                    temp[3] = 0;
                    strcat(reglist,temp);
                }
                inseq = 0;
            }
        }
        if(inseq>1) {
            temp[0] = '-';
            temp[1] = 'A';
            temp[2] = '4';
            temp[3] = 0;
            strcat(reglist,temp);
        }
    }
    
    count += acount;
    if(count > 1)
        print("\tMOVEM.L %s,-(SP)\n", reglist);
    else if(count == 1)
        print("\tMOVE.L %s,-(SP)\n", reglist);

    /* save floating point regs */
    for(i=FP4;i<=FP7;++i)
        if(usedmask[FREG] & (1<<i)) {
            print("\tFMOVE.D FP%d,-(SP)\n", i);
        }

    /* convert extended to declared format; FP1 is scratch register */
#if 0
    for (i = 0; callee[i]; i++) {
        Symbol p = callee[i];
        if(p->type == floattype) {
        print("\tFMOVE.X %d(A5),FP1\n", p->x.offset);
        print("\tFMOVE.S FP1,%d(A5)\n", p->x.offset);
        }
        else if(p->type == doubletype) {
        print("\tFMOVE.X %d(A5),FP1\n", p->x.offset);
        print("\tFMOVE.D FP1,%d(A5)\n", p->x.offset);
        }
    }
#endif

/* JOOP: emit the code needed to initialise ixemul.library/ixnet */
#ifdef IXEMUL
	if( strcmp(f->x.name,"_main") == 0)
	{
/*		print("\tmove.l d3,-(sp)\n");
		print("\tmove.l d2,-(sp)\n");*/
		print("\tbsr.l  ___main\n");
	}
#endif
    emitcode();
    
    for(i=FP7;i>=FP4;--i)
        if(usedmask[FREG] & (1<<i)) {
            /* pop saved float regs */
            print("\tFMOVE.D (SP)+,FP%d\n", i);
        }
    if(count > 1)
        print("\tMOVEM.L (SP)+,%s\n", reglist);
    else if(count == 1)
        print("\tMOVE.L (SP)+,%s\n", reglist);

    print("\tUNLK A5\n");
    print("\tRTS\n");
}

/* called by the front end when a symbol is defined, so that the Xsymbol
 * field can be initialized (p. 89)
 *
 * JOOP: added cfunc->name to label construction, to aid debugging
 *       27-12-95: found a horrible bug and fixed it!
 */
static void defsymbol(p) Symbol p; {
    if (p->scope >= LOCAL && p->sclass == STATIC)
        p->x.name = stringf("%s_L.%d", cfunc->name, genlabel(1));
    else if (p->generated)
		if(cfunc)
	        p->x.name = stringf("%s_L.%s", cfunc->name, p->name);
		else
	        p->x.name = stringf("L.%s", p->name);
    else if (p->scope == GLOBAL || p->sclass == EXTERN)
        p->x.name = stringf("_%s", p->name);
    else if (p->scope == CONSTANTS
    && (isint(p->type) || isptr(p->type))
    && p->name[0] == '0' && p->name[1] == 'x')
#ifdef IXEMUL
        p->x.name = stringf("0x%s", &p->name[2]); /* hex */
#else
        p->x.name = stringf("$%s", &p->name[2]); /* hex */
#endif
    else
        p->x.name = p->name;
}

static void address(q, p, n) Symbol q, p; int n; {
    if (p->scope == GLOBAL
    || p->sclass == STATIC || p->sclass == EXTERN)
        q->x.name = stringf("%s%s%d",
            p->x.name, n >= 0 ? "+" : "", n);
    else {
        q->x.offset = p->x.offset + n;
        q->x.name = stringd(q->x.offset);
    }
}
static void defconst(ty, v) Value v; {
    switch (ty) {
#ifdef IXEMUL
        case C: print("\t.byte %d\n",    v.uc); return;
        case S: print("\t.word %d\n",    v.ss); return;
        case I: print("\t.long %d\n",   v.i ); return;
        case U: print("\t.long 0x%x\n", v.u ); return;
        case P: print("\t.long 0x%x\n", v.p ); return;
        case F:
            print("\t.long 0x%x\n", *(unsigned *)&v.f);
            return;
        case D: {
            unsigned *p = (unsigned *)&v.d;
	    print("\t.long 0x%x\n", p[swap]);
	    print("\t.long 0x%x\n", p[1 - swap]);
            return;
            }
#else
        case C: print("\tDC.B %d\n",    v.uc); return;
        case S: print("\tDC.W %d\n",    v.ss); return;
        case I: print("\tDC.L %d\n",   v.i ); return;
        case U: print("\tDC.L $%x\n", v.u ); return;
        case P: print("\tDC.L $%x\n", v.p ); return;
        case F:
            print("\tDC.L $%x\n", *(unsigned *)&v.f);
            return;
        case D: {
            unsigned *p = (unsigned *)&v.d;
	    print("\tDC.L $%x\n", p[swap]);
	    print("\tDC.L $%x\n", p[1 - swap]);
            return;
            }
#endif
    }
    assert(0);
}
static void defaddress(p) Symbol p; {
/*  print( "\tdc.l\t%s\t; defaddress\n", p->x.name ) ;*/
  print( "\tdc.l %s\n", p->x.name ) ;
  }

static void defstring(n, str) int n; char *str; {
  char *s; int count = 0, instr = 0 ;

/*  print( "*\tdefstring\n" ) ; */
#ifdef IXEMUL
  print( "\t.ascii\t" ) ;
#else
  print( "\tdc.b\t" ) ;
#endif
  for (s = str; s < str + n; s++) {
    int c = (*s) & 0377 ;
    if( count == 40 ) {
#ifdef IXEMUL
      print( instr ? "\"\n\t.ascii\t" : "\n\t.ascii\t" ) ; instr = count = 0 ;
#else
      print( instr ? "'\n\tdc.b\t" : "\n\tdc.b\t" ) ; instr = count = 0 ;
#endif
      }
    if( isprint(c) && c != '\'' && c != '\\' && c!= '"') {
      if( !instr && count > 0 ) print( "," ) ;
#ifdef IXEMUL
      print( instr ? "%c" : "\"%c", c ) ; instr = 1 ;
#else
      print( instr ? "%c" : "'%c", c ) ; instr = 1 ;
#endif
      }
    else {
#ifdef IXEMUL
      if( instr ) print( "\"" ) ;
#else
      if( instr ) print( "\"" ) ;
#endif
      if( count > 0 ) print( "," ) ;
#ifdef IXEMUL
      print( "\"\\%o\"", c ) ;
#else
      print( "\\%d", c ) ;
#endif
      instr = 0 ;
      }
    count++ ;
    }
#ifdef IXEMUL
  print( instr ? "\"\n" : "\n" ) ;
#else
  print( instr ? "'\n" : "\n" ) ;
#endif
  }

static void export(p) Symbol p; {
#ifdef IXEMUL
  print( ".globl\t%s\n", p->x.name ) ;
#else
  print( "\txdef\t%s\n", p->x.name ) ;
#endif
  }
static void import(p) Symbol p; {
  if( p->ref > 0 ) 
#ifdef IXEMUL
    ;
#else
    print( "\txref\t%s\n", p->x.name ) ;
#endif
  }
static void global(p) Symbol p; {
  if( p->type->align > 1 )
#ifdef IXEMUL
    print( "\t.even\n") ;
#else
    print( "\tcnop\t0,%d\n", p->type->align ) ;
#endif
  print( "%s:\n", p->x.name ) ;
  }

static void space(n) {
#if 0
  int i ;

  print( "*\tspace(%d)\n", n ) ;
  while( n >= 16 ) {
    print( "\tdc.b\t0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n" ) ;
    n -= 16 ;
    }

  if( n == 0 ) return ;
  print( "\tdc.b\t0" ) ; n-- ;
  while( n > 0 )
    { print( ",0" ) ; n-- ; }
  print( "\n" ) ;
#else
#ifdef IXEMUL
  print( "\tDS.B %d\n", n);
#else
  print( "*\tspace(%d)\n", n ) ;
  print( "\tDS.B %d\n", n);
#endif
#endif
  }

Interface m68kIR = {
    1, 1, 0,    /* char */
    2, 2, 0,    /* short */
    4, 2, 0,    /* int */
    4, 2, 1,    /* float */
    8, 2, 1,    /* double */
    4, 2, 0,    /* T * */
    0, 2, 0,    /* struct so that ARGB keeps stack aligned */
    0,      /* little_endian */
    0,      /* mulops_calls */
    0,      /* wants_callb */
    1,      /* wants_argb */
    0,      /* left_to_right */
    0,      /* wants_dag */
    address,
    blockbeg,
    blockend,
    defaddress,
    defconst,
    defstring,
    defsymbol,
    emit,
    export,
    function,
    gen,
    global,
    import,
    local,
    progbeg,
    progend,
    segment,
    space,
#ifdef STABTEST
  #ifdef IXEMUL
	stabblock,
	0,0,
	stabinit, stabline,stabsym,stabtype,
  #else
    0,
    stabend,
    0,0,
    stabline,
    0, 0,
  #endif
#else
    0,
    0,
    0,0,
    0,
    0, 0,
#endif
    {1, blkfetch, blkstore, blkloop,
        _label,
        _rule,
        _nts,
        _kids,
        _opname,
        _arity,
        _string,
        _templates,
        _isinstruction,
        _ntname,
        emit2,
        doarg,
        target,
        clobber,
         }
};

/* symname - print prefix, p's name, declaration source coordinate, suffix */
static void symname(p) Symbol p; {
	if (p)
		print("%s@%w.%d", p->name, &p->src, p->src.x);
	else
		print("0");
}

char *input_file = "";

static void progbeg(argc, argv) char *argv[]; {
    int i;

    {
        union {
            char c;
            int i;
        } u;
        u.i = 0;
        u.c = 1;
        swap = (u.i == 1) != IR->little_endian;
    }
    parseflags(argc, argv);

    for(i=0;i<argc;++i) {
    }

    for (i = 0; i < 8; i++)
        ireg[i] = mkreg("D%d", i, 1, IREG);
    for (i = A0; i < A7; i++)
        areg[i+8] = mkreg("A%d", i, 0x100, IREG);
    for (i = 0; i < 8; i++)
        freg[i] = mkreg("FP%d", i, 1, FREG);
        
    rmap[C] = rmap[S] = rmap[B] = rmap[U] = rmap[I] = mkwildcard(dreg);
    rmap[P] = mkwildcard(areg);
    rmap[F] = rmap[D] = mkwildcard(freg);
     
    tmask[IREG] =   (1<<D0) | (1<<D1) | (1<<D2) | (1<<D3) |
                    (1<<D4) | (1<<D5) | (1<<D6) | (1<<D7) | /* D0-D7/A0-A4 */
                    (1<<A0+8) | (1<<A1+8) | (1<<A2+8) | (1<<A3+8) | (1<<A4+8);
    /*vmask[IREG] =   (1<<D3) | (1<<D4) | (1<<D5) | 
                    (1<<A2+8) | (1<<A3+8) | (1<<A4+8); */
    tmask[FREG] = (1<<FP0) | (1<<FP1) | (1<<FP2) | (1<<FP3);
    /* vmask[FREG] = (1<<FP4) | (1<<FP5) | (1<<FP6) | (1<<FP7); */
    
#ifdef IXEMUL
	print("#NO_APP\n");
	print("gcc2_compiled.:\n");
	print("___gnu_compiled_c:\n");
#else
    print("\tMACHINE\t68030\n");
    print("\tFPU\n") ;
    if (!firstfile) {
      firstfile = input_file;
    }
    print("\tIDNT\t%s\n", FILEPART(firstfile));

    if (glevel > 1) {
#ifdef AMIGA

	BPTR fh_lock;
	char namebuffer[256]; /* FILENAME_MAX=4096 in stdio.h, way too big ? */

	fh_lock=Lock((unsigned char *)firstfile, ACCESS_READ);
	if(fh_lock)    /* got the lock, now get the complete path/filename */
	{              /* should never fail but better safe then sorry     */
		NameFromLock(fh_lock, (unsigned char *)namebuffer,
			     sizeof(namebuffer));
	    print("\tDSOURCE\t%s\n", namebuffer);
		UnLock(fh_lock);
	}
    else /* in case it fails, use the file name currently being compiled */
#endif	/* AMIGA */
	    print("\tDSOURCE\t%s\n", firstfile);
    }
#endif /* !IXEMUL */
    cseg = 0;
}

#ifdef STABTEST
/* JOOP: 280996
   Currently it is not possible to use stab output for gdb because
   the lcc headers and the Amiga headers use conflicting types/enums
   I can't solve it
*/
/* JOOP: 280996
   Stab support copied from sparc.md. Intergrated with debugging support
   when using PhxAss/Barfly
*/
#include <stab.h>
static char *currentfile;       /* current file name */
static int ntypes;
static int nextlab = 1;
extern Interface solarisIR;

static void stabblock ARGS((int, int, Symbol*));
static void stabfend ARGS((Symbol, int));
static void stabinit ARGS((char *, int, char *[]));
static void stabline ARGS((Coordinate *));
static void stabsym ARGS((Symbol));
static void stabtype ARGS((Symbol));

static void asgncode ARGS((Type, int));
static void dbxout ARGS((Type));
static int dbxtype ARGS((Type));
static int emittype ARGS((Type, int, int));

/* asgncode - assign type code to ty */
static void asgncode(ty, lev) Type ty; int lev; {
	if (ty->x.marked || ty->x.typeno)
		return;
	ty->x.marked = 1;
	switch (ty->op) {
	case VOLATILE: case CONST: case VOLATILE+CONST:
		asgncode(ty->type, lev);
		ty->x.typeno = ty->type->x.typeno;
		break;
	case POINTER: case FUNCTION: case ARRAY:
		asgncode(ty->type, lev + 1);
		/* fall thru */
	case VOID: case CHAR: case SHORT: case INT: case UNSIGNED:
	case FLOAT: case DOUBLE:
		break;
	case STRUCT: case UNION: {
		Field p;
		for (p = fieldlist(ty); p; p = p->link)
			asgncode(p->type, lev + 1);
		/* fall thru */
	case ENUM:
		if (ty->x.typeno == 0)
			ty->x.typeno = ++ntypes;
		if (lev > 0 && (*ty->u.sym->name < '0' || *ty->u.sym->name > '9'))
			dbxout(ty);
		break;
		}
	default:
		assert(0);
	}
}

/* dbxout - output .stabs entry for type ty */
static void dbxout(ty) Type ty; {
	ty = unqual(ty);
	if (!ty->x.printed) {
		int col = 0;
		print(".stabs \""), col += 8;
		if (ty->u.sym && !(isfunc(ty) || isarray(ty) || isptr(ty)))
			print("%s", ty->u.sym->name), col += strlen(ty->u.sym->name);
		print(":%c", isstruct(ty) || isenum(ty) ? 'T' : 't'), col += 2;
		emittype(ty, 0, col);
		print("\",%d,0,0,0\n", N_LSYM);
	}
}

/* dbxtype - emit a stabs entry for type ty, return type code */
static int dbxtype(ty) Type ty; {
	asgncode(ty, 0);
	dbxout(ty);
	return ty->x.typeno;
}

/*
 * emittype - emit ty's type number, emitting its definition if necessary.
 * Returns the output column number after emission; col is the approximate
 * output column before emission and is used to emit continuation lines for long
 * struct, union, and enum types. Continuations are not emitted for other types,
 * even if the definition is long. lev is the depth of calls to emittype.
 */
static int emittype(ty, lev, col) Type ty; int lev, col; {
	int tc = ty->x.typeno;

	if (isconst(ty) || isvolatile(ty)) {
		col = emittype(ty->type, lev, col);
		ty->x.typeno = ty->type->x.typeno;
		ty->x.printed = 1;
		return col;
	}
	if (tc == 0) {
		ty->x.typeno = tc = ++ntypes;
/*              fprint(2,"`%t'=%d\n", ty, tc); */
	}
	print("%d", tc), col += 3;
	if (ty->x.printed)
		return col;
	ty->x.printed = 1;
	switch (ty->op) {
	case VOID:	/* void is defined as itself */
		print("=%d", tc), col += 1+3;
		break;
	case CHAR:	/* unsigned char is a subrange of int */
		if (ty == unsignedchar)
			print("=r1;0;255;"), col += 10;
		else	/* following pcc, char is a subrange of itself */
			print("=r%d;-128;127;", tc), col += 2+3+10;
		break;
	case SHORT:	/* short is a subrange of int */
		if (ty == unsignedshort)
			print("=r1;0;65535;"), col += 12;
		else	/* signed */
			print("=r1;-32768;32767;"), col += 17;
		break;
	case INT:	/* int is a subrange of itself */
		print("=r1;%d;%d;", INT_MIN, INT_MAX), col += 4+11+1+10+1;
		break;
	case UNSIGNED:	/* unsigned is a subrange of int */
		print("=r1;0;-1;"), col += 9;
		break;
	case FLOAT: case DOUBLE:	/* float, double get sizes instead of ranges */
		print("=r1;%d;0;", ty->size), col += 4+1+3;
		break;
	case POINTER:
		print("=*"), col += 2;
		col = emittype(ty->type, lev + 1, col);
		break;
	case FUNCTION:
		print("=f"), col += 2;
		col = emittype(ty->type, lev + 1, col);
		break;
	case ARRAY:	/* array includes subscript as an int range */
		if (ty->size && ty->type->size)
			print("=ar1;0;%d;", ty->size/ty->type->size - 1), col += 7+3+1;
		else
			print("=ar1;0;-1;"), col += 10;
		col = emittype(ty->type, lev + 1, col);
		break;
	case STRUCT: case UNION: {
		Field p;
		if (!ty->u.sym->defined) {
			print("=x%c%s:", ty->op == STRUCT ? 's' : 'u', ty->u.sym->name);
			col += 2+1+strlen(ty->u.sym->name)+1;
			break;
		}
		if (lev > 0 && (*ty->u.sym->name < '0' || *ty->u.sym->name > '9')) {
			ty->x.printed = 0;
			break;
		}
		print("=%c%d", ty->op == STRUCT ? 's' : 'u', ty->size), col += 1+1+3;
		for (p = fieldlist(ty); p; p = p->link) {
			if (p->name)
				print("%s:", p->name), col += strlen(p->name)+1;
			else
				print(":"), col += 1;
			col = emittype(p->type, lev + 1, col);
			if (p->lsb)
				print(",%d,%d;", 8*p->offset +
					(IR->little_endian ? fieldright(p) : fieldleft(p)),
					fieldsize(p));
			else
				print(",%d,%d;", 8*p->offset, 8*p->type->size);
			col += 1+3+1+3+1;	/* accounts for ,%d,%d; */
			if (col >= 80 && p->link) {
				print("\\\\\",%d,0,0,0\n.stabs \"", N_LSYM);
				col = 8;
			}
		}
		print(";"), col += 1;
		break;
		}
	case ENUM: {
		Symbol *p;
		if (lev > 0 && (*ty->u.sym->name < '0' || *ty->u.sym->name > '9')) {
			ty->x.printed = 0;
			break;
		}
		print("=e"), col += 2;
		for (p = ty->u.sym->u.idlist; *p; p++) {
			print("%s:%d,", (*p)->name, (*p)->u.value), col += strlen((*p)->name)+3;
			if (col >= 80 && p[1]) {
				print("\\\\\",%d,0,0,0\n.stabs \"", N_LSYM);
				col = 8;
			}
		}
		print(";"), col += 1;
		break;
		}
	default:
		assert(0);
	}
	return col;
}

#ifdef IXEMUL
/* stabblock - output a stab entry for '{' or '}' at level lev */
static void stabblock(brace, lev, p) int brace, lev; Symbol *p; {
	if (brace == '{')
		while (*p)
			stabsym(*p++);
	if (IR == &solarisIR) {
		print(".stabn 0x%x,0,%d,.LL%d-%s\n", brace == '{' ? N_LBRAC : N_RBRAC, lev, nextlab, cfunc->x.name);
		print(".LL%d:\n", nextlab++);
	} else
		print(".stabd 0x%x,0,%d\n", brace == '{' ? N_LBRAC : N_RBRAC, lev);
}
#else
#define stabblock 0
#endif

#ifdef IXEMUL
/* stabfend - end of function p */
static void stabfend(p, line) Symbol p; int line; {
	if (IR == &solarisIR) {
		print(".type %s,#function\n", p->x.name);
		print(".size %s,.-%s\n", p->x.name, p->x.name);
	}
}
#else
/* stabend - finalize stab output */
/* JOOP: currently not needed ??? */
static void stabend(cp, p, cpp, sp, stab) Coordinate *cp, **cpp; Symbol p, *sp, *stab; {
#if 0
	int i;

	symname(p);
	print("\n");
	if (cpp && sp)
		for (i = 0; cpp[i] && sp[i]; i++) {
			print("%w.%d: ", cpp[i], cpp[i]->x);
			symname(sp[i]);
			print("\n");
		}
#endif
}
#endif /* ~IXEMUL */


/* stabinit - initialize stab output */
static void stabinit(file, argc, argv) int argc; char *file, *argv[]; {
	typedef void (*Closure) ARGS((Symbol, void *));

	if (file && *file) {
		(*IR->segment)(CODE);
		print("Ltext:.stabs \"%s\",0x%x,0,0,Ltext\n", file, N_SO);
		currentfile = file;
	}
	dbxtype(inttype);
	dbxtype(chartype);
	dbxtype(doubletype);
	dbxtype(floattype);
	dbxtype(longdouble);
	dbxtype(longtype);
	dbxtype(shorttype);
	dbxtype(signedchar);
	dbxtype(unsignedchar);
	dbxtype(unsignedlong);
	dbxtype(unsignedshort);
	dbxtype(unsignedtype);
	dbxtype(voidtype);
	foreach(types, GLOBAL, (Closure)stabtype, NULL);
}

#ifdef IXEMUL
/* stabline - emit stab entry for source coordinate *cp */
static void stabline(cp) Coordinate *cp; {
	if (cp->file && cp->file != currentfile) {
		int lab = genlabel(1);
		print("L%d: .stabs \"%s\",0x%x,0,0,L%d\n", lab,
				cp->file, N_SOL, lab);
		currentfile = cp->file;
	}
	if (IR == &solarisIR) {
		print(".stabn 0x%x,0,%d,.LL%d-%s\n", N_SLINE, cp->y, nextlab, cfunc->x.name);
		print(".LL%d:\n", nextlab++);
	} else
		print(".stabd 0x%x,0,%d\n", N_SLINE, cp->y);
}
#else
/* stabline - emit line number information for source coordinate *cp */
/* JOOP: example output this generates when '-g' is used
 *       ;mod.c:14.1:		printf("second case %d\n",c%b);
 *       Suggestions whether or not the line info should stay are welcome
 *
 *       Source level debugging now works! with Bdebug from Barfly atleast
 *       If only -g is specified then only source level debug is active
 *       if you specify -g9 then the C-source will also be printed to the
 *       assembler file.
 */
static void stabline(cp) Coordinate *cp; {
	if(glevel == 1 && annotate == 1)
		print(";%s\n",cp->source_line);
	else if (glevel>1 && annotate) {
		print("\tDEBUG\t%d\n",cp->y);
		print(";%s\n",cp->source_line);
	}
	else 
		print("\tDEBUG\t%d\n",cp->y);
}
#endif /* ~IXEMUL */

/* stabsym - output a stab entry for symbol p */
static void stabsym(p) Symbol p; {
	int code, tc, sz = p->type->size;

	if (p->generated || p->computed)
		return;
	if (isfunc(p->type)) {
		print(".stabs \"%s:%c%d\",%d,0,0,%s\n", p->name,
			p->sclass == STATIC ? 'f' : 'F', dbxtype(freturn(p->type)),
			N_FUN, p->x.name);
		return;
	}
	if (!IR->wants_argb && p->scope == PARAM && p->structarg) {
		assert(isptr(p->type) && isstruct(p->type->type));
		tc = dbxtype(p->type->type);
		sz = p->type->type->size;
	} else
		tc = dbxtype(p->type);
	if (p->sclass == AUTO && p->scope == GLOBAL || p->sclass == EXTERN) {
		print(".stabs \"%s:G", p->name);
		code = N_GSYM;
	} else if (p->sclass == STATIC) {
		print(".stabs \"%s:%c%d\",%d,0,0,%s\n", p->name, p->scope == GLOBAL ? 'S' : 'V',
			tc, p->u.seg == BSS ? N_LCSYM : N_STSYM, p->x.name);
		return;
	} else if (p->sclass == REGISTER) {
		if (p->scope > PARAM) {
			int r = p->x.regnode->number;
			if (p->x.regnode->set == FREG)
				r += 32;	/* floating point */
			print(".stabs \"%s:r%d\",%d,0,", p->name, tc, N_RSYM);
			print("%d,%d\n", sz, r);
		}
		return;
	} else if (p->scope == PARAM) {
		print(".stabs \"%s:p", p->name);
		code = N_PSYM;
	} else if (p->scope >= LOCAL) {
		print(".stabs \"%s:", p->name);
		code = N_LSYM;
	} else
		assert(0);
	print("%d\",%d,0,0,%s\n", tc, code,
		p->scope >= PARAM && p->sclass != EXTERN ? p->x.name : "0");
}

/* stabtype - output a stab entry for type *p */
static void stabtype(p) Symbol p; {
	if (p->type) {
		if (p->sclass == 0)
			dbxtype(p->type);
		else if (p->sclass == TYPEDEF)
			print(".stabs \"%s:t%d\",%d,0,0,0\n", p->name, dbxtype(p->type), N_LSYM);
	}
}
#endif /* STABTEST */
