/*
    HLE bios emulation
    ==================

    Written by BERO
*/

#include        "fpse.h"

#define   PSADDR2(addr,base)  ((int)addr-(int)base)
#define   PSADDR(addr,base)   (int)(addr?PSADDR2(addr,base):(int)addr)
#define   PCADDR(addr,base)   (addr?(addr+base):(void*)addr)

static int bios_a0();
static int bios_b0();
static int bios_c0();

static char *a0name[] = {
     /* 0x00 */     "open",
     /* 0x01 */     "lseek",
     /* 0x02 */     "read",
     /* 0x03 */     "write",
     /* 0x04 */     "close",
     /* 0x05 */     "ioctl",
     /* 0x06 */     "exit",
     /* 0x07 */     "bios_a0_07",
     /* 0x08 */     "getc",
     /* 0x09 */     "putc",
     /* 0x0a */     "todigit",
     /* 0x0b */     "atof",
     /* 0x0c */     "strtoul",
     /* 0x0d */     "strtol",
     /* 0x0e */     "abs",
     /* 0x0f */     "labs",
     /* 0x10 */     "atoi",
     /* 0x11 */     "atol",
     /* 0x12 */     "atob",
     /* 0x13 */     "setjmp",
     /* 0x14 */     "longjmp",
     /* 0x15 */     "strcat",
     /* 0x16 */     "strncat",
     /* 0x17 */     "strcmp",
     /* 0x18 */     "strncmp",
     /* 0x19 */     "strcpy",
     /* 0x1a */     "strnpy",
     /* 0x1b */     "strlen",
     /* 0x1c */     "index",
     /* 0x1d */     "rindex",
     /* 0x1e */     "strchr",
     /* 0x1f */     "strrchr",
     /* 0x20 */     "strpbrk",
     /* 0x21 */     "strspn",
     /* 0x22 */     "strcspn",
     /* 0x23 */     "strtok",
     /* 0x24 */     "strstr",
     /* 0x25 */     "toupper",
     /* 0x26 */     "tolower",
     /* 0x27 */     "bcopy",
     /* 0x28 */     "bzero",
     /* 0x29 */     "bcmp",
     /* 0x2a */     "memcpy",
     /* 0x2b */     "memset",
     /* 0x2c */     "memmove",
     /* 0x2d */     "memcmp",
     /* 0x2e */     "memchr",
     /* 0x2f */     "rand",
     /* 0x30 */     "srand",
     /* 0x31 */     "qsort",
     /* 0x32 */     "strtod",
     /* 0x33 */     "malloc",
     /* 0x34 */     "free",
     /* 0x35 */     "lsearch",
     /* 0x36 */     "bsearch",
     /* 0x37 */     "calloc",
     /* 0x38 */     "realloc",
     /* 0x39 */     "InitHeap",
     /* 0x3a */     "_exit",
     /* 0x3b */     "getchar",
     /* 0x3c */     "putchar",
     /* 0x3d */     "gets",
     /* 0x3e */     "puts",
     /* 0x3f */     "printf",
     /* 0x40 */     "bios_a0_40",
     /* 0x41 */     "LoadTest",
     /* 0x42 */     "Load",
     /* 0x43 */     "Exec",
     /* 0x44 */     "FlushCache",
     /* 0x45 */     "InstallInterruptHandler",
     /* 0x46 */     "GPU_dw",
     /* 0x47 */     "mem2vram",
     /* 0x48 */     "SendGPU",
     /* 0x49 */     "GPU_cw",
     /* 0x4a */     "GPU_cwb",
     /* 0x4b */     "SendPackets",
     /* 0x4c */     "bios_a0_4c",
     /* 0x4d */     "GetGPUStatus",
     /* 0x4e */     "GPU_sync",
     /* 0x4f */     "bios_a0_4f",
     /* 0x50 */     "bios_a0_50",
     /* 0x51 */     "LoadExec",
     /* 0x52 */     "GetSysSp",
     /* 0x53 */     "bios_a0_53",
     /* 0x54 */     "_96_init",
     /* 0x55 */     "_bu_init",
     /* 0x56 */     "_96_remove",
     /* 0x57 */     "bios_a0_57",
     /* 0x58 */     "bios_a0_58",
     /* 0x59 */     "bios_a0_59",
     /* 0x5a */     "bios_a0_5a",
     /* 0x5b */     "dev_tty_init",
     /* 0x5c */     "dev_tty_open",
     /* 0x5d */     "dev_tty_5d",
     /* 0x5e */     "dev_tty_ioctl",
     /* 0x5f */     "dev_cd_open",
     /* 0x60 */     "dev_cd_read",
     /* 0x61 */     "dev_cd_close",
     /* 0x62 */     "dev_cd_firstfile",
     /* 0x63 */     "dev_cd_nextfile",
     /* 0x64 */     "dev_cd_chdir",
     /* 0x65 */     "dev_card_open",
     /* 0x66 */     "dev_card_read",
     /* 0x67 */     "dev_card_write",
     /* 0x68 */     "dev_card_close",
     /* 0x69 */     "dev_card_firstfile",
     /* 0x6a */     "dev_card_nextfile",
     /* 0x6b */     "dev_card_erase",
     /* 0x6c */     "dev_card_undelete",
     /* 0x6d */     "dev_card_format",
     /* 0x6e */     "dev_card_rename",
     /* 0x6f */     "dev_card_6f",
     /* 0x70 */     "_bu_init(a0_70)",
     /* 0x71 */     "_96_init(a0_71)",
     /* 0x72 */     "_96_remove(a0_72)",
     /* 0x73 */     "bios_a0_73",
     /* 0x74 */     "bios_a0_74",
     /* 0x75 */     "bios_a0_75",
     /* 0x76 */     "bios_a0_76",
     /* 0x77 */     "bios_a0_77",
     /* 0x78 */     "_96_CdSeekL",
     /* 0x79 */     "bios_a0_79",
     /* 0x7a */     "bios_a0_7a",
     /* 0x7b */     "bios_a0_7b",
     /* 0x7c */     "_96_CdGetStatus",
     /* 0x7d */     "bios_a0_7d",
     /* 0x7e */     "_96_CdRead",
     /* 0x7f */     "bios_a0_7f",
     /* 0x80 */     "bios_a0_80",
     /* 0x81 */     "bios_a0_81",
     /* 0x82 */     "bios_a0_82",
     /* 0x83 */     "bios_a0_83",
     /* 0x84 */     "bios_a0_84",
     /* 0x85 */     "_96_CdStop",
     /* 0x86 */     "bios_a0_86",
     /* 0x87 */     "bios_a0_87",
     /* 0x88 */     "bios_a0_88",
     /* 0x89 */     "bios_a0_89",
     /* 0x8a */     "bios_a0_8a",
     /* 0x8b */     "bios_a0_8b",
     /* 0x8c */     "bios_a0_8c",
     /* 0x8d */     "bios_a0_8d",
     /* 0x8e */     "bios_a0_8e",
     /* 0x8f */     "bios_a0_8f",
     /* 0x90 */     "bios_a0_90",
     /* 0x91 */     "bios_a0_91",
     /* 0x92 */     "bios_a0_92",
     /* 0x93 */     "bios_a0_93",
     /* 0x94 */     "bios_a0_94",
     /* 0x95 */     "bios_a0_95",
     /* 0x96 */     "AddCDROMDevice",
     /* 0x97 */     "AddMemCardDevice",
     /* 0x98 */     "DisableKernelIORedirection",
     /* 0x99 */     "EnableKernelIORedirection",
     /* 0x9a */     "bios_a0_9a",
     /* 0x9b */     "bios_a0_9b",
     /* 0x9c */     "SetConf # may be",
     /* 0x9d */     "GetConf # ",
     /* 0x9e */     "bios_a0_9e",
     /* 0x9f */     "SetMem",
     /* 0xa0 */     "_boot",
     /* 0xa1 */     "SystemError",
     /* 0xa2 */     "EnqueueCdIntr",
     /* 0xa3 */     "DequeueCdIntr",
     /* 0xa4 */     "bios_a0_a4",
     /* 0xa5 */     "ReadSector",
     /* 0xa6 */     "get_cd_status",
     /* 0xa7 */     "bufs_cb_0",
     /* 0xa8 */     "bufs_cb_1",
     /* 0xa9 */     "bufs_cb_2",
     /* 0xaa */     "bufs_cb_3",
     /* 0xab */     "_card_info",
     /* 0xac */     "_card_load",
     /* 0xad */     "_card_auto",
     /* 0xae */     "bufs_cb_4",
     /* 0xaf */     "bios_a0_af",
     /* 0xb0 */     "bios_a0_b0",
     /* 0xb1 */     "bios_a0_b1",
     /* 0xb2 */     "do_a_long_jmp",
     /* 0xb3 */     "bios_a0_b3",
     /* 0xb4 */     "bios_a0_b4",
};

