/**********************************************************************
 *
 *       *** HAPPy Pascal Compiler ***
 *               P-code assmebler
 *
 *           Copyright (c) H.Asano 1992-1994.
 *
 **********************************************************************/

#define EXTERN extern
#define Maxlabel   1800                 /* 最大ラベル数               */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "version.h"
#include "hapai.h"

/********* co-operation table **********/
static char cop[] = {
    115/*lod*/, 105/*ldo*/, 125/*str*/, 120/*sro*/,
    130/*sto*/, 110/*chk*/, 135/*ind*/ } ;

/***************************************/
/*            ラベル表                 */
/***************************************/
enum lbst {  entered,                   /* ラベル未登録の状態         */
             defined                    /* ラベル定義済の状態         */
          };

static struct  {
  short     val         ;               /* value                      */
  enum lbst st          ;               /* status {enterd,defined}    */
} labeltab[Maxlabel]    ;

/***************************************/
/*      仮想計算機 記憶装置            */
/***************************************/
static _store store[Maxstore] ;

/***************************************/
/*            変数定義                 */
/***************************************/
static char *PcodeSourceName ;          /* P-codeｿｰｽﾌｧｲﾙ名　　　　　　*/
static char *CompVersion     ;          /* コンパイラのバージョン     */
static FILE *pcsfile         ;          /* P-codeｿｰｽﾌｧｲﾙﾎﾟｲﾝﾀ         */
static FILE *objfile         ;          /* P-codeｵﾌﾞｼﾞｪｸﾄﾌｧｲﾙﾎﾟｲﾝﾀ    */
static short codesize        ;          /* コードのﾜｰﾄﾞｻｲｽﾞ           */
static short objsize         ;          /* オブジェクト全体のﾜｰﾄﾞｻｲｽﾞ */
static short labelvalue      ;          /* labelの値                  */
static short ch              ;          /* 読込文字                   */
static short intch           ;          /* 読込処理内での読込文字     */
static short  pc             ;          /* program counter            */
static _code  cd             ;          /* P-code                     */
static short start = -1      ;          /* オブジェクト開始の合図     */

/*********************************************************************/

/***************************************/
/*     エラーメッセージ出力処理        */
/***************************************/
static void paerr(short errno)
{
  static struct _errmsg {
       short msgno       ;              /* error message number       */
       char  *errmsg     ;              /* error message              */
     } errtb[]  =  {

   {1,  "命令数が多すぎてアセンブルできない"},
   {2,  "オブジェクトファイルがオープンできない"},
   {3,  "オブジェクトファイル書き込みでエラーが発生した"},
   {4,  "ラベル数が多すぎてアセンブルできない"},
   {5,  "定数を格納するメモリを使い果たしてアセンブルできない"},
   {6,  "P-codeソースファイルがオープンできない"},
   {7,  "PC.EXEとPA.OVLのバージョンが違う"}
} ;
  short i = -1 ;

     while(errtb[++i].msgno != errno) ; /* search message             */
     fprintf(stderr,"A%03d: %s\n", errno, errtb[i].errmsg);
     exit(3) ;                          /* アセンブルエラーで停止     */
}

/***************************************/
/*      reset() : reset関数            */
/***************************************/
static void reset(char *filename)
{

     pcsfile = fopen(filename,"r") ;
     if(pcsfile == NULL) paerr(6) ;

     intch =  getc(pcsfile)  ;          /* 1文字先読み                */
}

/***************************************/
/*       eoln() : eolnマクロ定義       */
/***************************************/
#define eoln()   (intch == '\n')        /* 改行を読んでいれば真       */

/***************************************/
/* readc() : char型read処理            */
/***************************************/
static short readc(void)
{
  short oldch ;

     oldch    = intch         ;
     intch    = getc(pcsfile) ;
     return(oldch)      ;
}

/***************************************/
/*   readi() : integer型入力処理       */
/***************************************/
static integer readi(void)
{
  integer  ival = 0 ;
  short    sign = 1 ;

     if(intch == ' ')
      while((intch = getc(pcsfile)) == ' '); /* 空白読み飛ばし        */

     if((intch=='+') || (intch=='-')) {  /* 符号の時                  */
      sign = (intch=='+') ? 1 : -1  ;    /* 符号に応じた正負          */
      intch  = getc(pcsfile) ;
     }

     do {
      intch -= '0' ;
      ival = ival*10 + intch ;
      if(eoln()) break ;                /* EOLNならばそこまで         */
      intch=getc(pcsfile) ;
     } while(('0' <= intch) && (intch <= '9')) ;

     return(sign*ival) ;
}

