psxbios: implement some pad reading details
[pcsx_rearmed.git] / libpcsxcore / psxbios.c
index 6624207..313bc50 100644 (file)
@@ -193,28 +193,21 @@ char *biosC0n[256] = {
 #define Rsp ((char *)PSXM(sp))
 
 typedef struct {
-       u32 desc;
-       s32 status;
-       s32 mode;
+       u32 class;
+       u32 status;
+       u32 spec;
+       u32 mode;
        u32 fhandler;
-} EvCB[32];
+       u32 unused[2];
+} EvCB;
 
-#define EvStUNUSED     0x0000
-#define EvStWAIT       0x1000
-#define EvStACTIVE     0x2000
-#define EvStALREADY 0x4000
+#define EvStUNUSED      0x0000
+#define EvStDISABLED    0x1000
+#define EvStACTIVE      0x2000
+#define EvStALREADY     0x4000
 
-#define EvMdINTR       0x1000
-#define EvMdNOINTR     0x2000
-
-/*
-typedef struct {
-       s32 next;
-       s32 func1;
-       s32 func2;
-       s32 pad;
-} SysRPst;
-*/
+#define EvMdCALL        0x1000
+#define EvMdMARK        0x2000
 
 typedef struct {
        u32 status;
@@ -258,23 +251,9 @@ typedef struct {
 } FileDesc;
 
 static int *pad_buf = NULL;
-static char *pad_buf1 = NULL, *pad_buf2 = NULL;
-static int pad_buf1len, pad_buf2len;
-static int pad_stopped = 0;
-
-static u32 regs[35];
-static EvCB *EventCB;
-static EvCB *HwEV; // 0xf0
-static EvCB *EvEV; // 0xf1
-static EvCB *RcEV; // 0xf2
-static EvCB *UeEV; // 0xf3
-static EvCB *SwEV; // 0xf4
-static EvCB *ThEV; // 0xff
 static u32 heap_size = 0;
 static u32 *heap_addr = NULL;
 static u32 *heap_end = NULL;
-static int CardState = -1;
-static int CurThread = 0;
 static FileDesc FDesc[32];
 static u32 card_active_chan = 0;
 
@@ -282,6 +261,7 @@ static u32 card_active_chan = 0;
 #define A_TT_ExCB       0x0100
 #define A_TT_PCB        0x0108
 #define A_TT_TCB        0x0110
+#define A_TT_EvCB       0x0120
 #define A_A0_TABLE      0x0200
 #define A_B0_TABLE      0x0874
 #define A_C0_TABLE      0x0674
@@ -289,9 +269,21 @@ static u32 card_active_chan = 0;
 #define A_EXCEPTION     0x0c80
 #define A_EXC_SP        0x6cf0
 #define A_EEXIT_DEF     0x6cf4
+#define A_KMALLOC_PTR   0x7460
+#define A_KMALLOC_SIZE  0x7464
+#define A_KMALLOC_END   0x7468
+#define A_PADCRD_CHN_E  0x74a8  // pad/card irq chain entry
+#define A_PAD_IRQR_ENA  0x74b8  // pad read on vint irq (nocash 'pad_enable_flag')
+#define A_CARD_IRQR_ENA 0x74bc  // same for card
+#define A_PAD_INBUF     0x74c8  // 2x buffers for rx pad data
+#define A_PAD_OUTBUF    0x74d0  // 2x buffers for tx pad data
+#define A_PAD_IN_LEN    0x74d8
+#define A_PAD_OUT_LEN   0x74e0
 #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_CD_EVENTS     0xb9b8
 #define A_EXC_GP        0xf450
 
 #define HLEOP(n) SWAPu32((0x3b << 26) | (n));
@@ -302,17 +294,34 @@ static u32 loadRam32(u32 addr)
        return SWAP32(*((u32 *)psxM + ((addr & 0x1fffff) >> 2)));
 }
 
+static void *castRam8ptr(u32 addr)
+{
+       assert(!(addr & 0x5f800000));
+       return psxM + (addr & 0x1fffff);
+}
+
 static void *castRam32ptr(u32 addr)
 {
        assert(!(addr & 0x5f800003));
        return psxM + (addr & 0x1ffffc);
 }
 
+static void *loadRam8ptr(u32 addr)
+{
+       return castRam8ptr(loadRam32(addr));
+}
+
 static void *loadRam32ptr(u32 addr)
 {
        return castRam32ptr(loadRam32(addr));
 }
 
+static void storeRam8(u32 addr, u8 d)
+{
+       assert(!(addr & 0x5f800000));
+       *((u8 *)psxM + (addr & 0x1fffff)) = d;
+}
+
 static void storeRam32(u32 addr, u32 d)
 {
        assert(!(addr & 0x5f800000));
@@ -374,14 +383,10 @@ static inline void softCallInException(u32 pc) {
                ra = sra;
 }
 
-static inline void DeliverEvent(u32 ev, u32 spec) {
-       if (EventCB[ev][spec].status != EvStACTIVE) return;
-
-//     EventCB[ev][spec].status = EvStALREADY;
-       if (EventCB[ev][spec].mode == EvMdINTR) {
-               softCall(EventCB[ev][spec].fhandler);
-       } else EventCB[ev][spec].status = EvStALREADY;
-}
+static u32 OpenEvent(u32 class, u32 spec, u32 mode, u32 func);
+static u32 DeliverEvent(u32 class, u32 spec);
+static u32 UnDeliverEvent(u32 class, u32 spec);
+static void CloseEvent(u32 ev);
 
 /*                                           *
 //                                           *
@@ -394,8 +399,8 @@ static inline void DeliverEvent(u32 ev, u32 spec) {
        ptr = Mcd##mcd##Data + 8192 * FDesc[1 + mcd].mcfile + FDesc[1 + mcd].offset; \
        memcpy(Ra1, ptr, length); \
        if (FDesc[1 + mcd].mode & 0x8000) { \
-       DeliverEvent(0x11, 0x2); /* 0xf0000011, 0x0004 */ \
-       DeliverEvent(0x81, 0x2); /* 0xf4000001, 0x0004 */ \
+       DeliverEvent(0xf0000011, 0x0004); \
+       DeliverEvent(0xf4000001, 0x0004); \
        v0 = 0; } \
        else v0 = length; \
        FDesc[1 + mcd].offset += v0; \