static char *b0name[] = {
     /* 0x00 */     "SysMalloc",
     /* 0x01 */     "bios_b0_01",
     /* 0x02 */     "bios_b0_02",
     /* 0x03 */     "bios_b0_03",
     /* 0x04 */     "bios_b0_04",
     /* 0x05 */     "bios_b0_05",
     /* 0x06 */     "bios_b0_06",
     /* 0x07 */     "DeliverEvent",
     /* 0x08 */     "OpenEvent",
     /* 0x09 */     "CloseEvent",
     /* 0x0a */     "WaitEvent",
     /* 0x0b */     "TestEvent",
     /* 0x0c */     "EnableEvent",
     /* 0x0d */     "DisableEvent",
     /* 0x0e */     "OpenTh",
     /* 0x0f */     "CloseTh",
     /* 0x10 */     "ChangeTh",
     /* 0x11 */     "bios_b0_11",
     /* 0x12 */     "InitPAD",
     /* 0x13 */     "StartPAD",
     /* 0x14 */     "StopPAD",
     /* 0x15 */     "PAD_init",
     /* 0x16 */     "PAD_dr",
     /* 0x17 */     "ReturnFromException",
     /* 0x18 */     "ResetEntryInt",
     /* 0x19 */     "HookEntryInt",
     /* 0x1a */     "bios_b0_1a",
     /* 0x1b */     "bios_b0_1b",
     /* 0x1c */     "bios_b0_1c",
     /* 0x1d */     "bios_b0_1d",
     /* 0x1e */     "bios_b0_1e",
     /* 0x1f */     "bios_b0_1f",
     /* 0x20 */     "UnDeliverEvent",
     /* 0x21 */     "bios_b0_21",
     /* 0x22 */     "bios_b0_22",
     /* 0x23 */     "bios_b0_23",
     /* 0x24 */     "bios_b0_24",
     /* 0x25 */     "bios_b0_25",
     /* 0x26 */     "bios_b0_26",
     /* 0x27 */     "bios_b0_27",
     /* 0x28 */     "bios_b0_28",
     /* 0x29 */     "bios_b0_29",
     /* 0x2a */     "bios_b0_2a",
     /* 0x2b */     "bios_b0_2b",
     /* 0x2c */     "bios_b0_2c",
     /* 0x2d */     "bios_b0_2d",
     /* 0x2e */     "bios_b0_2e",
     /* 0x2f */     "bios_b0_2f",
     /* 0x30 */     "bios_b0_30",
     /* 0x31 */     "bios_b0_31",
     /* 0x32 */     "open(b0)",
     /* 0x33 */     "lseek(b0)",
     /* 0x34 */     "read(b0)",
     /* 0x35 */     "write(b0)",
     /* 0x36 */     "close(b0)",
     /* 0x37 */     "ioctl(b0)",
     /* 0x38 */     "exit(b0)",
     /* 0x39 */     "bios_b0_39",
     /* 0x3a */     "getc(b0)",
     /* 0x3b */     "putc(b0)",
     /* 0x3c */     "getchar(b0)",
     /* 0x3d */     "putchar(b0)",
     /* 0x3e */     "gets(b0)",
     /* 0x3f */     "puts(b0)",
     /* 0x40 */     "cd",
     /* 0x41 */     "format",
     /* 0x42 */     "firstfile",
     /* 0x43 */     "nextfile",
     /* 0x44 */     "rename",
     /* 0x45 */     "delete",
     /* 0x46 */     "undelete",
     /* 0x47 */     "AddDevice",
     /* 0x48 */     "RemoteDevice",
     /* 0x49 */     "PrintInstalledDevices",
     /* 0x4a */     "InitCARD",
     /* 0x4b */     "StartCARD",
     /* 0x4c */     "StopCARD",
     /* 0x4d */     "bios_b0_4d",
     /* 0x4e */     "_card_write",
     /* 0x4f */     "_card_read",
     /* 0x50 */     "_new_card",
     /* 0x51 */     "Krom2RawAdd",
     /* 0x52 */     "bios_b0_52",
     /* 0x53 */     "bios_b0_53",
     /* 0x54 */     "_get_errno",
     /* 0x55 */     "_get_error",
     /* 0x56 */     "GetC0Table",
     /* 0x57 */     "GetB0Table",
     /* 0x58 */     "_card_chan",
     /* 0x59 */     "bios_b0_59",
     /* 0x5a */     "bios_b0_5a",
     /* 0x5b */     "ChangeClearPAD",
     /* 0x5c */     "_card_status",
     /* 0x5d */     "_card_wait",
};