/***************************************/
/*   readr() : real型入力処理          */
/***************************************/
static float readr(void)
{
  char   buf[20] ;
  short  i = 0   ;

     while(intch == ' ') intch = getc(pcsfile) ;   /* 空白読み飛ばし */
     do {
      buf[i++] = (char)intch ;
      intch = getc(pcsfile) ;
     } while (intch != '\n') ;
     buf[i] = '\0' ;

     return((float)atof(buf)) ;         /* 浮動小数点へ変換           */
}

/***************************************/
/* readln() : EOLまで読み飛ばす処理    */
/***************************************/
static void readln(void)
{
     while(!eoln()) intch = getc(pcsfile) ;
     intch = getc(pcsfile);
}

/***************************************/
/*    オブジェクトファイル書出処理     */
/***************************************/
static void putobject(char *area, short size)
{
      fwrite(area,size,1,objfile) ;
      if(ferror(objfile)) paerr(3) ;    /* 書き込み失敗               */
}

/***************************************/
/*          初期設定処理               */
/***************************************/
static void init(void)
{
  short i;

     for(i=0;i<Maxlabel;i++) {          /* ラベル表 の 初期設定       */
      labeltab[i].val = -1 ;
      labeltab[i].st  = entered;
     }

     reset(PcodeSourceName) ;           /* P-codeｿｰｽﾌｧｲﾙのｵｰﾌﾟﾝ       */

     objfile = fopen("pcode.pco","wb") ;/* ファイルオープン           */
     if(objfile == NULL) paerr(2) ;     /* オープンできない           */

     if(codesize > Maxstore)            /* 記憶装置の大きさより大きい時*/
      paerr(1) ;                        /* ﾌﾟﾛｸﾞﾗﾑ停止                */

     if(strcmp(CompVersion,version))
      paerr(7) ;                        /* バージョン不一致           */

     putobject(version,strlen(version)+1) ; /* バージョン番号書出     */

     objsize = codesize ;
}

/***************************************/
/*    ラベル表登録処理                 */
/***************************************/
static void update(short x)
{                                       /*  x: label名                */
   short curr,succ ;

     if(x >= Maxlabel) paerr(4) ;       /* ラベル数が多すぎる         */

     if(labeltab[x].val != -1) {        /* 前方参照されている時       */
      curr = labeltab[x].val;
      do {
       succ = store[curr].vo.q;
       store[curr].vo.q = labelvalue;
       curr = succ ;
      } while(succ != -1) ;
     }
     labeltab[x].st  = defined    ;
     labeltab[x].val = labelvalue ;
}

/***************************************/
/*     ラベル値読込処理                */
/***************************************/
static void labelsearch(void)
{
  short x ;

     while(ch != 'L') ch = readc() ;    /* 'L' まで 読み飛ばし        */
     x = (short)readi() ;
     if(labeltab[x].st == entered) {    /* 未定義                     */
      cd.q = labeltab[x].val  ;         /* 初期値のまま　-1           */
      labeltab[x].val = pc    ;         /* 今のpcを格納               */
     }
     else cd.q = labeltab[x].val ;      /* 定義済の時                 */
}


/********* typesymbol() : instruction名4文字目によってopを決定する *********/
static void typesymbol(void)
{
   short i;

     switch(ch) {
      case 'i' : return ;       /* 'i' の時は opはそのまま */
      case 'a' : i=0; break;
      case 'r' : i=1; break;
      case 's' : i=2; break;
      case 'b' : i=3; break;
      case 'c' : i=4;
     }
     cd.op=cop[cd.op]+i;             /* opの変更 */
}

/**************************************/
/* PTN0() : オペランドのない命令      */
/**************************************/
static void PTN0(void)
{
}

/**************************************/
/* PTN1() : lod,strのアセンブル       */
/**************************************/
static void PTN1(void)
{
     typesymbol() ;
     cd.p=(char)readi() ;
     cd.q=(short)readi() ;
}

