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