/*
    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]))

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];
     UINT8  w,h;
     UINT8  type;
     UINT8  ntbl;
     UINT16 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)
{
    UINT16 *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];
    INT32   attr;
    UINT32  size;
    struct  DIRENTRY *next;
    INT32   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 = (int)firstfile((char *)realaddr(a0),
                                   (struct DIRENTRY *)realaddr(a1));
            if (t) t=a1;
            v0 = t;
        }
        break;
    case 0x43:  /* 0x43 nextfile */
        {
            int 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;
}