/* $VER: pasm ppcasm.h V0.8 (14.02.98)
 *
 * This file is part of pasm, a portable PowerPC assembler.
 * Copyright (c) 1997-98  Frank Wille
 *
 * pasm is freeware and part of the portable and retargetable ANSI C
 * compiler vbcc, copyright (c) 1995-98 by Volker Barthelmann.
 * pasm may be freely redistributed as long as no modifications are
 * made and nothing is charged for it. Non-commercial usage is allowed
 * without any restrictions.
 * EVERY PRODUCT OR PROGRAM DERIVED DIRECTLY FROM MY SOURCE MAY NOT BE
 * SOLD COMMERCIALLY WITHOUT PERMISSION FROM THE AUTHOR.
 *
 *
 * v0.8 (14.02.98) phx
 *      Alignment list for each section. This fixes the problems
 *      with optimizations.
 * v0.7 (02.01.98) phx
 *      Define "NetBSDAmiga68k" changed to "NetBSD68k".
 *      Changed ParsedLine (next) and GlobalVars (anotherpass) to
 *      allow more than two assembler passes - as required for
 *      optimizations.
 *      search_instr() is global.
 *      Output format 3 is ADOS (like EHF, but doesn't use HUNK_PPC_CODE).
 * v0.6 (30.10.97) phx
 *      More options. GlobalVars: optinstrmode and supermode.
 * v0.5 (12.10.97) phx
 *      Add userdeflist and usrdefs to GlobalVars for symbol definitions
 *      via the command line.
 *      .set allows symbols to be reused.
 *      Last line of a source text was ignored, if newline is missing.
 * v0.4 (05.07.97) phx
 *      Program returns EXIT_FAILURE if an error occurs.
 *      Base address for absolute code may be set with -B option.
 *      EHF support.
 *      Added R_PPC_TOC16 relocation type.
 *      Option -x automatically declares unknown symbols as
 *      externally defined. New GlobalVars entry: autoextern.
 *      Runs on Linux/DEC-Alpha with 64-bit integers.
 *      Changed program name from "PPCasm" to "pasm". Reason: There
 *      is already a PPCasm for Apple Macintosh.
 * v0.3 (20.04.97) phx
 *      Using correct names for PowerPC relocations.
 *      Added little-endian conversion macros.
 * v0.2 (25.03.97) phx
 *      Writes ELF object for 32-bit PowerPC big-endian. Either absolute
 *      or ELF output format may be selected. ELF is default for all
 *      currently supported platforms. PPCasm supports nine different
 *      relocation types (there are much more...).
 *      Compiles and works also under NetBSD/amiga (68k).
 *      Changed function declaration to 'new style' in all sources
 *      (to avoid problems with '...' for example).
 *      Included NetBSD/amiga as supported architecture.
 * v0.1 (11.03.97) phx
 *      First test version with all PowerPC instructions and most
 *      important directives. Only raw, absolute output.
 * v0.0 (14.02.97) phx
 *      File created. Project started.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>

/* program's name */
#define PNAME "pasm"

/* version/revision */
#define VERSION 0
#define REVISION 80

/* architecture specific defines */
#if defined (AmigaOS68k)
#define MACHINE "Amiga OS/M68k"
#define BIGENDIAN
#define STDTYPES
#elif defined (AmigaOSPPC)
#define MACHINE "Amiga OS/PowerPC"
#define BIGENDIAN
#define STDTYPES
#elif defined (NetBSD68k)
#define MACHINE "NetBSD/M68k"
#define BIGENDIAN
#define STDTYPES
#elif defined (SolarisSparc)
#define MACHINE "Solaris/Sparc"
#define BIGENDIAN
#define STDTYPES
#elif defined (SunOSSparc)
#define MACHINE "SunOS/Sparc"
#define BIGENDIAN
#define STDTYPES
#elif defined (SCOi386)
#define MACHINE "SCO/i386"
#define LITTLEENDIAN
#define STDTYPES
#elif defined (Linuxi386)
#define MACHINE "Linux/i386"
#define LITTLEENDIAN
#define STDTYPES
#elif defined (LinuxAlpha)
#define MACHINE "Linux/Alpha"
#define LITTLEENDIAN
#define TYPES64BIT
#else
#error Unsupported architecture! Please adapt the source text.
#endif

