/************************************************
 **
 **    *** HAPPy P-code Interpreter ***
 **
 **             メイン処理
 **
 **          Copyright (c) H.Asano. 1992-1994.
 ************************************************/

#define EXTERN

#include <signal.h>
#include <process.h>
#include <io.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <float.h>
#include "version.h"
#include "hapai.h"

#define abnormal  1                     /* usage関数への引数          */
#define normal    0                     /* usage関数への引数          */

extern void interpret(void) ;   /* P-code命令解釈実行処理             */

_store  store[Maxstore] ;       /* 記憶装置                           */

_store *sp   ;                  /* stack pointer                      */
short   pc   ;                  /* program counter                    */
short   mp   ;                  /* begginning of a data segment       */
short   ep   ;                  /* the maxmum extent of the stack     */
short   np   ;                  /* top of the dynamically allocated area */

_code   cd   ;                  /* P-code                             */

short   fileno   ;              /* ファイル数                         */

boolean trace             ;     /* 命令トレースフラグ                 */
boolean readlnflag = true ;     /* 起動時及びinputにreadlnをした時 真 */

const  char *ext = ".pco" ;     /* P-codeオプジェクトファイルの拡張子 */
static char pcofname[50]  ;     /* P-codeオブジェクトファイル名       */
static char progname[33]  ;     /* Pascalプログラム名                 */

static short   objsize  ;
static FILE    *pcofile ;
static boolean infor = false ;          /* -iｵﾌﾟｼｮﾝ  ｲﾝﾀﾌﾟﾘﾀ情報出力  */
static boolean fileoptflag = false ;    /* /fｵﾌﾟｼｮﾝ  ﾌｧｲﾙ名指定       */
static short   fno      ;               /* /fｵﾌﾟｼｮﾝで取得したﾌｧｲﾙ数   */
static int     wargc    ;               /* argc 退避                  */
static char    **wargv  ;               /* argv 退避                  */

/******************************************/
/*  puteoln() : 終了時のeoln付与処理      */
/******************************************/
void puteoln(void)
{
   short i ;

     for(i=0;i<fileno;i++) {            /* ファイルクローズを行う     */
      if((fi[i].mode == generation) &&  /* 生成モードでテキストで     */
         (fi[i].textfile) && (!fi[i].writelnflag)) /* 最後が改行でない*/
        fputc('\n',fi[i].fp) ;          /* 改行を付け加える           */
      fclose(fi[i].fp) ;                /* エラーチェックはしない     */
     }
}

/******************************/
/* title() : タイトル表示処理 */
/******************************/
static void title(void)
{
  static putflag = false ;

  if(!putflag) {                        /* 一度だけ表示する           */
   fprintf(stderr,
    "HAPPy P-code Interpreter Version %s  Copyright (c) H.Asano 1992-1994.\n",
                                    version) ;
   putflag = true ;
  }
}

/******************************************/
/* prerr() : Run-timeエラーメッセージ出力 */
/******************************************/
void prerr(short errno,char *msg)
{
     fprintf(stderr,"\n*** [ADDR=%d] HAPPy Run-time error R%03d:\n   --  %s",
                    pc-1,errno,msg) ;
     fputs(" : 処理打ち切り ***\n",stderr);
     puteoln() ;                        /* ファイルクローズ & eoln付与*/
     exit(1)   ;                        /* ランタイムエラーで終了     */
}

/**********************************************/
/* pierr() : インタプリタエラーメッセージ出力 */
/**********************************************/
static void pierr(char *msg, char *umecomi)
{
     title() ;
     fprintf(stderr,msg,umecomi) ;
     exit(2) ;                          /* その他のエラーで終了       */
}

/****************************************/
/*   cntl_c(): cntl_cが押された時の処理 */
/****************************************/
static void cntl_c(int sig, int subcode)
{
     prerr(152,"<CTRL-C>を受け付けた");
}

/****************************************/
/*   real_err(): 浮動小数点例外         */
/****************************************/
static void real_err(int sig, int subcode)
{
  char *type ;
  char buf[80] ;

     switch(subcode) {
      case FPE_INVALID        : type = "invalid";             break ;
      case FPE_OVERFLOW       : type = "overflow";            break ;
      case FPE_STACKOVERFLOW  : type = "stack overflow";      break ;
      case FPE_STACKUNDERFLOW : type = "stack underflow";     break ;
     }
     sprintf(buf,"実数演算で異常が起きた(%s)",type) ;
     prerr(150,buf) ;
}

