|
|
1.1 root 1: /* Generator is (c) James Ponder, 1997-2001 http://www.squish.net/generator/ */
2:
3: #include "generator.h"
4: #include "vdp.h"
5: #include "cpu68k.h"
6: #include "cpuz80.h"
7: #include "reg68k.h"
8: #include "ui.h"
9: #include "gensound.h"
10:
11: #include "snprintf.h"
12:
1.1.1.2 root 13: /* due to DMA transfers, event_nextevent can be called during an instruction
14: cycle (reg68k_external_execute -> instruction -> vdp write -> dma ->
15: event_freeze -> event_nextevent). Be careful */
16:
1.1 root 17: /* time for next event - update vdp_event - return when to call again */
18:
19: inline void event_nextevent(void)
20: {
21: /* call this when it *is* time for the next event as dictated by vdp_event,
22: so we switch on it and update vdp_event at the same time */
23:
1.1.1.2 root 24: switch (vdp_event++) {
1.1 root 25: case 0:
1.1.1.2 root 26: EVENT_NEWLINE:
1.1 root 27: LOG_DEBUG1(("%08X due %08X, %d A: %d (cd=%d)",
28: cpu68k_clocks, vdp_event_start,
29: vdp_line - vdp_visstartline, vdp_reg[10],
30: vdp_hskip_countdown));
1.1.1.3 root 31: if (vdp_line == 0)
32: sound_startfield();
1.1.1.2 root 33: if (vdp_line == (vdp_visstartline - 1)) {
1.1 root 34: vdp_vblank = 0;
1.1.1.2 root 35: vdp_hskip_countdown = vdp_reg[10];
1.1 root 36: }
37: if ((vdp_nextevent = vdp_event_vint - cpu68k_clocks) > 0)
38: break;
39: vdp_event++;
40: case 1:
41: LOG_DEBUG1(("%08X due %08X, %d B: %d (cd=%d)",
42: cpu68k_clocks, vdp_event_vint,
43: vdp_line - vdp_visstartline, vdp_reg[10],
44: vdp_hskip_countdown));
45: if (vdp_line == vdp_visendline) {
46: vdp_vblank = 1;
47: vdp_vsync = 1;
1.1.1.2 root 48: if (vdp_reg[1] & 1 << 5)
49: reg68k_external_autovector(6); /* vertical interrupt */
1.1 root 50: }
51: if ((vdp_nextevent = vdp_event_hint - cpu68k_clocks) > 0)
52: break;
53: vdp_event++;
54: case 2:
55: LOG_DEBUG1(("%08X due %08X, %d C: %d (cd=%d)",
56: cpu68k_clocks, vdp_event_hint,
57: vdp_line - vdp_visstartline, vdp_reg[10],
58: vdp_hskip_countdown));
59: if (vdp_line >= vdp_visstartline && vdp_line < vdp_visendline)
60: vdp_hblank = 1;
1.1.1.2 root 61: if (vdp_line == (vdp_visstartline - 1) || (vdp_line > vdp_visendline)) {
1.1 root 62: vdp_hskip_countdown = vdp_reg[10];
63: LOG_DEBUG1(("H counter reset to %d", vdp_hskip_countdown));
64: }
1.1.1.2 root 65: if (vdp_reg[0] & 1 << 4) {
1.1 root 66: LOG_DEBUG1(("pre = %d", vdp_hskip_countdown));
67: if (vdp_hskip_countdown-- == 0) {
68: LOG_DEBUG1(("in = %d", vdp_hskip_countdown));
69: /* re-initialise counter */
70: vdp_hskip_countdown = vdp_reg[10];
71: LOG_DEBUG1(("H counter looped to %d", vdp_hskip_countdown));
1.1.1.2 root 72: if (vdp_line >= vdp_visstartline - 1 && vdp_line < vdp_visendline - 1)
73: reg68k_external_autovector(4); /* horizontal interrupt */
1.1 root 74: /* since this game is obviously timing sensitive, we sacrifice
75: executing the right number of 68k clocks this frame in order
76: to accurately do the moments following H-Int */
77: cpu68k_clocks = vdp_event_hint;
78: }
79: LOG_DEBUG1(("post = %d", vdp_hskip_countdown));
80: }
1.1.1.2 root 81: /* the 68k is frozen for 68k ram to vram copies, see event_freeze */
82: if (vdp_dmabytes) { /* blank mode ? */
83: vdp_dmabytes -= (vdp_vblank || !(vdp_reg[1] & 1 << 6))
1.1 root 84: ? ((vdp_reg[12] & 1) ? 205 : 167) : ((vdp_reg[12] & 1) ? 18 : 16);
85: if (vdp_dmabytes <= 0) {
86: vdp_dmabytes = 0;
87: vdp_dmabusy = 0;
88: }
89: }
90: if ((vdp_nextevent = vdp_event_hdisplay - cpu68k_clocks) > 0)
91: break;
92: vdp_event++;
93: case 3:
94: LOG_DEBUG1(("%08X due %08X, %d D: %d (cd=%d)",
95: cpu68k_clocks, vdp_event_hdisplay,
96: vdp_line - vdp_visstartline, vdp_reg[10],
97: vdp_hskip_countdown));
1.1.1.3 root 98: ui_line(vdp_line - vdp_visstartline + 1);
1.1 root 99: if ((vdp_nextevent = vdp_event_end - cpu68k_clocks) > 0)
100: break;
101: /* vdp_event++; - not required, we set vdp_event to 0 below */
102: case 4:
103: /* end of line, do sound, platform stuff */
104: LOG_DEBUG1(("%08X due %08X, %d E: %d (cd=%d)",
105: cpu68k_clocks, vdp_event_end,
1.1.1.2 root 106: vdp_line - vdp_visstartline, vdp_reg[10],
1.1 root 107: vdp_hskip_countdown));
108: if (vdp_line >= vdp_visstartline && vdp_line < vdp_visendline)
109: vdp_hblank = 0;
110: cpuz80_sync();
1.1.1.3 root 111: sound_line();
1.1 root 112: vdp_line++;
113: if (vdp_line == vdp_visendline)
114: cpuz80_interrupt();
115: if (vdp_line == vdp_totlines) {
1.1.1.3 root 116: /* the order of these is important */
1.1.1.4 ! root 117: sound_endfield(); /* must be before ui_endfield for GYM log and also
! 118: for avi output */
1.1 root 119: ui_endfield();
1.1.1.3 root 120: vdp_endfield(); /* must be after ui_endfield as it alters state */
1.1 root 121: cpuz80_endfield();
122: cpu68k_endfield();
123: cpu68k_frames++;
124: }
1.1.1.2 root 125: vdp_event_start += vdp_clksperline_68k;
126: vdp_event_vint += vdp_clksperline_68k;
127: vdp_event_hint += vdp_clksperline_68k;
128: vdp_event_hdisplay += vdp_clksperline_68k;
129: vdp_event_end += vdp_clksperline_68k;
1.1 root 130: vdp_event = 1;
131: goto EVENT_NEWLINE;
1.1.1.2 root 132: } /* switch */
1.1 root 133: }
134:
135: /*** event_doframe - execute until the end of the current frame ***/
136:
137: void event_doframe(void)
138: {
139: unsigned int startframe = cpu68k_frames;
140:
141: do {
142: while (vdp_nextevent > 0)
143: vdp_nextevent = -reg68k_external_execute(vdp_nextevent);
144: event_nextevent();
1.1.1.2 root 145: }
146: while (startframe == cpu68k_frames);
1.1 root 147:
148: }
149:
150: /*** event_step - execute one instruction with no caching ***/
151:
152: void event_dostep(void)
153: {
154: /* execute one instruction and subtract from vdp_nextevent those cycles */
1.1.1.2 root 155: vdp_nextevent -= reg68k_external_step();
1.1 root 156: /* if negative or 0, i.e. we have done all the cycles we need to this event,
157: call event_nextevent! */
158: while (vdp_nextevent <= 0)
159: event_nextevent();
160: }
161:
162: /*** event_freeze_clocks - freeze 68k for given clock cycles ***/
163:
164: /* NB: this routine is called by event_freeze which is called by
165: vdp routines. This could all happen IN THE MIDDLE OF A BLOCK at which
166: point cpu68k_clocks will have been updated but vdp_nextevent will not!
167: therefore we must at this point work out what vdp_nextevent should be
168: at the current moment in time, add on the clocks, wind forward time and
169: then subtract the clocks that will be added later */
170:
171: void event_freeze_clocks(unsigned int clocks)
172: {
173: int missed = 0;
174:
175: /* first - fix vdp_nextevent to be correct for right now, due to block
176: marking delay */
177:
178: /* find out how many clocks vdp_nextevent has missed */
179: /* modify vdp_nextevent to reflect the real state as of now */
180:
181: switch (vdp_event) {
182: case 0:
183: missed = vdp_nextevent - (vdp_event_start - cpu68k_clocks);
1.1.1.2 root 184: vdp_nextevent -= missed;
1.1 root 185: break;
186: case 1:
187: missed = vdp_nextevent - (vdp_event_vint - cpu68k_clocks);
1.1.1.2 root 188: vdp_nextevent -= missed;
1.1 root 189: break;
190: case 2:
191: missed = vdp_nextevent - (vdp_event_hint - cpu68k_clocks);
1.1.1.2 root 192: vdp_nextevent -= missed;
1.1 root 193: break;
194: case 3:
195: missed = vdp_nextevent - (vdp_event_hdisplay - cpu68k_clocks);
1.1.1.2 root 196: vdp_nextevent -= missed;
1.1 root 197: break;
198: case 4:
199: missed = vdp_nextevent - (vdp_event_end - cpu68k_clocks);
1.1.1.2 root 200: vdp_nextevent -= missed;
1.1 root 201: break;
202: default:
203: printf("assertion failed: bad vdp_event in event_freeze_clocks: %d\n",
204: vdp_event);
205: }
206:
207: /* move cpu68k_clocks and vdp_nextevent forward in time */
208:
1.1.1.2 root 209: cpu68k_clocks += clocks;
210: vdp_nextevent -= clocks;
1.1 root 211:
212: /* now catch up events */
213:
214: while (vdp_nextevent <= 0)
215: event_nextevent();
216:
217: /* and then un-adjust vdp_nextevent, as the block marking code will update
218: this later */
219:
1.1.1.2 root 220: vdp_nextevent += missed;
1.1 root 221: }
222:
223: /*** event_freeze - freeze 68k for given VDP byte transfer ***/
224:
225: void event_freeze(unsigned int bytes)
226: {
227: int wide = vdp_reg[12] & 1;
228: int clocks, possible;
229: double percent_possible;
230: int togo = (int)bytes;
231:
1.1.1.2 root 232: cpu68k_frozen = 1; /* prohibit interrupts since PC is not known in the
233: middle of a 68k block due to register mappings */
1.1 root 234:
235: while (togo > 0) {
236: /* clocks will be negative if we're in the middle of a cpu block */
237: clocks = vdp_event_end - cpu68k_clocks;
238: if (clocks < 0)
239: clocks = 0;
1.1.1.2 root 240: percent_possible = clocks / vdp_clksperline_68k;
241: if (vdp_reg[1] & 1 << 6 && !vdp_vblank) {
1.1 root 242: /* vdp active */
1.1.1.2 root 243: possible = (unsigned int)(percent_possible * (wide ? 18 : 16));
1.1 root 244: } else {
245: /* vdp inactive */
1.1.1.2 root 246: possible = (unsigned int)(percent_possible * (wide ? 205 : 167));
1.1 root 247: }
248: if (togo >= possible) {
249: event_freeze_clocks(clocks);
1.1.1.2 root 250: togo -= possible;
1.1 root 251: } else {
1.1.1.2 root 252: event_freeze_clocks(((double)togo / possible) * clocks);
1.1 root 253: togo = 0;
254: }
255: }
256: cpu68k_frozen = 0;
257: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.