#ifdef STDTYPES
typedef signed char int8;
typedef unsigned char uint8;
typedef signed short int int16;
typedef unsigned short int uint16;
typedef signed long int int32;
typedef unsigned long int uint32;
typedef signed char bool;
#elif defined (TYPES64BIT)
typedef signed char int8;
typedef unsigned char uint8;
typedef signed short int16;
typedef unsigned short uint16;
typedef signed int int32;
typedef unsigned int uint32;
typedef int bool;
#else
#error Unsupported architecture! Please adapt the source text.
#endif

/* endian conversion */
#if defined (BIGENDIAN)
#define ECH(x) x
#define ECW(x) x
#define ECVH(x) x
#define ECVW(x) x
#elif defined (LITTLEENDIAN)
#define ECH(x) (((x)&0xff)<<8|((x)&0xff00)>>8)
#define ECW(x) (((x)&0xff)<<24|((x)&0xff00)<<8|((x)&0xff0000)>>8|((x)&0xff000000)>>24)
#define ECVH(x) l2bh(x)
#define ECVW(x) l2bw(x)
#else
#error You have to define either BIGENDIAN or LITTLEENDIAN.
#endif


/* program constants */

#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef NULL
#define NULL 0
#endif

#define FNAMEBUFSIZE 1024       /* buffer size for file names */
#define EXPSTACKSIZE 32         /* maximum arguments in an expression */


/* structures */

struct node {
  struct node *next;
  struct node *pred;
};

struct list {
  struct node *first;
  struct node *dummy;
  struct node *last;
};

struct CPUInstr {
  struct CPUInstr *hash_chain;  /* next instruction in hash chain */
  char *name;                   /* instruction's name */
  uint16 flags;                 /* format flags */
  uint8 type;                   /* instruction format, see defines */
  uint8 opcd;                   /* opcode (bit 0-5) */
  uint8 fieldD;                 /* preset D field (bit 6-10) */
  uint8 fieldA;                 /* preset D field (bit 11-15) */
  uint16 xo;                    /* extended opcode (bit 21-31) */
};
/* CPU instruction formats */
#define T_I 0                   /* Bx */
#define T_B 1                   /* BCx */
#define T_DD 2                  /* LWZ */
#define T_DI 3                  /* ADDI */
#define T_DS 4                  /* LD */
#define T_X 5                   /* AND */
#define T_IMM 6                 /* MTFSFI */
#define T_XLB 7                 /* BCLRx */
#define T_XSPR 8                /* MFSPR */
#define T_XCRM 9                /* MTCRF */
#define T_XFL 10                /* MTFSF */
#define T_XS 11                 /* SRADIx */
#define T_A 12                  /* FMADDx */
#define T_M 13                  /* RLWIMIx */
#define T_MD 14                 /* RLDICx */
#define T_CMP 15                /* CMP */
/* CPU instruction flags */
#define F_SUPP_D 0x01           /* D = 0 */
#define F_SUPP_A 0x02           /* A = 0 */
#define F_SUPP_B 0x04           /* B = 0 */
#define F_SUPP_C 0x08           /* C = 0 */
#define F_CRF_D 0x10            /* D = CR field */
#define F_CRF_S 0x20            /* S = CR field */
#define F_SWAP 0x40             /* S instr.: D = S, A and S are swapped */
#define F_SIGNED 0x80           /* 16 bit signed immediate */
#define F_64BIT 0x100           /* 64 bit instruction */
#define F_SUPER 0x200           /* supervisor-only instruction */
#define F_OPTIONAL 0x400        /* optional instruction */
#define F_EXTENDED 0x8000       /* extended mnemonic */

struct Directive {
  struct Directive *hash_chain; /* next directive in hash chain */
  char *name;                   /* directive's name */
  void (*dfunct)();
};