/******************************/
/* usage() : 使用方法出力処理 */
/******************************/
static void usage(short type)
{
 title() ;
 if(type==abnormal) fputs("\nI004: 起動パラメータが誤っている\n",stderr);
 else  {                                /* pi のみで ｱｰｷﾞｭﾒﾝﾄなしの時 */
  fputs("\n  HAPPy is the H.Asano Pascal Processing system. (^_^)\n",
             stderr);
  fputs(
   "\n  HAPPyはISO7185規格水準0にほぼ準拠したMS-DOS汎用Pascal処理系です。\n",
            stderr) ;
  fputs("  HAPPyの複写・再配付は自由です。\n", stderr) ;
 }

 fputs("\n  piｺﾏﾝﾄﾞはpcｺﾏﾝﾄﾞで作ったP-codeｵﾌﾞｼﾞｪｸﾄを実行します。\n\n",stderr);

 fputs("  使い方:  pi [ ｵﾌﾟｼｮﾝ...] P-codeｵﾌﾞｼﾞｪｸﾄﾌｧｲﾙ名[.pco] [ ｵﾌﾟｼｮﾝ...]\n",
       stderr) ;
 fputs("  ｵﾌﾟｼｮﾝ:\n",stderr);
 fputs("     -i ･･･ インタプリタ情報を出力する\n",stderr);
 fputs("     -t ･･･ トレースをを標準出力に出力する\n",stderr);
 fputs("     /f ･･･ プログラム引数のファイル名と実ファイル名を対応させる\n",
        stderr);
 fputs("                 例:  /fputfile=h:\\work\\list.txt\n",stderr) ;

 exit(2) ;
}

/************************************/
/* information() : -iオプション処理 */
/************************************/
static void information(void)
{
  short i,j     ;
  short max = 0 ;

     title()  ;
     fprintf(stderr,"\n * Program name = %s\n",progname) ;
     fprintf(stderr," * Total memory = %5d words\n",Maxstore) ;
     fprintf(stderr," * Object size  = %5d words\n",objsize)  ;
     fprintf(stderr," * stack/heap   = %5d words\n",Maxstore-objsize) ;
     if(fileno!=2) {
      fputs(" * input,output以外のファイルと実ファイルの対応\n",stderr) ;
      for(i=2;i<fileno;i++)
       if(strlen(fi[i].filename) > max) max = strlen(fi[i].filename) ;
      for(i=2;i<fileno;i++) {
       fprintf(stderr,"    %s",fi[i].filename) ;
       for(j=strlen(fi[i].filename);j<max;j++)
        fputc(' ',stderr) ;             /* 最大の長さに合わせるため   */
       fprintf(stderr," = %s\n",fi[i].rfname) ;
      }
     }
     fputc('\n',stderr) ;
}

/******************************************/
/*  fileoption() : ファイルオプション処理 */
/******************************************/
static void fileoption(char *fileopt)
{
  short i=0 ;
  short j   ;
  short len ;
  char logicalf[MaxIDlng+1] ;           /* プログラム上のファイル名   */

     if(fileno==2) usage(abnormal) ;    /* ﾌｧｲﾙ未使用時 /f は 指定不可*/

     while(*(fileopt+i)!='=') i++ ;     /* プログラム上のファイル名取得*/
     if(i>MaxIDlng) i=MaxIDlng ;
     for(j=0;j<i;j++)
      logicalf[j] = (char)tolower(*(fileopt+j)) ;
     logicalf[i] = '\0' ;

     j=2;
     while((j<fileno) &&
           strcmp(fi[j].filename,logicalf)) j++ ;
     if(j==fileno) {                 /* プログラム上で使われていない時*/
      title() ;
      fprintf(stderr,"I005:/fオプション(%s)無効 : 無視する\n",fileopt) ;
      return ;
     }
     if(fi[j].askflag) {                /* まだ/fｵﾌﾟｼｮﾝで未処理のﾌｧｲﾙ */
      fi[j].askflag = false ;
      fno++ ;                           /* /fｵﾌﾟｼｮﾝ で 指定したﾌｧｲﾙ数 */
     }
     len = strlen(fileopt)-i-1 ;
     if(len > MaxRFlen) len = MaxRFlen ;
     strncpy(fi[j].rfname,fileopt+i+1,len) ;
     fi[j].rfname[len] = '\0' ;
}

