/************************************************
 **
 **    *** 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"

extern void interpret(void) ;   /* P-code命令解釈実行処理             */

_store  store[Maxstore] ;       /* 記憶装置                           */

_store  *sp  ;                  /* スタックポインタ                   */
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をした時 真 */


static char *pcofname = "pcode.pco" ; /* P-codeオブジェクトファイル名 */
static char progname[33] ;      /* Pascalプログラム名                 */

static short   objsize  ;
static FILE    *pcofile ;
static boolean infor = false ;  /* -iオプション ｲﾝﾀﾌﾟﾘﾀ情報出力       */


/******************************************/
/* prerr() : Run-timeエラーメッセージ出力 */
/******************************************/
void prerr(short errno,char *msg)
{
  short i ;

     fprintf(stderr,"\n*** [ADDR=%d] HAPPy Run-time error R%03d:\n   --  %s",
                    pc-1,errno,msg) ;
     fprintf(stderr," : 処理打ち切り ***\n");

     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) ;                /* エラーチェックはしない     */
     }

     exit(1) ;                          /* 異常終了                   */
}

/****************************************/
/*   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) ;
}

/***************************************/
/* information() : ｲﾝﾀﾌﾟﾘﾀ情報出力処理 */
/***************************************/
static void information(void)
{
   fprintf(stderr,
    "HAPPy P-code Interpreter Version %s  Copyright (c) H.Asano 1992-1994.\n",
                                      version) ;
 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ｵﾌﾞｼﾞｪｸﾄ(pcode.pco)を実行します。\n",
         stderr);
}

/*****************************************/
/*  inputfilename() : ファイル名入力処理 */
/*****************************************/
static void inputfilename(void)
{
  short i,j ;
  short  ch ;

     fputs("*HAPPy: ファイル( ",stderr) ;
     for(i=2;i<fileno;i++)  fprintf(stderr,"%s ",fi[i].filename) ;
     fputs(")の実ファイル名を入力して下さい\n",stderr);

     for(i=2;i<fileno;i++) {
      fi[i].textfile = false      ;     /* とりあえずテキストでない   */
                                        /* としておく                 */
      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' ;
     }

     fprintf(stderr,"\n*HAPPy: プログラム(%s)を実行します\n",progname);
}

/***************************************/
/*      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 = 2 ;

     pcofile = fopen(pcofname,"rb");
     if(pcofile==NULL) {
      fprintf(stderr,
"I001: P-codeオブジェクトファイル(%s)がない\n",pcofname);
      exit(2);
     }
     objsize = (short)filelength(fileno(pcofile));
     i = 0 ;
     do {                               /* バージョン番号を読む       */
      ch = (char)fgetc(pcofile) ;
      if(feof(pcofile)) {          /* 途中でファイルが終わってしまった*/
       fprintf(stderr,
"I002: P-codeオブジェクトファイル(%s)が不当である",pcofname) ;
       exit(2) ;
      }
     } while((compversion[i++]=ch)) ;
     headerlen = i ;
     if(strcmp(compversion,version)) {
      fprintf(stderr,
"I003: コンパイラ(Version %s)とインタプリタ(Version %s)のバージョンが違う\n",
                    compversion, version) ;

      exit(2);
     }
     i = 0 ;
     do {                               /* プログラム名を読む         */
      progname[i] = (char)fgetc(pcofile) ;
     } while(progname[i++]) ;
     headerlen += i ;

     while((fi[fileno].fileadr = getw(pcofile)) != -1) {
      headerlen += sizeof(short) ;
      fi[fileno].filesize = getw(pcofile) ; /* バッファ変数の大きさ   */
      headerlen += sizeof(short) ;
      i = 0 ;
      do {                               /* ファイル名を読む          */
       fi[fileno].filename[i] = (char)fgetc(pcofile) ;
      } while(fi[fileno].filename[i++]) ;
      fi[fileno].mode = undefined ;     /* ファイルモードは不定       */

      headerlen += i ;
      fileno++ ;
     }
     if(feof(pcofile)) {                /* コード部分がない場合       */
      fprintf(stderr,
"I002: P-codeオブジェクトファイル(%s)が不当である",pcofname) ;
      exit(2) ;
     }

     headerlen += sizeof(short) ;
     objsize   -= headerlen ;
     fread((char*)store,objsize,1,pcofile) ; /* P-codeオブジェクトを読む*/
     objsize /= sizeof(_store) ;

     if(infor) {                        /* -iオプション指定           */
      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\n",Maxstore-objsize) ;
     }

     if(fileno>2) inputfilename() ;     /* input,output以外のﾌｧｲﾙがある*/
     for(i=0;i<fileno;i++) {
      fi[i].fileadr += objsize ;        /* ファイルアドレスの修正     */
      fi[i].filebuf =  store + fi[i].fileadr ; /* バッファ変数アドレス*/
     }

     signal(SIGINT,cntl_c) ;            /* CTRL-Cシグナル登録        */
     signal(SIGFPE,real_err) ;          /* 実数演算シグナル登録      */
}

/******************************************/
/*  main() : P-codeインタプリタメイン処理 */
/******************************************/
void main(short argc,char **argv)
{
  short i,j ;

     for(i=2;i<=argc;i++) {             /* オプションの処理           */
      if(**++argv == '-') {
       for(j=1;*(*argv+j)!='\0';j++) {
        switch(tolower(*(*argv+j))) {   /* 大文字の時は小文字に変換   */
         case 'i' : infor = true   ;    /* information option         */
                    break          ;
         case 't' : trace = true   ;    /* trace option               */
                    break          ;
        }
       }
      }
     }

     if(infor) information() ;          /* -i  ｲﾝﾀﾌﾟﾘﾀ情報出力        */
     init()          ;                  /* 各種初期設定               */

     pc = 0          ;                  /* Program Counter 初期設定   */
     mp = objsize    ;                  /* mp              初期設定   */
     ep = mp + 5     ;                  /* ep              初期設定   */
     np = Maxstore   ;                  /* np              初期設定   */
     sp = store+objsize-1  ;            /* Stack Pointer   初期設定   */

     interpret() ;                      /* P-code命令解釈実行         */
}