struct Macro {
  struct Macro *hash_chain;     /* next macro in hash chain */
  char *name;                   /* macro's name */
  char *text;                   /* ptr to first macro line */
  unsigned long nlines;         /* number of lines in this macro */
};

struct AlignPoint {             /* defines an alignment point (.align) */
  struct AlignPoint *next;
  unsigned long offset;         /* section offset for alignent */
  long val;                     /* alignment value */
  long gap;                     /* current alignment gap */
};

struct Section {
  struct node n;
  char *name;                   /* section's name */
  uint8 type;                   /* type: code, data, bss, offsets, ... */
  uint8 flags;
  uint8 protection;             /* readable, writable, executable, ... */
  uint8 alignment;              /* number of bits, which have to be zero */
  unsigned long pc;             /* current program counter (sect. offset) */
  unsigned long size;           /* size of section in bytes */
  void *contents;               /* contents, allocated in pass 2 */
  void *data;
  struct AlignPoint *first_align;    /* pointer to first alignment point */
  struct AlignPoint *current_align;  /* current alignment point */
  struct list reloclist;        /* section offsets to relocate */
  struct list xreflist;         /* external references in this section */
  uint32 index;                 /* e.g. section header index for ELF */
};
/* section types */
#define ST_UNDEFINED 0
#define ST_CODE 1               /* section contains code */
#define ST_DATA 2               /* section contains initialized data */
#define ST_UDATA 3              /* section contains uninitialized data */
#define ST_STRUCT 4             /* offset section (will be discarded) */
/* section flags */
#define SF_DISCARD 1            /* can be discarded (e.g. ST_STRUCT) */
#define SF_UNINITIALIZED 2      /* section has uninitialized contents */
/* protection */
#define SP_READ 1
#define SP_WRITE 2
#define SP_EXEC 4
#define SP_SHARE 8

struct Symbol {
  struct Symbol *hash_chain;    /* next symbol in hash chain */
  char *name;                   /* symbol's name */
  uint32 value;                 /* absolute value or relocation offset */
  struct Section *relsect;      /* symbol def. relative to this section */
  struct AlignPoint *alignpoint;/* sym. was defined after this aligment */
  uint8 type;                   /* absolute, relocatable or extern */
  uint8 flags;
  uint8 info;                   /* section, function or object */
  uint8 bind;                   /* local or global */
  uint32 size;                  /* object's size in bytes */
};
/* symbol type */
#define SYM_UNDEF 0
#define SYM_ABS 1
#define SYM_RELOC 2
#define SYM_EXTERN 3
/* object type */
#define SYMI_NOTYPE 0
#define SYMI_OBJECT 1
#define SYMI_FUNC 2
#define SYMI_SECTION 3
#define SYMI_FILE 4
/* symbol bind */
#define SYMB_NONE 0
#define SYMB_LOCAL 1
#define SYMB_GLOBAL 2
#define SYMB_WEAK 3