/*****************************************/
/*  inputfilename() : ファイル名入力処理 */
/*****************************************/
static void inputfilename(void)
{
  short i,j ;
  short  ch ;

     title() ;
     fputs("*HAPPy: ファイル( ",stderr) ;
     for(i=2;i<fileno;i++)
      if(fi[i].askflag)  fprintf(stderr,"%s ",fi[i].filename) ;
     fputs(")の実ファイル名を入力して下さい\n",stderr);

     for(i=2;i<fileno;i++)
      if(fi[i].askflag) {
       fprintf(stderr," %s : ",fi[i].filename) ;
       while(((ch=getc(stdin)) == ' ') || (ch == '\t'));
                                        /* 空白,タブの読み飛ばし      */
       j = 0 ;
       while(ch != '\n') {
        fi[i].rfname[j++] = (char)ch ;
        if(j == MaxRFlen) break ;
        ch = getc(stdin) ;
       }  ;
       fi[i].rfname[j] = '\0' ;
      }
     fputs("*HAPPy: ファイル名入力終了 *\n\n",stderr) ;
}

/***************************************/
/*   fcheck() : ファイルチェック処理    */
/***************************************/
static void fcheck(void)
{
     if(feof(pcofile))            /* 途中でファイルが終わってしまった*/
      pierr("I002: P-codeオブジェクトファイル(%s)が不当である",pcofname) ;
}

/***************************************/
/*      init() :  初期設定処理         */
/***************************************/
static void init(void)
{
  short i   ;
  char ch   ;
  short headerlen     ;                 /* ヘッダ部分の長さ           */
  char compversion[6] ;                 /* コンパイラバージョン番号   */

  /**** input,outputファイルのファイル情報を設定する ****/

     strcpy(fi[0].filename,"input")  ;
     fi[0].fileadr  = inputadr       ;
     fi[0].filesize = 1              ;
     strcpy(fi[0].rfname,"標準入力") ;
     fi[0].fp       = stdin          ;  /* 標準入力                   */
     fi[0].mode     = inspection     ;  /* 検査モード                 */
     fi[0].textfile = true           ;  /* テキストファイル           */

     strcpy(fi[1].filename,"output") ;
     fi[1].fileadr  = outputadr      ;
     fi[1].filesize = 1              ;
     strcpy(fi[1].rfname,"標準出力") ;
     fi[1].fp       = stdout         ;  /* 標準出力                   */
     fi[1].mode     = generation     ;  /* 生成モード                 */
     fi[1].textfile = true           ;  /* テキストファイル           */
     fi[1].writelnflag = true        ;

     fileno = fno = 2 ;

     objsize = (short)filelength(fileno(pcofile));
     i = 0 ;
     do {                               /* バージョン番号を読む       */
      ch = (char)fgetc(pcofile) ;
      fcheck() ;
     } while((compversion[i++]=ch)) ;
     headerlen = i ;
     if(strcmp(compversion,version))
      pierr(
       "I003: コンパイラ(Version %s)とバージョンが一致しない\n",compversion) ;
     i = 0 ;
     do {                               /* プログラム名を読む         */
      progname[i] = (char)fgetc(pcofile) ;
      fcheck() ;
     } while(progname[i++]) ;
     headerlen += i ;

     while((fi[fileno].fileadr = getw(pcofile)) != -1) {
      fcheck() ;
      headerlen += sizeof(short) ;
      fi[fileno].filesize = getw(pcofile) ; /* バッファ変数の大きさ   */
      headerlen += sizeof(short) ;
      i = 0 ;
      do {                               /* ファイル名を読む          */
       fi[fileno].filename[i] = (char)fgetc(pcofile) ;
       fcheck() ;
      } while(fi[fileno].filename[i++]) ;
      fi[fileno].mode = undefined  ;    /* ファイルモードは不定       */
      fi[fileno].askflag  = true   ;    /* ﾌｧｲﾙ名問い合わせ要としておく*/
      fi[fileno].textfile = false  ;    /* テキストでないとしておく   */

      headerlen += i ;
      fileno++ ;
     }
     fcheck() ;

     headerlen += sizeof(short) ;
     objsize   -= headerlen ;
     fread((char*)store,objsize,1,pcofile) ; /* P-codeオブジェクトを読む*/
     objsize /= sizeof(_store) ;

     for(i=0;i<fileno;i++) {
      fi[i].fileadr += objsize ;        /* ファイルアドレスの修正     */
      fi[i].filebuf =  store + fi[i].fileadr ; /* バッファ変数アドレス*/
     }
}