static char *c0name[]={
     /* 0x00 */     "InitRCnt",
     /* 0x01 */     "InitException",
     /* 0x02 */     "SysEnqIntRP",
     /* 0x03 */     "SysDeqIntRP",
     /* 0x04 */     "get_free_EvCB_slot",
     /* 0x05 */     "get_free_TCB_slot",
     /* 0x06 */     "ExceptionHandler",
     /* 0x07 */     "InstallExceptionHandler",
     /* 0x08 */     "SysInitMemory",
     /* 0x09 */     "SysInitKMem",
     /* 0x0a */     "ChangeClearRCnt",
     /* 0x0b */     "SystemError(c0)",
     /* 0x0c */     "InitDefInt",
     /* 0x0d */     "bios_c0_0d",
     /* 0x0e */     "bios_c0_0e",
     /* 0x0f */     "bios_c0_0f",
     /* 0x10 */     "bios_c0_10",
     /* 0x11 */     "bios_c0_11",
     /* 0x12 */     "InstallDevices",
     /* 0x13 */     "FlushStdInOutPut",
     /* 0x14 */     "bios_c0_14",
     /* 0x15 */     "_cdevinput",
     /* 0x16 */     "_cdevscan",
     /* 0x17 */     "_circgetc",
     /* 0x18 */     "_circputc",
     /* 0x19 */     "ioabort",
     /* 0x1a */     "bios_c0_1a",
     /* 0x1b */     "KernelRedirect",
     /* 0x1c */     "PatchAOTable",
};

static void *realaddr(int addr) { return (char*)baseaddr(addr)+addr; }

#define   v0   reg.r[2]
#define   v1   reg.r[3]
#define   a0   reg.r[4]
#define   a1   reg.r[5]
#define   a2   reg.r[6]
#define   a3   reg.r[7]
#define   sp   reg.r[29]
#define   stack     ((int*)realaddr(reg.r[29]))

#ifdef __WATCOMC__
#include  <direct.h>
#else
#include  <dirent.h>
#endif
#include  <sys/stat.h>

int biosprint(int pc)
{
    int ret = 0;
    int no = reg.r[9];

    if (verbose) {
     char *name = NULL;
     switch(pc){
     case 0xa0:
          if (no<sizeof(a0name)/sizeof(a0name[0]))
               name = a0name[no];
          break;
     case 0xb0:
          if (no<sizeof(b0name)/sizeof(b0name[0]))
               name = b0name[no];
          break;
     case 0xc0:
          if (no<sizeof(c0name)/sizeof(c0name[0]))
               name = c0name[no];
          break;
     }
     if (name) {
          printf("bios:%s ",name);
     } else {
          printf("bios:%02x,%x",(int)pc,(int)no);
     }
    }

    if (!emulate_bios) {
        switch(pc) {
        case 0xa0:
            switch(no) {
            case 0x3f:  // printf
                PRINTF("%s",(char *)realaddr(a0)); break;
            }
            break;
        case 0xb0:
            switch(no) {
            case 0x08: // OpenEvent
                PRINTF("(%x,%x,%x,%x)",(int)a0,(int)a1,(int)a2,(int)a3);
                break;
            case 0x09: //
            case 0x0C: //
            case 0x0D: //
                PRINTF("(%x)",(int)a0); break;
            case 0x3c:  // getchar
                v0 = getchar(); PC = reg.r[31]; break;
            case 0x3e:  // gets
            {
                char *base = (char*)baseaddr(a0);
                char *ret = gets(base + a0);
                v0 = PSADDR(ret,base);
                PC = reg.r[31];
            }
            break;
            case 0x3f: // puts
                v0 = puts(realaddr(a0)); PC = reg.r[31]; break;
            case 0x3d: // putchar
                putchar(a0); PC = reg.r[31]; break;
            }
        }
    } else {
        switch (pc) {
        case 0xa0: ret = bios_a0(); break;
        case 0xb0: ret = bios_b0(); break;
        case 0xc0: ret = bios_c0(); break;
        }
        if ((pc!=0xb0) || (no!=0x17)) // ReturnFromException
            PC = reg.r[31];
    }

    PRINTF("\n");
    return ret;
}