struct Reloc {
  struct node n;
  unsigned long offset;         /* section-offset of relocation */
  uint32 addend;                /* add this to relocation value */
  struct Section *relocsect;    /* base addr of this sect. has to be added */
  uint8 type;
};
/* reloc types (identical with the reloc types used for ELF) */
#define R_NONE 0
#define R_PPC_ADDR32 1          /* 32-bit relocation */
#define R_PPC_ADDR24 2          /* 26-bit relocation for B-instruction */
#define R_PPC_ADDR16 3          /* 16-bit relocation */
#define R_PPC_ADDR16_LO 4       /* relocation of the lower half-word */
#define R_PPC_ADDR16_HI 5       /* relocation of the higher half-word */
#define R_PPC_ADDR16_HA 6       /* higher half-word reloc. for ADDI */
#define R_PPC_ADDR14 7          /* BC-instruction, 16-bit absolute */
#define R_PPC_ADDR14_BRTAKEN 8
#define R_PPC_ADDR14_BRNTAKEN 9
#define R_PPC_REL24 10          /* relative 26-bit (PowerPC B-instruction) */
#define R_PPC_REL14 11          /* BC-instruction, 16-bit relative */
#define R_PPC_REL14_BRTAKEN 12
#define R_PPC_REL14_BRNTAKEN 13
#define R_PPC_GOT16 14
#define R_PPC_GOT16_LO 15
#define R_PPC_GOT16_HI 16
#define R_PPC_GOT16_HA 17
#define R_PPC_PLTREL24 18
#define R_PPC_COPY 19
#define R_PPC_GLOB_DAT 20
#define R_PPC_JMP_SLOT 21
#define R_PPC_RELATIVE 22
#define R_PPC_LOCAL24PC 23
#define R_PPC_UADDR32 24
#define R_PPC_UADDR16 25
#define R_PPC_REL32 26          /* section base offset */
#define R_PPC_PLT32 27
#define R_PPC_PLTREL32 28
#define R_PPC_PLT16_LO 29
#define R_PPC_PLT16_HI 30
#define R_PPC_PLT16_HA 31
#define R_PPC_SDAREL16 32
#define R_PPC_SECTOFF 33
#define R_PPC_SECTOFF_LO 34
#define R_PPC_SECTOFF_HI 35
#define R_PPC_SECTOFF_HA 36
#define R_PPC_TOC16 255

struct XReference {
  struct node n;
  struct Symbol *xsymbol;       /* external symbol, which is referenced */
  unsigned long offset;
  uint32 addend;
  uint8 type;                   /* relocation type, see struct Reloc */
  uint8 size;                   /* size of reference in bytes (3 = 26bit) */
};

struct ParsedLine {
  uint8 type;                   /* opcode type */
  uint8 flags;
  int8 branch_hint;             /* 0=no hint, 1=b. taken(+), -1=not taken(-)*/
  uint8 narg;                   /* value for $NARG, when calling macro */
  char *lineptr;                /* ptr to source text line */
  void *opcode;                 /* ptr to CPUInstr, Directive or SourceText */
  char *operand;                /* ptr to operand-string */
  struct ParsedLine *next;      /* pasm added another opcode here, if != 0 */
};
/* opcode types */
#define OT_IGNORE 0             /* line is empty or commented out */
#define OT_INSTRUCTION 1
#define OT_DIRECTIVE 2
#define OT_MACRO 3
#define OT_SECTION 4
/* flags */
#define PLF_NONEWLINE 1         /* new statement is still part of same line */
#define PLF_ALIGN 2

struct SourceText {             /* start address and size of all text files */
  struct node n;
  char *name;                   /* name of source, include file or macro */
  char *text;                   /* source text pointer */
  unsigned long nlines;         /* number of lines in source text */
  struct ParsedLine *plin;      /* ParsedLine structures for nlines */
};

#define MAX_MACPARAMS 10        /* macro parameters \0-\9 */
struct MacroParams {
  char *param[MAX_MACPARAMS];   /* parameter strings, which replace \x */
  uint32 call_id;               /* macro call id */
  uint32 narg;                  /* number of arguments */
  char param0[2];               /* parameter 0 contains the branch hint */
};

struct SourceThread {           /* main source, includes and macros */
  struct SourceThread *prev;    /* previous source thread */
  struct MacroParams *macro;    /* only assigned in macro mode */
  struct SourceText *csource;   /* ptr to current source text node */
  char *lineptr;                /* source text pointer to current line */
  char *srcptr;                 /* current source text pointer */
  unsigned long line;           /* current line inside this thread */
  struct Macro *macskip;        /* indicates macro skip mode */
};

struct Expression {
  uint32 value;
  uint8 type;                   /* same type definitions as used for Symbol */
  uint8 reloctype;              /* see struct Reloc */
  struct Symbol *symbol;        /* reloc or extern symbol, used in exp. */
};

struct UserDefine {
  struct node n;
  char *line;                   /* .set <symbol>,<assignment> */
};


