initial hle support for lightrec
[pcsx_rearmed.git] / libpcsxcore / psxbios.c
index be3bc84..fdac556 100644 (file)
@@ -47,6 +47,8 @@
 #define PSXBIOS_LOG(...)
 #endif
 
+#define PTR_1 (void *)(size_t)1
+
 char *biosA0n[256] = {
 // 0x00
        "open",         "lseek",        "read",         "write",
@@ -253,11 +255,11 @@ typedef struct {
        u32  mcfile;
 } FileDesc;
 
-static u32 heap_size = 0;
-static u32 *heap_addr = NULL;
-static u32 *heap_end = NULL;
+// todo: FileDesc layout is wrong
+// todo: get rid of these globals
 static FileDesc FDesc[32];
-static u32 card_active_chan = 0;
+static char ffile[64];
+static int nfile;
 
 // fixed RAM offsets, SCPH1001 compatible
 #define A_TT_ExCB       0x0100
@@ -282,12 +284,21 @@ static u32 card_active_chan = 0;
 #define A_PAD_IN_LEN    0x74d8
 #define A_PAD_OUT_LEN   0x74e0
 #define A_PAD_DR_DST    0x74c4
+#define A_CARD_CHAN1    0x7500
 #define A_PAD_DR_BUF1   0x7570
 #define A_PAD_DR_BUF2   0x7598
 #define A_EEXIT_PTR     0x75d0
 #define A_EXC_STACK     0x85d8  // exception stack top
 #define A_RCNT_VBL_ACK  0x8600
 #define A_PAD_ACK_VBL   0x8914  // enable vint ack by pad reading code
+#define A_HEAP_BASE     0x9000
+#define A_HEAP_SIZE     0x9004
+#define A_HEAP_END      0x9008
+#define A_HEAP_FLAG     0x900c
+#define A_RND_SEED      0x9010
+#define A_CONF_TCB      0xb940
+#define A_CONF_EvCB     0xb944
+#define A_CONF_SP       0xb948
 #define A_CD_EVENTS     0xb9b8
 #define A_EXC_GP        0xf450
 
@@ -339,6 +350,11 @@ static void mips_return(u32 val)
        pc0 = ra;
 }
 
+static void mips_return_void(void)
+{
+       pc0 = ra;
+}
+
 static void use_cycles(u32 cycle)
 {
        psxRegs.cycle += cycle * 2;
@@ -370,9 +386,13 @@ static inline void softCall(u32 pc) {
        ra = 0x80001000;
        psxRegs.CP0.n.SR &= ~0x404; // disable interrupts
 
+       psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, PTR_1);
+
        while (pc0 != 0x80001000 && ++lim < 1000000)
                psxCpu->ExecuteBlock(EXEC_CALLER_HLE);
 
+       psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, PTR_1);
+
        if (lim == 1000000)
                PSXBIOS_LOG("softCall @%x hit lim\n", pc);
        ra = sra;
@@ -383,11 +403,19 @@ static inline void softCallInException(u32 pc) {
        u32 sra = ra;
        u32 lim = 0;
        pc0 = pc;
+
+       assert(ra != 0x80001000);
+       if (ra == 0x80001000)
+               return;
        ra = 0x80001000;
 
+       psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, PTR_1);
+
        while (!returned_from_exception() && pc0 != 0x80001000 && ++lim < 1000000)
                psxCpu->ExecuteBlock(EXEC_CALLER_HLE);
 
+       psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, PTR_1);
+
        if (lim == 1000000)
                PSXBIOS_LOG("softCallInException @%x hit lim\n", pc);
        if (pc0 == 0x80001000)