static char *qbase;
static int qsub;

static int save_reg[32+3];
static int *jmpbuf;

static char *heapbase;

static int qcmp(const void *arg0,const void *arg1)
{
     a0 = PSADDR2(arg0,qbase);
     a1 = PSADDR2(arg1,qbase);
     /* qsubを実行 */
     return v0;
}

typedef struct {
     char id[6];
     char fontname[8];
     unsigned char w,h;
     unsigned char type;
     unsigned char ntbl;
     unsigned short codetbl[1];
} FONTXHDR;
static FONTXHDR *font;

/*
     FONTX     format
     idx  siz
     0    6    "FONTX"
     6    8    font name
     14   1    width
     15   1    height
     16   1    type 0=ANK,1=Japanese
ANK:
     17        font data
Japanese:
     17   1    Number of code table
     18   2    start of code (little endian)
     19   2    end of code
     .
     .
     18+N*4    font data
*/
void *Krom2RawAdd(int code)
{
     unsigned short *ptbl = font->codetbl;
     int size = ((font->w+7)/8)*font->h;
     int n=0,i;

     for(i=0;i<font->ntbl;i++) {
          if (code>=ptbl[0] && code<=ptbl[1]) {
               return (char*)&font->codetbl[font->ntbl*2] + (code-ptbl[0]+n)*size
               +2; /* skip */
          }
          n += ptbl[1]-ptbl[0] + 1;
          ptbl+=2;
     }
     return NULL;
}

#define   strcmpz(a,b)   memcmp(a,b,strlen(b))

static char *cd_path="e:";
static char *bu00_path="bu00\\";
static char *bu10_path="bu10\\";
static char *pcdrv_path="";

static char *real_path(char *name)
{
     static char buf[256];
     if (strcmpz(name,"cdrom:")==0) {
          strcpy(buf,cd_path);
          strcat(buf,name+6);
          { char *p = buf+strlen(buf);
          p[-2]='\0';
          /* "cdrom:filename;1" */
          }
     } else
     if (strcmpz(name,"bu00:")==0) {
          strcpy(buf,bu00_path);
          strcat(buf,name+5);
     } else
     if (strcmpz(name,"bu10:")==0) {
          strcpy(buf,bu10_path);
          strcat(buf,name+5);
     } else
     if (strcmpz(name,"pcdrv:")==0) {
          strcpy(buf,pcdrv_path);
          strcat(buf,name+6);
     } else {
          return NULL;
     }
     return buf;
}

static int bios_open(char *name,int psxmode)
{
/* psx define
#define   O_RDONLY  1
#define   O_WRONLY  2
#define   O_RDWR         3
#define   O_CREAT        0x200
*/
     int mode=O_BINARY;
     switch(psxmode&3) {
     case 1: mode|=O_RDONLY; break;
     case 2: mode|=O_WRONLY; break;
     case 3: mode|=O_RDWR; break;
     }
     if (psxmode&0x200) mode|=O_CREAT;

     PRINTF("%s",name);
     return open(real_path(name),mode);
}

#if 0
static void bios_setjmp(int *jmpbuf)
{
          jmpbuf[0] = reg.r[31]; /* ra */
          jmpbuf[1] = reg.r[29]; /* sp */
          jmpbuf[2] = reg.r[28]; /* fp */
          jmpbuf[3] = reg.r[16]; /* s0 */
          jmpbuf[4] = reg.r[17]; /* s1 */
          jmpbuf[5] = reg.r[18]; /* s2 */
          jmpbuf[6] = reg.r[19]; /* s3 */
          jmpbuf[7] = reg.r[20]; /* s4 */
          jmpbuf[8] = reg.r[21]; /* s5 */
          jmpbuf[9] = reg.r[22]; /* s6 */
          jmpbuf[10] = reg.r[23]; /* s7 */
          jmpbuf[11] = reg.r[28]; /* gp */
}
#endif

static void bios_longjmp(int *jmpbuf)
{
          reg.r[31] = jmpbuf[0]; /* ra */
          reg.r[29] = jmpbuf[1]; /* sp */
          reg.r[28] = jmpbuf[2]; /* fp */
          reg.r[16] = jmpbuf[3]; /* s0 */
          reg.r[17] = jmpbuf[4]; /* s1 */
          reg.r[18] = jmpbuf[5]; /* s2 */
          reg.r[19] = jmpbuf[6]; /* s3 */
          reg.r[20] = jmpbuf[7]; /* s4 */
          reg.r[21] = jmpbuf[8]; /* s5 */
          reg.r[22] = jmpbuf[9]; /* s6 */
          reg.r[23] = jmpbuf[10]; /* s7 */
          reg.r[28] = jmpbuf[11]; /* gp */
}