@@ -409,8 +414,8 @@ static inline void DeliverEvent(u32 ev, u32 spec) {
        FDesc[1 + mcd].offset += length; \
        SaveMcd(Config.Mcd##mcd, Mcd##mcd##Data, offset, length); \
        if (FDesc[1 + mcd].mode & 0x8000) { \
-       DeliverEvent(0x11, 0x2); /* 0xf0000011, 0x0004 */ \
-       DeliverEvent(0x81, 0x2); /* 0xf4000001, 0x0004 */ \
+       DeliverEvent(0xf0000011, 0x0004); \
+       DeliverEvent(0xf4000001, 0x0004); \
        v0 = 0; } \
        else v0 = length; \
 }
@@ -1430,15 +1435,12 @@ void psxBios_Load() { // 0x42
        EXE_HEADER eheader;
        void *pa1;
 
-#ifdef PSXBIOS_LOG
-       PSXBIOS_LOG("psxBios_%s: %s, %x\n", biosA0n[0x42], Ra0, a1);
-#endif
-
        pa1 = Ra1;
        if (pa1 && LoadCdromFile(Ra0, &eheader) == 0) {
                memcpy(pa1, ((char*)&eheader)+16, sizeof(EXEC));
                v0 = 1;
        } else v0 = 0;
+       PSXBIOS_LOG("psxBios_%s: %s, %d -> %d\n", biosA0n[0x42], Ra0, a1, v0);
 
        pc0 = ra;
 }
@@ -1602,8 +1604,8 @@ void psxBios__bu_init() { // 70
        PSXBIOS_LOG("psxBios_%s\n", biosA0n[0x70]);
 #endif
 
-       DeliverEvent(0x11, 0x2); // 0xf0000011, 0x0004
-       DeliverEvent(0x81, 0x2); // 0xf4000001, 0x0004
+       DeliverEvent(0xf0000011, 0x0004);
+       DeliverEvent(0xf4000001, 0x0004);
 
        pc0 = ra;
 }
@@ -1616,13 +1618,13 @@ void psxBios__96_init() { // 71
        pc0 = ra;
 }
 
-static void psxBios_SysDeqIntRP_();
+static void write_chain(u32 *d, u32 next, u32 handler1, u32 handler2);
+static void psxBios_SysEnqIntRP_(u32 priority, u32 chain_eptr);
+static void psxBios_SysDeqIntRP_(u32 priority, u32 chain_rm_eptr);
 
 static void psxBios_DequeueCdIntr_() {
-       a0 = 0; a1 = 0x91d0;
-       psxBios_SysDeqIntRP_();
-       a0 = 0; a1 = 0x91e0;
-       psxBios_SysDeqIntRP_();
+       psxBios_SysDeqIntRP_(0, 0x91d0);
+       psxBios_SysDeqIntRP_(0, 0x91e0);
        use_cycles(16);
 }
 
@@ -1634,11 +1636,11 @@ static void psxBios_DequeueCdIntr() { // a3
 static void psxBios_CdRemove() { // 56, 72
        PSXBIOS_LOG("psxBios_%s\n", biosA0n[0x72]);
 
-       // CloseEvent 0xf1000000
-       // CloseEvent 0xf1000001
-       // CloseEvent 0xf1000002
-       // CloseEvent 0xf1000003
-       // CloseEvent 0xf1000004
+       CloseEvent(loadRam32(A_CD_EVENTS + 0x00));
+       CloseEvent(loadRam32(A_CD_EVENTS + 0x04));
+       CloseEvent(loadRam32(A_CD_EVENTS + 0x08));
+       CloseEvent(loadRam32(A_CD_EVENTS + 0x0c));
+       CloseEvent(loadRam32(A_CD_EVENTS + 0x10));
        psxBios_DequeueCdIntr_();
 
        // EnterCriticalSection - should be done at the beginning,
@@ -1693,24 +1695,24 @@ void psxBios__card_info() { // ab
        switch (port) {
        case 0x0:
        case 0x1:
-               ret = 0x2;
+               ret = 0x0004;
                if (McdDisable[port & 1])
-                       ret = 0x8;
+                       ret = 0x0100;
                break;
        default:
 #ifdef PSXBIOS_LOG
                PSXBIOS_LOG("psxBios_%s: UNKNOWN PORT 0x%x\n", biosA0n[0xab], card_active_chan);
 #endif
-               ret = 0x11;
+               ret = 0x0302;
                break;
        }
 
        if (McdDisable[0] && McdDisable[1])
-               ret = 0x8;
+               ret = 0x0100;
 
-       DeliverEvent(0x11, 0x2); // 0xf0000011, 0x0004
-//     DeliverEvent(0x81, 0x2); // 0xf4000001, 0x0004
-       DeliverEvent(0x81, ret); // 0xf4000001, 0x0004
+       DeliverEvent(0xf0000011, 0x0004);
+//     DeliverEvent(0xf4000001, 0x0004);
+       DeliverEvent(0xf4000001, ret);
        v0 = 1; pc0 = ra;
 }
 
@@ -1721,14 +1723,23 @@ void psxBios__card_load() { // ac
 
        card_active_chan = a0;
 
-//     DeliverEvent(0x11, 0x2); // 0xf0000011, 0x0004
-       DeliverEvent(0x81, 0x2); // 0xf4000001, 0x0004
+//     DeliverEvent(0xf0000011, 0x0004);
+       DeliverEvent(0xf4000001, 0x0004);
 
        v0 = 1; pc0 = ra;
 }
 
 /* System calls B0 */
 
+static u32 psxBios_SysMalloc_(u32 size);
+
+static void psxBios_SysMalloc() { // B 00
+       u32 ret = psxBios_SysMalloc_(a0);
+
+       PSXBIOS_LOG("psxBios_%s 0x%x -> %x\n", biosB0n[0x00], a0, ret);
+       mips_return_c(ret, 33);
+}
+
 void psxBios_SetRCnt() { // 02
 #ifdef PSXBIOS_LOG
        PSXBIOS_LOG("psxBios_%s\n", biosB0n[0x02]);
@@ -1797,154 +1808,176 @@ void psxBios_ResetRCnt() { // 06
        pc0 = ra;
 }
 
+static u32 DeliverEvent(u32 class, u32 spec) {
+       EvCB *ev = (EvCB *)loadRam32ptr(A_TT_EvCB);
+       u32 evcb_len = loadRam32(A_TT_EvCB + 4);
+       u32 ret = loadRam32(A_TT_EvCB) + evcb_len;
+       u32 i, lim = evcb_len / 0x1c;
 
-/* gets ev for use with EventCB */
-#define GetEv() \
-       ev = (a0 >> 24) & 0xf; \
-       if (ev == 0xf) ev = 0x5; \
-       ev*= 32; \
-       ev+= a0&0x1f;
-
-/* gets spec for use with EventCB */
-#define GetSpec() \
-       spec = 0; \
-       switch (a1) { \
-               case 0x0301: spec = 16; break; \
-               case 0x0302: spec = 17; break; \
-               default: \
-                       for (i=0; i<16; i++) if (a1 & (1 << i)) { spec = i; break; } \
-                       break; \
+       for (i = 0; i < lim; i++, ev++) {
+               use_cycles(8);
+               if (SWAP32(ev->status) != EvStACTIVE)
+                       continue;
+               use_cycles(4);
+               if (SWAP32(ev->class) != class)
+                       continue;
+               use_cycles(4);
+               if (SWAP32(ev->spec) != spec)
+                       continue;
+               use_cycles(6);
+               ret = SWAP32(ev->mode);
+               if (ret == EvMdMARK) {
+                       ev->status = SWAP32(EvStALREADY);
+                       continue;
+               }
+               use_cycles(8);
+               if (ret == EvMdCALL) {
+                       ret = SWAP32(ev->fhandler);
+                       if (ret) {
+                               v0 = ret;
+                               softCall(ret);
+                               ret = v0;
+                       }
+               }
        }
+       use_cycles(29);
+       return ret;
+}
 
-void psxBios_DeliverEvent() { // 07
-       int ev, spec;
-       int i;
-
-       GetEv();
-       GetSpec();
-
-#ifdef PSXBIOS_LOG
-       PSXBIOS_LOG("psxBios_%s %x,%x\n", biosB0n[0x07], ev, spec);
-#endif
-
-       DeliverEvent(ev, spec);
+static u32 UnDeliverEvent(u32 class, u32 spec) {
+       EvCB *ev = (EvCB *)loadRam32ptr(A_TT_EvCB);
+       u32 evcb_len = loadRam32(A_TT_EvCB + 4);
+       u32 ret = loadRam32(A_TT_EvCB) + evcb_len;
+       u32 i, lim = evcb_len / 0x1c;
 
-       pc0 = ra;
+       for (i = 0; i < lim; i++, ev++) {
+               use_cycles(8);
+               if (SWAP32(ev->status) != EvStALREADY)
+                       continue;
+               use_cycles(4);
+               if (SWAP32(ev->class) != class)
+                       continue;
+               use_cycles(4);
+               if (SWAP32(ev->spec) != spec)
+                       continue;
+               use_cycles(6);
+               if (SWAP32(ev->mode) == EvMdMARK)
+                       ev->status = SWAP32(EvStACTIVE);
+       }
+       use_cycles(28);
+       return ret;
 }
 
-void psxBios_OpenEvent() { // 08
-       int ev, spec;
-       int i;
-
-       GetEv();
-       GetSpec();
+static void psxBios_DeliverEvent() { // 07
+       u32 ret;
+       PSXBIOS_LOG("psxBios_%s %x %04x\n", biosB0n[0x07], a0, a1);
 
-#ifdef PSXBIOS_LOG
-       PSXBIOS_LOG("psxBios_%s %x,%x (class:%x, spec:%x, mode:%x, func:%x)\n", biosB0n[0x08], ev, spec, a0, a1, a2, a3);
-#endif
+       ret = DeliverEvent(a0, a1);
+       mips_return(ret);
+}
 
-       EventCB[ev][spec].status = EvStWAIT;
-       EventCB[ev][spec].mode = a2;
-       EventCB[ev][spec].fhandler = a3;
+static s32 get_free_EvCB_slot() {
+       EvCB *ev = (EvCB *)loadRam32ptr(A_TT_EvCB);
+       u32 i, lim = loadRam32(A_TT_EvCB + 4) / 0x1c;
 
-       v0 = ev | (spec << 8);
-       pc0 = ra;
+       use_cycles(19);
+       for (i = 0; i < lim; i++, ev++) {
+               use_cycles(8);
+               if (ev->status == SWAP32(EvStUNUSED))
+                       return i;
+       }
+       return -1;
 }
 
-void psxBios_CloseEvent() { // 09
-       int ev, spec;
-
-       ev   = a0 & 0xff;
-       spec = (a0 >> 8) & 0xff;
+static u32 OpenEvent(u32 class, u32 spec, u32 mode, u32 func) {
+       u32 ret = get_free_EvCB_slot();
+       if ((s32)ret >= 0) {
+               EvCB *ev = (EvCB *)loadRam32ptr(A_TT_EvCB) + ret;
+               ev->class = SWAP32(class);
+               ev->status = SWAP32(EvStDISABLED);
+               ev->spec = SWAP32(spec);
+               ev->mode = SWAP32(mode);
+               ev->fhandler = SWAP32(func);
+               ret |= 0xf1000000u;
+       }
+       return ret;
+}
 
-#ifdef PSXBIOS_LOG
-       PSXBIOS_LOG("psxBios_%s %x,%x\n", biosB0n[0x09], ev, spec);
-#endif
+static void psxBios_OpenEvent() { // 08
+       u32 ret = OpenEvent(a0, a1, a2, a3);
+       PSXBIOS_LOG("psxBios_%s (class:%x, spec:%04x, mode:%04x, func:%x) -> %x\n",
+               biosB0n[0x08], a0, a1, a2, a3, ret);
+       mips_return_c(ret, 36);
+}
 
-       EventCB[ev][spec].status = EvStUNUSED;
+static void CloseEvent(u32 ev)
+{
+       u32 base = loadRam32(A_TT_EvCB);
+       storeRam32(base + (ev & 0xffff) * sizeof(EvCB) + 4, EvStUNUSED);
+}
 
-       v0 = 1; pc0 = ra;
+static void psxBios_CloseEvent() { // 09
+       PSXBIOS_LOG("psxBios_%s %x (%x)\n", biosB0n[0x09], a0,
+               loadRam32(loadRam32(A_TT_EvCB) + (a0 & 0xffff) * sizeof(EvCB) + 4));
+       CloseEvent(a0);
+       mips_return_c(1, 10);
 }
 
-void psxBios_WaitEvent() { // 0a
-       int ev, spec;
+static void psxBios_WaitEvent() { // 0a
+       u32 base = loadRam32(A_TT_EvCB);
+       u32 status = loadRam32(base + (a0 & 0xffff) * sizeof(EvCB) + 4);
+       PSXBIOS_LOG("psxBios_%s %x (status=%x)\n", biosB0n[0x0a], a0, status);
 
-       ev   = a0 & 0xff;
-       spec = (a0 >> 8) & 0xff;
-#ifdef PSXBIOS_LOG
-       PSXBIOS_LOG("psxBios_%s %x,%x\n", biosB0n[0x0a], ev, spec);
-#endif
-       if (EventCB[ev][spec].status == EvStUNUSED)
-       {
-               v0 = 0;
-               pc0 = ra;
+       use_cycles(15);
+       if (status == EvStALREADY) {
+               storeRam32(base + (a0 & 0xffff) * sizeof(EvCB) + 4, EvStACTIVE);
+               mips_return(1);
                return;
        }
-
-       if (EventCB[ev][spec].status == EvStALREADY)
+       if (status != EvStACTIVE)
        {
-               /* Callback events (mode=EvMdINTR) do never set the ready flag (and thus WaitEvent would hang forever). */
-               if (!(EventCB[ev][spec].mode == EvMdINTR)) EventCB[ev][spec].status = EvStACTIVE;
-               v0 = 1;
-               pc0 = ra;
+               mips_return_c(0, 2);
                return;
        }
 
-       v0 = 0;
-       pc0 = ra;
+       // retrigger this hlecall after the next emulation event
+       pc0 -= 4;
+       if ((s32)(next_interupt - psxRegs.cycle) > 0)
+               psxRegs.cycle = next_interupt;
+       psxBranchTest();
 }
 
-void psxBios_TestEvent() { // 0b
-       int ev, spec;
-
-       ev   = a0 & 0xff;
-       spec = (a0 >> 8) & 0xff;
-
-       if (EventCB[ev][spec].status == EvStALREADY)
-       {
-               if (!(EventCB[ev][spec].mode == EvMdINTR)) EventCB[ev][spec].status = EvStACTIVE;
-               v0 = 1;
-       }
-       else
-       {
-               v0 = 0;
+static void psxBios_TestEvent() { // 0b
+       u32 base = loadRam32(A_TT_EvCB);
+       u32 status = loadRam32(base + (a0 & 0xffff) * sizeof(EvCB) + 4);
+       u32 ret = 0;
+       PSXBIOS_LOG("psxBios_%s    %x %x\n", biosB0n[0x0b], a0, status);
+       if (status == EvStALREADY) {
+               storeRam32(base + (a0 & 0xffff) * sizeof(EvCB) + 4, EvStACTIVE);
+               ret = 1;
        }
 
-#ifdef PSXBIOS_LOG
-       PSXBIOS_LOG("psxBios_%s %x,%x: %x\n", biosB0n[0x0b], ev, spec, v0);
-#endif
-
-       pc0 = ra;
+       mips_return_c(ret, 15);
 }
 
-void psxBios_EnableEvent() { // 0c
-       int ev, spec;
-
-       ev   = a0 & 0xff;
-       spec = (a0 >> 8) & 0xff;
-
-#ifdef PSXBIOS_LOG
-       PSXBIOS_LOG("psxBios_%s %x,%x\n", biosB0n[0x0c], ev, spec);
-#endif
-
-       EventCB[ev][spec].status = EvStACTIVE;
+static void psxBios_EnableEvent() { // 0c
+       u32 base = loadRam32(A_TT_EvCB);
+       u32 status = loadRam32(base + (a0 & 0xffff) * sizeof(EvCB) + 4);
+       PSXBIOS_LOG("psxBios_%s %x (%x)\n", biosB0n[0x0c], a0, status);
+       if (status != EvStUNUSED)
+               storeRam32(base + (a0 & 0xffff) * sizeof(EvCB) + 4, EvStACTIVE);
 
-       v0 = 1; pc0 = ra;
+       mips_return_c(1, 15);
 }
 
-void psxBios_DisableEvent() { // 0d
-       int ev, spec;
-
-       ev   = a0 & 0xff;
-       spec = (a0 >> 8) & 0xff;
-
-#ifdef PSXBIOS_LOG
-       PSXBIOS_LOG("psxBios_%s %x,%x\n", biosB0n[0x0d], ev, spec);
-#endif
+static void psxBios_DisableEvent() { // 0d
+       u32 base = loadRam32(A_TT_EvCB);
+       u32 status = loadRam32(base + (a0 & 0xffff) * sizeof(EvCB) + 4);
+       PSXBIOS_LOG("psxBios_%s %x: %x\n", biosB0n[0x0d], a0, status);
+       if (status != EvStUNUSED)
+               storeRam32(base + (a0 & 0xffff) * sizeof(EvCB) + 4, EvStDISABLED);
 
-       EventCB[ev][spec].status = EvStWAIT;
-
-       v0 = 1; pc0 = ra;
+       mips_return_c(1, 15);
 }
 
 /*
@@ -2023,36 +2056,55 @@ void psxBios_ChangeTh() { // 10
 }
 
 void psxBios_InitPAD() { // 0x12
-#ifdef PSXBIOS_LOG
-       PSXBIOS_LOG("psxBios_%s\n", biosB0n[0x12]);
-#endif
+       u32 i, *ram32 = (u32 *)psxM;
+       PSXBIOS_LOG("psxBios_%s %x %x %x %x\n", biosB0n[0x12], a0, a1, a2, a3);
 
-       pad_buf1 = (char*)Ra0;
-       pad_buf1len = a1;
-       pad_buf2 = (char*)Ra2;
-       pad_buf2len = a3;
+       // printf("%s", "PS-X Control PAD Driver  Ver 3.0");
+       // PAD_dr_enable = 0;
+       ram32[A_PAD_OUTBUF/4 + 0] = 0;
+       ram32[A_PAD_OUTBUF/4 + 1] = 0;
+       ram32[A_PAD_OUT_LEN/4 + 0] = 0;
+       ram32[A_PAD_OUT_LEN/4 + 1] = 0;
+       ram32[A_PAD_INBUF/4 + 0] = SWAP32(a0);
+       ram32[A_PAD_INBUF/4 + 1] = SWAP32(a2);
+       ram32[A_PAD_IN_LEN/4 + 0] = SWAP32(a1);
+       ram32[A_PAD_IN_LEN/4 + 1] = SWAP32(a3);
 
-       v0 = 1; pc0 = ra;
+       for (i = 0; i < a1; i++) {
+               use_cycles(4);
+               storeRam8(a0 + i, 0);
+       }
+       for (i = 0; i < a3; i++) {
+               use_cycles(4);
+               storeRam8(a2 + i, 0);
+       }
+       write_chain(ram32 + A_PADCRD_CHN_E/4, 0, 0x49bc, 0x4a4c);
+
+       ram32[A_PAD_IRQR_ENA/4] = SWAP32(1);
+
+       mips_return_c(1, 200);
 }
 
 void psxBios_StartPAD() { // 13
-#ifdef PSXBIOS_LOG
        PSXBIOS_LOG("psxBios_%s\n", biosB0n[0x13]);
-#endif
-       pad_stopped = 0;
-       psxHwWrite16(0x1f801074, (unsigned short)(psxHwRead16(0x1f801074) | 0x1));
+
+       psxBios_SysDeqIntRP_(2, A_PADCRD_CHN_E);
+       psxBios_SysEnqIntRP_(2, A_PADCRD_CHN_E);
+       psxHwWrite16(0x1f801070, ~1);
+       psxHwWrite16(0x1f801074, psxHu32(0x1074) | 1);
+       storeRam32(A_PAD_ACK_VBL, 1);
+       storeRam32(A_RCNT_VBL_ACK + (3 << 2), 0);
        psxRegs.CP0.n.SR |= 0x401;
-       pc0 = ra;
+
+       mips_return_c(1, 300);
 }
 
 void psxBios_StopPAD() { // 14
-#ifdef PSXBIOS_LOG
        PSXBIOS_LOG("psxBios_%s\n", biosB0n[0x14]);
-#endif
-       pad_stopped = 1;
-       pad_buf1 = NULL;
-       pad_buf2 = NULL;
-       pc0 = ra;
+       storeRam32(A_RCNT_VBL_ACK + (3 << 2), 1);
+       psxBios_SysDeqIntRP_(2, A_PADCRD_CHN_E);
+       psxRegs.CP0.n.SR |= 0x401;
+       mips_return_void_c(200);
 }
 
 void psxBios_PAD_init() { // 15
@@ -2114,22 +2166,12 @@ void psxBios_HookEntryInt() { // 19
        mips_return_void_c(3);
 }
 
-void psxBios_UnDeliverEvent() { // 0x20
-       int ev, spec;
-       int i;
-
-       GetEv();
-       GetSpec();
-
-#ifdef PSXBIOS_LOG
-       PSXBIOS_LOG("psxBios_%s %x,%x\n", biosB0n[0x20], ev, spec);
-#endif
-
-       if (EventCB[ev][spec].status == EvStALREADY &&
-               EventCB[ev][spec].mode == EvMdNOINTR)
-               EventCB[ev][spec].status = EvStACTIVE;
+static void psxBios_UnDeliverEvent() { // 0x20
+       u32 ret;
+       PSXBIOS_LOG("psxBios_%s %x %x\n", biosB0n[0x20], a0, a1);
 
-       pc0 = ra;
+       ret = UnDeliverEvent(a0, a1);
+       mips_return(ret);
 }
 
 char ffile[64], *pfile;
@@ -2237,8 +2279,8 @@ void psxBios_lseek() { // 0x33
                case 0: // SEEK_SET
                        FDesc[a0].offset = a1;
                        v0 = a1;
-//                     DeliverEvent(0x11, 0x2); // 0xf0000011, 0x0004
-//                     DeliverEvent(0x81, 0x2); // 0xf4000001, 0x0004
+//                     DeliverEvent(0xf0000011, 0x0004);
+//                     DeliverEvent(0xf4000001, 0x0004);
                        break;
 
                case 1: // SEEK_CUR
@@ -2417,11 +2459,11 @@ void psxBios_firstfile() { // 42
                nfile = 0;
                if (!strncmp(pa0, "bu00", 4)) {
                        // firstfile() calls _card_read() internally, so deliver it's event
-                       DeliverEvent(0x11, 0x2);
+                       DeliverEvent(0xf0000011, 0x0004);
                        bufile(1);
                } else if (!strncmp(pa0, "bu10", 4)) {
                        // firstfile() calls _card_read() internally, so deliver it's event
-                       DeliverEvent(0x11, 0x2);
+                       DeliverEvent(0xf0000011, 0x0004);
                        bufile(2);
                }
        }
@@ -2545,33 +2587,37 @@ void psxBios_delete() { // 45
 }
 
 void psxBios_InitCARD() { // 4a
-#ifdef PSXBIOS_LOG
+       u32 *ram32 = (u32 *)psxM;
        PSXBIOS_LOG("psxBios_%s: %x\n", biosB0n[0x4a], a0);
-#endif
+       write_chain(ram32 + A_PADCRD_CHN_E/4, 0, 0x49bc, 0x4a4c);
+       // (maybe) todo: early_card_irq, FlushCache etc
 
-       CardState = 0;
+       ram32[A_PAD_IRQR_ENA/4] = SWAP32(a0);
 
-       pc0 = ra;
+       mips_return_c(0, 300);
 }
 
 void psxBios_StartCARD() { // 4b
-#ifdef PSXBIOS_LOG
        PSXBIOS_LOG("psxBios_%s\n", biosB0n[0x4b]);
-#endif
+       psxBios_SysDeqIntRP_(2, A_PADCRD_CHN_E);
+       psxBios_SysEnqIntRP_(2, A_PADCRD_CHN_E);
 
-       if (CardState == 0) CardState = 1;
+       psxHwWrite16(0x1f801074, psxHu32(0x1074) | 1);
+       storeRam32(A_PAD_ACK_VBL, 1);
+       storeRam32(A_RCNT_VBL_ACK + (3 << 2), 0);
+       storeRam32(A_CARD_IRQR_ENA, 1);
+       psxRegs.CP0.n.SR |= 0x401;
 
-       pc0 = ra;
+       mips_return_c(1, 200);
 }
 
 void psxBios_StopCARD() { // 4c
-#ifdef PSXBIOS_LOG
        PSXBIOS_LOG("psxBios_%s\n", biosB0n[0x4c]);
-#endif
-
-       if (CardState == 1) CardState = 0;
-
-       pc0 = ra;
+       storeRam32(A_RCNT_VBL_ACK + (3 << 2), 1);
+       psxBios_SysDeqIntRP_(2, A_PADCRD_CHN_E);
+       storeRam32(A_CARD_IRQR_ENA, 0);
+       psxRegs.CP0.n.SR |= 0x401;
+       mips_return_void_c(200);
 }
 
 void psxBios__card_write() { // 0x4e
@@ -2604,8 +2650,8 @@ void psxBios__card_write() { // 0x4e
                }
        }
 
-       DeliverEvent(0x11, 0x2); // 0xf0000011, 0x0004
-//     DeliverEvent(0x81, 0x2); // 0xf4000001, 0x0004
+       DeliverEvent(0xf0000011, 0x0004);
+//     DeliverEvent(0xf4000001, 0x0004);
 
        v0 = 1; pc0 = ra;
 }
@@ -2638,8 +2684,8 @@ void psxBios__card_read() { // 0x4f
                }
        }
 
-       DeliverEvent(0x11, 0x2); // 0xf0000011, 0x0004
-//     DeliverEvent(0x81, 0x2); // 0xf4000001, 0x0004
+       DeliverEvent(0xf0000011, 0x0004);
+//     DeliverEvent(0xf4000001, 0x0004);
 
        v0 = 1; pc0 = ra;
 }
@@ -2727,12 +2773,13 @@ void psxBios__card_chan() { // 0x58
        pc0 = ra;
 }
 
-void psxBios_ChangeClearPad() { // 5b
-#ifdef PSXBIOS_LOG
+static void psxBios_ChangeClearPad() { // 5b
+       u32 ret;
        PSXBIOS_LOG("psxBios_%s: %x\n", biosB0n[0x5b], a0);
-#endif
+       ret = loadRam32(A_PAD_ACK_VBL);
+       storeRam32(A_PAD_ACK_VBL, a0);
 
-       pc0 = ra;
+       mips_return_c(ret, 6);
 }
 
 void psxBios__card_status() { // 5c
@@ -2755,44 +2802,67 @@ void psxBios__card_wait() { // 5d
 
 /* System calls C0 */
 
+static void psxBios_InitRCnt() { // 00
+       int i;
+       PSXBIOS_LOG("psxBios_%s %x\n", biosC0n[0x00], a0);
+       psxHwWrite16(0x1f801074, psxHu32(0x1074) & ~0x71);
+       for (i = 0; i < 3; i++) {
+               psxHwWrite16(0x1f801100 + i*0x10 + 4, 0);
+               psxHwWrite16(0x1f801100 + i*0x10 + 8, 0);
+               psxHwWrite16(0x1f801100 + i*0x10 + 0, 0);
+       }
+       psxBios_SysEnqIntRP_(a0, 0x6d88);
+       mips_return_c(0, 9);
+}
+
+static void psxBios_InitException() { // 01
+       PSXBIOS_LOG("psxBios_%s %x\n", biosC0n[0x01], a0);
+       psxBios_SysEnqIntRP_(a0, 0x6da8);
+       mips_return_c(0, 9);
+}
+
 /*
  * int SysEnqIntRP(int index , long *queue);
  */
 
-void psxBios_SysEnqIntRP() { // 02
+static void psxBios_SysEnqIntRP_(u32 priority, u32 chain_eptr) {
        u32 old, base = loadRam32(A_TT_ExCB);
-       PSXBIOS_LOG("psxBios_%s %x %x\n", biosC0n[0x02], a0, a1);
 
-       old = loadRam32(base + (a0 << 3));
-       storeRam32(base + (a0 << 3), a1);
-       storeRam32(a1, old);
+       old = loadRam32(base + (priority << 3));
+       storeRam32(base + (priority << 3), chain_eptr);
+       storeRam32(chain_eptr, old);
        mips_return_c(0, 9);
 }
 
+static void psxBios_SysEnqIntRP() { // 02
+       PSXBIOS_LOG("psxBios_%s %x %x\n", biosC0n[0x02], a0, a1);
+       psxBios_SysEnqIntRP_(a0, a1);
+}
+
 /*
  * int SysDeqIntRP(int index , long *queue);
  */
 
-static void psxBios_SysDeqIntRP_() { // 03
+static void psxBios_SysDeqIntRP_(u32 priority, u32 chain_rm_eptr) {
        u32 ptr, next, base = loadRam32(A_TT_ExCB);
        u32 lim = 0, ret = 0;
 
        // as in original: no arg checks of any kind, bug if a1 == 0
-       ptr = loadRam32(base + (a0 << 3));
+       ptr = loadRam32(base + (priority << 3));
        while (ptr) {
                next = loadRam32(ptr);
-               if (ptr == a1) {
-                       storeRam32(base + (a0 << 3), next);
+               if (ptr == chain_rm_eptr) {
+                       storeRam32(base + (priority << 3), next);
                        ret = ptr;
                        use_cycles(6);
                        break;
                }
-               while (next && next != a1 && lim++ < 100) {
+               while (next && next != chain_rm_eptr && lim++ < 100) {
                        ptr = next;
                        next = loadRam32(ptr);
                        use_cycles(8);
                }
-               if (next == a1) {
+               if (next == chain_rm_eptr) {
                        next = loadRam32(next);
                        storeRam32(ptr, next);
                        ret = ptr;
@@ -2801,17 +2871,47 @@ static void psxBios_SysDeqIntRP_() { // 03
                break;
        }
        if (lim == 100)
-               PSXBIOS_LOG("bad chain %u %x\n", a0, base);
+               PSXBIOS_LOG("bad chain %u %x\n", priority, base);
 
        mips_return_c(ret, 12);
 }
 
 static void psxBios_SysDeqIntRP() { // 03
        PSXBIOS_LOG("psxBios_%s %x %x\n", biosC0n[0x03], a0, a1);
-       psxBios_SysDeqIntRP_();
+       psxBios_SysDeqIntRP_(a0, a1);
+}
+
+static void psxBios_get_free_EvCB_slot() { // 04
+       PSXBIOS_LOG("psxBios_%s\n", biosC0n[0x04]);
+       s32 ret = get_free_EvCB_slot();
+       mips_return_c(ret, 0);
+}
+       
+static void psxBios_SysInitMemory_(u32 base, u32 size) {
+       storeRam32(base, 0);
+       storeRam32(A_KMALLOC_PTR, base);
+       storeRam32(A_KMALLOC_SIZE, size);
+       storeRam32(A_KMALLOC_END, base + (size & ~3) + 4);
+}
+
+// this should be much more complicated, but maybe that'll be enough
+static u32 psxBios_SysMalloc_(u32 size) {
+       u32 ptr = loadRam32(A_KMALLOC_PTR);
+
+       size = (size + 3) & ~3;
+       storeRam32(A_KMALLOC_PTR, ptr + 4 + size);
+       storeRam32(ptr, size);
+       return ptr + 4;
+}
+
+static void psxBios_SysInitMemory() { // 08
+       PSXBIOS_LOG("psxBios_%s %x %x\n", biosC0n[0x08], a0, a1);
+
+       psxBios_SysInitMemory_(a0, a1);
+       mips_return_void_c(12);
 }
 
-void psxBios_ChangeClearRCnt() { // 0a
+static void psxBios_ChangeClearRCnt() { // 0a
        u32 ret;
 
        PSXBIOS_LOG("psxBios_%s: %x, %x\n", biosC0n[0x0a], a0, a1);
@@ -2821,6 +2921,13 @@ void psxBios_ChangeClearRCnt() { // 0a
        mips_return_c(ret, 8);
 }
 
+static void psxBios_InitDefInt() { // 0c
+       PSXBIOS_LOG("psxBios_%s %x\n", biosC0n[0x0c], a0);
+       // should also clear the autoack table
+       psxBios_SysEnqIntRP_(a0, 0x6d98);
+       mips_return_c(0, 20 + 6*2);
+}
+
 void psxBios_dummy() {
        u32 pc = (pc0 & 0x1fffff) - 4;
        char **ntab = pc == 0xa0 ? biosA0n : pc == 0xb0 ? biosB0n
@@ -2884,6 +2991,8 @@ static const struct {
        {     0x1920, hleop_exc1_3_1 },
        {     0x1794, hleop_exc1_3_2 },
        {     0x2458, hleop_exc3_0_2 },
+       {     0x49bc, hleop_exc_padcard1 },
+       {     0x4a4c, hleop_exc_padcard2 },
 };
 
 static int chain_hle_op(u32 handler)
@@ -2902,14 +3011,59 @@ static void write_chain(u32 *d, u32 next, u32 handler1, u32 handler2)
        d[1] = SWAPu32(handler1);
        d[2] = SWAPu32(handler2);
 
-       // install hle traps
+       // install the hle traps
        PSXMu32ref(handler1) = HLEOP(chain_hle_op(handler1));
        PSXMu32ref(handler2) = HLEOP(chain_hle_op(handler2));
 }
 
+static void setup_tt(u32 tcb_cnt, u32 evcb_cnt)
+{
+       u32 *ram32 = (u32 *)psxM;
+       u32 s_excb = 0x20, s_evcb = 0x1c * evcb_cnt;
+       u32 s_pcb = 4, s_tcb = 0xc0 * tcb_cnt;
+       u32 p_excb, p_evcb, p_pcb, p_tcb;
+
+       memset(ram32 + 0xe000/4, 0, s_excb + s_evcb + s_pcb + s_tcb + 5*4);
+       psxBios_SysInitMemory_(0xa000e000, 0x2000);
+       p_excb = psxBios_SysMalloc_(s_excb);
+       p_evcb = psxBios_SysMalloc_(s_evcb);
+       p_pcb  = psxBios_SysMalloc_(s_pcb);
+       p_tcb  = psxBios_SysMalloc_(s_tcb);
+
+       // "table of tables". Some games modify it
+       assert(A_TT_ExCB == 0x0100);
+       ram32[0x0100/4] = SWAPu32(p_excb);  // ExCB - exception chains
+       ram32[0x0104/4] = SWAPu32(s_excb);  // ExCB size
+       ram32[0x0108/4] = SWAPu32(p_pcb);   // PCB - process control
+       ram32[0x010c/4] = SWAPu32(s_pcb);   // PCB size
+       ram32[0x0110/4] = SWAPu32(p_tcb);   // TCB - thread control
+       ram32[0x0114/4] = SWAPu32(s_tcb);   // TCB size
+       ram32[0x0120/4] = SWAPu32(p_evcb);  // EvCB - event control
+       ram32[0x0124/4] = SWAPu32(s_evcb);  // EvCB size
+       ram32[0x0140/4] = SWAPu32(0x8648);  // FCB - file control
+       ram32[0x0144/4] = SWAPu32(0x02c0);  // FCB size
+       ram32[0x0150/4] = SWAPu32(0x6ee0);  // DCB - device control
+       ram32[0x0154/4] = SWAPu32(0x0320);  // DCB size
+
+       storeRam32(p_excb + 0*4, 0x91e0);   // chain0
+       storeRam32(p_excb + 2*4, 0x6d88);   // chain1
+       storeRam32(p_excb + 4*4, 0x0000);   // chain2
+       storeRam32(p_excb + 6*4, 0x6d98);   // chain3
+
+       storeRam32(p_pcb, p_tcb);
+       storeRam32(p_tcb, 0x4000);          // first TCB
+
+       // default events
+       storeRam32(A_CD_EVENTS + 0x00, OpenEvent(0xf0000003, 0x0010, EvMdMARK, 0));
+       storeRam32(A_CD_EVENTS + 0x04, OpenEvent(0xf0000003, 0x0020, EvMdMARK, 0));
+       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);
+}
+
 void psxBiosInit() {
-       u32 base, size;
-       u32 *ptr, *ram32;
+       u32 *ptr, *ram32, *rom32;
        int i;
        uLongf len;
 
@@ -3112,7 +3266,7 @@ void psxBiosInit() {
        //biosA0[0xb3] = psxBios_sys_a0_b3;
        //biosA0[0xb4] = psxBios_sub_function;
 //*******************B0 CALLS****************************
-       //biosB0[0x00] = psxBios_SysMalloc;
+       biosB0[0x00] = psxBios_SysMalloc;
        //biosB0[0x01] = psxBios_sys_b0_01;
        biosB0[0x02] = psxBios_SetRCnt;
        biosB0[0x03] = psxBios_GetRCnt;
@@ -3205,19 +3359,19 @@ void psxBiosInit() {
        biosB0[0x5c] = psxBios__card_status;
        biosB0[0x5d] = psxBios__card_wait;
 //*******************C0 CALLS****************************
-       //biosC0[0x00] = psxBios_InitRCnt;
-       //biosC0[0x01] = psxBios_InitException;
+       biosC0[0x00] = psxBios_InitRCnt;
+       biosC0[0x01] = psxBios_InitException;
        biosC0[0x02] = psxBios_SysEnqIntRP;
        biosC0[0x03] = psxBios_SysDeqIntRP;
-       //biosC0[0x04] = psxBios_get_free_EvCB_slot;
+       biosC0[0x04] = psxBios_get_free_EvCB_slot;
        //biosC0[0x05] = psxBios_get_free_TCB_slot;
        //biosC0[0x06] = psxBios_ExceptionHandler;
        //biosC0[0x07] = psxBios_InstallExeptionHandler;
-       //biosC0[0x08] = psxBios_SysInitMemory;
+       biosC0[0x08] = psxBios_SysInitMemory;
        //biosC0[0x09] = psxBios_SysInitKMem;
        biosC0[0x0a] = psxBios_ChangeClearRCnt;
        //biosC0[0x0b] = psxBios_SystemError;
-       //biosC0[0x0c] = psxBios_InitDefInt;
+       biosC0[0x0c] = psxBios_InitDefInt;
        //biosC0[0x0d] = psxBios_sys_c0_0d;
        //biosC0[0x0e] = psxBios_sys_c0_0e;
        //biosC0[0x0f] = psxBios_sys_c0_0f;
@@ -3236,33 +3390,23 @@ void psxBiosInit() {
        //biosC0[0x1c] = psxBios_PatchAOTable;
 //************** THE END ***************************************
 /**/
-       base = 0x1000;
-       size = sizeof(EvCB) * 32;
-       EventCB = (void *)&psxR[base]; base += size * 6;
-       memset(EventCB, 0, size * 6);
-       HwEV = EventCB;
-       EvEV = EventCB + 32;
-       RcEV = EventCB + 32 * 2;
-       UeEV = EventCB + 32 * 3;
-       SwEV = EventCB + 32 * 4;
-       ThEV = EventCB + 32 * 5;
-
-       pad_stopped = 1;
+
        pad_buf = NULL;
-       pad_buf1 = NULL;
-       pad_buf2 = NULL;
-       pad_buf1len = pad_buf2len = 0;
        heap_addr = NULL;
        heap_end = NULL;
        heap_size = 0;
-       CardState = -1;
-       CurThread = 0;
        memset(FDesc, 0, sizeof(FDesc));
        card_active_chan = 0;
 
        // initial RNG seed
        psxMu32ref(0x9010) = SWAPu32(0xac20cc00);
 
+       rom32 = (u32 *)psxR;
+       rom32[0x100/4] = SWAP32(0x19951204);
+       rom32[0x104/4] = SWAP32(3);
+       strcpy(psxR + 0x108, "PCSX authors");
+       strcpy(psxR + 0x12c, "PCSX HLE");
+
        // fonts
        len = 0x80000 - 0x66000;
        uncompress((Bytef *)(psxR + 0x66000), &len, font_8140, sizeof(font_8140));
@@ -3297,30 +3441,7 @@ void psxBiosInit() {
        ram32[0x00b0/4] = HLEOP(hleop_b0);
        ram32[0x00c0/4] = HLEOP(hleop_c0);
 
-       // "table of tables". Some games modify it
-       assert(A_TT_ExCB == 0x0100);
-       ram32[0x0100/4] = SWAPu32(0x0000e004); // ExCB - exception chains
-       ram32[0x0104/4] = SWAPu32(0x00000020); // ExCB size
-       ram32[0x0108/4] = SWAPu32(0x0000e1ec); // PCB - process control
-       ram32[0x010c/4] = SWAPu32(0x00000004); // PCB size
-       ram32[0x0110/4] = SWAPu32(0x0000e1f4); // TCB - thread control
-       ram32[0x0114/4] = SWAPu32(0x00000300); // TCB size
-       ram32[0x0120/4] = SWAPu32(0x0000e028); // EvCB - event control
-       ram32[0x0124/4] = SWAPu32(0x000001c0); // EvCB size
-       ram32[0x0140/4] = SWAPu32(0x00008648); // FCB - file control
-       ram32[0x0144/4] = SWAPu32(0x000002c0); // FCB size
-       ram32[0x0150/4] = SWAPu32(0x00006ee0); // DCB - device control
-       ram32[0x0154/4] = SWAPu32(0x00000320); // DCB size
-
-       ram32[0xe000/4] = SWAPu32(0x00000020); // SysMalloc block size
-       ram32[0xe004/4] = SWAPu32(0x000091e0); // chain0
-       ram32[0xe00c/4] = SWAPu32(0x00006d88); // chain1
-       ram32[0xe014/4] = SWAPu32(0x00000000); // chain2
-       ram32[0xe01c/4] = SWAPu32(0x00006d98); // chain3
-
-       ram32[0xe1ec/4] = SWAPu32(0x0000e1f4); // TCB
-       ram32[0xe1f0/4] = SWAPu32(0x00000300); // SysMalloc block size
-       ram32[0xe1f4/4] = SWAPu32(0x00004000); // first TCB
+       setup_tt(4, 16);
 
        ram32[0x6ee0/4] = SWAPu32(0x0000eff0); // DCB
        strcpy((char *)&ram32[0xeff0/4], "bu");
@@ -3351,9 +3472,14 @@ void psxBiosInit() {
        //  patch: +3d8, +4dc, +594, +62c, +9c8, +1988
        //  call:  +7a0=4b70, +884=4c54, +894=4c64
        ptr[0x5b] = SWAP32(0x43d0);
-       ram32[0x4b70/4] = SWAP32(0x03e00008); // jr $ra
-       ram32[0x4c54/4] = SWAP32(0x03e00008); // jr $ra
+       ram32[0x4b70/4] = SWAP32(0x03e00008); // jr $ra // setPadOutputBuf
+
+       ram32[0x4c54/4] = SWAP32(0x240e0001); // mov $t6, 1
+       ram32[0x4c58/4] = SWAP32(0x03e00008); // jr $ra
+       ram32[0x4c5c/4] = SWAP32(0xac0e0000 + A_PAD_IRQR_ENA); // sw $t6, ...
+
        ram32[0x4c64/4] = SWAP32(0x03e00008); // jr $ra
+       ram32[0x4c68/4] = SWAP32(0xac000000 + A_PAD_IRQR_ENA); // sw $0, ...
 
        ptr = (u32 *)&psxM[A_C0_TABLE];
        for (i = 0; i < 256/2; i++)
@@ -3373,18 +3499,14 @@ 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);
-
-       psxRegs.CP0.n.SR &= ~0x400000; // use ram vector
 }
 
 void psxBiosShutdown() {
 }
 
-void psxBiosCnfLoaded(u32 tcbs, u32 events) {
-       if (tcbs > 4)
-               log_unhandled("FIXME: TCB = %x\n", tcbs);
-       if (events > 16)
-               log_unhandled("FIXME: EVENT = %x\n", tcbs);
+void psxBiosCnfLoaded(u32 tcb_cnt, u32 evcb_cnt) {
+       if (tcb_cnt != 4 || evcb_cnt != 16)
+               setup_tt(tcb_cnt, evcb_cnt);
 }
 
 #define psxBios_PADpoll(pad) { \
@@ -3404,8 +3526,6 @@ void psxBiosCnfLoaded(u32 tcbs, u32 events) {
 }
 
 static void biosPadHLE() {
-       int i, bufcount;
-
        if (pad_buf != NULL) {
                u32 *buf = (u32*)pad_buf;
 
@@ -3437,15 +3557,6 @@ static void biosPadHLE() {
                        *buf |= PAD2_poll(0) << 16;
                }
        }
-       if (!pad_stopped)  {
-               if (pad_buf1) {
-                       psxBios_PADpoll(1);
-               }
-
-               if (pad_buf2) {
-                       psxBios_PADpoll(2);
-               }
-       }
 }
 
 static void handle_chain_x_x_1(u32 enable, u32 irqbit)
@@ -3475,7 +3586,7 @@ void hleExc0_0_2() // A(91h) - CdromDmaIrqFunc1
        if (psxHu32(0x1074) & psxHu32(0x1070) & 8) { // IRQ3 DMA
                psxHwWrite32(0x1f8010f4, (psxHu32(0x10f4) & 0xffffff) | 0x88000000);
                //if (--cdrom_irq_counter == 0) // 0xa0009180
-               //      DeliverEvent();         // 0xf0000003, 0x10
+               //      DeliverEvent(0xf0000003, 0x10);
                use_cycles(22);
                ret = 1;
        }
@@ -3506,7 +3617,7 @@ void hleExc0_2_2_syscall() // not in any A/B/C table
 
        if (code != R3000E_Syscall) {
                if (code != 0) {
-                       // DeliverEvent(); // 0xf0000010, 0x1000
+                       DeliverEvent(0xf0000010, 0x1000);
                        psxBios_SystemErrorUnresolvedException();
                }
                mips_return_c(0, 17);
@@ -3535,7 +3646,7 @@ void hleExc0_2_2_syscall() // not in any A/B/C table
                        break;
                }
                default:
-                       // DeliverEvent(); // 0xf0000010, 0x4000
+                       DeliverEvent(0xf0000010, 0x4000);
                        break;
        }
        use_cycles(30);
@@ -3552,10 +3663,7 @@ static void handle_chain_1_x_2(u32 ev_index, u32 irqbit)
 {
        u32 ret = 0;
        if (psxHu32(0x1074) & psxHu32(0x1070) & (1u << irqbit)) {
-               // DeliverEvent 0xf2000000 + ev_index, 2
-               if (RcEV[ev_index][1].status == EvStACTIVE) {
-                       softCall(RcEV[ev_index][1].fhandler);
-               }
+               DeliverEvent(0xf2000000 + ev_index, 0x0002);
                ret = 1;
        }
        mips_return_c(ret, 22);
@@ -3619,7 +3727,7 @@ void hleExc3_0_2_defint(void)
        size_t i;
        for (i = 0; i < sizeof(tab) / sizeof(tab[0]); i++) {
                if (psxHu32(0x1074) & psxHu32(0x1070) & (1u << tab[i].irqbit)) {
-                       // DeliverEvent 0xf0000000 + ev, 0x1000
+                       DeliverEvent(0xf0000000 + tab[i].ev, 0x1000);
                        use_cycles(7);
                }
 
@@ -3627,6 +3735,33 @@ void hleExc3_0_2_defint(void)
        mips_return_c(0, 11 + 7*11 + 7*11 + 12);
 }
 
+void hleExcPadCard1(void)
+{
+       if (loadRam32(A_PAD_IRQR_ENA)) {
+               u8 *pad_buf1 = loadRam8ptr(A_PAD_INBUF + 0);
+               u8 *pad_buf2 = loadRam8ptr(A_PAD_INBUF + 4);
+               int i, bufcount;
+
+               psxBios_PADpoll(1);
+               psxBios_PADpoll(2);
+               biosPadHLE();
+               use_cycles(100);
+       }
+       if (loadRam32(A_PAD_ACK_VBL))
+               psxHwWrite16(0x1f801070, ~1);
+       if (loadRam32(A_CARD_IRQR_ENA)) {
+               // todo, maybe
+       }
+
+       mips_return_c(0, 18);
+}
+
+void hleExcPadCard2(void)
+{
+       u32 ret = psxHu32(0x1074) & psxHu32(0x1070) & 1;
+       mips_return_c(ret, 15);
+}
+
 void psxBiosException() {
        u32 tcbPtr = loadRam32(A_TT_PCB);
        u32 *chains = loadRam32ptr(A_TT_ExCB);
@@ -3675,10 +3810,6 @@ void psxBiosException() {
        }
        assert(lim < 100);
 
-       // TODO make this a chain entry
-       if (psxHu32(0x1070) & 1)
-               biosPadHLE();
-
        // return from exception (custom or default)
        use_cycles(23);
        ptr = loadRam32(A_EEXIT_PTR);
@@ -3716,16 +3847,8 @@ void psxBiosFreeze(int Mode) {
        u32 base = 0x40000;
 
        bfreezepsxMptr(pad_buf, int);
-       bfreezepsxMptr(pad_buf1, char);
-       bfreezepsxMptr(pad_buf2, char);
        bfreezepsxMptr(heap_addr, u32);
-       bfreezel(&pad_buf1len);
-       bfreezel(&pad_buf2len);
-       bfreezes(regs);
-       bfreezel(&CardState);
-       bfreezel(&CurThread);
        bfreezes(FDesc);
        bfreezel(&card_active_chan);
-       bfreezel(&pad_stopped);
        bfreezel(&heap_size);
 }