@@ -409,6 +437,7 @@ static void CloseEvent(u32 ev);
        PSXBIOS_LOG("read %d: %x,%x (%s)\n", FDesc[1 + mcd].mcfile, FDesc[1 + mcd].offset, a2, Mcd##mcd##Data + 128 * FDesc[1 + mcd].mcfile + 0xa); \
        ptr = Mcd##mcd##Data + 8192 * FDesc[1 + mcd].mcfile + FDesc[1 + mcd].offset; \
        memcpy(Ra1, ptr, length); \
+       psxCpu->Clear(a1, (length + 3) / 4); \
        if (FDesc[1 + mcd].mode & 0x8000) { \
        DeliverEvent(0xf0000011, 0x0004); \
        DeliverEvent(0xf4000001, 0x0004); \
@@ -899,37 +928,69 @@ void psxBios_tolower() { // 0x26
        pc0 = ra;
 }
 
-void psxBios_bcopy() { // 0x27
-       char *p1 = (char *)Ra1, *p2 = (char *)Ra0;
-       v0 = a0;
-       if (a0 == 0 || a2 > 0x7FFFFFFF)
-       {
-               pc0 = ra;
-               return;
+static void do_memset(u32 dst, u32 v, s32 len)
+{
+       u32 d = dst;
+       s32 l = len;
+       while (l-- > 0) {
+               u8 *db = PSXM(d);
+               if (db != INVALID_PTR)
+                       *db = v;
+               d++;
        }
-       while ((s32)a2-- > 0) *p1++ = *p2++;
-       a2 = 0;
-       pc0 = ra;
+       psxCpu->Clear(dst, (len + 3) / 4);
 }
 
-void psxBios_bzero() { // 0x28
-       char *p = (char *)Ra0;
-       v0 = a0;
-       /* Same as memset here (See memset below) */
-       if (a1 > 0x7FFFFFFF || a1 == 0)
+static void do_memcpy(u32 dst, u32 src, s32 len)
+{
+       u32 d = dst, s = src;
+       s32 l = len;
+       while (l-- > 0) {
+               const u8 *sb = PSXM(s);
+               u8 *db = PSXM(d);
+               if (db != INVALID_PTR && sb != INVALID_PTR)
+                       *db = *sb;
+               d++;
+               s++;
+       }
+       psxCpu->Clear(dst, (len + 3) / 4);
+}
+
+static void psxBios_memcpy();
+
+static void psxBios_bcopy() { // 0x27 - memcpy with args swapped
+       //PSXBIOS_LOG("psxBios_%s %x %x %x\n", biosA0n[0x27], a0, a1, a2);
+       u32 ret = a0, cycles = 0;
+       if (a0 == 0) // ...but it checks src this time
        {
-               v0 = 0;
-               pc0 = ra;
+               mips_return_c(0, 4);
                return;
        }
-       else if (a0 == 0)
+       v1 = a0;
+       if ((s32)a2 > 0) {
+               do_memcpy(a1, a0, a2);
+               cycles = a2 * 6;
+               a0 += a2;
+               a1 += a2;
+               a2 = 0;
+       }
+       mips_return_c(ret, cycles + 5);
+}
+
+static void psxBios_bzero() { // 0x28
+       /* Same as memset here (See memset below) */
+       u32 ret = a0, cycles;
+       if (a0 == 0 || (s32)a1 <= 0)
        {
-               pc0 = ra;
+               mips_return_c(0, 6);
                return;
        }
-       while ((s32)a1-- > 0) *p++ = '\0';
+       do_memset(a0, 0, a1);
+       cycles = a1 * 4;
+       a0 += a1;
        a1 = 0;
-       pc0 = ra;
+       // todo: many more cycles due to uncached bios mem
+       mips_return_c(ret, cycles + 5);
 }
 
 void psxBios_bcmp() { // 0x29
@@ -948,57 +1009,70 @@ void psxBios_bcmp() { // 0x29
        v0 = 0; pc0 = ra;
 }
 
-void psxBios_memcpy() { // 0x2a
-       char *p1 = (char *)Ra0, *p2 = (char *)Ra1;
-       v0 = a0;
-       if (a0 == 0 || a2 > 0x7FFFFFFF)
+static void psxBios_memcpy() { // 0x2a
+       u32 ret = a0, cycles = 0;
+       if (a0 == 0)
        {
-               pc0 = ra;
+               mips_return_c(0, 4);
                return;
        }
-       while ((s32)a2-- > 0) {
-               *p1++ = *p2++;
+       v1 = a0;
+       if ((s32)a2 > 0) {
+               do_memcpy(a0, a1, a2);
+               cycles = a2 * 6;
+               a0 += a2;
+               a1 += a2;
+               a2 = 0;
        }
-       a2 = 0;
-       pc0 = ra;
+       mips_return_c(ret, cycles + 5);
 }
 
-void psxBios_memset() { // 0x2b
-       char *p = (char *)Ra0;
-       v0 = a0;
-       if (a2 > 0x7FFFFFFF || a2 == 0)
+static void psxBios_memset() { // 0x2b
+       u32 ret = a0, cycles;
+       if (a0 == 0 || (s32)a2 <= 0)
        {
-               v0 = 0;
-               pc0 = ra;
+               mips_return_c(0, 6);
                return;
        }
-       if (a0 == 0)
-       {
-               pc0 = ra;
-               return;
-       }
-       while ((s32)a2-- > 0) *p++ = (char)a1;
+       do_memset(a0, a1, a2);
+       cycles = a2 * 4;
+       a0 += a2;
        a2 = 0;
-       v0 = a0; pc0 = ra;
+       // todo: many more cycles due to uncached bios mem
+       mips_return_c(ret, cycles + 5);
 }
 
 void psxBios_memmove() { // 0x2c
-       char *p1 = (char *)Ra0, *p2 = (char *)Ra1;
-       v0 = a0;
-       if (a0 == 0 || a2 > 0x7FFFFFFF)
+       u32 ret = a0, cycles = 0;
+       if (a0 == 0)
        {
-               pc0 = ra;
+               mips_return_c(0, 4);
                return;
        }
-       if (p2 <= p1 && p2 + a2 > p1) {
-               a2++; // BUG: copy one more byte here
-               p1 += a2;
-               p2 += a2;
-               while ((s32)a2-- > 0) *--p1 = *--p2;
-       } else {
-               while ((s32)a2-- > 0) *p1++ = *p2++;
+       v1 = a0;
+       if ((s32)a2 > 0 && a0 > a1 && a0 < a1 + a2) {
+               u32 dst = a0, len = a2 + 1;
+               a0 += a2;
+               a1 += a2;
+               while ((s32)a2 >= 0) { // BUG: copies one more byte here
+                       const u8 *sb = PSXM(a1);
+                       u8 *db = PSXM(a0);
+                       if (db != INVALID_PTR && sb != INVALID_PTR)
+                               *db = *sb;
+                       a0--;
+                       a1--;
+                       a2--;
+               }
+               psxCpu->Clear(dst, (len + 3) / 4);
+               cycles = 10 + len * 8;
+       } else if ((s32)a2 > 0) {
+               do_memcpy(a0, a1, a2);
+               cycles = a2 * 6;
+               a0 += a2;
+               a1 += a2;
+               a2 = 0;
        }
-       pc0 = ra;
+       mips_return_c(ret, cycles + 5);
 }
 
 void psxBios_memcmp() { // 0x2d
@@ -1024,16 +1098,16 @@ void psxBios_memchr() { // 0x2e
        v0 = 0; pc0 = ra;
 }
 
-void psxBios_rand() { // 0x2f
-       u32 s = psxMu32(0x9010) * 1103515245 + 12345;
-       v0 = (s >> 16) & 0x7fff;
-       psxMu32ref(0x9010) = SWAPu32(s);
-       pc0 = ra;
+static void psxBios_rand() { // 0x2f
+       u32 s = loadRam32(A_RND_SEED) * 1103515245 + 12345;
+       storeRam32(A_RND_SEED, s);
+       v1 = s;
+       mips_return_c((s >> 16) & 0x7fff, 12+37);
 }
 
-void psxBios_srand() { // 0x30
-       psxMu32ref(0x9010) = SWAPu32(a0);
-       pc0 = ra;
+static void psxBios_srand() { // 0x30
+       storeRam32(A_RND_SEED, a0);
+       mips_return_void_c(3);
 }
 
 static u32 qscmpfunc, qswidth;
@@ -1142,14 +1216,16 @@ void psxBios_qsort() { // 0x31
        pc0 = ra;
 }
 
-void psxBios_malloc() { // 0x33
+// this isn't how the real bios works, but maybe good enough
+static void psxBios_malloc() { // 0x33
+       u32 *heap_addr, *heap_end;
        u32 *chunk, *newchunk = NULL;
        unsigned int dsize = 0, csize, cstat;
        int colflag;
-#ifdef PSXBIOS_LOG
-       PSXBIOS_LOG("psxBios_%s\n", biosA0n[0x33]);
-#endif
-       if (!a0 || (!heap_size || !heap_addr)) {
+       PSXBIOS_LOG("psxBios_%s %x\n", biosA0n[0x33], a0);
+       heap_addr = loadRam32ptr(A_HEAP_BASE);
+       heap_end = loadRam32ptr(A_HEAP_END);
+       if (heap_addr >= heap_end) {
                v0 = 0;
                pc0 = ra;
                return;
@@ -1246,27 +1322,24 @@ void psxBios_malloc() { // 0x33
        pc0 = ra;
 }
 
-void psxBios_free() { // 0x34
-
-       PSXBIOS_LOG("psxBios_%s\n", biosA0n[0x34]);
-       PSXBIOS_LOG("free %x: %x bytes\n", a0, *(u32*)(Ra0-4));
-
-       if (a0)
-               *(u32*)(Ra0-4) |= 1;    // set chunk to free
-       pc0 = ra;
+static void psxBios_free() { // 0x34
+       PSXBIOS_LOG("psxBios_%s %x (%x bytes)\n", biosA0n[0x34], a0, loadRam32(a0 - 4));
+       storeRam32(a0 - 4, loadRam32(a0 - 4) | 1); // set chunk to free
+       mips_return_void_c(5);
 }
 
-void psxBios_calloc() { // 0x37
-       void *pv0;
-#ifdef PSXBIOS_LOG
-       PSXBIOS_LOG("psxBios_%s\n", biosA0n[0x37]);
-#endif
+static void psxBios_calloc() { // 0x37
+       u32 ret, size;
+       PSXBIOS_LOG("psxBios_%s %x %x\n", biosA0n[0x37], a0, a1);
 
-       a0 = a0 * a1;
+       a0 = size = a0 * a1;
        psxBios_malloc();
-       pv0 = Rv0;
-       if (pv0)
-               memset(pv0, 0, a0);
+       ret = v0;
+       if (ret) {
+               a0 = ret; a1 = size;
+               psxBios_bzero();
+       }
+       mips_return_c(ret, 21);
 }
 
 void psxBios_realloc() { // 0x38
@@ -1299,27 +1372,16 @@ void psxBios_realloc() { // 0x38
 
 
 /* InitHeap(void *block , int n) */
-void psxBios_InitHeap() { // 0x39
-       unsigned int size;
+static void psxBios_InitHeap() { // 0x39
+       PSXBIOS_LOG("psxBios_%s %x %x\n", biosA0n[0x39], a0, a1);
 
-#ifdef PSXBIOS_LOG
-       PSXBIOS_LOG("psxBios_%s\n", biosA0n[0x39]);
-#endif
+       storeRam32(A_HEAP_BASE, a0);
+       storeRam32(A_HEAP_SIZE, a1);
+       storeRam32(A_HEAP_END, a0 + (a1 & ~3) + 4);
+       storeRam32(A_HEAP_FLAG, 0);
+       storeRam32(a0, 0);
 
-       if (((a0 & 0x1fffff) + a1)>= 0x200000) size = 0x1ffffc - (a0 & 0x1fffff);
-       else size = a1;
-
-       size &= 0xfffffffc;
-
-       heap_addr = (u32 *)Ra0;
-       heap_size = size;
-       heap_end = (u32 *)((u8 *)heap_addr + heap_size);
-       /* HACKFIX: Commenting out this line fixes GTA2 crash */
-       //*heap_addr = SWAP32(size | 1);
-
-       PSXBIOS_LOG("InitHeap %x,%x : %x %x\n",a0,a1, (int)((uptr)heap_addr-(uptr)psxM), size);
-
-       pc0 = ra;
+       mips_return_void_c(14);
 }
 
 void psxBios_getchar() { //0x3b
@@ -1431,6 +1493,14 @@ static void psxBios_SystemErrorUnresolvedException() {
        mips_return_void_c(1000);
 }
 
+static void FlushCache() {
+       psxCpu->Notify(R3000ACPU_NOTIFY_CACHE_ISOLATED, NULL);
+       psxCpu->Notify(R3000ACPU_NOTIFY_CACHE_UNISOLATED, NULL);
+       k0 = 0xbfc0193c;
+       // runs from uncached mem so tons of cycles
+       use_cycles(500);
+}
+
 /*
  *     long Load(char *name, struct EXEC *header);
  */
@@ -1440,8 +1510,10 @@ void psxBios_Load() { // 0x42
        void *pa1;
 
        pa1 = Ra1;
-       if (pa1 && LoadCdromFile(Ra0, &eheader) == 0) {
+       if (pa1 != INVALID_PTR && LoadCdromFile(Ra0, &eheader) == 0) {
                memcpy(pa1, ((char*)&eheader)+16, sizeof(EXEC));
+               psxCpu->Clear(a1, sizeof(EXEC) / 4);
+               FlushCache();
                v0 = 1;
        } else v0 = 0;
        PSXBIOS_LOG("psxBios_%s: %s, %d -> %d\n", biosA0n[0x42], Ra0, a1, v0);
@@ -1488,13 +1560,10 @@ void psxBios_Exec() { // 43
        pc0 = SWAP32(header->_pc0);
 }
 
-void psxBios_FlushCache() { // 44
-#ifdef PSXBIOS_LOG
+static void psxBios_FlushCache() { // 44
        PSXBIOS_LOG("psxBios_%s\n", biosA0n[0x44]);
-#endif
-       psxCpu->Notify(R3000ACPU_NOTIFY_CACHE_ISOLATED, NULL);
-       psxCpu->Notify(R3000ACPU_NOTIFY_CACHE_UNISOLATED, NULL);
-       pc0 = ra;
+       FlushCache();
+       mips_return_void();
 }
 
 void psxBios_GPU_dw() { // 0x46
@@ -1520,7 +1589,7 @@ void psxBios_GPU_dw() { // 0x46
 
 void psxBios_mem2vram() { // 0x47
        int size;
-       gpuSyncPluginSR();
+       gpuSyncPluginSR(); // flush
        GPU_writeData(0xa0000000);
        GPU_writeData((a1<<0x10)|(a0&0xffff));
        GPU_writeData((a3<<0x10)|(a2&0xffff));
@@ -1542,8 +1611,8 @@ void psxBios_SendGPU() { // 0x48
 }
 
 void psxBios_GPU_cw() { // 0x49
-       gpuSyncPluginSR();
        GPU_writeData(a0);
+       gpuSyncPluginSR();
        v0 = HW_GPU_STATUS;
        pc0 = ra;
 }
@@ -1657,6 +1726,23 @@ static void psxBios_CdRemove() { // 56, 72
        use_cycles(30);
 }
 
+static void setup_tt(u32 tcb_cnt, u32 evcb_cnt, u32 stack);
+
+static void psxBios_SetConf() { // 9c
+       PSXBIOS_LOG("psxBios_%s %x %x %x\n", biosA0n[0x9c], a0, a1, a2);
+       setup_tt(a1, a0, a2);
+       psxRegs.CP0.n.SR |= 0x401;
+       mips_return_void_c(500);
+}
+
+static void psxBios_GetConf() { // 9d
+       PSXBIOS_LOG("psxBios_%s %x %x %x\n", biosA0n[0x9d], a0, a1, a2);
+       storeRam32(a0, loadRam32(A_CONF_EvCB));
+       storeRam32(a1, loadRam32(A_CONF_TCB));
+       storeRam32(a2, loadRam32(A_CONF_SP));
+       mips_return_void_c(10);
+}
+
 void psxBios_SetMem() { // 9f
        u32 new = psxHu32(0x1060);
 
@@ -1685,19 +1771,18 @@ void psxBios_SetMem() { // 9f
 }
 
 /* TODO FIXME : Not compliant. -1 indicates failure but using 1 for now. */
-void psxBios_get_cd_status(void) //a6
+static void psxBios_get_cd_status() // a6
 {
+       PSXBIOS_LOG("psxBios_%s\n", biosA0n[0xa6]);
        v0 = 1;
        pc0 = ra;
 }
 
-void psxBios__card_info() { // ab
-#ifdef PSXBIOS_LOG
+static void psxBios__card_info() { // ab
        PSXBIOS_LOG("psxBios_%s: %x\n", biosA0n[0xab], a0);
-#endif
        u32 ret, port;
-       card_active_chan = a0;
-       port = card_active_chan >> 4;
+       storeRam32(A_CARD_CHAN1, a0);
+       port = a0 >> 4;
 
        switch (port) {
        case 0x0:
@@ -1707,9 +1792,7 @@ void psxBios__card_info() { // ab
                        ret = 0x0100;
                break;
        default:
-#ifdef PSXBIOS_LOG
-               PSXBIOS_LOG("psxBios_%s: UNKNOWN PORT 0x%x\n", biosA0n[0xab], card_active_chan);
-#endif
+               PSXBIOS_LOG("psxBios_%s: UNKNOWN PORT 0x%x\n", biosA0n[0xab], a0);
                ret = 0x0302;
                break;
        }
@@ -1728,7 +1811,7 @@ void psxBios__card_load() { // ac
        PSXBIOS_LOG("psxBios_%s: %x\n", biosA0n[0xac], a0);
 #endif
 
-       card_active_chan = a0;
+       storeRam32(A_CARD_CHAN1, a0);
 
 //     DeliverEvent(0xf0000011, 0x0004);
        DeliverEvent(0xf4000001, 0x0004);
@@ -1834,6 +1917,7 @@ static u32 DeliverEvent(u32 class, u32 spec) {
        u32 ret = loadRam32(A_TT_EvCB) + evcb_len;
        u32 i, lim = evcb_len / 0x1c;
 
+       //printf("%s %08x %x\n", __func__, class, spec);
        for (i = 0; i < lim; i++, ev++) {
                use_cycles(8);
                if (SWAP32(ev->status) != EvStACTIVE)
@@ -2023,7 +2107,7 @@ void psxBios_OpenTh() { // 0e
                mips_return_c(0xffffffff, 20);
                return;
        }
-       PSXBIOS_LOG("psxBios_%s: %x\n", biosB0n[0x0e], th);
+       PSXBIOS_LOG("psxBios_%s -> %x\n", biosB0n[0x0e], 0xff000000 + th);
 
        tcb[th].status  = SWAP32(0x4000);
        tcb[th].mode    = SWAP32(0x1000);
@@ -2039,21 +2123,15 @@ void psxBios_OpenTh() { // 0e
  *     int CloseTh(long thread);
  */
 
-void psxBios_CloseTh() { // 0f
-       TCB *tcb = loadRam32ptr(A_TT_TCB);
-       u32 limit = loadRam32(A_TT_TCB + 4) / 0xc0u;
-       u32 th = a0 & 0xff;
+static void psxBios_CloseTh() { // 0f
+       u32 tcb = loadRam32(A_TT_TCB);
+       u32 th = a0 & 0xffff;
 
-#ifdef PSXBIOS_LOG
-       PSXBIOS_LOG("psxBios_%s: %x\n", biosB0n[0x0f], th);
-#endif
-       /* The return value is always 1 (even if the handle was already closed). */
-       v0 = 1;
-       if (th < limit && tcb[th].status == SWAP32(0x4000)) {
-               tcb[th].status = SWAP32(0x1000);
-       }
+       PSXBIOS_LOG("psxBios_%s %x\n", biosB0n[0x0f], a0);
+       // in the usual bios fashion no checks, just write and return 1
+       storeRam32(tcb + th * sizeof(TCB), 0x1000);
 
-       pc0 = ra;
+       mips_return_c(1, 11);
 }
 
 /*
@@ -2221,9 +2299,6 @@ static void psxBios_UnDeliverEvent() { // 0x20
        mips_return(ret);
 }
 
-char ffile[64], *pfile;
-int nfile;
-
 static void buopen(int mcd, char *ptr, char *cfg)
 {
        int i;
@@ -2294,9 +2369,7 @@ static void buopen(int mcd, char *ptr, char *cfg)
 void psxBios_open() { // 0x32
        void *pa0 = Ra0;
 
-#ifdef PSXBIOS_LOG
-       PSXBIOS_LOG("psxBios_%s: %s,%x\n", biosB0n[0x32], Ra0, a1);
-#endif
+       PSXBIOS_LOG("psxBios_%s %s %x\n", biosB0n[0x32], Ra0, a1);
 
        v0 = -1;
 
@@ -2441,76 +2514,86 @@ void psxBios_puts() { // 3e/3f
        pc0 = ra;
 }
 
+static void bufile(const u8 *mcd_data, u32 dir_) {
+       struct DIRENTRY *dir = (struct DIRENTRY *)castRam8ptr(dir_);
+       const char *pfile = ffile + 5;
+       const u8 *data = mcd_data;
+       int i = 0, match = 0;
+       int blocks = 1;
+       u32 head = 0;
 
-/* To avoid any issues with different behaviour when using the libc's own strlen instead.
- * We want to mimic the PSX's behaviour in this case for bufile. */
-static size_t strlen_internal(char* p)
-{
-       size_t size_of_array = 0;
-       while (*p++) size_of_array++;
-       return size_of_array;
-}
-
-#define bufile(mcd) { \
-       size_t size_of_name = strlen_internal(dir->name); \
-       while (nfile < 16) { \
-               int match=1; \
- \
-               ptr = Mcd##mcd##Data + 128 * (nfile + 1); \
-               nfile++; \
-               if ((*ptr & 0xF0) != 0x50) continue; \
-               /* Bug link files show up as free block. */ \
-               if (!ptr[0xa]) continue; \
-               ptr+= 0xa; \
-               if (pfile[0] == 0) { \
-                       strncpy(dir->name, ptr, sizeof(dir->name) - 1); \
-                       if (size_of_name < sizeof(dir->name)) dir->name[size_of_name] = '\0'; \
-               } else for (i=0; i<20; i++) { \
-                       if (pfile[i] == ptr[i]) { \
-                                                               dir->name[i] = ptr[i]; continue; } \
-                       if (pfile[i] == '?') { \
-                               dir->name[i] = ptr[i]; continue; } \
-                       if (pfile[i] == '*') { \
-                               strcpy(dir->name+i, ptr+i); break; } \
-                       match = 0; break; \
-               } \
-               PSXBIOS_LOG("%d : %s = %s + %s (match=%d)\n", nfile, dir->name, pfile, ptr, match); \
-               if (match == 0) { continue; } \
-               dir->size = 8192; \
-               v0 = _dir; \
-               break; \
-       } \
+       v0 = 0;
+       for (; nfile <= 15 && !match; nfile++) {
+               const char *name;
+
+               head = nfile * 0x40;
+               data = mcd_data + 128 * nfile;
+               name = (const char *)data + 0x0a;
+               if ((data[0] & 0xF0) != 0x50) continue;
+               /* Bug link files show up as free block. */
+               if (!name[0]) continue;
+               match = 1;
+               for (i = 0; i < 20; i++) {
+                       if (pfile[i] == name[i] || pfile[i] == '?')
+                               dir->name[i] = name[i];
+                       else if (pfile[i] == '*') {
+                               int len = strlen(name + i);
+                               if (i + len > 20)
+                                       len = 20 - i;
+                               memcpy(dir->name + i, name + i, len + 1);
+                               i += len;
+                               break;
+                       }
+                       else {
+                               match = 0;
+                               break;
+                       }
+                       if (!name[i])
+                               break;
+               }
+               PSXBIOS_LOG("%d : %s = %s + %s (match=%d)\n",
+                       nfile, dir->name, pfile, name, match);
+       }
+       for (; nfile <= 15; nfile++, blocks++) {
+               const u8 *data2 = mcd_data + 128 * nfile;
+               const char *name = data2 + 0x0a;
+               if ((data2[0] & 0xF0) != 0x50 || name[0])
+                       break;
+       }
+       if (match) {
+               // nul char of full lenth name seems to overwrite .attr
+               dir->attr = SWAP32(i < 20 ? data[0] & 0xf0 : 0); // ?
+               dir->size = 8192 * blocks;
+               dir->head = head;
+               v0 = dir_;
+       }
+       PSXBIOS_LOG("  -> %x '%s' %x %x %x %x\n", v0, v0 ? dir->name : "",
+                   dir->attr, dir->size, dir->next, dir->head);
 }
 
 /*
  *     struct DIRENTRY* firstfile(char *name,struct DIRENTRY *dir);
  */
 
-void psxBios_firstfile() { // 42
-       struct DIRENTRY *dir = (struct DIRENTRY *)Ra1;
-       void *pa0 = Ra0;
-       u32 _dir = a1;
-       char *ptr;
-       int i;
-
-#ifdef PSXBIOS_LOG
-       PSXBIOS_LOG("psxBios_%s: %s\n", biosB0n[0x42], Ra0);
-#endif
+static void psxBios_firstfile() { // 42
+       char *pa0 = castRam8ptr(a0);
 
+       PSXBIOS_LOG("psxBios_%s %s %x\n", biosB0n[0x42], pa0, a1);
        v0 = 0;
 
-       if (pa0 != INVALID_PTR) {
-               strcpy(ffile, pa0);
-               pfile = ffile+5;
-               nfile = 0;
+       {
+               snprintf(ffile, sizeof(ffile), "%s", pa0);
+               if (ffile[5] == 0)
+                       strcpy(ffile + 5, "*"); // maybe?
+               nfile = 1;
                if (!strncmp(pa0, "bu00", 4)) {
                        // firstfile() calls _card_read() internally, so deliver it's event
                        DeliverEvent(0xf0000011, 0x0004);
-                       bufile(1);
+                       bufile(Mcd1Data, a1);
                } else if (!strncmp(pa0, "bu10", 4)) {
                        // firstfile() calls _card_read() internally, so deliver it's event
                        DeliverEvent(0xf0000011, 0x0004);
-                       bufile(2);
+                       bufile(Mcd2Data, a1);
                }
        }
 
@@ -2522,24 +2605,13 @@ void psxBios_firstfile() { // 42
  */
 
 void psxBios_nextfile() { // 43
-       struct DIRENTRY *dir = (struct DIRENTRY *)Ra0;
-       u32 _dir = a0;
-       char *ptr;
-       int i;
-
-#ifdef PSXBIOS_LOG
-       PSXBIOS_LOG("psxBios_%s: %s\n", biosB0n[0x43], dir->name);
-#endif
+       PSXBIOS_LOG("psxBios_%s %x\n", biosB0n[0x43], a0);
 
        v0 = 0;
-
-       if (!strncmp(ffile, "bu00", 4)) {
-               bufile(1);
-       }
-
-       if (!strncmp(ffile, "bu10", 4)) {
-               bufile(2);
-       }
+       if (!strncmp(ffile, "bu00", 4))
+               bufile(Mcd1Data, a0);
+       else if (!strncmp(ffile, "bu10", 4))
+               bufile(Mcd2Data, a0);
 
        pc0 = ra;
 }
@@ -2636,11 +2708,12 @@ void psxBios_InitCARD() { // 4a
        u32 *ram32 = (u32 *)psxM;
        PSXBIOS_LOG("psxBios_%s: %x\n", biosB0n[0x4a], a0);
        write_chain(ram32 + A_PADCRD_CHN_E/4, 0, 0x49bc, 0x4a4c);
-       // (maybe) todo: early_card_irq, FlushCache etc
+       // (maybe) todo: early_card_irq, etc
 
        ram32[A_PAD_IRQR_ENA/4] = SWAP32(a0);
 
-       mips_return_c(0, 300);
+       psxBios_FlushCache();
+       mips_return_c(0, 34+13+15+6);
 }
 
 void psxBios_StartCARD() { // 4b
@@ -2683,7 +2756,7 @@ void psxBios__card_write() { // 0x4e
                v0 = 0; pc0 = ra;
                return;
        }
-       card_active_chan = a0;
+       storeRam32(A_CARD_CHAN1, a0);
        port = a0 >> 4;
 
        if (pa2 != INVALID_PTR) {
@@ -2719,7 +2792,7 @@ void psxBios__card_read() { // 0x4f
                v0 = 0; pc0 = ra;
                return;
        }
-       card_active_chan = a0;
+       storeRam32(A_CARD_CHAN1, a0);
        port = a0 >> 4;
 
        if (pa2 != INVALID_PTR) {
@@ -2810,13 +2883,14 @@ void psxBios_GetB0Table() { // 57
        mips_return_c(A_B0_TABLE, 3);
 }
 
-void psxBios__card_chan() { // 0x58
-#ifdef PSXBIOS_LOG
+static void psxBios__card_chan() { // 0x58
+       u32 ret;
        PSXBIOS_LOG("psxBios_%s\n", biosB0n[0x58]);
-#endif
 
-       v0 = card_active_chan;
-       pc0 = ra;
+       // todo: should return active slot chan
+       // (active - which was last processed by irq code)
+       ret = loadRam32(A_CARD_CHAN1);
+       mips_return_c(ret, 8);
 }
 
 static void psxBios_ChangeClearPad() { // 5b
@@ -2828,21 +2902,17 @@ static void psxBios_ChangeClearPad() { // 5b
        mips_return_c(ret, 6);
 }
 
-void psxBios__card_status() { // 5c
-#ifdef PSXBIOS_LOG
-       PSXBIOS_LOG("psxBios_%s: %x\n", biosB0n[0x5c], a0);
-#endif
+static void psxBios__card_status() { // 5c
+       PSXBIOS_LOG("psxBios_%s %x\n", biosB0n[0x5c], a0);
 
-       v0 = card_active_chan;
+       v0 = 1; // ready
        pc0 = ra;
 }
 
-void psxBios__card_wait() { // 5d
-#ifdef PSXBIOS_LOG
-       PSXBIOS_LOG("psxBios_%s: %x\n", biosB0n[0x5d], a0);
-#endif
+static void psxBios__card_wait() { // 5d
+       PSXBIOS_LOG("psxBios_%s %x\n", biosB0n[0x5d], a0);
 
-       v0 = 1;
+       v0 = 1; // ready
        pc0 = ra;
 }
 
@@ -3055,16 +3125,25 @@ static void write_chain(u32 *d, u32 next, u32 handler1, u32 handler2)
        d[2] = SWAPu32(handler2);
 
        // install the hle traps
-       PSXMu32ref(handler1) = HLEOP(chain_hle_op(handler1));
-       PSXMu32ref(handler2) = HLEOP(chain_hle_op(handler2));
+       if (handler1) PSXMu32ref(handler1) = HLEOP(chain_hle_op(handler1));
+       if (handler2) PSXMu32ref(handler2) = HLEOP(chain_hle_op(handler2));
 }
 
-static void setup_tt(u32 tcb_cnt, u32 evcb_cnt)
+static void setup_tt(u32 tcb_cnt, u32 evcb_cnt, u32 stack)
 {
        u32 *ram32 = (u32 *)psxM;
-       u32 s_excb = 0x20, s_evcb = 0x1c * evcb_cnt;
-       u32 s_pcb = 4, s_tcb = 0xc0 * tcb_cnt;
+       u32 s_excb = 0x20, s_evcb, s_pcb = 4, s_tcb;
        u32 p_excb, p_evcb, p_pcb, p_tcb;
+       u32 i;
+
+       PSXBIOS_LOG("setup: tcb %u, evcb %u\n", tcb_cnt, evcb_cnt);
+
+       // the real bios doesn't care, but we just don't
+       // want to crash in case of garbage parameters
+       if (tcb_cnt > 1024) tcb_cnt = 1024;
+       if (evcb_cnt > 1024) evcb_cnt = 1024;
+       s_evcb = 0x1c * evcb_cnt;
+       s_tcb = 0xc0 * tcb_cnt;
 
        memset(ram32 + 0xe000/4, 0, s_excb + s_evcb + s_pcb + s_tcb + 5*4);
        psxBios_SysInitMemory_(0xa000e000, 0x2000);
@@ -3095,6 +3174,8 @@ static void setup_tt(u32 tcb_cnt, u32 evcb_cnt)
 
        storeRam32(p_pcb, p_tcb);
        storeRam32(p_tcb, 0x4000);          // first TCB
+       for (i = 1; i < tcb_cnt; i++)
+               storeRam32(p_tcb + sizeof(TCB) * i, 0x1000);
 
        // default events
        storeRam32(A_CD_EVENTS + 0x00, OpenEvent(0xf0000003, 0x0010, EvMdMARK, 0));
@@ -3102,7 +3183,10 @@ static void setup_tt(u32 tcb_cnt, u32 evcb_cnt)
        storeRam32(A_CD_EVENTS + 0x08, OpenEvent(0xf0000003, 0x0040, EvMdMARK, 0));
        storeRam32(A_CD_EVENTS + 0x0c, OpenEvent(0xf0000003, 0x0080, EvMdMARK, 0));
        storeRam32(A_CD_EVENTS + 0x10, OpenEvent(0xf0000003, 0x8000, EvMdMARK, 0));
-       DeliverEvent(0xf0000003, 0x0010);
+
+       storeRam32(A_CONF_EvCB, evcb_cnt);
+       storeRam32(A_CONF_TCB, tcb_cnt);
+       storeRam32(A_CONF_SP, stack);
 }
 
 static const u32 gpu_ctl_def[] = {
@@ -3370,8 +3454,8 @@ void psxBiosInit() {
        //biosA0[0x99] = psxBios_EnableKernelIORedirection;
        //biosA0[0x9a] = psxBios_sys_a0_9a;
        //biosA0[0x9b] = psxBios_sys_a0_9b;
-       //biosA0[0x9c] = psxBios_SetConf;
-       //biosA0[0x9d] = psxBios_GetConf;
+       biosA0[0x9c] = psxBios_SetConf;
+       biosA0[0x9d] = psxBios_GetConf;
        //biosA0[0x9e] = psxBios_sys_a0_9e;
        biosA0[0x9f] = psxBios_SetMem;
        //biosA0[0xa0] = psxBios__boot;
@@ -3523,14 +3607,7 @@ void psxBiosInit() {
 //************** THE END ***************************************
 /**/
 
-       heap_addr = NULL;
-       heap_end = NULL;
-       heap_size = 0;
        memset(FDesc, 0, sizeof(FDesc));
-       card_active_chan = 0;
-
-       // initial RNG seed
-       psxMu32ref(0x9010) = SWAPu32(0xac20cc00);
 
        // somewhat pretend to be a SCPH1001 BIOS
        // some games look for these and take an exception if they're missing
@@ -3548,6 +3625,13 @@ void psxBiosInit() {
        len = 0x80000 - 0x69d68;
        uncompress((Bytef *)(psxR + 0x69d68), &len, font_889f, sizeof(font_889f));
 
+       // trap attempts to call bios directly
+       rom32[0x00000/4] = HLEOP(hleop_dummy);
+       rom32[0x00180/4] = HLEOP(hleop_dummy);
+       rom32[0x3fffc/4] = HLEOP(hleop_dummy);
+       rom32[0x65ffc/4] = HLEOP(hleop_dummy);
+       rom32[0x7ff2c/4] = HLEOP(hleop_dummy);
+
        /*      Some games like R-Types, CTR, Fade to Black read from adress 0x00000000 due to uninitialized pointers.
                See Garbage Area at Address 00000000h in Nocash PSX Specfications for more information.
                Here are some examples of games not working with this fix in place :
@@ -3573,7 +3657,8 @@ void psxBiosInit() {
        ram32[0x00b0/4] = HLEOP(hleop_b0);
        ram32[0x00c0/4] = HLEOP(hleop_c0);
 
-       setup_tt(4, 16);
+       setup_tt(4, 16, 0x801fff00);
+       DeliverEvent(0xf0000003, 0x0010);
 
        ram32[0x6ee0/4] = SWAPu32(0x0000eff0); // DCB
        strcpy((char *)&ram32[0xeff0/4], "bu");
@@ -3622,7 +3707,6 @@ void psxBiosInit() {
        ram32[0x1000/4] = HLEOP(hleop_dummy);
        ram32[0x2000/4] = HLEOP(hleop_dummy);
        ram32[0x3000/4] = HLEOP(hleop_dummy);
-       ram32[0x4c54/4] = HLEOP(hleop_dummy);   // for B12_InitPad?
        ram32[0x8000/4] = HLEOP(hleop_execret);
 
        ram32[A_EEXIT_PTR/4] = SWAP32(A_EEXIT_DEF);
@@ -3631,14 +3715,20 @@ void psxBiosInit() {
        ram32[A_RCNT_VBL_ACK/4 + 1] = SWAP32(1);
        ram32[A_RCNT_VBL_ACK/4 + 2] = SWAP32(1);
        ram32[A_RCNT_VBL_ACK/4 + 3] = SWAP32(1);
+       ram32[A_RND_SEED/4] = SWAPu32(0x24040001); // was 0xac20cc00
 }
 
 void psxBiosShutdown() {
 }
 
-void psxBiosCnfLoaded(u32 tcb_cnt, u32 evcb_cnt) {
-       if (tcb_cnt != 4 || evcb_cnt != 16)
-               setup_tt(tcb_cnt, evcb_cnt);
+void psxBiosCnfLoaded(u32 tcb_cnt, u32 evcb_cnt, u32 stack) {
+       if (stack == 0)
+               stack = 0x801FFF00;
+       if (tcb_cnt != 4 || evcb_cnt != 16) {
+               setup_tt(tcb_cnt, evcb_cnt, stack);
+               DeliverEvent(0xf0000003, 0x0010);
+       }
+       storeRam32(A_CONF_SP, stack);
 }
 
 #define psxBios_PADpoll(pad) { \
@@ -3709,9 +3799,9 @@ void hleExc0_1_2() // A(90h) - CdromIoIrqFunc1
 
 void hleExc0_2_2_syscall() // not in any A/B/C table
 {
-       u32 code = (psxRegs.CP0.n.Cause & 0x3c) >> 2;
        u32 tcbPtr = loadRam32(A_TT_PCB);
        TCB *tcb = loadRam32ptr(tcbPtr);
+       u32 code = (SWAP32(tcb->cause) & 0x3c) >> 2;
 
        if (code != R3000E_Syscall) {
                if (code != 0) {
@@ -3722,9 +3812,9 @@ void hleExc0_2_2_syscall() // not in any A/B/C table
                return;
        }
 
-       //printf("%s c=%d a0=%d\n", __func__, code, a0);
+       //printf("%s c=%d a0=%d\n", __func__, code, SWAP32(tcb->reg[4]));
        tcb->epc += SWAP32(4);
-       switch (a0) {
+       switch (SWAP32(tcb->reg[4])) { // a0
                case 0: // noop
                        break;
 
@@ -3740,7 +3830,7 @@ void hleExc0_2_2_syscall() // not in any A/B/C table
 
                case 3: { // ChangeThreadSubFunction
                        u32 tcbPtr = loadRam32(A_TT_PCB);
-                       storeRam32(tcbPtr, a1);
+                       storeRam32(tcbPtr, SWAP32(tcb->reg[5])); // a1
                        break;
                }
                default:
@@ -3929,24 +4019,12 @@ void psxBiosException() {
 }
 
 #define bfreezes(ptr) bfreeze(ptr, sizeof(ptr))
-#define bfreezel(ptr) bfreeze(ptr, sizeof(*ptr))
-
-#define bfreezepsxMptr(ptr, type) { \
-       if (Mode == 1) { \
-               if (ptr) psxRu32ref(base) = SWAPu32((s8 *)(ptr) - psxM); \
-               else psxRu32ref(base) = 0; \
-       } else { \
-               if (psxRu32(base) != 0) ptr = (type *)(psxM + psxRu32(base)); \
-               else (ptr) = NULL; \
-       } \
-       base += sizeof(u32); \
-}
+#define bfreezel(ptr) bfreeze(ptr, sizeof(*(ptr)))
 
 void psxBiosFreeze(int Mode) {
        u32 base = 0x40000;
 
-       bfreezepsxMptr(heap_addr, u32);
        bfreezes(FDesc);
-       bfreezel(&card_active_chan);
-       bfreezel(&heap_size);
+       bfreezes(ffile);
+       bfreezel(&nfile);
 }