/******************************************/
/*  main() : P-codeインタプリタメイン処理 */
/******************************************/
void main(int argc,char **argv)
{
  short i,j ;
  boolean getfileflag = false ;

     wargc = argc ;
     wargv = argv ;

     if(argc < 2)  usage(normal);       /* アーギュメントなしはusage  */

     while(--argc) {                    /* オプションの処理           */
      if(**++argv == '-') {
       for(i=1;*(*argv+i)!='\0';i++)
        switch(*(*argv+i)) {
         case 'I' :
         case 'i' : infor = true   ;    /* information option         */
                    break          ;
         case 'T' :
         case 't' : trace = true   ;    /* trace option               */
                     break         ;
         default  : usage(abnormal);    /* -t -i 以外はエラー         */
        }
       if(i==1)  usage(abnormal);       /* - だけの時はusage          */
      }
      else if(**argv == '/') {
       if((*(*argv+1) == 'f') || (*(*argv+1) == 'F')) {
        j=2 ;
        while((*(*argv+j)) &&           /* /f････=･････ であること    */
              (*(*argv+j)!='=')) j++ ;  /*    をチェックする          */
        if((*(*argv+j)!='=') ||         /*   = がない                 */
           !(*(*argv+j+1))   ||         /*   = で終わっている         */
            (j==2))   usage(abnormal) ; /*   /f のみ                  */
        fileoptflag = true ;            /* /fｵﾌﾟｼｮﾝ あり              */
       }
      }
      else {                            /* - / から始まらないｱｰｷﾞｭﾒﾝﾄ */
       if(getfileflag) {                /* すでにソースファイル名を   */
         usage(abnormal);               /* 取得している時は 誤り      */
         break ;                        /* whileループ脱出            */
       }
       getfileflag = true      ;        /* ファイル名取得済とする     */
       *pcofname   = '\0'      ;
       strcpy(pcofname, *argv) ;
       j=0 ;                             /* 拡張子が省略されているか   */
       while((*(pcofname+j)!='\0') && (*(pcofname+j)!='.')) j++ ;
       if(*(pcofname+j)=='\0') strcat(pcofname,ext) ; /* 拡張子を付与  */
      }
     }

     if(getfileflag) {                  /* ファイル名が指定された時   */
      pcofile = fopen(pcofname,"rb");
      if(pcofile==NULL)
       pierr("I001: P-codeオブジェクトファイル(%s)がない\n",pcofname);
     }
     else usage(abnormal) ;             /* ファイル名の指定がない時   */

     init()          ;                  /* 各種初期設定               */

     if(fileoptflag)                    /* /fｵﾌﾟｼｮﾝあり               */
      while(--wargc)
       if(**++wargv == '/')
        if((*(*wargv+1) == 'f') || (*(*wargv+1) == 'F'))
         fileoption(*wargv+2) ;

     if(fileno!=fno) inputfilename() ;  /* 全ﾌｧｲﾙが決定していない時   */

     if(infor) information() ;          /* -i  ｲﾝﾀﾌﾟﾘﾀ情報出力        */


     signal(SIGINT,cntl_c) ;            /* CTRL-Cシグナル登録         */
     signal(SIGFPE,real_err) ;          /* 実数演算シグナル登録       */

     pc = 0          ;                  /* Program Counter 初期設定   */
     mp = objsize    ;                  /* mp              初期設定   */
     ep = mp + 5     ;                  /* ep              初期設定   */
     np = Maxstore   ;                  /* np              初期設定   */
     sp = store+mp-1 ;                  /* Stack Pointer   初期設定   */

     interpret() ;                      /* P-code命令解釈実行         */
}