static int match(char *s1, char *s2)
{
     if (*s1=='.') return 0;
     for ( ; ; ) {
          while (*s2 == '*' || *s2 == '?') {
               if (*s2++ == '*')
                    while (*s1 && toupper(*s1) != toupper(*s2)) s1++;
               else if (*s1 == 0)
                    return 0;
               else s1++;
          }
          if (toupper(*s1) != toupper(*s2)) return 0;
          if (*s1 == 0  ) return 1;
          s1++;  s2++;
     }
}

/* PSX struct */
struct DIRENTRY {
     char name[20];
     long attr;
     long size;
     struct DIRENTRY *next;
     long head;
     char system[4];
};

static DIR *hdir;
static char pathbuf[256/*MAXPATH*/];
static char matchname[20],*nameptr;

struct DIRENTRY* nextfile(struct DIRENTRY *dir)
{
     struct dirent *dirent;
     struct stat statbuf;

     if (hdir==NULL) return NULL;
     do {
          dirent = readdir(hdir);
          if (dirent == NULL) {
               closedir(hdir);
               hdir = NULL;
               return NULL;
          }
     //   printf("%s %s\n",dirent->d_name,matchname);
     } while(!match(dirent->d_name,matchname));

     //printf("match");

     strcpy(dir->name,dirent->d_name);
     strcpy(nameptr,dirent->d_name);
     stat(pathbuf,&statbuf);
     dir->size = statbuf.st_size;

     return dir;
}

#define   PATH_DELIMITER '\\'

struct DIRENTRY* firstfile(char *path,struct DIRENTRY *dir)
{
     char *p,*matchpath;

     //printf("path;%s\n",path);

     strcpy(pathbuf,real_path(path));
     p = strrchr(pathbuf,PATH_DELIMITER);
     if (p) {
          *p=0;
          matchpath = pathbuf;
          nameptr = p+1;
     } else {
          matchpath = ".";
          nameptr = pathbuf;
     }
     strcpy(matchname,nameptr);

//   printf("%s %s:\n",matchpath,matchname);

     if (hdir) {
          closedir(hdir);
     }
     hdir = opendir(matchpath);

     if (hdir==NULL) return NULL;

     if (p) {
          *p = PATH_DELIMITER;
     }

     return nextfile(dir);
}

static char *padbuf1,*padbuf2;
static int padbuf1len,padbuf2len;
static int *padbuf;

static void pad_update(void)
{
     int pad1,pad2;
     pad1 = 0xFFFF; // JOY_Poll(0);
     pad2 = 0xFFFF; // JOY_Poll(1);
     if (padbuf) *padbuf = (pad1 | (pad2<<16));
     if (padbuf1) {
          padbuf1[0] = 0;
          padbuf1[1] = 0x41;
          padbuf1[2] = pad1 >> 8;
          padbuf1[3] = pad1;
     
          padbuf2[0] = 0;
          padbuf2[1] = 0x41;
          padbuf2[2] = pad2>>8;
          padbuf2[3] = pad2;
     }
}

static int bios_b0()
{
     switch(reg.r[9]) {
     case 0x12:     /* InitPAD */
          padbuf1 = realaddr(a0);
          padbuf1len = a1;
          padbuf2 = realaddr(a2);
          padbuf2len = a3;
          break;
     case 0x13:     /* StartPAD */
          hw_write16(0x1f801074,hw_read16(0x1f801074)|1); /* Vsync Interrupt enable */
          SR |=  0x401;  /* interrupt enable */
          break;
     case 0x14:     /* StopPAD */
          break;
     case 0x15:     /* PAD_init */
          padbuf = realaddr(a1);
          *padbuf = -1;
          hw_write16(0x1f801074,hw_read16(0x1f801074)|1); /* Vsync Interrupt enable */
          SR |=  0x401;  /* interrupt enable */
          break;
     case 0x16:     /* PAD_dr */
          break;
     case 0x17:     /* ReturnFromException */
          SR = (SR & ~0xf)| ((SR>>2)&0xf);
          /* レジスタを例外発生時に戻す */
          memcpy(reg.r,save_reg,sizeof(save_reg));
          PC = EPC;
          break;
     case 0x19:     /* HookEntryInt */
          jmpbuf = realaddr(a0);
          break;
     case 0x32:     /* open */
          v0 = bios_open(realaddr(a0),a1);   break;
     case 0x33:     /* lseek */
          v0 = lseek(a0,a1,a2);    break;
     case 0x34:     /* read */
          printf("%d %x %d",(int)a0,(int)a1,(int)a2);
          v0 = read(a0,realaddr(a1),a2);     break;
     case 0x35:     /* write */
          v0 = write(a0,realaddr(a1),a2);    break;
     case 0x36:     /* close */
          v0 = close(a0);     break;
     case 0x37:     /* ioctl */
     case 0x38:     /* exit */
     case 0x39:     /* ?? */
     case 0x3a:     /* getc */
     case 0x3b:     /* putc */
          return -1;
     case 0x3c:     /* getchar */
          v0 = getchar();     break;
     case 0x3d:     /* putchar */
          v0 = putchar(a0);   break;
     case 0x3e:     /* gets */
          { char *base = (char*)baseaddr(a0);
          char *ret = gets(base + a0);
          v0 = PSADDR(ret,base);
          }
          break;
     case 0x3f:     /* puts */
          v0 = puts(realaddr(a0)); break;
     case 0x42:  /* 0x42 firstfile */
          {int t;
          t = (int)firstfile((char *)realaddr(a0),
                                   (struct DIRENTRY *)realaddr(a1));
          if (t) t=a1;
          v0 = t;
          }
          break;
     case 0x43: /* 0x43 nextfile */
          {int t;
          t = (int)nextfile((struct DIRENTRY *)realaddr(a0));
          if (t) t=a0;
          v0 = t;
          }
          break;
     case 0x51:     /* Krom2RawAdd */
          {void *ret = Krom2RawAdd(a0);
          v0 = PSADDR(ret,rom-0xbfc00000);
          }
          break;
     default:
          /* unsupport */
          return -1;
     }
     return 0;
}

