-
Notifications
You must be signed in to change notification settings - Fork 1
/
vdp_ints.c
102 lines (82 loc) · 3.58 KB
/
vdp_ints.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// VDP code for the TI-99/4A by Tursi
// You can copy this file and use it at will if it's useful
#include "vdp.h"
#include <stdint.h>
// Coleco specific init code so that the nmi counts like the TI interrupt
// it also provides the user interrupt hook
// no other TI functionality is simulated (automotion, automusic, quit)
// but if needed, this is where it goes
// storage for VDP status byte
volatile unsigned char VDP_STATUS_MIRROR = 0;
// lock variable to prevent NMI from doing anything
// Z80 int is edge triggered, so it won't break us
// 0x80 = interrupt pending, 0x01 - interrupts enabled
// (This must be defined in the crt0.s)
//volatile unsigned char vdpLimi = 0; // NO ints by default!
// address of user interrupt function
static void (*userint)() = 0;
// interrupt counter
volatile unsigned char VDP_INT_COUNTER = 0;
// used by vdpwaitvint - make certain it's reset
extern unsigned char gSaveIntCnt;
// May be called from true NMI or from VDP_INTERRUPT_ENABLE, depending on
// the flag setting when the true NMI fires.
void my_nmi() {
// I think we're okay from races. There are only three conditions this is called:
//
// VDP_INTERRUPT_ENABLE - detects that vdpLimi&0x80 was set by the interrupt code.
// Calls this code. But the interrupt line is still active,
// so we can't retrigger until it's cleared by the VDPST read
// below. At that time the vdpLimi is zeroed, and so we can't loop.
//
// nmi - detects that vdpLimi&0x01 is valid, and calls directly.
// Again, the interrupt line is still active.
//
// maskable int (spinner)-detects that vdpLimi&0x80 was set by the interrupt code.
// this one might be a little bit racey... still need
// to think it all the way through...
//
// I think the edge cases are covered. Except if the user is manually reading VDPST,
// then the state of vdpLimi could be out of sync with the real interrupt line, and cause
// a double call. User apps should only read the mirror variable. Should I enforce that?
VDP_CLEAR_VBLANK; // release the VDP - we could instantly trigger again, but the vdpLimi is zeroed, so no loop
VDP_INT_COUNTER++; // count up the frames
// the TI is running with ints off, so it won't retrigger in the
// user code, even if it's slow. Our current process won't either because
// the vdpLimi is set to 0.
if (0 != userint) userint();
// the TI interrupt would normally exit with the ints disabled
// if it fired, so we will do the same here and not reset it.
}
// NOT atomic! Do NOT call with interrupts enabled!
void setUserIntHook(void (*hookfn)()) {
userint = hookfn;
}
// NOT atomic! Do NOT call with interrupts enabled!
void clearUserIntHook() {
userint = 0;
}
// the init code needs this to mute the audio channels
volatile __sfr __at 0xff SOUND;
// called automatically by crt0.S (not in TI version)
void vdpinit() {
volatile unsigned int x;
// shut off the sound generator - if the cart skips the BIOS screen, this is needed.
SOUND = 0x9f;
SOUND = 0xbf;
SOUND = 0xdf;
SOUND = 0xff;
// zero variables
VDP_STATUS_MIRROR = 0;
userint = (void (*)())0;
VDP_INT_COUNTER = 1;
gSaveIntCnt = 0;
// interrupts off
vdpLimi = 0;
// before touching VDP, a brief delay. This gives time for the F18A to finish
// initializing before we touch the VDP itself. This is needed on the Coleco if
// you don't use the BIOS startup delay. This is roughly 200ms.
x=60000;
while (++x != 0) { } // counts till we loop at 65536
VDP_STATUS_MIRROR = VDPST; // init and clear any pending interrupt
}