/**************************************/
/* PTN2() : lda,movのアセンブル       */
/**************************************/
static void PTN2(void)
{
     cd.p=(char)readi() ;
     cd.q=(short)readi() ;
}

/***************************************/
/* PTN3() : mst,cui,bas,traのアセンブル*/
/***************************************/
static void PTN3(void)
{
     cd.p=(char)readi() ;
}

/**************************************/
/* PTN4() : cup,ent,ejpのアセンブル   */
/**************************************/
static void PTN4(void)
{
     cd.p=(char)readi() ;
     labelsearch();
}

/************************************************/
/* PTN5() : equ,neq,geq,grt,leq,lesのアセンブル */
/************************************************/
static void PTN5(void)
{
     switch(ch) {
    /*case 'a' :     ; break ;   何もしない */
      case 'i' : cd.p=1 ; break ;
      case 'r' : cd.p=2 ; break ;
      case 'b' : cd.p=3 ; break ;
      case 's' : cd.p=4 ; break ;
      case 'c' : cd.p=6 ; break ;
      case 'm' : cd.p=5 ;
                 cd.q=(short)readi() ;
     }
}

/**************************************/
/* PTN6() : ldo,sro,indのアセンブル   */
/**************************************/
static void PTN6(void)
{
     typesymbol() ;
     cd.q=(short)readi();
}

/**************************************/
/* PTN7() : inc,decのアセンブル       */
/**************************************/
static void PTN7(void)
{
     switch(ch) {
    /*case 'a' :  cd.p=0;    break ;  何もしない */
      case 'i' :  cd.p=1;    break ;
      case 'r' :  cd.p=2;    break ;
      case 'b' :  cd.p=3;    break ;
      case 'c' :  cd.p=6;    break ;
     }
     cd.q = (short)readi() ;
}

/****************************************/
/* PTN8() : ujp,fjp,lapのアセンブル     */
/****************************************/
#define PTN8  labelsearch

/******************************************/
/* PTN9() : lao,new,dis,wrsのアセンブル   */
/******************************************/
static void PTN9(void)
{
     cd.q=(short)readi();
}

/**************************************/
/* CHK() : chkのアセンブル            */
/**************************************/
static void CHK(void)
{
   integer  lb,ub ;

     typesymbol() ;
     cd.p = (char)readi() ;             /* チェック種別　             */
     lb = readi();                      /* 下限値                     */
     ub = readi();                      /* 上限値                     */
     store[objsize  ].vi = lb ;
     store[objsize+1].vi = ub ;
     cd.q=codesize;
     do {
      cd.q++ ;
     } while((store[cd.q-1].vi != lb) || (store[cd.q].vi != ub)) ;
     if(cd.q   == objsize) objsize++   ;/* 最後に1つだけ定数追加の時  */
     if(cd.q-1 == objsize) objsize += 2;/* 最後に2つの  対数追加の時  */
     if(objsize>=Maxstore) paerr(5)  ;  /* 定数格納不可               */
}

/**************************************/
/* IXA() : ixaのアセンブル            */
/**************************************/
static void IXA(void)
{
  integer lb,ub ;

     lb = readi() ;                     /* 下限値                     */
     ub = readi() ;                     /* ixa値                      */
     store[objsize  ].vi = lb ;
     store[objsize+1].vi = ub ;
     cd.q=codesize;
     do {
      cd.q++ ;
     } while((store[cd.q-1].vi != lb) || (store[cd.q].vi != ub)) ;
     if(cd.q   == objsize) objsize++   ;/* 最後に1つだけ定数追加の時  */
     if(cd.q-1 == objsize) objsize += 2;/* 最後に2つの  対数追加の時  */
     if(objsize>=Maxstore) paerr(5)  ;  /* 定数格納不可               */
}

/**************************************/
/* LCA() : lcaのアセンブル            */
/**************************************/
/* lca '文字列'\n という形式とする。最後が改行で終わる。最初の ' と
  改行の前の ' は P-codeソースを読む人間にわかりやすいようについているだけ */