#define DEF_MAXERRORS 5
#define MAX_INCPATHS 8
#define MAX_IFLEVELS 16
#define LINEBUFSIZE 1024
#define STRBUFSIZE 256
/* number of entries in hash tables */
#define SYMHTABSIZE 0x4000      /* symbol hash table */
#define INSTRHTABSIZE 0x1000    /* instruction hash table */
#define DIRHTABSIZE 0x800       /* directive hash table */
#define MACROHTABSIZE 0x800     /* macro hash table */
/* supported output formats */
#define OFMT_ABSOLUTE 0
#define OFMT_ELF 1
#define OFMT_EHF 2
#define OFMT_ADOS 3
#define OFMT_LAST 3
/* optimization flags */
#define OPT_FAR_BRANCH 0x00010000
/* "Bcc label" with destination out of range will be converted into a */
/* "B!cc *+8/B label" combination. */

struct GlobalVars {
  char *source_name;            /* source text file name */
  char *dest_name;              /* output file name */

  /* options */
  bool dontwarn;                /* suppress warnings */
  bool noregsymbols;            /* don't predefine register symbols etc. */
  bool noextmnemo;              /* no extended mnemonics */
  bool sixtyfourmode;           /* 64-bit mode activated */
  bool optinstrmode;            /* optional instructions */
  bool supermode;               /* supervisor mode instructions */
  bool autoextern;              /* autom. declare undef. sym. as extern */
  uint8 output;                 /* output format */
  uint32 opt;                   /* optimization flags */
  struct list userdeflist;      /* user defines */
  bool usrdefs;

  int maxerrors;                /* # of errors to display, before aborting */
  int errcnt;                   /* number of errors displayed */
  int returncode;               /* return code for exit() */
  char *incpaths[MAX_INCPATHS]; /* paths where to search for files */
  char *ident;                  /* unit identification or comment */
  char *file;                   /* source file name for debugging */
  unsigned long absbase;        /* base address for absolute code */
  struct list sourcelist;       /* source text list */
  struct list sectionlist;      /* defined sections */

  struct SourceThread *cthread; /* current source thread */
  struct Section *csect;        /* current section */
  struct Symbol *lcsym;         /* location counter symbol '$' */
  struct Symbol *nargsym;       /* symbol for number of arguments '$NARG' */
  struct SourceText *srctxtp;   /* source text pointer for get_source() */
  uint32 macrocnt;              /* incremented on every macro invocation */
  unsigned long absline;        /* absolute line number */
  uint8 pass;                   /* 0 or 1 */
  uint8 iflevel;                /* current level for conditional assembly */
  uint8 ifignore;               /* number of else/endifs to ignore */
  bool ifcond[MAX_IFLEVELS];    /* ifcond[0] is always TRUE */
  bool signedexp;               /* current expression to eval is signed */
  bool alignflag,vc;
  bool anotherpass;             /* true, if another pass is required */

  /* TOC */
  int rtoc;                     /* register number for toc-mode */
  struct Section *tocsect;      /* ptr to TOC section, or NULL */

  /* hash tables */
  struct Symbol **symbols;      /* symbol hash table */
  struct CPUInstr **instr;      /* instruction hash table */
  struct Directive **directives; /* directive hash table */
  struct Macro **macros;        /* macro hash table */

  /* buffers */
  char linebuf[LINEBUFSIZE];    /* buffer for a whole source text line */
  char strbuf[STRBUFSIZE];      /* string buffer for labels, opcodes, etc. */
  uint8 alignment_bytes[4];     /* four zero bytes */
};


/* global functions */

/* main.c */
#ifndef MAIN_C
extern struct GlobalVars gvars;
extern void cleanup(struct GlobalVars *);
#endif

/* version.c */
#ifndef VERSION_C
extern void show_version(void);
extern void show_usage(void);
#endif

/* pass.c */
#ifndef PASS_C
extern void exec_pass1(struct GlobalVars *);
extern void pass1(struct GlobalVars *,struct SourceText *,
                  struct MacroParams *,struct SourceThread *);