static int bios_c0()
{
     switch(reg.r[9]) {
     default:
          /* unsupport */
          return -1;
     }
     return 0;
}

static int bios_a0()
{
     switch(reg.r[9]) {
     case 0x00:     /* open */
          v0 = bios_open(realaddr(a0),a1);   break;
     case 0x01:     /* lseek */
          v0 = lseek(a0,a1,a2);    break;
     case 0x02:     /* read */
          printf("%d %x %d",(int)a0,(int)a1,(int)a2);
          v0 = read(a0,realaddr(a1),a2);     break;
     case 0x03:     /* write */
          v0 = write(a0,realaddr(a1),a2);    break;
     case 0x04:     /* close */
          v0 = close(a0);     break;

     case 0x05:     /* ioctl */
     case 0x06:     /* exit */
     case 0x07:     /* ?? */
     case 0x08:     /* getc */
     case 0x09:     /* putc */
          return -1;

     case 0x0a:     /* todigit */
          v0 = a0-'0';   break;
     case 0x0b:     /* atof */
          { union { double d; int lo,hi; } t;
               t.d = atof(realaddr(a0));
               v0 = t.lo;
               v1 = t.hi;
          }
          break;
     case 0x0c:     /* strtoul */
          { char *base = baseaddr(a0);
          char *endp;
          v0 = strtoul(base+a0,&endp,a2);
          if (a1) a1 = PSADDR2(endp,base);
          }
          break;
     case 0x0d:     /* strtol */
          { char *base = baseaddr(a0);
          char *endp;
          v0 = strtol(base+a0,&endp,a2);
          if (a1) a1 = PSADDR2(endp,base);
          }
          break;
     case 0x0e:     /* abs */
     case 0x0f:     /* labs */
          v0 = (a0>=0)?a0:-a0; break;
     case 0x10:     /* atoi */
     case 0x11:     /* atol */
          v0 = atoi(realaddr(a0)); break;
     case 0x12:     /* atob */
          return -1;

     case 0x13:     /* setjmp */
          { int *jmpbuf=realaddr(a0);
          jmpbuf[0] = reg.r[31]; /* ra */
          jmpbuf[1] = reg.r[29]; /* sp */
          jmpbuf[2] = reg.r[28]; /* fp */
          jmpbuf[3] = reg.r[16]; /* s0 */
          jmpbuf[4] = reg.r[17]; /* s1 */
          jmpbuf[5] = reg.r[18]; /* s2 */
          jmpbuf[6] = reg.r[19]; /* s3 */
          jmpbuf[7] = reg.r[20]; /* s4 */
          jmpbuf[8] = reg.r[21]; /* s5 */
          jmpbuf[9] = reg.r[22]; /* s6 */
          jmpbuf[10] = reg.r[23]; /* s7 */
          jmpbuf[11] = reg.r[28]; /* gp */
          }
          v0 = 0;
          break;
     case 0x14:     /* longjmp */
          { int *jmpbuf=realaddr(a0);
          reg.r[31] = jmpbuf[0]; /* ra */
          reg.r[29] = jmpbuf[1]; /* sp */
          reg.r[28] = jmpbuf[2]; /* fp */
          reg.r[16] = jmpbuf[3]; /* s0 */
          reg.r[17] = jmpbuf[4]; /* s1 */
          reg.r[18] = jmpbuf[5]; /* s2 */
          reg.r[19] = jmpbuf[6]; /* s3 */
          reg.r[20] = jmpbuf[7]; /* s4 */
          reg.r[21] = jmpbuf[8]; /* s5 */
          reg.r[22] = jmpbuf[9]; /* s6 */
          reg.r[23] = jmpbuf[10]; /* s7 */
          reg.r[28] = jmpbuf[11]; /* gp */
          }
          v0 = a1;
          break;
     case 0x15:     /* strcat */
          v0 = a0; strcat(realaddr(a0),realaddr(a1));  break;
     case 0x16:     /* strncat */
          v0 = a0; strncat(realaddr(a0),realaddr(a1),a2);   break;
     case 0x17:     /* strcat */
          v0 = strcmp(realaddr(a0),realaddr(a1)); break;
     case 0x18:     /* strncmp */
          v0 = strncmp(realaddr(a0),realaddr(a1),a2);  break;
     case 0x19:     /* strcpy */
          v0 = a0; strcpy(realaddr(a0),realaddr(a1));  break;
     case 0x1a:     /* strncpy */
          v0 = a0; strncpy(realaddr(a0),realaddr(a1),a2);   break;
     case 0x1b:     /* strlen */
          v0 = strlen(realaddr(a0));    break;
     case 0x1c:     /* index */
     case 0x1e:     /* strchr */
          { char *base = baseaddr(a0);
          char *ret = strchr(base + a0,a1);
          v0 = PSADDR(ret,base);
          }
          break;
     case 0x1d:     /* rindex */
     case 0x1f:     /* strrchr */
          { char *base = baseaddr(a0);
          char *ret = strrchr(base + a0,a1);
          v0 = PSADDR(ret,base);
          }
          break;
     case 0x20:     /* strpbrk */
          { char *base = baseaddr(a0);
          char *ret = strpbrk(base + a0,realaddr(a1));
          v0 = PSADDR(ret,base);
          }
          break;
     case 0x21:     /* strspn */
          v0 = strspn(realaddr(a0),realaddr(a1)); break;
     case 0x22:     /* strcspn */
          v0 = strcspn(realaddr(a0),realaddr(a1));     break;
     case 0x23:     /* strtok */
          { char *base = baseaddr(a0);
          char *ret = strtok(base + a0,a1?realaddr(a1):(char*)a1);
          v0 = PSADDR(ret,base);
          }
          break;
     case 0x24:     /* strstr */
          { char *base = baseaddr(a0);
          char *ret = strstr(base + a0,realaddr(a1));
          v0 = PSADDR(ret,base);
          }
          break;
     case 0x25:     /* toupper */
          v0 = toupper(a0);   break;
     case 0x26:     /* tolower */
          v0 = tolower(a0);   break;
     case 0x27:     /* bcopy */
          memcpy(realaddr(a1),realaddr(a0),a2);   break;
     case 0x28:     /* bzero */
          memset(realaddr(a0),0,a1);    break;
     case 0x29:     /* bcmp */
          v0 = memcmp(realaddr(a0),realaddr(a1),a2);   break;
     case 0x2a:     /* memcpy */
          v0 = a0; memcpy(realaddr(a0),realaddr(a1),a2);    break;
     case 0x2b:     /* memset */
          v0 = a0; memset(realaddr(a0),a1,a2);    break;
     case 0x2c:     /* memmove */
          v0 = a0; memmove(realaddr(a0),realaddr(a1),a2);   break;
     case 0x2d:     /* memcmp */
          v0 = memcmp(realaddr(a0),realaddr(a1),a2);   break;
     case 0x2e:     /* memchr */
          { char *base = baseaddr(a0);
          char *ret = memchr(base + a0,a1,a2);
          v0 = PSADDR(ret,base);
          }
          break;
     case 0x2f:     /* rand */
          v0 = rand(); break;
     case 0x30:     /* srand */
          srand(a0);     break;
     case 0x31:     /* qsort */
          qbase = baseaddr(a0);
          qsub = a3;
          qsort(qbase + a0,a1,a2,qcmp);
          break;
     case 0x32:     /* strtod */
          { char *base = baseaddr(a0);
          char *endp;
          union { double d; int lo,hi; } t;
          t.d = strtod(base+a0,&endp);
          v0 = t.lo;
          v1 = t.hi;
          if (a1) a1 = PSADDR2(endp,base);
          }
          break;
     case 0x33:     /* malloc */
          {
          char *ret = bios_malloc(a0);
          v0 = PSADDR(ret,heapbase);
          }
          break;
     case 0x34:     /* free */
          bios_free(PCADDR(a0,heapbase));
          break;
     case 0x35:     /* lsearch */
          return -1;
/*
          qbase = baseaddr(a0);
          qsub = stack[4];
          {
          char *ret = lsearch(qbase+a0,qbase+a1,realaddr(a2),a3,qcmp);
          v0 = PSADDR(ret,qbase);
          }
          break;
*/
     case 0x36:     /* bsearch */
          qbase = baseaddr(a0);
          qsub = stack[4];
          {
          char *ret = bsearch(qbase+a0,qbase+a1,a2,a3,qcmp);
          v0 = PSADDR(ret,qbase);
          }
          break;
     case 0x37:     /* calloc */
          {
          char *ret = bios_calloc(a0,a1);
          v0 = PSADDR(ret,heapbase);
          }
          break;
     case 0x38:     /* realloc */
          {
          char *ret = bios_realloc(PCADDR(a0,heapbase),a1);
          v0 = PSADDR(ret,heapbase);
          }
          break;
     case 0x39:     /* InitHeap */
          heapbase = baseaddr(a0);
          bios_InitHeap(heapbase + a0,a1);
          break;
     case 0x3a:     /* _exit */
          return -1;
     case 0x3b:     /* getchar */
          v0 = getchar();     break;
     case 0x3c:     /* putchar */
          v0 = putchar(a0);   break;
     case 0x3d:     /* gets */
          { char *base = (char*)baseaddr(a0);
          char *ret = gets(base + a0);
          v0 = PSADDR(ret,base);
          }
          break;
     case 0x3e:     /* puts */
          v0 = puts(realaddr(a0)); break;
     case 0x3f:     /* printf */
          /* 仮 */
          {char *s = realaddr(a0);

//        if (strstr(s,"%s")) {
//             v0 = printf("%s",s);
//        } else {

               long *ptr = realaddr(sp);
               ptr[1] = a1;
               ptr[2] = a2;
               ptr[3] = a3;
#ifdef __WATCOMC__
               v0 = vprintf(s,(char*)(ptr+1));
#else
               v0 = vprintf(s,*((va_list*)(ptr+1)));
#endif
//        }
          }
          break;

     case 0x46:     /* GPU_dw */
       {
          int size;
          long *ptr;
          GP0_Write(0xa0000000);
          GP0_Write((a1<<16)|(a0&0xffff));
          GP0_Write((a3<<16)|(a2&0xffff));
          size = (a2*a3+1)/2;
          ptr = realaddr(stack[4]);
          do {
               GP0_Write(*ptr++);
          } while(--size);
       }
          break;
     case 0x47:     /* mem2vram */
       {
          int size;
          PRINTF("%d %d %d %d %x",(int)a0,(int)a1,(int)a2,(int)a3,(int)stack[4]);
          GP0_Write(0xa0000000);
          GP0_Write((a1<<16)|(a0&0xffff));
          GP0_Write((a3<<16)|(a2&0xffff));
          size = (a2*a3+1)/2;
          GP1_Write(0x04000002);
          hw_write32(0x1f8010f4,0);
          hw_write32(0x1f8010f0,hw_read32(0x1f8010f0)|0x800);
          hw_write32(0x1f8010a0,stack[4]);
          hw_write32(0x1f8010a4,((size/16)<<16)|16);
          hw_write32(0x1f8010a8,0x01000201);
       }
          break;
     case 0x48:     /* SendGPU */
          GP1_Write(a0);
          break;
     case 0x49:     /* GPU_cw */
          GP0_Write(a0);
          break;
     case 0x4a:     /* GPU_cwb */
       {
          long *ptr = realaddr(a0);
          int size = a1;
          while(size--) {
               GP0_Write(*ptr++);
          }
       }
          break;
     case 0x4b:     /* SendPackets */
          GP1_Write(0x04000002);
          hw_write32(0x1f8010f4,0);
          hw_write32(0x1f8010f0,hw_read32(0x1f8010f0)|0x800);
          hw_write32(0x1f8010a0,a0);
          hw_write32(0x1f8010a4,0);
          hw_write32(0x1f8010a8,0x010000401);
          break;
     case 0x4c:     /* ??? */
          hw_write32(0x1f8010a8,0x00000401);
          GP0_Write(0x0400000);
          GP0_Write(0x0200000);
          GP0_Write(0x0100000);
          break;
     case 0x4d:     /* GetGPUStatus */
          v0 = GP1_Read();
          break;
     case 0x4e:     /* GPU_sync */
          break;
     default:
          /* unsupport */
          return -1;
     }
     return 0;
}