static void LCA(void)
{
     cd.q=objsize;
     readc() ;                          /* 最初の ' を読み飛ばす      */
     while(!eoln()) {                   /* 改行までの文字列を格納     */
      store[objsize++].vc = readc() ;
      if(objsize>=Maxstore)
       paerr(5) ;                       /* 定数格納不可               */
     }
     objsize-- ;                        /* 最後の ' を捨てる          */
}

/**************************************/
/* LDC() : ldcのアセンブル            */
/**************************************/
static void LDC(void)
{
  integer lnumber ;
  float   rnumber ;
  long    s,s1    ;

     switch(ch) {
      case 'i' : cd.p=1 ;               /* integer type               */
                 lnumber = readi() ;
                 if(labs(lnumber) < (long)Largeint)
                  cd.q = (short)lnumber;
                 else {                 /* 大きな数の時               */
                  cd.op = 66 ;          /* lci命令に変更              */
                  store[objsize].vi = lnumber ;
                  cd.q=codesize-1;
                  do ;
                  while(store[++cd.q].vi != lnumber) ;
                  if(cd.q == objsize) {
                   objsize++  ;
                   if(objsize>=Maxstore)
                    paerr(5) ;          /* 定数格納不可               */
                  }
                 }
                 break ;

      case 'c' : cd.p=6 ;               /* char type                  */
                 while((ch=readc())==' ') ;
                 cd.q=readc() ;
                 break ;

      case 'b' : cd.p=3;                /* boolean                    */
                 cd.q=(short)readi() ;
                 break;

      case 's' : cd.p = 4;              /* set type                   */
                 s=0;
                 while(readc() != '(') ;
                 ch=readc() ;
                 while(ch!=')') {
                  s1=(short)readi() ;
                  addset(s,s1)  ;
                  ch=readc()   ;
                 }
                 store[objsize].vs = s ;
                 cd.q = codesize ;
                 while(store[cd.q].vs != s) cd.q++ ;
                 if(cd.q==objsize) {
                  objsize++ ;
                  if(objsize>=Maxstore)
                   paerr(5) ;           /* 定数格納不可               */
                 }
                 break ;

      case 'r' : cd.p = 2 ;
                 rnumber = readr() ;
                 store[objsize].vr = rnumber ;
                 lnumber = store[objsize].vi ;
                 /* 浮動小数点の形で比較を行うと思わぬ落とし穴があるので
                   ビット列の比較をするために、union型のviから数を得る*/
                 cd.q=codesize-1;
                 do ;
                 while(store[++cd.q].vi != lnumber) ;
                 if(cd.q == objsize) {
                  objsize++  ;
                  if(objsize>=Maxstore)
                   paerr(5) ;           /* 定数格納不可               */
                 }
                 break ;

    /*case 'n' : cd.p=0 ; */            /* ldc nil                    */
              /* cd.q=0 ; */
     }
}

/**************************************/
/* ORD() : ordのアセンブル            */
/**************************************/
static void ORD(void)
{
     switch(ch) {
      case 'b' : cd.p=3 ; break ;
      case 'c' : cd.p=6 ;
     }
}

/**************************************/
/* RET() : retのアセンブル            */
/**************************************/
static void RET(void)
{
     switch(ch) {
    /*case 'p' :     ; break ;  何もしない */
      case 'i' : cd.p=1 ; break ;
      case 'r' : cd.p=2 ; break ;
      case 'c' : cd.p=3 ; break ;
      case 'b' : cd.p=4 ; break ;
      case 'a' : cd.p=5 ; break ;
     }
}

/**************************************/
/* STO() : stoのアセンブル            */
/**************************************/
#define STO  typesymbol

/**********************************************************************/
/*                   P-code ニーモニック表                            */
/**********************************************************************/
static char instr[][4] =
{
   /*          x0    x1    x2    x3    x4    x5    x6    x7    x8    x9  */
   /* 0x */  "lod","ldo","str","sro","sto","chk","ind","ldc","lda","dec",
   /* 1x */  "inc","mst","cup","ent","ret","...","ixa","equ","neq","geq",
   /* 2x */  "grt","leq","les","ujp","fjp","xjp","ejp","lap","adi","adr",
   /* 3x */  "sbi","sbr","sgs","flt","flo","trc","ngi","ngr","sqi","sqr",
   /* 4x */  "abi","abr","not","and","ior","dif","int","uni","inn","mod",
   /* 5x */  "odd","mpi","mpr","dvi","dvr","mov","lca","lao","stp","ord",
   /* 6x */  "chr","ujc","mms","msi","cui","bas","...","cka","tra","rou",
   /* 7x */  "...","...","...","...","...","new","dis","pge","eof","eol",
   /* 8x */  "rst","rwt","get","put","wrs","wrb","wri","wrr","wrc","wrf",
   /* 9x */  "wln","rdi","rdr","rdc","rln","trs","trw","tgt","tpt","atn",
   /*10x */  "sin","cos","exp","log","sqt"
} ;

