Search
lxdream.org :: lxdream :: r265:5daf59b7f31b
lxdream 0.9.1
released Jun 29
Download Now
changeset265:5daf59b7f31b
parent264:e3b8a3ab32b8
child266:2f811793bd0a
authornkeynes
dateSat Jan 06 04:06:36 2007 +0000 (17 years ago)
Implement event queue.
Fix pvr2 timing (yes, again).
src/Makefile.am
src/Makefile.in
src/asic.h
src/clock.h
src/dream.h
src/dreamcast.c
src/eventq.c
src/eventq.h
src/pvr2/pvr2.c
src/sh4/intc.c
src/sh4/sh4core.c
src/sh4/sh4core.h
1.1 --- a/src/Makefile.am Sat Jan 06 04:05:32 2007 +0000
1.2 +++ b/src/Makefile.am Sat Jan 06 04:06:36 2007 +0000
1.3 @@ -15,7 +15,7 @@
1.4 syscall.c syscall.h bios.c dcload.c \
1.5 gdrom/ide.c gdrom/ide.h gdrom/packet.h \
1.6 gdrom/gdrom.c gdrom/gdrom.h gdrom/nrg.c gdrom/cdi.c gdrom/linux.c \
1.7 - dreamcast.c dreamcast.h \
1.8 + dreamcast.c dreamcast.h eventq.c eventq.h \
1.9 sh4/intc.c sh4/intc.h sh4/sh4mem.c sh4/timer.c sh4/dmac.c \
1.10 sh4/sh4core.c sh4/sh4core.h sh4/sh4dasm.c sh4/sh4dasm.h \
1.11 sh4/sh4mmio.c sh4/sh4mmio.h sh4/scif.c \
2.1 --- a/src/Makefile.in Sat Jan 06 04:05:32 2007 +0000
2.2 +++ b/src/Makefile.in Sat Jan 06 04:06:36 2007 +0000
2.3 @@ -149,7 +149,7 @@
2.4 syscall.c syscall.h bios.c dcload.c \
2.5 gdrom/ide.c gdrom/ide.h gdrom/packet.h \
2.6 gdrom/gdrom.c gdrom/gdrom.h gdrom/nrg.c gdrom/cdi.c gdrom/linux.c \
2.7 - dreamcast.c dreamcast.h \
2.8 + dreamcast.c dreamcast.h eventq.c eventq.h \
2.9 sh4/intc.c sh4/intc.h sh4/sh4mem.c sh4/timer.c sh4/dmac.c \
2.10 sh4/sh4core.c sh4/sh4core.h sh4/sh4dasm.c sh4/sh4dasm.h \
2.11 sh4/sh4mmio.c sh4/sh4mmio.h sh4/scif.c \
2.12 @@ -187,18 +187,19 @@
2.13 asic.$(OBJEXT) syscall.$(OBJEXT) bios.$(OBJEXT) \
2.14 dcload.$(OBJEXT) ide.$(OBJEXT) gdrom.$(OBJEXT) nrg.$(OBJEXT) \
2.15 cdi.$(OBJEXT) linux.$(OBJEXT) dreamcast.$(OBJEXT) \
2.16 - intc.$(OBJEXT) sh4mem.$(OBJEXT) timer.$(OBJEXT) dmac.$(OBJEXT) \
2.17 - sh4core.$(OBJEXT) sh4dasm.$(OBJEXT) sh4mmio.$(OBJEXT) \
2.18 - scif.$(OBJEXT) armcore.$(OBJEXT) armdasm.$(OBJEXT) \
2.19 - armmem.$(OBJEXT) aica.$(OBJEXT) audio.$(OBJEXT) pvr2.$(OBJEXT) \
2.20 - tacore.$(OBJEXT) render.$(OBJEXT) rendcore.$(OBJEXT) \
2.21 - rendbkg.$(OBJEXT) rendsort.$(OBJEXT) texcache.$(OBJEXT) \
2.22 - maple.$(OBJEXT) controller.$(OBJEXT) support.$(OBJEXT) \
2.23 - interface.$(OBJEXT) callbacks.$(OBJEXT) gui.$(OBJEXT) \
2.24 - mmr_win.$(OBJEXT) debug_win.$(OBJEXT) dump_win.$(OBJEXT) \
2.25 - loader.$(OBJEXT) bootstrap.$(OBJEXT) util.$(OBJEXT) \
2.26 - display.$(OBJEXT) audio_null.$(OBJEXT) audio_esd.$(OBJEXT) \
2.27 - video_null.$(OBJEXT) video_gtk.$(OBJEXT) video_x11.$(OBJEXT)
2.28 + eventq.$(OBJEXT) intc.$(OBJEXT) sh4mem.$(OBJEXT) \
2.29 + timer.$(OBJEXT) dmac.$(OBJEXT) sh4core.$(OBJEXT) \
2.30 + sh4dasm.$(OBJEXT) sh4mmio.$(OBJEXT) scif.$(OBJEXT) \
2.31 + armcore.$(OBJEXT) armdasm.$(OBJEXT) armmem.$(OBJEXT) \
2.32 + aica.$(OBJEXT) audio.$(OBJEXT) pvr2.$(OBJEXT) tacore.$(OBJEXT) \
2.33 + render.$(OBJEXT) rendcore.$(OBJEXT) rendbkg.$(OBJEXT) \
2.34 + rendsort.$(OBJEXT) texcache.$(OBJEXT) maple.$(OBJEXT) \
2.35 + controller.$(OBJEXT) support.$(OBJEXT) interface.$(OBJEXT) \
2.36 + callbacks.$(OBJEXT) gui.$(OBJEXT) mmr_win.$(OBJEXT) \
2.37 + debug_win.$(OBJEXT) dump_win.$(OBJEXT) loader.$(OBJEXT) \
2.38 + bootstrap.$(OBJEXT) util.$(OBJEXT) display.$(OBJEXT) \
2.39 + audio_null.$(OBJEXT) audio_esd.$(OBJEXT) video_null.$(OBJEXT) \
2.40 + video_gtk.$(OBJEXT) video_x11.$(OBJEXT)
2.41 lxdream_OBJECTS = $(am_lxdream_OBJECTS)
2.42 lxdream_DEPENDENCIES =
2.43 lxdream_LDFLAGS =
2.44 @@ -215,23 +216,23 @@
2.45 @AMDEP_TRUE@ ./$(DEPDIR)/controller.Po ./$(DEPDIR)/dcload.Po \
2.46 @AMDEP_TRUE@ ./$(DEPDIR)/debug_win.Po ./$(DEPDIR)/display.Po \
2.47 @AMDEP_TRUE@ ./$(DEPDIR)/dmac.Po ./$(DEPDIR)/dreamcast.Po \
2.48 -@AMDEP_TRUE@ ./$(DEPDIR)/dump_win.Po ./$(DEPDIR)/gdrom.Po \
2.49 -@AMDEP_TRUE@ ./$(DEPDIR)/gui.Po ./$(DEPDIR)/ide.Po \
2.50 -@AMDEP_TRUE@ ./$(DEPDIR)/intc.Po ./$(DEPDIR)/interface.Po \
2.51 -@AMDEP_TRUE@ ./$(DEPDIR)/linux.Po ./$(DEPDIR)/loader.Po \
2.52 -@AMDEP_TRUE@ ./$(DEPDIR)/main.Po ./$(DEPDIR)/maple.Po \
2.53 -@AMDEP_TRUE@ ./$(DEPDIR)/mem.Po ./$(DEPDIR)/mmr_win.Po \
2.54 -@AMDEP_TRUE@ ./$(DEPDIR)/nrg.Po ./$(DEPDIR)/pvr2.Po \
2.55 -@AMDEP_TRUE@ ./$(DEPDIR)/rendbkg.Po ./$(DEPDIR)/rendcore.Po \
2.56 -@AMDEP_TRUE@ ./$(DEPDIR)/render.Po ./$(DEPDIR)/rendsort.Po \
2.57 -@AMDEP_TRUE@ ./$(DEPDIR)/scif.Po ./$(DEPDIR)/sh4core.Po \
2.58 -@AMDEP_TRUE@ ./$(DEPDIR)/sh4dasm.Po ./$(DEPDIR)/sh4mem.Po \
2.59 -@AMDEP_TRUE@ ./$(DEPDIR)/sh4mmio.Po ./$(DEPDIR)/support.Po \
2.60 -@AMDEP_TRUE@ ./$(DEPDIR)/syscall.Po ./$(DEPDIR)/tacore.Po \
2.61 -@AMDEP_TRUE@ ./$(DEPDIR)/texcache.Po ./$(DEPDIR)/timer.Po \
2.62 -@AMDEP_TRUE@ ./$(DEPDIR)/util.Po ./$(DEPDIR)/video_gtk.Po \
2.63 -@AMDEP_TRUE@ ./$(DEPDIR)/video_null.Po ./$(DEPDIR)/video_x11.Po \
2.64 -@AMDEP_TRUE@ ./$(DEPDIR)/watch.Po
2.65 +@AMDEP_TRUE@ ./$(DEPDIR)/dump_win.Po ./$(DEPDIR)/eventq.Po \
2.66 +@AMDEP_TRUE@ ./$(DEPDIR)/gdrom.Po ./$(DEPDIR)/gui.Po \
2.67 +@AMDEP_TRUE@ ./$(DEPDIR)/ide.Po ./$(DEPDIR)/intc.Po \
2.68 +@AMDEP_TRUE@ ./$(DEPDIR)/interface.Po ./$(DEPDIR)/linux.Po \
2.69 +@AMDEP_TRUE@ ./$(DEPDIR)/loader.Po ./$(DEPDIR)/main.Po \
2.70 +@AMDEP_TRUE@ ./$(DEPDIR)/maple.Po ./$(DEPDIR)/mem.Po \
2.71 +@AMDEP_TRUE@ ./$(DEPDIR)/mmr_win.Po ./$(DEPDIR)/nrg.Po \
2.72 +@AMDEP_TRUE@ ./$(DEPDIR)/pvr2.Po ./$(DEPDIR)/rendbkg.Po \
2.73 +@AMDEP_TRUE@ ./$(DEPDIR)/rendcore.Po ./$(DEPDIR)/render.Po \
2.74 +@AMDEP_TRUE@ ./$(DEPDIR)/rendsort.Po ./$(DEPDIR)/scif.Po \
2.75 +@AMDEP_TRUE@ ./$(DEPDIR)/sh4core.Po ./$(DEPDIR)/sh4dasm.Po \
2.76 +@AMDEP_TRUE@ ./$(DEPDIR)/sh4mem.Po ./$(DEPDIR)/sh4mmio.Po \
2.77 +@AMDEP_TRUE@ ./$(DEPDIR)/support.Po ./$(DEPDIR)/syscall.Po \
2.78 +@AMDEP_TRUE@ ./$(DEPDIR)/tacore.Po ./$(DEPDIR)/texcache.Po \
2.79 +@AMDEP_TRUE@ ./$(DEPDIR)/timer.Po ./$(DEPDIR)/util.Po \
2.80 +@AMDEP_TRUE@ ./$(DEPDIR)/video_gtk.Po ./$(DEPDIR)/video_null.Po \
2.81 +@AMDEP_TRUE@ ./$(DEPDIR)/video_x11.Po ./$(DEPDIR)/watch.Po
2.82 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
2.83 $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
2.84 CCLD = $(CC)
2.85 @@ -302,6 +303,7 @@
2.86 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dmac.Po@am__quote@
2.87 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dreamcast.Po@am__quote@
2.88 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dump_win.Po@am__quote@
2.89 +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eventq.Po@am__quote@
2.90 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdrom.Po@am__quote@
2.91 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gui.Po@am__quote@
2.92 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ide.Po@am__quote@
3.1 --- a/src/asic.h Sat Jan 06 04:05:32 2007 +0000
3.2 +++ b/src/asic.h Sat Jan 06 04:06:36 2007 +0000
3.3 @@ -1,5 +1,5 @@
3.4 /**
3.5 - * $Id: asic.h,v 1.14 2006-12-19 09:51:35 nkeynes Exp $
3.6 + * $Id: asic.h,v 1.15 2007-01-06 04:06:36 nkeynes Exp $
3.7 *
3.8 * Support for the miscellaneous ASIC functions (Primarily event multiplexing,
3.9 * and DMA). Includes MMIO definitions for the 5f6000 and 5f7000 regions,
3.10 @@ -173,8 +173,8 @@
3.11 MMIO_REGION_END
3.12
3.13 #define EVENT_PVR_RENDER_DONE 2
3.14 -#define EVENT_SCANLINE1 3
3.15 -#define EVENT_SCANLINE2 4
3.16 +#define EVENT_SCANLINE2 3
3.17 +#define EVENT_SCANLINE1 4
3.18 #define EVENT_RETRACE 5
3.19 #define EVENT_PVR_UNK 6
3.20 #define EVENT_PVR_OPAQUE_DONE 7
4.1 --- a/src/clock.h Sat Jan 06 04:05:32 2007 +0000
4.2 +++ b/src/clock.h Sat Jan 06 04:06:36 2007 +0000
4.3 @@ -1,5 +1,5 @@
4.4 /**
4.5 - * $Id: clock.h,v 1.4 2007-01-03 09:01:51 nkeynes Exp $
4.6 + * $Id: clock.h,v 1.5 2007-01-06 04:06:36 nkeynes Exp $
4.7 * External interface to the dreamcast serial port, implemented by
4.8 * sh4/scif.c
4.9 *
4.10 @@ -25,9 +25,10 @@
4.11 #endif
4.12
4.13 #define MHZ
4.14 +#define KHZ
4.15 #define SH4_BASE_RATE 200 MHZ
4.16 #define ARM_BASE_RATE 33 MHZ
4.17 -#define PVR2_DOT_CLOCK 27 MHZ
4.18 +#define PVR2_DOT_CLOCK 27068 KHZ
4.19
4.20 extern uint32_t sh4_freq;
4.21 extern uint32_t sh4_peripheral_freq;
5.1 --- a/src/dream.h Sat Jan 06 04:05:32 2007 +0000
5.2 +++ b/src/dream.h Sat Jan 06 04:06:36 2007 +0000
5.3 @@ -1,5 +1,5 @@
5.4 /**
5.5 - * $Id: dream.h,v 1.11 2006-09-12 08:36:09 nkeynes Exp $
5.6 + * $Id: dream.h,v 1.12 2007-01-06 04:06:36 nkeynes Exp $
5.7 *
5.8 * Miscellaneous application-wide declarations (mainly logging atm)
5.9 *
5.10 @@ -86,6 +86,7 @@
5.11 extern struct dreamcast_module maple_module;
5.12 extern struct dreamcast_module pvr2_module;
5.13 extern struct dreamcast_module gui_module;
5.14 +extern struct dreamcast_module eventq_module;
5.15 extern struct dreamcast_module unknown_module;
5.16
5.17 /*************************** Logging **************************/
6.1 --- a/src/dreamcast.c Sat Jan 06 04:05:32 2007 +0000
6.2 +++ b/src/dreamcast.c Sat Jan 06 04:06:36 2007 +0000
6.3 @@ -1,5 +1,5 @@
6.4 /**
6.5 - * $Id: dreamcast.c,v 1.18 2006-07-02 04:59:00 nkeynes Exp $
6.6 + * $Id: dreamcast.c,v 1.19 2007-01-06 04:06:36 nkeynes Exp $
6.7 * Central switchboard for the system. This pulls all the individual modules
6.8 * together into some kind of coherent structure. This is also where you'd
6.9 * add Naomi support, if I ever get a board to play with...
6.10 @@ -58,6 +58,7 @@
6.11 */
6.12 void dreamcast_configure( )
6.13 {
6.14 + dreamcast_register_module( &eventq_module );
6.15 /* Register the memory framework */
6.16 dreamcast_register_module( &mem_module );
6.17
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/src/eventq.c Sat Jan 06 04:06:36 2007 +0000
7.3 @@ -0,0 +1,341 @@
7.4 +/**
7.5 + * $Id: eventq.c,v 1.1 2007-01-06 04:06:36 nkeynes Exp $
7.6 + *
7.7 + * Simple implementation of one-shot timers. Effectively this allows IO
7.8 + * devices to wait until a particular time before completing. We expect
7.9 + * there to be at least half a dozen or so continually scheduled events
7.10 + * (TMU and PVR2), peaking around 20+.
7.11 + *
7.12 + * Copyright (c) 2005 Nathan Keynes.
7.13 + *
7.14 + * This program is free software; you can redistribute it and/or modify
7.15 + * it under the terms of the GNU General Public License as published by
7.16 + * the Free Software Foundation; either version 2 of the License, or
7.17 + * (at your option) any later version.
7.18 + *
7.19 + * This program is distributed in the hope that it will be useful,
7.20 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
7.21 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
7.22 + * GNU General Public License for more details.
7.23 + */
7.24 +
7.25 +#include <assert.h>
7.26 +#include "dreamcast.h"
7.27 +#include "eventq.h"
7.28 +#include "sh4core.h"
7.29 +
7.30 +#define LONG_SCAN_PERIOD 1000000000 /* 1 second */
7.31 +
7.32 +typedef struct event {
7.33 + uint32_t id;
7.34 + uint32_t seconds;
7.35 + uint32_t nanosecs;
7.36 + event_func_t func;
7.37 +
7.38 + struct event *next;
7.39 +} *event_t;
7.40 +
7.41 +static struct event events[MAX_EVENT_ID];
7.42 +
7.43 +/**
7.44 + * Countdown to the next scan of the long-duration list (greater than 1 second).
7.45 + */
7.46 +static int long_scan_time_remaining;
7.47 +
7.48 +static event_t event_head;
7.49 +static event_t long_event_head;
7.50 +
7.51 +void event_reset();
7.52 +void event_init();
7.53 +uint32_t event_run_slice( uint32_t nanosecs );
7.54 +void event_save_state( FILE *f );
7.55 +int event_load_state( FILE * f );
7.56 +
7.57 +struct dreamcast_module eventq_module = { "EVENTQ", event_init, event_reset, NULL, event_run_slice,
7.58 + NULL, event_save_state, event_load_state };
7.59 +
7.60 +static void event_update_pending( )
7.61 +{
7.62 + if( event_head == NULL ) {
7.63 + if( !(sh4r.event_types & PENDING_IRQ) ) {
7.64 + sh4r.event_pending = NOT_SCHEDULED;
7.65 + }
7.66 + sh4r.event_types &= (~PENDING_EVENT);
7.67 + } else {
7.68 + if( !(sh4r.event_types & PENDING_IRQ) ) {
7.69 + sh4r.event_pending = event_head->nanosecs;
7.70 + }
7.71 + sh4r.event_types |= PENDING_EVENT;
7.72 + }
7.73 +}
7.74 +
7.75 +uint32_t event_get_next_time( )
7.76 +{
7.77 + if( event_head == NULL ) {
7.78 + return NOT_SCHEDULED;
7.79 + } else {
7.80 + return event_head->nanosecs;
7.81 + }
7.82 +}
7.83 +
7.84 +/**
7.85 + * Add the event to the short queue.
7.86 + */
7.87 +static void event_enqueue( event_t event )
7.88 +{
7.89 + if( event_head == NULL || event->nanosecs < event_head->nanosecs ) {
7.90 + event->next = event_head;
7.91 + event_head = event;
7.92 + event_update_pending();
7.93 + } else {
7.94 + event_t cur = event_head;
7.95 + event_t next = cur->next;
7.96 + while( next != NULL && event->nanosecs >= next->nanosecs ) {
7.97 + cur = next;
7.98 + next = cur->next;
7.99 + }
7.100 + event->next = next;
7.101 + cur->next = event;
7.102 + }
7.103 +}
7.104 +
7.105 +static void event_dequeue( event_t event )
7.106 +{
7.107 + if( event_head == NULL ) {
7.108 + ERROR( "Empty event queue but should contain event %d", event->id );
7.109 + } else if( event_head == event ) {
7.110 + /* removing queue head */
7.111 + event_head = event_head->next;
7.112 + event_update_pending();
7.113 + } else {
7.114 + event_t cur = event_head;
7.115 + event_t next = cur->next;
7.116 + while( next != NULL ) {
7.117 + if( next == event ) {
7.118 + cur->next = next->next;
7.119 + break;
7.120 + }
7.121 + cur = next;
7.122 + next = cur->next;
7.123 + }
7.124 + }
7.125 +}
7.126 +
7.127 +static void event_dequeue_long( event_t event )
7.128 +{
7.129 + if( long_event_head == NULL ) {
7.130 + ERROR( "Empty long event queue but should contain event %d", event->id );
7.131 + } else if( long_event_head == event ) {
7.132 + /* removing queue head */
7.133 + long_event_head = long_event_head->next;
7.134 + } else {
7.135 + event_t cur = long_event_head;
7.136 + event_t next = cur->next;
7.137 + while( next != NULL ) {
7.138 + if( next == event ) {
7.139 + cur->next = next->next;
7.140 + break;
7.141 + }
7.142 + cur = next;
7.143 + next = cur->next;
7.144 + }
7.145 + }
7.146 +}
7.147 +
7.148 +void register_event_callback( int eventid, event_func_t func )
7.149 +{
7.150 + events[eventid].func = func;
7.151 +}
7.152 +
7.153 +void event_schedule( int eventid, uint32_t nanosecs )
7.154 +{
7.155 + int i;
7.156 +
7.157 + nanosecs += sh4r.slice_cycle;
7.158 +
7.159 + event_t event = &events[eventid];
7.160 +
7.161 + if( event->nanosecs != NOT_SCHEDULED ) {
7.162 + /* Event is already scheduled. Remove it from the list first */
7.163 + event_cancel(eventid);
7.164 + }
7.165 +
7.166 + event->id = eventid;
7.167 + event->seconds = 0;
7.168 + event->nanosecs = nanosecs;
7.169 +
7.170 + event_enqueue( event );
7.171 +}
7.172 +
7.173 +void event_schedule_long( int eventid, uint32_t seconds, uint32_t nanosecs ) {
7.174 + if( seconds == 0 ) {
7.175 + event_schedule( eventid, nanosecs );
7.176 + } else {
7.177 + event_t event = &events[eventid];
7.178 +
7.179 + if( event->nanosecs != NOT_SCHEDULED ) {
7.180 + /* Event is already scheduled. Remove it from the list first */
7.181 + event_cancel(eventid);
7.182 + }
7.183 +
7.184 + event->id = eventid;
7.185 + event->seconds = seconds;
7.186 + event->nanosecs = nanosecs;
7.187 + event->next = long_event_head;
7.188 + long_event_head = event;
7.189 + }
7.190 +
7.191 +}
7.192 +
7.193 +void event_cancel( int eventid )
7.194 +{
7.195 + event_t event = &events[eventid];
7.196 + if( event->nanosecs == NOT_SCHEDULED ) {
7.197 + return; /* not scheduled */
7.198 + } else {
7.199 + event->nanosecs = NOT_SCHEDULED;
7.200 + if( event->seconds != 0 ) { /* long term event */
7.201 + event_dequeue_long( event );
7.202 + } else {
7.203 + event_dequeue( event );
7.204 + }
7.205 + }
7.206 +}
7.207 +
7.208 +
7.209 +void event_execute()
7.210 +{
7.211 + /* Loop in case we missed some or got a couple scheduled for the same time */
7.212 + while( event_head != NULL && event_head->nanosecs <= sh4r.slice_cycle ) {
7.213 + event_t event = event_head;
7.214 + event_head = event->next;
7.215 + event->nanosecs = NOT_SCHEDULED;
7.216 + // Note: Make sure the internal state is consistent before calling the
7.217 + // user function, as it will (quite likely) enqueue another event.
7.218 + event->func( event->id );
7.219 + }
7.220 +
7.221 + event_update_pending();
7.222 +}
7.223 +
7.224 +void event_asic_callback( int eventid )
7.225 +{
7.226 + asic_event( eventid );
7.227 +}
7.228 +
7.229 +void event_init()
7.230 +{
7.231 + int i;
7.232 + for( i=0; i<MAX_EVENT_ID; i++ ) {
7.233 + events[i].id = i;
7.234 + events[i].nanosecs = NOT_SCHEDULED;
7.235 + if( i < 96 ) {
7.236 + events[i].func = event_asic_callback;
7.237 + } else {
7.238 + events[i].func = NULL;
7.239 + }
7.240 + events[i].next = NULL;
7.241 + }
7.242 + event_head = NULL;
7.243 + long_event_head = NULL;
7.244 + long_scan_time_remaining = LONG_SCAN_PERIOD;
7.245 +}
7.246 +
7.247 +
7.248 +
7.249 +void event_reset()
7.250 +{
7.251 + int i;
7.252 + event_head = NULL;
7.253 + long_event_head = NULL;
7.254 + long_scan_time_remaining = LONG_SCAN_PERIOD;
7.255 + for( i=0; i<MAX_EVENT_ID; i++ ) {
7.256 + events[i].nanosecs = NOT_SCHEDULED;
7.257 + }
7.258 +}
7.259 +
7.260 +void event_save_state( FILE *f )
7.261 +{
7.262 + int id, i;
7.263 + id = event_head == NULL ? -1 : event_head->id;
7.264 + fwrite( &id, sizeof(id), 1, f );
7.265 + id = long_event_head == NULL ? -1 : long_event_head->id;
7.266 + fwrite( &id, sizeof(id), 1, f );
7.267 + fwrite( &long_scan_time_remaining, sizeof(long_scan_time_remaining), 1, f );
7.268 + for( i=0; i<MAX_EVENT_ID; i++ ) {
7.269 + fwrite( &events[i].id, sizeof(uint32_t), 3, f ); /* First 3 words from structure */
7.270 + id = events[i].next == NULL ? -1 : events[i].next->id;
7.271 + fwrite( &id, sizeof(id), 1, f );
7.272 + }
7.273 +}
7.274 +
7.275 +int event_load_state( FILE *f )
7.276 +{
7.277 + int id, i;
7.278 + fread( &id, sizeof(id), 1, f );
7.279 + event_head = id == -1 ? NULL : &events[id];
7.280 + fread( &id, sizeof(id), 1, f );
7.281 + long_event_head = id == -1 ? NULL : &events[id];
7.282 + fread( &long_scan_time_remaining, sizeof(long_scan_time_remaining), 1, f );
7.283 + for( i=0; i<MAX_EVENT_ID; i++ ) {
7.284 + fread( &events[i].id, sizeof(uint32_t), 3, f );
7.285 + fread( &id, sizeof(id), 1, f );
7.286 + events[i].next = id == -1 ? NULL : &events[id];
7.287 + }
7.288 + return 0;
7.289 +}
7.290 +
7.291 +/**
7.292 + * Scan all entries in the long queue, decrementing each by 1 second. Entries
7.293 + * that are now < 1 second are moved to the short queue.
7.294 + */
7.295 +static void event_scan_long()
7.296 +{
7.297 + while( long_event_head != NULL && --long_event_head->seconds == 0 ) {
7.298 + event_t event = long_event_head;
7.299 + long_event_head = event->next;
7.300 + event_enqueue(event);
7.301 + }
7.302 +
7.303 + if( long_event_head != NULL ) {
7.304 + event_t last = long_event_head;
7.305 + event_t cur = last->next;
7.306 + while( cur != NULL ) {
7.307 + if( --cur->seconds == 0 ) {
7.308 + last->next = cur->next;
7.309 + event_enqueue(cur);
7.310 + } else {
7.311 + last = cur;
7.312 + }
7.313 + cur = last->next;
7.314 + }
7.315 + }
7.316 +}
7.317 +
7.318 +/**
7.319 + * Decrement the event time on all pending events by the supplied nanoseconds.
7.320 + * It may or may not be faster to wrap around instead, but this has the benefit
7.321 + * of simplicity.
7.322 + */
7.323 +uint32_t event_run_slice( uint32_t nanosecs )
7.324 +{
7.325 + event_t event = event_head;
7.326 + while( event != NULL ) {
7.327 + if( event->nanosecs <= nanosecs ) {
7.328 + event->nanosecs = 0;
7.329 + } else {
7.330 + event->nanosecs -= nanosecs;
7.331 + }
7.332 + event = event->next;
7.333 + }
7.334 +
7.335 + long_scan_time_remaining -= nanosecs;
7.336 + if( long_scan_time_remaining <= 0 ) {
7.337 + long_scan_time_remaining += LONG_SCAN_PERIOD;
7.338 + event_scan_long();
7.339 + }
7.340 +
7.341 + event_update_pending();
7.342 + return nanosecs;
7.343 +}
7.344 +
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
8.2 +++ b/src/eventq.h Sat Jan 06 04:06:36 2007 +0000
8.3 @@ -0,0 +1,71 @@
8.4 +/**
8.5 + * $Id: eventq.h,v 1.1 2007-01-06 04:06:36 nkeynes Exp $
8.6 + *
8.7 + * Simple implementation of one-shot timers. Effectively this allows IO
8.8 + * devices to wait until a particular time before completing. We expect
8.9 + * there to be at least half a dozen or so continually scheduled events
8.10 + * (TMU and PVR2), peaking around 20+.
8.11 + *
8.12 + * Copyright (c) 2005 Nathan Keynes.
8.13 + *
8.14 + * This program is free software; you can redistribute it and/or modify
8.15 + * it under the terms of the GNU General Public License as published by
8.16 + * the Free Software Foundation; either version 2 of the License, or
8.17 + * (at your option) any later version.
8.18 + *
8.19 + * This program is distributed in the hope that it will be useful,
8.20 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
8.21 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
8.22 + * GNU General Public License for more details.
8.23 + */
8.24 +
8.25 +#include "dream.h"
8.26 +
8.27 +#define NOT_SCHEDULED 0xFFFFFFFF /* Sentinel value */
8.28 +
8.29 +
8.30 +typedef void (*event_func_t)(int eventid);
8.31 +
8.32 +/**
8.33 + * Register the callback to be associated with the given event ID.
8.34 + * Note: These should be registered at init time and never changed.
8.35 + */
8.36 +void register_event_callback( int eventid, event_func_t func );
8.37 +
8.38 +/**
8.39 + * Schedule a new pending event.
8.40 + * @param eventid Unique ID identifying the event in question (used to remove it
8.41 + * at a later time). If the eventid is scheduled more than once, only the lastest
8.42 + * schedule for that ID will be valid.
8.43 + * @param nanosecs Nanoseconds from the current SH4 time at which the event
8.44 + * should occur.
8.45 + */
8.46 +void event_schedule( int eventid, uint32_t nanosecs );
8.47 +
8.48 +/**
8.49 + * Schedule a long-duration pending event
8.50 + */
8.51 +void event_schedule_long( int eventid, uint32_t seconds, uint32_t nanosecs );
8.52 +
8.53 +/**
8.54 + * Remove a previously created event without triggering it. This is usually
8.55 + * only used when an operation is aborted.
8.56 + */
8.57 +void event_cancel( int eventid );
8.58 +
8.59 +/**
8.60 + * Return the slice cycle time of the next event, or NOT_SCHEDULED
8.61 + * if no events are scheduled for this time slice.
8.62 + */
8.63 +uint32_t event_get_next_time();
8.64 +
8.65 +/**
8.66 + * Execute the event on the top of the queue, and remove it.
8.67 + */
8.68 +void event_execute();
8.69 +
8.70 +#define MAX_EVENT_ID 128
8.71 +
8.72 +/* Events 1..96 are defined as the corresponding ASIC events. */
8.73 +
8.74 +
9.1 --- a/src/pvr2/pvr2.c Sat Jan 06 04:05:32 2007 +0000
9.2 +++ b/src/pvr2/pvr2.c Sat Jan 06 04:06:36 2007 +0000
9.3 @@ -1,5 +1,5 @@
9.4 /**
9.5 - * $Id: pvr2.c,v 1.34 2007-01-03 09:01:51 nkeynes Exp $
9.6 + * $Id: pvr2.c,v 1.35 2007-01-06 04:06:36 nkeynes Exp $
9.7 *
9.8 * PVR2 (Video) Core module implementation and MMIO registers.
9.9 *
9.10 @@ -18,6 +18,7 @@
9.11 #define MODULE pvr2_module
9.12
9.13 #include "dream.h"
9.14 +#include "eventq.h"
9.15 #include "display.h"
9.16 #include "mem.h"
9.17 #include "asic.h"
9.18 @@ -34,6 +35,10 @@
9.19 static uint32_t pvr2_run_slice( uint32_t );
9.20 static void pvr2_save_state( FILE *f );
9.21 static int pvr2_load_state( FILE *f );
9.22 +static void pvr2_update_raster_posn( uint32_t nanosecs );
9.23 +static void pvr2_schedule_line_event( int eventid, int line );
9.24 +static void pvr2_schedule_scanline_event( int eventid, int line );
9.25 +uint32_t pvr2_get_sync_status();
9.26
9.27 void pvr2_display_frame( void );
9.28
9.29 @@ -60,10 +65,10 @@
9.30 uint32_t frame_count;
9.31 uint32_t line_count;
9.32 uint32_t line_remainder;
9.33 + uint32_t cycles_run; /* Cycles already executed prior to main time slice */
9.34 uint32_t irq_vpos1;
9.35 uint32_t irq_vpos2;
9.36 uint32_t odd_even_field; /* 1 = odd, 0 = even */
9.37 - gboolean retrace;
9.38
9.39 /* timing */
9.40 uint32_t dot_clock;
9.41 @@ -74,6 +79,8 @@
9.42 uint32_t hsync_width_ns;
9.43 uint32_t front_porch_ns;
9.44 uint32_t back_porch_ns;
9.45 + uint32_t retrace_start_line;
9.46 + uint32_t retrace_end_line;
9.47 gboolean interlaced;
9.48 struct video_timing timing;
9.49 } pvr2_state;
9.50 @@ -81,20 +88,39 @@
9.51 struct video_buffer video_buffer[2];
9.52 int video_buffer_idx = 0;
9.53
9.54 +/**
9.55 + * Event handler for the retrace callback (fires on line 0 normally)
9.56 + */
9.57 +static void pvr2_retrace_callback( int eventid ) {
9.58 + asic_event( eventid );
9.59 + pvr2_update_raster_posn(sh4r.slice_cycle);
9.60 + pvr2_schedule_line_event( EVENT_RETRACE, 0 );
9.61 +}
9.62 +
9.63 +/**
9.64 + * Event handler for the scanline callbacks. Fires the corresponding
9.65 + * ASIC event, and resets the timer for the next field.
9.66 + */
9.67 +static void pvr2_scanline_callback( int eventid ) {
9.68 + asic_event( eventid );
9.69 + pvr2_update_raster_posn(sh4r.slice_cycle);
9.70 + if( eventid == EVENT_SCANLINE1 ) {
9.71 + pvr2_schedule_scanline_event( eventid, pvr2_state.irq_vpos1 );
9.72 + } else {
9.73 + pvr2_schedule_scanline_event( eventid, pvr2_state.irq_vpos2 );
9.74 + }
9.75 +}
9.76 +
9.77 static void pvr2_init( void )
9.78 {
9.79 register_io_region( &mmio_region_PVR2 );
9.80 register_io_region( &mmio_region_PVR2PAL );
9.81 register_io_region( &mmio_region_PVR2TA );
9.82 + register_event_callback( EVENT_RETRACE, pvr2_retrace_callback );
9.83 + register_event_callback( EVENT_SCANLINE1, pvr2_scanline_callback );
9.84 + register_event_callback( EVENT_SCANLINE2, pvr2_scanline_callback );
9.85 video_base = mem_get_region_by_name( MEM_REGION_VIDEO );
9.86 texcache_init();
9.87 - pvr2_state.dot_clock = 27069;
9.88 - pvr2_state.total_lines = pal_timing.total_lines;
9.89 - pvr2_state.line_time_ns = pal_timing.line_time_ns;
9.90 - pvr2_state.front_porch_ns = 12000;
9.91 - pvr2_state.back_porch_ns = 4000;
9.92 - pvr2_state.hsync_width_ns = 4000;
9.93 - pvr2_state.vsync_lines = 5;
9.94 pvr2_reset();
9.95 pvr2_ta_reset();
9.96 }
9.97 @@ -103,10 +129,14 @@
9.98 {
9.99 pvr2_state.line_count = 0;
9.100 pvr2_state.line_remainder = 0;
9.101 + pvr2_state.cycles_run = 0;
9.102 pvr2_state.irq_vpos1 = 0;
9.103 pvr2_state.irq_vpos2 = 0;
9.104 - pvr2_state.retrace = FALSE;
9.105 pvr2_state.timing = ntsc_timing;
9.106 + pvr2_state.dot_clock = PVR2_DOT_CLOCK;
9.107 + pvr2_state.back_porch_ns = 4000;
9.108 + mmio_region_PVR2_write( DISP_TOTAL, 0x0270035F );
9.109 + mmio_region_PVR2_write( DISP_SYNCTIME, 0x07D6A53F );
9.110 video_buffer_idx = 0;
9.111
9.112 pvr2_ta_init();
9.113 @@ -127,33 +157,41 @@
9.114 return pvr2_ta_load_state(f);
9.115 }
9.116
9.117 +/**
9.118 + * Update the current raster position to the given number of nanoseconds,
9.119 + * relative to the last time slice. (ie the raster will be adjusted forward
9.120 + * by nanosecs - nanosecs_already_run_this_timeslice)
9.121 + */
9.122 +static void pvr2_update_raster_posn( uint32_t nanosecs )
9.123 +{
9.124 + uint32_t old_line_count = pvr2_state.line_count;
9.125 + if( pvr2_state.line_time_ns == 0 ) {
9.126 + return; /* do nothing */
9.127 + }
9.128 + pvr2_state.line_remainder += (nanosecs - pvr2_state.cycles_run);
9.129 + pvr2_state.cycles_run = nanosecs;
9.130 + while( pvr2_state.line_remainder >= pvr2_state.line_time_ns ) {
9.131 + pvr2_state.line_count ++;
9.132 + pvr2_state.line_remainder -= pvr2_state.line_time_ns;
9.133 + }
9.134 +
9.135 + if( pvr2_state.line_count >= pvr2_state.total_lines ) {
9.136 + pvr2_state.line_count -= pvr2_state.total_lines;
9.137 + if( pvr2_state.interlaced ) {
9.138 + pvr2_state.odd_even_field = !pvr2_state.odd_even_field;
9.139 + }
9.140 + }
9.141 + if( pvr2_state.line_count >= pvr2_state.retrace_end_line &&
9.142 + (old_line_count < pvr2_state.retrace_end_line ||
9.143 + old_line_count > pvr2_state.line_count) ) {
9.144 + pvr2_display_frame();
9.145 + }
9.146 +}
9.147 +
9.148 static uint32_t pvr2_run_slice( uint32_t nanosecs )
9.149 {
9.150 - pvr2_state.line_remainder += nanosecs;
9.151 - while( pvr2_state.line_remainder >= pvr2_state.line_time_ns ) {
9.152 - pvr2_state.line_remainder -= pvr2_state.line_time_ns;
9.153 -
9.154 - pvr2_state.line_count++;
9.155 - if( pvr2_state.line_count == pvr2_state.total_lines ) {
9.156 - asic_event( EVENT_RETRACE );
9.157 - pvr2_state.line_count = 0;
9.158 - pvr2_state.retrace = TRUE;
9.159 - }
9.160 -
9.161 - if( pvr2_state.line_count == pvr2_state.irq_vpos1 ) {
9.162 - asic_event( EVENT_SCANLINE1 );
9.163 - }
9.164 - if( pvr2_state.line_count == pvr2_state.irq_vpos2 ) {
9.165 - asic_event( EVENT_SCANLINE2 );
9.166 - }
9.167 -
9.168 - if( pvr2_state.line_count == pvr2_state.timing.retrace_lines ) {
9.169 - if( pvr2_state.retrace ) {
9.170 - pvr2_display_frame();
9.171 - pvr2_state.retrace = FALSE;
9.172 - }
9.173 - }
9.174 - }
9.175 + pvr2_update_raster_posn( nanosecs );
9.176 + pvr2_state.cycles_run = 0;
9.177 return nanosecs;
9.178 }
9.179
9.180 @@ -273,9 +311,10 @@
9.181 case DISP_ADDR1:
9.182 val &= 0x00FFFFFC;
9.183 MMIO_WRITE( PVR2, reg, val );
9.184 - if( pvr2_state.retrace ) {
9.185 + pvr2_update_raster_posn(sh4r.slice_cycle);
9.186 + if( pvr2_state.line_count >= pvr2_state.retrace_start_line ||
9.187 + pvr2_state.line_count < pvr2_state.retrace_end_line ) {
9.188 pvr2_display_frame();
9.189 - pvr2_state.retrace = FALSE;
9.190 }
9.191 break;
9.192 case DISP_ADDR2:
9.193 @@ -301,6 +340,9 @@
9.194 val = val & 0x03FF03FF;
9.195 pvr2_state.irq_vpos1 = (val >> 16);
9.196 pvr2_state.irq_vpos2 = val & 0x03FF;
9.197 + pvr2_update_raster_posn(sh4r.slice_cycle);
9.198 + pvr2_schedule_scanline_event( EVENT_SCANLINE1, pvr2_state.irq_vpos1 );
9.199 + pvr2_schedule_scanline_event( EVENT_SCANLINE2, pvr2_state.irq_vpos2 );
9.200 MMIO_WRITE( PVR2, reg, val );
9.201 break;
9.202 case RENDER_NEARCLIP:
9.203 @@ -359,9 +401,15 @@
9.204 case DISP_TOTAL:
9.205 val = val & 0x03FF03FF;
9.206 MMIO_WRITE( PVR2, reg, val );
9.207 + pvr2_update_raster_posn(sh4r.slice_cycle);
9.208 pvr2_state.total_lines = (val >> 16) + 1;
9.209 pvr2_state.line_size = (val & 0x03FF) + 1;
9.210 pvr2_state.line_time_ns = 1000000 * pvr2_state.line_size / pvr2_state.dot_clock;
9.211 + pvr2_state.retrace_end_line = 0x2A;
9.212 + pvr2_state.retrace_start_line = pvr2_state.total_lines - 6;
9.213 + pvr2_schedule_line_event( EVENT_RETRACE, 0 );
9.214 + pvr2_schedule_scanline_event( EVENT_SCANLINE1, pvr2_state.irq_vpos1 );
9.215 + pvr2_schedule_scanline_event( EVENT_SCANLINE2, pvr2_state.irq_vpos2 );
9.216 break;
9.217 case DISP_SYNCCFG:
9.218 MMIO_WRITE( PVR2, reg, val&0x000003FF );
9.219 @@ -456,50 +504,82 @@
9.220 * 12 Horizontal sync off
9.221 * 13 Vertical sync off
9.222 * Note this method is probably incorrect for anything other than straight
9.223 - * interlaced PAL, and needs further testing.
9.224 + * interlaced PAL/NTSC, and needs further testing.
9.225 */
9.226 uint32_t pvr2_get_sync_status()
9.227 {
9.228 - uint32_t tmp = pvr2_state.line_remainder + sh4r.slice_cycle;
9.229 - uint32_t line = pvr2_state.line_count + (tmp / pvr2_state.line_time_ns);
9.230 - uint32_t remainder = tmp % pvr2_state.line_time_ns;
9.231 - uint32_t field = pvr2_state.odd_even_field;
9.232 - uint32_t result;
9.233 + pvr2_update_raster_posn(sh4r.slice_cycle);
9.234 + uint32_t result = pvr2_state.line_count;
9.235
9.236 - if( line >= pvr2_state.total_lines ) {
9.237 - line -= pvr2_state.total_lines;
9.238 - if( pvr2_state.interlaced ) {
9.239 - field == 1 ? 0 : 1;
9.240 - }
9.241 - }
9.242 -
9.243 - result = line;
9.244 -
9.245 - if( field ) {
9.246 + if( pvr2_state.odd_even_field ) {
9.247 result |= 0x0400;
9.248 }
9.249 - if( (line & 0x01) == field ) {
9.250 - if( remainder > pvr2_state.hsync_width_ns ) {
9.251 + if( (pvr2_state.line_count & 0x01) == pvr2_state.odd_even_field ) {
9.252 + if( pvr2_state.line_remainder > pvr2_state.hsync_width_ns ) {
9.253 result |= 0x1000; /* !HSYNC */
9.254 }
9.255 - if( line >= pvr2_state.vsync_lines ) {
9.256 - if( remainder > pvr2_state.front_porch_ns ) {
9.257 + if( pvr2_state.line_count >= pvr2_state.vsync_lines ) {
9.258 + if( pvr2_state.line_remainder > pvr2_state.front_porch_ns ) {
9.259 result |= 0x2800; /* Display active */
9.260 } else {
9.261 result |= 0x2000; /* Front porch */
9.262 }
9.263 }
9.264 } else {
9.265 - if( remainder < (pvr2_state.line_time_ns - pvr2_state.back_porch_ns) &&
9.266 - line >= pvr2_state.vsync_lines ) {
9.267 + if( pvr2_state.line_remainder < (pvr2_state.line_time_ns - pvr2_state.back_porch_ns) &&
9.268 + pvr2_state.line_count >= pvr2_state.vsync_lines ) {
9.269 result |= 0x3800; /* Display active */
9.270 } else {
9.271 result |= 0x1000; /* Back porch */
9.272 }
9.273 }
9.274 +
9.275 return result;
9.276 }
9.277
9.278 +/**
9.279 + * Schedule an event for the start of the given line. If the line is actually
9.280 + * the current line, schedules it for the next field.
9.281 + * The raster position should be updated before calling this method.
9.282 + */
9.283 +static void pvr2_schedule_line_event( int eventid, int line )
9.284 +{
9.285 + uint32_t time;
9.286 + if( line <= pvr2_state.line_count ) {
9.287 + time = (pvr2_state.total_lines - pvr2_state.line_count + line) * pvr2_state.line_time_ns
9.288 + - pvr2_state.line_remainder;
9.289 + } else {
9.290 + time = (line - pvr2_state.line_count) * pvr2_state.line_time_ns - pvr2_state.line_remainder;
9.291 + }
9.292 +
9.293 + if( line < pvr2_state.total_lines ) {
9.294 + event_schedule( eventid, time );
9.295 + } else {
9.296 + event_cancel( eventid );
9.297 + }
9.298 +}
9.299 +
9.300 +/**
9.301 + * Schedule a "scanline" event. This actually goes off at
9.302 + * 2 * line in even fields and 2 * line + 1 in odd fields.
9.303 + * Otherwise this behaves as per pvr2_schedule_line_event().
9.304 + * The raster position should be updated before calling this
9.305 + * method.
9.306 + */
9.307 +static void pvr2_schedule_scanline_event( int eventid, int line )
9.308 +{
9.309 + uint32_t field = pvr2_state.odd_even_field;
9.310 + if( line <= pvr2_state.line_count && pvr2_state.interlaced ) {
9.311 + field = !field;
9.312 + }
9.313 +
9.314 + line <<= 1;
9.315 + if( field ) {
9.316 + line += 1;
9.317 + }
9.318 + pvr2_schedule_line_event( eventid, line );
9.319 +}
9.320 +
9.321 MMIO_REGION_READ_FN( PVR2, reg )
9.322 {
9.323 switch( reg ) {
10.1 --- a/src/sh4/intc.c Sat Jan 06 04:05:32 2007 +0000
10.2 +++ b/src/sh4/intc.c Sat Jan 06 04:06:36 2007 +0000
10.3 @@ -1,5 +1,5 @@
10.4 /**
10.5 - * $Id: intc.c,v 1.6 2006-06-15 10:27:10 nkeynes Exp $
10.6 + * $Id: intc.c,v 1.7 2007-01-06 04:06:36 nkeynes Exp $
10.7 *
10.8 * SH4 onboard interrupt controller (INTC) implementation
10.9 *
10.10 @@ -20,6 +20,7 @@
10.11 #include "sh4mmio.h"
10.12 #include "sh4core.h"
10.13 #include "intc.h"
10.14 +#include "eventq.h"
10.15
10.16 struct intc_sources_t {
10.17 char *name;
10.18 @@ -106,7 +107,8 @@
10.19 intc_state.num_pending = 0;
10.20 for( i=0; i<INT_NUM_SOURCES; i++ )
10.21 intc_state.priority[i] = intc_default_priority[i];
10.22 - sh4r.int_pending = 0;
10.23 + sh4r.event_pending = event_get_next_time();
10.24 + sh4r.event_types &= (~PENDING_IRQ);
10.25 }
10.26
10.27
10.28 @@ -123,7 +125,7 @@
10.29 }
10.30
10.31 /* We basically maintain a priority queue here, raise_interrupt adds an entry,
10.32 - * accept_interrupt takes it off. At the moment this is does as a simple
10.33 + * accept_interrupt takes it off. At the moment this is done as a simple
10.34 * ordered array, on the basis that in practice there's unlikely to be more
10.35 * than one at a time. There are lots of ways to optimize this if it turns out
10.36 * to be necessary, but I'd doubt it will be...
10.37 @@ -148,8 +150,10 @@
10.38 intc_state.pending[j] = intc_state.pending[j-1];
10.39 intc_state.pending[i] = which;
10.40
10.41 - if( i == intc_state.num_pending && (sh4r.sr&SR_BL)==0 && SH4_INTMASK() < pri )
10.42 - sh4r.int_pending = 1;
10.43 + if( i == intc_state.num_pending && (sh4r.sr&SR_BL)==0 && SH4_INTMASK() < pri ) {
10.44 + sh4r.event_pending = 0;
10.45 + sh4r.event_types |= PENDING_IRQ;
10.46 + }
10.47
10.48 intc_state.num_pending++;
10.49 }
10.50 @@ -180,9 +184,14 @@
10.51 void intc_mask_changed( void )
10.52 {
10.53 if( intc_state.num_pending > 0 && (sh4r.sr&SR_BL)==0 &&
10.54 - SH4_INTMASK() < PRIORITY(intc_state.pending[intc_state.num_pending-1]) )
10.55 - sh4r.int_pending = 1;
10.56 - else sh4r.int_pending = 0;
10.57 + SH4_INTMASK() < PRIORITY(intc_state.pending[intc_state.num_pending-1]) ) {
10.58 + sh4r.event_pending = 0;
10.59 + sh4r.event_types |= PENDING_IRQ ;
10.60 + }
10.61 + else {
10.62 + sh4r.event_pending = event_get_next_time();
10.63 + sh4r.event_types &= (~PENDING_IRQ);
10.64 + }
10.65 }
10.66
10.67
11.1 --- a/src/sh4/sh4core.c Sat Jan 06 04:05:32 2007 +0000
11.2 +++ b/src/sh4/sh4core.c Sat Jan 06 04:06:36 2007 +0000
11.3 @@ -1,5 +1,5 @@
11.4 /**
11.5 - * $Id: sh4core.c,v 1.36 2007-01-03 09:00:17 nkeynes Exp $
11.6 + * $Id: sh4core.c,v 1.37 2007-01-06 04:06:36 nkeynes Exp $
11.7 *
11.8 * SH4 emulation core, and parent module for all the SH4 peripheral
11.9 * modules.
11.10 @@ -58,6 +58,7 @@
11.11 void sh4_stop( void );
11.12 void sh4_save_state( FILE *f );
11.13 int sh4_load_state( FILE *f );
11.14 +static void sh4_accept_interrupt( void );
11.15
11.16 struct dreamcast_module sh4_module = { "SH4", sh4_init, sh4_reset,
11.17 NULL, sh4_run_slice, sh4_stop,
11.18 @@ -138,24 +139,43 @@
11.19
11.20 uint32_t sh4_run_slice( uint32_t nanosecs )
11.21 {
11.22 - int target = sh4r.icount + nanosecs / sh4_cpu_period;
11.23 - int start = sh4r.icount;
11.24 int i;
11.25 + sh4r.slice_cycle = 0;
11.26
11.27 if( sh4r.sh4_state != SH4_STATE_RUNNING ) {
11.28 - if( sh4r.int_pending != 0 )
11.29 - sh4r.sh4_state = SH4_STATE_RUNNING;;
11.30 + if( sh4r.event_pending < nanosecs ) {
11.31 + sh4r.sh4_state = SH4_STATE_RUNNING;
11.32 + sh4r.slice_cycle = sh4r.event_pending;
11.33 + }
11.34 }
11.35
11.36 if( sh4_breakpoint_count == 0 ) {
11.37 - for( sh4r.slice_cycle = 0; sh4r.slice_cycle < nanosecs; sh4r.slice_cycle += sh4_cpu_period ) {
11.38 + for( ; sh4r.slice_cycle < nanosecs; sh4r.slice_cycle += sh4_cpu_period ) {
11.39 + if( SH4_EVENT_PENDING() ) {
11.40 + if( sh4r.event_types & PENDING_EVENT ) {
11.41 + event_execute();
11.42 + }
11.43 + /* Eventq execute may (quite likely) deliver an immediate IRQ */
11.44 + if( sh4r.event_types & PENDING_IRQ ) {
11.45 + sh4_accept_interrupt();
11.46 + }
11.47 + }
11.48 if( !sh4_execute_instruction() ) {
11.49 break;
11.50 }
11.51 }
11.52 } else {
11.53 -
11.54 - for( sh4r.slice_cycle = 0; sh4r.slice_cycle < nanosecs; sh4r.slice_cycle += sh4_cpu_period ) {
11.55 + for( ;sh4r.slice_cycle < nanosecs; sh4r.slice_cycle += sh4_cpu_period ) {
11.56 + if( SH4_EVENT_PENDING() ) {
11.57 + if( sh4r.event_types & PENDING_EVENT ) {
11.58 + event_execute();
11.59 + }
11.60 + /* Eventq execute may (quite likely) deliver an immediate IRQ */
11.61 + if( sh4r.event_types & PENDING_IRQ ) {
11.62 + sh4_accept_interrupt();
11.63 + }
11.64 + }
11.65 +
11.66 if( !sh4_execute_instruction() )
11.67 break;
11.68 #ifdef ENABLE_DEBUG_MODE
11.69 @@ -444,9 +464,6 @@
11.70 #define FPULf *((float *)&sh4r.fpul)
11.71 #define FPULi (sh4r.fpul)
11.72
11.73 - if( SH4_INT_PENDING() )
11.74 - sh4_accept_interrupt();
11.75 -
11.76 pc = sh4r.pc;
11.77 if( pc > 0xFFFFFF00 ) {
11.78 /* SYSCALL Magic */
12.1 --- a/src/sh4/sh4core.h Sat Jan 06 04:05:32 2007 +0000
12.2 +++ b/src/sh4/sh4core.h Sat Jan 06 04:06:36 2007 +0000
12.3 @@ -1,5 +1,5 @@
12.4 /**
12.5 - * $Id: sh4core.h,v 1.15 2007-01-03 09:00:17 nkeynes Exp $
12.6 + * $Id: sh4core.h,v 1.16 2007-01-06 04:06:36 nkeynes Exp $
12.7 *
12.8 * This file defines the internal functions exported/used by the SH4 core,
12.9 * except for disassembly functions defined in sh4dasm.h
12.10 @@ -53,6 +53,9 @@
12.11 */
12.12 #define SH4_STATE_STANDBY 4
12.13
12.14 +#define PENDING_IRQ 1
12.15 +#define PENDING_EVENT 2
12.16 +
12.17 struct sh4_registers {
12.18 uint32_t r[16];
12.19 uint32_t r_bank[8]; /* hidden banked registers */
12.20 @@ -67,7 +70,9 @@
12.21
12.22 uint32_t new_pc; /* Not a real register, but used to handle delay slots */
12.23 uint32_t icount; /* Also not a real register, instruction counter */
12.24 - uint32_t int_pending; /* flag set by the INTC = pending priority level */
12.25 + uint32_t event_pending; /* slice cycle time of the next pending event, or FFFFFFFF
12.26 + when no events are pending */
12.27 + uint32_t event_types; /* bit 0 = IRQ pending, bit 1 = general event pending */
12.28 int in_delay_slot; /* flag to indicate the current instruction is in
12.29 * a delay slot (certain rules apply) */
12.30 uint32_t slice_cycle; /* Current cycle within the timeslice */
12.31 @@ -147,7 +152,7 @@
12.32
12.33 #define IS_SH4_PRIVMODE() (sh4r.sr&SR_MD)
12.34 #define SH4_INTMASK() ((sh4r.sr&SR_IMASK)>>4)
12.35 -#define SH4_INT_PENDING() (sh4r.int_pending && !sh4r.in_delay_slot)
12.36 +#define SH4_EVENT_PENDING() (sh4r.event_pending <= sh4r.slice_cycle && !sh4r.in_delay_slot)
12.37
12.38 #define FPSCR_FR 0x00200000 /* FPU register bank */
12.39 #define FPSCR_SZ 0x00100000 /* FPU transfer size (0=32 bits, 1=64 bits) */
.