Path: blob/master/libmupen64plus/mupen64plus-core/src/r4300/interupt.c
2 views
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1* Mupen64plus - interupt.c *2* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *3* Copyright (C) 2002 Hacktarux *4* *5* This program is free software; you can redistribute it and/or modify *6* it under the terms of the GNU General Public License as published by *7* the Free Software Foundation; either version 2 of the License, or *8* (at your option) any later version. *9* *10* This program is distributed in the hope that it will be useful, *11* but WITHOUT ANY WARRANTY; without even the implied warranty of *12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *13* GNU General Public License for more details. *14* *15* You should have received a copy of the GNU General Public License *16* along with this program; if not, write to the *17* Free Software Foundation, Inc., *18* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *19* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */2021#include <stdlib.h>2223#include <SDL.h>2425#define M64P_CORE_PROTOTYPES 126#include "api/m64p_types.h"27#include "api/callbacks.h"28#include "api/m64p_vidext.h"29#include "api/vidext.h"30#include "memory/memory.h"31#include "main/rom.h"32#include "main/main.h"33#include "main/savestates.h"34#include "main/cheat.h"35#include "osd/osd.h"36#include "plugin/plugin.h"3738#include "interupt.h"39#include "r4300.h"40#include "macros.h"41#include "exception.h"42#include "reset.h"43#include "new_dynarec/new_dynarec.h"4445#ifdef WITH_LIRC46#include "main/lirc.h"47#endif4849unsigned int next_vi;50int vi_field=0;51static int vi_counter=0;5253int interupt_unsafe_state = 0;5455typedef struct _interupt_queue56{57int type;58unsigned int count;59struct _interupt_queue *next;60} interupt_queue;6162static interupt_queue *q = NULL;6364static void clear_queue(void)65{66while(q != NULL)67{68interupt_queue *aux = q->next;69free(q);70q = aux;71}72}7374/*static void print_queue(void)75{76interupt_queue *aux;77//if (Count < 0x7000000) return;78DebugMessage(M64MSG_INFO, "------------------ 0x%x", (unsigned int)Count);79aux = q;80while (aux != NULL)81{82DebugMessage(M64MSG_INFO, "Count:%x, %x", (unsigned int)aux->count, aux->type);83aux = aux->next;84}85}*/8687static int SPECIAL_done = 0;8889static int before_event(unsigned int evt1, unsigned int evt2, int type2)90{91if(evt1 - Count < 0x80000000)92{93if(evt2 - Count < 0x80000000)94{95if((evt1 - Count) < (evt2 - Count)) return 1;96else return 0;97}98else99{100if((Count - evt2) < 0x10000000)101{102switch(type2)103{104case SPECIAL_INT:105if(SPECIAL_done) return 1;106else return 0;107break;108default:109return 0;110}111}112else return 1;113}114}115else return 0;116}117118void add_interupt_event(int type, unsigned int delay)119{120unsigned int count = Count + delay/**2*/;121int special = 0;122interupt_queue *aux = q;123124if(type == SPECIAL_INT /*|| type == COMPARE_INT*/) special = 1;125if(Count > 0x80000000) SPECIAL_done = 0;126127if (get_event(type)) {128DebugMessage(M64MSG_WARNING, "two events of type 0x%x in interrupt queue", type);129return;130}131132if (q == NULL)133{134q = (interupt_queue *) malloc(sizeof(interupt_queue));135q->next = NULL;136q->count = count;137q->type = type;138next_interupt = q->count;139//print_queue();140return;141}142143if(before_event(count, q->count, q->type) && !special)144{145q = (interupt_queue *) malloc(sizeof(interupt_queue));146q->next = aux;147q->count = count;148q->type = type;149next_interupt = q->count;150//print_queue();151return;152}153154while (aux->next != NULL && (!before_event(count, aux->next->count, aux->next->type) || special))155aux = aux->next;156157if (aux->next == NULL)158{159aux->next = (interupt_queue *) malloc(sizeof(interupt_queue));160aux = aux->next;161aux->next = NULL;162aux->count = count;163aux->type = type;164}165else166{167interupt_queue *aux2;168if (type != SPECIAL_INT)169while(aux->next != NULL && aux->next->count == count)170aux = aux->next;171aux2 = aux->next;172aux->next = (interupt_queue *) malloc(sizeof(interupt_queue));173aux = aux->next;174aux->next = aux2;175aux->count = count;176aux->type = type;177}178}179180void add_interupt_event_count(int type, unsigned int count)181{182add_interupt_event(type, (count - Count)/*/2*/);183}184185static void remove_interupt_event(void)186{187interupt_queue *aux = q->next;188if(q->type == SPECIAL_INT) SPECIAL_done = 1;189free(q);190q = aux;191if (q != NULL && (q->count > Count || (Count - q->count) < 0x80000000))192next_interupt = q->count;193else194next_interupt = 0;195}196197unsigned int get_event(int type)198{199interupt_queue *aux = q;200if (q == NULL) return 0;201if (q->type == type)202return q->count;203while (aux->next != NULL && aux->next->type != type)204aux = aux->next;205if (aux->next != NULL)206return aux->next->count;207return 0;208}209210int get_next_event_type(void)211{212if (q == NULL) return 0;213return q->type;214}215216void remove_event(int type)217{218interupt_queue *aux = q;219if (q == NULL) return;220if (q->type == type)221{222aux = aux->next;223free(q);224q = aux;225return;226}227while (aux->next != NULL && aux->next->type != type)228aux = aux->next;229if (aux->next != NULL) // it's a type int230{231interupt_queue *aux2 = aux->next->next;232free(aux->next);233aux->next = aux2;234}235}236237void translate_event_queue(unsigned int base)238{239interupt_queue *aux;240remove_event(COMPARE_INT);241remove_event(SPECIAL_INT);242aux=q;243while (aux != NULL)244{245aux->count = (aux->count - Count)+base;246aux = aux->next;247}248add_interupt_event_count(COMPARE_INT, Compare);249add_interupt_event_count(SPECIAL_INT, 0);250}251252int save_eventqueue_infos(char *buf)253{254int len = 0;255interupt_queue *aux = q;256if (q == NULL)257{258*((unsigned int*)&buf[0]) = 0xFFFFFFFF;259return 4;260}261while (aux != NULL)262{263memcpy(buf+len , &aux->type , 4);264memcpy(buf+len+4, &aux->count, 4);265len += 8;266aux = aux->next;267}268*((unsigned int*)&buf[len]) = 0xFFFFFFFF;269return len+4;270}271272void load_eventqueue_infos(char *buf)273{274int len = 0;275clear_queue();276while (*((unsigned int*)&buf[len]) != 0xFFFFFFFF)277{278int type = *((unsigned int*)&buf[len]);279unsigned int count = *((unsigned int*)&buf[len+4]);280add_interupt_event_count(type, count);281len += 8;282}283}284285void init_interupt(void)286{287SPECIAL_done = 1;288next_vi = next_interupt = 5000;289vi_register.vi_delay = next_vi;290vi_field = 0;291clear_queue();292add_interupt_event_count(VI_INT, next_vi);293add_interupt_event_count(SPECIAL_INT, 0);294}295296void check_interupt(void)297{298if (MI_register.mi_intr_reg & MI_register.mi_intr_mask_reg)299Cause = (Cause | 0x400) & 0xFFFFFF83;300else301Cause &= ~0x400;302if ((Status & 7) != 1) return;303if (Status & Cause & 0xFF00)304{305if(q == NULL)306{307q = (interupt_queue *) malloc(sizeof(interupt_queue));308q->next = NULL;309q->count = Count;310q->type = CHECK_INT;311}312else313{314interupt_queue* aux = (interupt_queue *) malloc(sizeof(interupt_queue));315aux->next = q;316aux->count = Count;317aux->type = CHECK_INT;318q = aux;319}320next_interupt = Count;321}322}323324void gen_interupt(void)325{326if (stop == 1)327{328vi_counter = 0; // debug329dyna_stop();330}331332if (!interupt_unsafe_state)333{334if (savestates_get_job() == savestates_job_load)335{336savestates_load();337return;338}339340if (reset_hard_job)341{342reset_hard();343reset_hard_job = 0;344return;345}346}347348if (skip_jump)349{350unsigned int dest = skip_jump;351skip_jump = 0;352353if (q->count > Count || (Count - q->count) < 0x80000000)354next_interupt = q->count;355else356next_interupt = 0;357358last_addr = dest;359generic_jump_to(dest);360return;361}362363switch(q->type)364{365case SPECIAL_INT:366if (Count > 0x10000000) return;367remove_interupt_event();368add_interupt_event_count(SPECIAL_INT, 0);369return;370break;371case VI_INT:372if(vi_counter < 60)373{374if (vi_counter == 0)375cheat_apply_cheats(ENTRY_BOOT);376vi_counter++;377}378else379{380cheat_apply_cheats(ENTRY_VI);381}382gfx.updateScreen();383#ifdef WITH_LIRC384lircCheckInput();385#endif386SDL_PumpEvents();387388refresh_stat();389390// if paused, poll for input events391//if(rompause)392//{393osd_render(); // draw Paused message in case gfx.updateScreen didn't do it394//VidExt_GL_SwapBuffers();395// while(rompause)396// {397//SDL_Delay(10);398399SDL_PumpEvents();400#ifdef WITH_LIRC401lircCheckInput();402#endif //WITH_LIRC403// }404//}405406new_vi();407WaitForSingleObject(rompausesem, INFINITE);408if (vi_register.vi_v_sync == 0) vi_register.vi_delay = 500000;409else vi_register.vi_delay = ((vi_register.vi_v_sync + 1)*1500);410next_vi += vi_register.vi_delay;411if (vi_register.vi_status&0x40) vi_field=1-vi_field;412else vi_field=0;413414remove_interupt_event();415add_interupt_event_count(VI_INT, next_vi);416417MI_register.mi_intr_reg |= 0x08;418if (MI_register.mi_intr_reg & MI_register.mi_intr_mask_reg)419Cause = (Cause | 0x400) & 0xFFFFFF83;420else421return;422if ((Status & 7) != 1) return;423if (!(Status & Cause & 0xFF00)) return;424break;425426case COMPARE_INT:427remove_interupt_event();428Count+=2;429add_interupt_event_count(COMPARE_INT, Compare);430Count-=2;431432Cause = (Cause | 0x8000) & 0xFFFFFF83;433if ((Status & 7) != 1) return;434if (!(Status & Cause & 0xFF00)) return;435break;436437case CHECK_INT:438remove_interupt_event();439break;440441case SI_INT:442#ifdef WITH_LIRC443lircCheckInput();444#endif //WITH_LIRC445SDL_PumpEvents();446PIF_RAMb[0x3F] = 0x0;447remove_interupt_event();448MI_register.mi_intr_reg |= 0x02;449si_register.si_stat |= 0x1000;450if (MI_register.mi_intr_reg & MI_register.mi_intr_mask_reg)451Cause = (Cause | 0x400) & 0xFFFFFF83;452else453return;454if ((Status & 7) != 1) return;455if (!(Status & Cause & 0xFF00)) return;456break;457458case PI_INT:459remove_interupt_event();460MI_register.mi_intr_reg |= 0x10;461pi_register.read_pi_status_reg &= ~3;462if (MI_register.mi_intr_reg & MI_register.mi_intr_mask_reg)463Cause = (Cause | 0x400) & 0xFFFFFF83;464else465return;466if ((Status & 7) != 1) return;467if (!(Status & Cause & 0xFF00)) return;468break;469470case AI_INT:471if (ai_register.ai_status & 0x80000000) // full472{473unsigned int ai_event = get_event(AI_INT);474remove_interupt_event();475ai_register.ai_status &= ~0x80000000;476ai_register.current_delay = ai_register.next_delay;477ai_register.current_len = ai_register.next_len;478add_interupt_event_count(AI_INT, ai_event+ai_register.next_delay);479480MI_register.mi_intr_reg |= 0x04;481if (MI_register.mi_intr_reg & MI_register.mi_intr_mask_reg)482Cause = (Cause | 0x400) & 0xFFFFFF83;483else484return;485if ((Status & 7) != 1) return;486if (!(Status & Cause & 0xFF00)) return;487}488else489{490remove_interupt_event();491ai_register.ai_status &= ~0x40000000;492493//-------494MI_register.mi_intr_reg |= 0x04;495if (MI_register.mi_intr_reg & MI_register.mi_intr_mask_reg)496Cause = (Cause | 0x400) & 0xFFFFFF83;497else498return;499if ((Status & 7) != 1) return;500if (!(Status & Cause & 0xFF00)) return;501}502break;503504case SP_INT:505remove_interupt_event();506sp_register.sp_status_reg |= 0x203;507// sp_register.sp_status_reg |= 0x303;508509if (!(sp_register.sp_status_reg & 0x40)) return; // !intr_on_break510MI_register.mi_intr_reg |= 0x01;511if (MI_register.mi_intr_reg & MI_register.mi_intr_mask_reg)512Cause = (Cause | 0x400) & 0xFFFFFF83;513else514return;515if ((Status & 7) != 1) return;516if (!(Status & Cause & 0xFF00)) return;517break;518519case DP_INT:520remove_interupt_event();521dpc_register.dpc_status &= ~2;522dpc_register.dpc_status |= 0x81;523MI_register.mi_intr_reg |= 0x20;524if (MI_register.mi_intr_reg & MI_register.mi_intr_mask_reg)525Cause = (Cause | 0x400) & 0xFFFFFF83;526else527return;528if ((Status & 7) != 1) return;529if (!(Status & Cause & 0xFF00)) return;530break;531532case HW2_INT:533// Hardware Interrupt 2 -- remove interrupt event from queue534remove_interupt_event();535// setup r4300 Status flags: reset TS, and SR, set IM2536Status = (Status & ~0x00380000) | 0x1000;537Cause = (Cause | 0x1000) & 0xFFFFFF83;538/* the exception_general() call below will jump to the interrupt vector (0x80000180) and setup the539* interpreter or dynarec540*/541break;542543case NMI_INT:544// Non Maskable Interrupt -- remove interrupt event from queue545remove_interupt_event();546// setup r4300 Status flags: reset TS and SR, set BEV, ERL, and SR547Status = (Status & ~0x00380000) | 0x00500004;548Cause = 0x00000000;549// simulate the soft reset code which would run from the PIF ROM550r4300_reset_soft();551// clear all interrupts, reset interrupt counters back to 0552Count = 0;553vi_counter = 0;554init_interupt();555// clear the audio status register so that subsequent write_ai() calls will work properly556ai_register.ai_status = 0;557// set ErrorEPC with the last instruction address558ErrorEPC = PC->addr;559// reset the r4300 internal state560if (r4300emu != CORE_PURE_INTERPRETER)561{562// clear all the compiled instruction blocks and re-initialize563free_blocks();564init_blocks();565}566// adjust ErrorEPC if we were in a delay slot, and clear the delay_slot and dyna_interp flags567if(delay_slot==1 || delay_slot==3)568{569ErrorEPC-=4;570}571delay_slot = 0;572dyna_interp = 0;573// set next instruction address to reset vector574last_addr = 0xa4000040;575generic_jump_to(0xa4000040);576return;577578default:579DebugMessage(M64MSG_ERROR, "Unknown interrupt queue event type %.8X.", q->type);580remove_interupt_event();581break;582}583584#ifdef NEW_DYNAREC585if (r4300emu == CORE_DYNAREC) {586EPC = pcaddr;587pcaddr = 0x80000180;588Status |= 2;589Cause &= 0x7FFFFFFF;590pending_exception=1;591} else {592exception_general();593}594#else595exception_general();596#endif597598if (!interupt_unsafe_state)599{600if (savestates_get_job() == savestates_job_save)601{602savestates_save();603return;604}605}606}607608609610