/******* ニーモニック対応のエントリ表 ********/
static struct entry {
       void (*func)(void) ;
} OP[] = {
   /*          x0    x1    x2    x3    x4    x5    x6    x7    x8    x9  */
   /* 0x */   PTN1, PTN6 ,PTN2, PTN6, STO , CHK , PTN6, LDC , PTN2, PTN7,
   /* 1x */   PTN7, PTN3, PTN4, PTN4, RET , PTN0, IXA , PTN5, PTN5, PTN5,
   /* 2x */   PTN5, PTN5, PTN5, PTN8, PTN8, PTN0, PTN4, PTN8, PTN0, PTN0,
   /* 3x */   PTN0, PTN0, PTN0, PTN0, PTN0, PTN0, PTN0, PTN0, PTN0, PTN0,
   /* 4x */   PTN0, PTN0, PTN0, PTN0, PTN0, PTN0, PTN0, PTN0, PTN0, PTN0,
   /* 5x */   PTN0, PTN0, PTN0, PTN0, PTN0, PTN2, LCA , PTN9, PTN0, ORD ,
   /* 6x */   PTN0, PTN0, PTN0, PTN0, PTN3, PTN3, PTN0, PTN0, PTN3, PTN0,
   /* 7x */   PTN0, PTN0, PTN0, PTN0, PTN0, PTN9, PTN9, PTN0, PTN0, PTN0,
   /* 8x */   PTN0, PTN0, PTN0, PTN0, PTN9, PTN0, PTN0, PTN0, PTN0, PTN0,
   /* 9x */   PTN0, PTN0, PTN0, PTN0, PTN0, PTN0, PTN0, PTN0, PTN0, PTN0,
   /*10x */   PTN0, PTN0, PTN0, PTN0, PTN0
} ;

/***************************************/
/*     1行アセンブル処理               */
/***************************************/
static void assemble(void)
{
   char name[4];

     *name     = (char)readc()  ;       /* 1文字目 */
     *(name+1) = (char)readc()  ;       /* 2文字目 */
     *(name+2) = (char)readc()  ;       /* 3文字目 */
     *(name+3) = '\0'     ;
     if(!eoln()) ch=readc();
      /* そこで行が終わってなければ次の文字を読む */

     cd.op = -1 ;
     while(strcmp(instr[++cd.op],name)) ;/* instructionよりopを決定   */
         /* このようなリニアサーチはスピードが遅いので改良しよう      */

     cd.p   =  0 ;
     cd.q   =  0 ;
     OP[cd.op].func() ;                 /* opに対応したアセンブル     */

     store[pc++].vo = cd ;              /* メモリに格納               */
}

