Fix timer handling to make IRQ 0 an edge trigger interrupt. Add code to PIC emulation to process said interrupts as edge trigger, do not signal the IRQ unless PIC_DeActivateIRQ() is called, then PIC_ActivateIRQ(). For some reason, this also seems to fix the audio in Tyrian 2000, though the audio still sounds a tiny bit crackly

This commit is contained in:
Jonathan Campbell 2025-03-02 11:49:46 -08:00
parent cf3e9afe95
commit ba48a170bf
3 changed files with 37 additions and 1 deletions

View File

@ -75,6 +75,7 @@ static INLINE pic_tickindex_t PIC_FullIndex(void) {
void PIC_ActivateIRQ(Bitu irq);
void PIC_DeActivateIRQ(Bitu irq);
void PIC_EdgeTrigger(Bitu irq,bool set=true);
void PIC_runIRQs(void);
bool PIC_RunQueue(void);

View File

@ -49,6 +49,8 @@ struct PIC_Controller {
bool request_issr;
uint8_t vector_base;
uint8_t input; // input signal (directly set by raise/lower irq) used to filter for edge detect
uint8_t edge; // which signals are to be filtered for edge trigger
uint8_t irr; // request register
uint8_t imr; // mask register
uint8_t imrr; // mask register reversed (makes bit tests simpler)
@ -92,6 +94,7 @@ struct PIC_Controller {
void lower_irq(uint8_t val){
uint8_t bit = 1 << ( val);
input&=~bit;
if(irr & bit) { //value will change (as it is currently active)
irr&=~bit;
if((bit&imrr)&isrr) { //not masked and not in service
@ -137,6 +140,11 @@ void PIC_Controller::check_for_irq(){
void PIC_Controller::raise_irq(uint8_t val){
uint8_t bit = 1 << (val);
// edge detect: only if the signal goes from low to high
if (bit&edge&input) return;
input|=bit;
if((irr & bit)==0) { //value changed (as it is currently not active)
irr|=bit;
if((bit&imrr)&isrr) { //not masked and not in service
@ -371,6 +379,25 @@ static void pc_xt_nmi_write(Bitu port,Bitu val,Bitu iolen) {
CPU_Check_NMI();
}
void PIC_EdgeTrigger(Bitu irq,bool set) {
if (IS_PC98_ARCH) {
if (irq == 7) return;
}
else if (enable_slave_pic) { /* PC/AT emulation with slave PIC cascade to master */
if (irq == 2) irq = 9;
}
else { /* PC/XT emulation with only master PIC */
if (irq == 9) irq = 2;
if (irq >= 8) return;
}
Bitu t = irq>7 ? (irq - 8): irq;
PIC_Controller * pic=&pics[irq>7 ? 1 : 0];
if (set) pic->edge |= 1u << (unsigned char)t;
else pic->edge &= ~(1u << (unsigned char)t);
}
/* FIXME: This should be called something else that's true to the ISA bus, like PIC_PulseIRQ, not Activate IRQ.
* ISA interrupts are edge triggered, not level triggered. */
void PIC_ActivateIRQ(Bitu irq) {
@ -939,6 +966,8 @@ void PIC_Reset(Section *sec) {
pics[i].irr = pics[i].isr = pics[i].imrr = 0;
pics[i].isrr = pics[i].imr = 0xff;
pics[i].isr_ignore = 0x00;
pics[i].edge = 0x00;
pics[i].input = 0x00;
pics[i].active_irq = 8;
}

View File

@ -374,7 +374,10 @@ unsigned long PIT_TICK_RATE = PIT_TICK_RATE_IBM;
pic_tickindex_t VGA_PITSync_delay(void);
static void PIT0_Event(Bitu /*val*/) {
/* HACK: Despite edge trigger, force IRQ */
PIC_DeActivateIRQ(0);
PIC_ActivateIRQ(0);
/* NTS: "Days of Thunder" leaves PIT 0 in mode 1 for some reason, which triggers once and then stops. "start" does not advance in that mode.
* For any non-periodic mode, this code would falsely detect an ever increasing error and act badly. */
if (pit[0].mode == 2 || pit[0].mode == 3) {
@ -468,7 +471,9 @@ static void counter_latch(Bitu counter,bool do_latch=true) {
}
if (counter == 0/*IRQ 0*/) {
if (!p->output)
if (p->output)
PIC_ActivateIRQ(0);
else
PIC_DeActivateIRQ(0);
}
}
@ -940,6 +945,7 @@ static IO_WriteHandleObject WriteHandler2[4];
void TIMER_BIOS_INIT_Configure() {
PIC_RemoveEvents(PIT0_Event);
PIC_DeActivateIRQ(0);
PIC_EdgeTrigger(0,true);
/* Setup Timer 0 */
pit[0].output = true;