#if 0
int bios_printf(char *fmt,int *arg)
{
     int ch;
     static char decimal = '.';

     for(cnt=0;;) {
          while((ch = *fmt++) && ch!='%') { putchar(ch); cnt++; }
          if (ch=='\0') break;
     rflag:
          switch(*fmt++) {
          case ' ': if (!sign) sign=' '; goto rflag;
          case '#': flags |= ALT; goto rflag;
          case '*':
          case '-': flags |= LADJUST; goto rflag;
          case '+': sign = '+'; goto rflag;
          case '.': if (*fmt=='*') {}
          case '0':
          case '1':case '2':case '3':case '4':
          case '5':case '6':case '7':case '8':case '9':
          case 'L': flags |= LONGDBL; goto rflag;
          case 'h': flags |= SHORTINT; goto rflag;
          case 'l': if (flags&LONGINT) flags |= LONGDBL; else flags = LONGINT;
               goto rflag;
          case 'q':
          case 'c':
          case 'D': flags |= LONGINT;
          case 'd':
          case 'i':
          case 'E':
          case 'e':
          case 'f':
          case 'g':
          case 'G':
          case 'n':
          case 'O':
          case 'o':
          case 'p':
          case 's':
          case 'U': flags |= LONGINT;
          case 'u': 
          case 'X':
          case 'x':
          
          }
     }
}
#endif