/********** generate() :  行の最初に呼ばれる処理 **********/
static void generate(void)
{
   short x     ;                        /* label値                    */
   short i     ;                        /* 作業カウンタ               */
   char progname[33] ;                  /* プログラム名               */
   char filename[33] ;                  /* ファイル名                 */
   short fileadr       ;                /* ファイルアトレス           */
   short filesize      ;                /* バッファ変数の大きさ       */

     for(;;) {
      ch=readc();                       /* 行の最初の文字を読む       */
      switch(ch) {
       case ' ' : assemble();           /* 通常命令 その行をアセンブル*/
                  break     ;
       case 'L' : x=(short)readi();     /* label名を読む              */
                  if(!eoln()) ch=readc() ;
                  if(ch=='=') labelvalue=(short)readi();
                                                   /* label値がある時 */
                  else         labelvalue=pc;      /* ない時はpcの値  */
                  update(x);                       /* label登録       */
                  break ;
       case 'F' :                       /* ファイルアドレス           */
                  readc() ;             /* 空白を読み飛ばす           */
                  i=0 ;
                  while((ch=readc()) != ' ') /* ファイル名取得        */
                   filename[i++] = (char)ch ;
                  filename[i++] = '\0'  ;
                  readc() ;                       /*  空白を読み飛ばす*/
                  fileadr = (short)readi() ;      /* ファイルアトレス */
                  putobject((char*)&fileadr,sizeof(fileadr));/* 書出  */
                  filesize = (short)readi();      /* バッファサイズ   */
                  putobject((char*)&filesize,sizeof(filesize));/* 書出*/
                  putobject(filename,i) ;         /* ファイル名書出   */
                  break ;

       case ';' :                       /* 注釈行を無視               */
                  break ;
       case 'N' :                       /* プログラム名               */
                  readc() ;             /* 空白を読み飛ばす           */
                  i=0 ;
                  while(!eoln())        /* プログラム名取得           */
                   progname[i++] = (char)readc() ;
                  progname[i++] = '\0' ;
                  putobject(progname,i);/* 書出                       */
                  break ;

       case 'Q' : return ;              /* アセンブル終わり           */

       default  : ;
      }
      readln() ;                        /* 行の残りを読み飛ばし       */
     }
}

/*****************************************/
/* assemblelist() : アセンブルリスト出力 */
/*****************************************/
static void assemblelist(void)
{
     reset(PcodeSourceName) ;   /* P-codeｿｰｽﾌｧｲﾙのｵｰﾌﾟﾝ       */

     printf("\n ADDR   OP   P      Q    P-code source statement\n");
     printf(  "================================================\n");

     pc  = 0 ;

     for(;;) {
      ch=readc();                       /* 行の最初の文字             */
      switch(ch) {
       case 'N' :
       case 'F' :
       case 'Q' :
       case ';' :
       case 'L' :  printf("                         %c",ch);
		   while(!eoln()) printf("%c",readc());
		   printf("\n");
		   readln();
		   if(ch=='Q') {
		    if(codesize != objsize)
		     printf(" %4d:\n   〜: constant data\n %4d:\n",
         	              codesize,objsize-1) ;
                    fclose(pcsfile) ;   /* ソースファイルのクローズ   */
		    return;
		   }
		   break;

       case ' ' :  printf(" %4d: %3d %3d %6d",
		       pc, store[pc].vo.op, store[pc].vo.p ,
		           store[pc].vo.q);
		   pc++;
		   printf("    %c",ch);
		   while(!eoln()) printf("%c",readc());
		   printf("\n");
		   readln();
		   break;
      }
     }
}

/***************************************/
/* main() : P-codeアセンブラメイン処理 */
/***************************************/
short main(short argc,char *argv[])
{
     codesize = atoi(argv[1]) ;         /* 命令コードの数             */
     PcodeSourceName = argv[2] ;        /* P-codeソースファイル名     */
     CompVersion     = argv[3] ;        /* コンパイラのバージョン     */
     init();                            /* 各種初期設定               */
     generate();                        /* アセンブル                 */
     fclose(pcsfile)  ;                 /* ソースファイルのクローズ   */

     for(pc=0;pc<codesize;pc++) {       /* qｵﾍﾟﾗﾝﾄﾞの補正処理         */
      cd.op = store[pc].vo.op ;
      if((/*lao*/ cd.op==57)||(/*ldox*/cd.op==1 || (105<=cd.op && cd.op<=109))
                            ||(/*srox*/cd.op==3 || (120<=cd.op && cd.op<=124)))
       store[pc].vo.q += objsize ;
     }

     putobject((char*)&start,sizeof(start)) ; /* オブジェクト開始合図 */
     putobject((char*)store,objsize*sizeof(_store)) ; /* オブジェクト出力*/
     if(fflush(objfile) == EOF) paerr(3) ;
     if(fclose(objfile) == EOF) paerr(3) ;
                 /* クローズできなければ書き込み失敗の可能性がある    */

     fputs(         " *** Assemble completed. ***\n",stderr);
     fprintf(stderr," *** code = %5d words  constant data = %5d words ***\n",
                                codesize,       objsize-codesize);

    if(argc==5) assemblelist();         /* アセンブルリストの出力     */

    return(0) ;                         /* 正常終了                   */
}