extern struct SourceText *include_source(struct GlobalVars *,char *);
extern void exec_pass2(struct GlobalVars *);
extern void pass2(struct GlobalVars *,struct SourceText *,
                  struct MacroParams *,struct SourceThread *);
extern struct SourceText *get_source(struct GlobalVars *);
#endif

/* support.c */
#ifndef SUPPORT_C
extern void *alloc(size_t);
extern void *alloczero(size_t);
extern char *allocstring(char *);
extern void initlist(struct list *);
extern void addtail(struct list *,struct node *);
extern struct node *remhead(struct list *);
extern struct node *remnode(struct node *);
extern char *mapfile(struct GlobalVars *,char *);
extern void checkrange(uint32,int,bool);
extern void lower_case(char *);
#ifdef LITTLEENDIAN
extern uint16 l2bh(uint16);
extern uint32 l2bw(uint32);
#endif
#endif

/* eval.c */
#ifndef EVAL_C
extern char *getsymbol(struct GlobalVars *,char *);
extern char *getarg(struct GlobalVars *,char *);
extern char *skipspaces(char *);
extern char *remquotes(char *);
extern void checkEOL(char *);
extern char *skipexpression(struct GlobalVars *,char *);
extern void read_macro_params(struct GlobalVars *,struct ParsedLine *,
                              struct MacroParams *,char *);
extern char *getexp(struct GlobalVars *,char *,uint32 *,uint8);
extern uint32 makereloc(struct GlobalVars *,struct Expression *);
extern uint32 makexref(struct GlobalVars *,struct Expression *,uint8);
extern char *getintexp(struct GlobalVars *,char *,uint32 *);
extern char *eval_expression(struct GlobalVars *,struct Expression *,char *);
#endif

/* tables.c */
#ifndef TABLES_C
extern void init_hashtables(struct GlobalVars *);
extern void add_macro(struct GlobalVars *,struct Macro *);
extern struct Symbol *add_symbol(struct GlobalVars *,char *,uint8,uint32);
extern struct CPUInstr *search_instr(struct GlobalVars *,char *);
extern unsigned long elf_hash(unsigned char *);
extern struct Symbol *search_symbol(struct GlobalVars *,char *);
extern struct Section *search_section(struct GlobalVars *,char *);
extern void search_opcode(struct GlobalVars *,struct ParsedLine *,
                          char *,char *);
#endif

/* errors.c */
#ifndef ERRORS_C
extern void error(int,...);
extern void ierror(char *,...);
#endif

/* predefs.c */
extern char stdsects[];
extern char stdsets[];
extern char *xmnemos[];

/* elfrelocnames.c */
extern char *elfrel_name[];
#define ELFRELNAMMSK 0x3f  /* number of names in elfrel_name[] minus 1 */

/* instructions.c */
#ifndef INSTRUCTIONS_C
extern struct CPUInstr instructions[];
extern void instr(struct GlobalVars *,struct ParsedLine *);
extern char *check_comma(char *);
extern void pcadd(struct GlobalVars *,unsigned long);
extern void store_byte(struct GlobalVars *,uint8);
extern void store_half(struct GlobalVars *,uint16);
extern void store_word(struct GlobalVars *,uint32);
extern void store_float(struct GlobalVars *,double);
extern void store_double(struct GlobalVars *,double);
extern void store_space(struct GlobalVars *,unsigned long);
#endif

/* directives.c */
#ifndef DIRECTIVES_C
extern struct Directive directives[];
extern void activate_section(struct GlobalVars *,struct Section *);
extern void alignment(struct GlobalVars *,unsigned long);
extern char escchar(char);
#endif

/* output_abs.c */
#ifndef OUTPUT_ABS_C
extern void output_absolute(struct GlobalVars *);
#endif

/* output_elf.c */
#ifndef OUTPUT_ELF_C
extern void output_elf32msb(struct GlobalVars *);
#endif

/* output_ehf.c */
#ifndef OUTPUT_EHF_C
extern void output_ehf(struct GlobalVars *);
#endif