int interrupt_handler()
{
     int intr;

     intr = hw_read16(0x1f801070) & hw_read16(0x1f801074); /* int reg & mask */
     if (intr&1) { /* VSync interrupt */
          pad_update();
     }
     hw_write16(0x1f801070,0);
        hw_write32(0x1F8010F4,hw_read32(0x1F8010F4));
     return 0;
}

int exception_handler()
{
     if (!emulate_bios) return 0;
     switch((CAUSE&0x3c)>>2) {
     case E_Int:
          /* レジスタ保存 */
          memcpy(save_reg,reg.r,sizeof(save_reg));
          if (jmpbuf) {
               /* HookEntryIntの設定に制御が移る */
               bios_longjmp(jmpbuf);
               v0 = 1;
               PC = reg.r[31];
                        hw_write32(0x1f801070,0xFFFFFFFF);
          } else {
               interrupt_handler();
               PC = EPC;
               SR = (SR & ~0xf)| ((SR>>2)&0xf); /* rfe */
          }
          break;
     case E_Sys:
          switch(reg.r[4]) {
          case 1: SR &= ~0x404; PC = EPC+4; break;
          case 2: SR |=  0x404; PC = EPC+4; break;
          default: return -1;
          }
          SR = (SR & ~0xf)| ((SR>>2)&0xf); /* rfe */
          break;
     default:
          return -1;
     }

     return 0;
}

/*
     FONTX format

     idx  siz
     0    6    id   "FONTX"
     6    8    name
     14   1    width
     15   1    height
     16   1    type (0=ANK 1=Japanese)
ANK:
     17        font data (0x00-0xff)

Japanese:
     17   1    Number of code table
     18   2    start of code
     20   2    end of code
     .
     .
     18+N*4    font data
*/

void *fileload(char *file,void *buf)
{
     FILE *fp;
     int fsize;

     fp = fopen(file,"rb");
     if (fp==NULL) return NULL;
     fseek(fp,0,SEEK_END);
     fsize = ftell(fp);
     fseek(fp,0,SEEK_SET);
     if (buf!=NULL || (buf = malloc(fsize))!=NULL) {
          fread(buf,1,fsize,fp);
     }
     fclose(fp);
     return buf;
}

static char *fontfile = "JPNZN16X.X11";

int bios_init(void)
{
     /* font load at rom area */
     font = (void*)(rom+0x20000);
     fileload(fontfile,font);
        return 0;
}