diff --git a/main.c b/main.c index b81a5d67..45d091bd 100644 --- a/main.c +++ b/main.c @@ -31,7 +31,7 @@ PROGNAME = "SBEMU"; #define MAIN_TRAP_PIC_ONDEMAND 1 #define MAIN_INSTALL_RM_ISR 1 //not needed. but to workaround some rm games' problem. need RAW_HOOk in dpmi_dj2.c #define MAIN_DOUBLE_OPL_VOLUME 1 //hack: double the amplitude of OPL PCM. should be 1 or 0 -#define MAIN_ISR_CHAINED 0 //auto calls next handler AFTER current handler exits +#define MAIN_ISR_CHAINED 1 //auto calls next handler AFTER current handler exits #define MAIN_TSR_INT 0x2D //AMIS multiplex. TODO: 0x2F? #define MAIN_TSR_INTSTART_ID 0x01 //start id @@ -989,7 +989,7 @@ int main(int argc, char* argv[]) //IRQ routing path: //PM: IDT -> PM handlers after SBEMU -> SBEMU MAIN_InterruptPM(*) -> PM handlers befoe SBEMU -> IVT -> SBEMU MAIN_InterruptRM(*) -> DPMI entrance -> IVT handlers before DPMI installed //RM: IVT -> RM handlers before SBEMU -> SBEMU MAIN_InterruptRM(*) -> RM handlers after SBEMU -> DPMI entrance -> PM handlers after SBEMU -> SBEMU MAIN_InterruptPM(*) -> PM handlers befoe SBEMU -> IVT handlers before DPMI installed -//(*) means SBEMU might early terminate the calling chain if sound irq is handled. +//(*) means SBEMU might early terminate the calling chain if sound irq is handled (when MAIN_ISR_CHAINED==0). //early terminating is OK because PCI irq are level triggered, IRQ signal will keep high (raised) unless the hardware IRQ is ACKed. static void MAIN_InterruptPM() @@ -1010,9 +1010,9 @@ static void MAIN_InterruptPM() // //it has one problem that if other drivers (shared IRQ) enables interrupts (because it needs wait or is time consuming) //then because we're still in MAIN_InterruptPM, so MAIN_InterruptPM is never enterred agian (guarded by go32 or MAIN_ININT_PM), - //so the newly coming irq will never be processed and the IRQ will flood the system + //so the newly coming irq will never be processed and the IRQ will flood the system (freeze) //an alternative chained methods will EXIT MAIN_InterruptPM FIRST and calls next handler, which will avoid this case, see @MAIN_ISR_CHAINED - //but we need a hack if the default handler in IVT doesn't send EOI or masks the irq (TODO) + //but we need a hack if the default handler in IVT doesn't send EOI or masks the irq - this is done in the RM final wrapper, see @DPMI_RMISR_ChainedWrapper //MAIN_IntContext.EFLAGS |= (MAIN_InINT&MAIN_ININT_RM) ? (MAIN_IntContext.EFLAGS&CPU_VMFLAG) : 0; HDPMIPT_GetInterrupContext(&MAIN_IntContext); diff --git a/sbemu/dpmi/dpmi_dj2.c b/sbemu/dpmi/dpmi_dj2.c index 4ef5ae6c..03726ef2 100644 --- a/sbemu/dpmi/dpmi_dj2.c +++ b/sbemu/dpmi/dpmi_dj2.c @@ -12,6 +12,7 @@ #include #include "xms.h" #include "dbgutil.h" +#include "../pic.h" extern DPMI_ADDRESSING DPMI_Addressing; @@ -489,6 +490,48 @@ static void __NAKED DPMI_RMISR_ChainedWrapper() _ASM(call dword ptr cs:[0]) //call target _ASM(pushf) //calling iret _ASM(call dword ptr cs:[4]) //call chained next + + _ASM(push ax) + + //unmask IRQ because default entry in IVT may mask the irq out + _ASM(in al, 0x21) + _ASM(and al, byte ptr cs:[8]) + _ASM(out 0x21, al) + _ASM(in al, 0xA1) + _ASM(and al, byte ptr cs:[9]) + _ASM(out 0xA1, al) + + //read pic + _ASM(xor ah, ah) + _ASM(mov al, 0x0B) //read isr + _ASM(out 0x20, al) + _ASM(in al, 0x20) + _ASM(test al, 0x04) + _ASM(jz noslave) + _ASM(and al, ~0x04) + _ASM(mov ah, al) + _ASM(mov al, 0x0B) //read slave isr + _ASM(out 0xA0, al) + _ASM(in al, 0xA0) + _ASM(xchg ah, al) + _ASMLBL(noslave:) + _ASM(test ax, ax) + _ASM(jz done) //no pending irq in pic isr, done + _ASM(bsf ax, ax) + _ASM(cmp al, byte ptr cs:[10]) //compare with irq + _ASM(jne done) //different irq pending, done + + //same irq pending after the full chain is called, send EOI in case the default entry in IVT doesn't send EOI + //note if this irq is new coming, send EOI doesn't matter (at least for SBEMU) since it is level triggered. + _ASM(mov al, 0x20) + _ASM(cmp byte ptr cs:[10], 8) //irq: 0-7? + _ASM(jb masterEOI) + _ASM(out 0xA0, al) + _ASMLBL(masterEOI:) + _ASM(out 0x20, al) + + _ASMLBL(done:) + _ASM(pop ax) _ASM(iret) _ASM_END16 } @@ -525,28 +568,37 @@ uint16_t DPMI_InstallRealModeISR(uint8_t i, void(*ISR_RM)(void), DPMI_REG* RMReg if(chained) { uint32_t codesize = (uintptr_t)&DPMI_RMISR_ChainedWrapperEnd - (uintptr_t)&DPMI_RMISR_ChainedWrapper; - handle->chainedDOSMem = DPMI_HighMalloc((codesize + 8 + 15)>>4, TRUE); //+target + chained next (both real mode far ptr) + const uint32_t extra_size = 11; //+target + chained next (both real mode far ptr) + irqunmask + irq + handle->chainedDOSMem = DPMI_HighMalloc((codesize + extra_size + 15)>>4, TRUE); if(handle->chainedDOSMem == 0) { _go32_dpmi_free_real_mode_callback(&go32pa_rm); assert(FALSE); return -1; } + uint8_t irq = PIC_VEC2IRQ(i); + uint16_t unmask = 1<= 8) unmask |= 0x04; //mark slave cascaded in master + unmask = ~unmask; - uint8_t* buf = (uint8_t*)malloc(codesize+8); + uint8_t* buf = (uint8_t*)malloc(codesize+extra_size); //copy target memcpy(buf+0, &go32pa_rm.rm_offset, 2); memcpy(buf+2, &go32pa_rm.rm_segment, 2); //copy chained next memcpy(buf+4, &ra.offset16, 2); memcpy(buf+6, &ra.segment, 2); + //copy irq unmask + memcpy(buf+8, &unmask, 2); + //copy irq + memcpy(buf+10, &irq, 1); //copy warpper code - memcpy(buf+8, &DPMI_RMISR_ChainedWrapper, codesize); - DPMI_CopyLinear(DPMI_SEGOFF2L(handle->chainedDOSMem, 0), DPMI_PTR2L(buf), codesize+8); + memcpy(buf+extra_size, &DPMI_RMISR_ChainedWrapper, codesize); + DPMI_CopyLinear(DPMI_SEGOFF2L(handle->chainedDOSMem, 0), DPMI_PTR2L(buf), codesize+extra_size); free(buf); go32pa_rm.rm_segment = handle->chainedDOSMem&0xFFFF; - go32pa_rm.rm_offset = 8; + go32pa_rm.rm_offset = (uint16_t)extra_size; } int result = -1; diff --git a/sbemu/pic.c b/sbemu/pic.c index 5426e483..37ed961c 100644 --- a/sbemu/pic.c +++ b/sbemu/pic.c @@ -62,7 +62,8 @@ uint8_t PIC_GetIRQ(void) if(mask&0x4) { outp(PIC_PORT2, PIC_READISR); - mask = (uint16_t)(inp(PIC_PORT2)<<8); + mask &= ~0x04; + mask = (uint16_t)(inp(PIC_PORT2)<<8) | mask; } STIL(); if(mask == 0) @@ -110,7 +111,7 @@ void PIC_MaskIRQ(uint8_t irq) void PIC_UnmaskIRQ(uint8_t irq) { uint16_t port = PIC_DATA1; - CLIS(); + CLIS(); if(irq >= 8) { uint8_t master = inp(port);