emu: remember page editor cursor position in memview
[zymosis.git] / src / ZXEmuT / emuexec.c
blob95721b785b443a6468a1a911c459179e8bc4760e
1 /***************************************************************************
3 * ZXEmuT -- ZX Spectrum Emulator with Tcl scripting
5 * Copyright (C) 2012-2022 Ketmar Dark <ketmar@ketmar.no-ip.org>
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, version 3 of the License ONLY.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 **************************************************************************/
20 #include "emuexec.h"
21 #include "../libfusefdc/libfusefdc.h"
22 #include "libvideo/video.h"
23 #include "console.h"
24 #include "lssnap.h"
25 #include "emuutils.h"
26 #include "emuevents.h"
27 #include "zxscrdraw.h"
28 #include "snd_alsa.h"
29 #include "tapes.h"
32 ////////////////////////////////////////////////////////////////////////////////
33 int z80_was_frame_end = 0;
35 int tsmark_active = 0;
36 int tsmark_frames = 0;
37 int tsmark_tstart = 0;
40 ////////////////////////////////////////////////////////////////////////////////
41 static inline void autofireEndFrame (void) {
42 if (optAutofire) {
43 if (--afireLeft <= 0) {
44 afireLeft = ((afireDown = !afireDown) ? optAutofireHold : optAutofireDelay);
45 // if optAutofireDelay is zero, we'll just hold the fire
46 if (!afireDown && !afireLeft) {
47 afireDown = 1;
48 afireLeft = optAutofireHold;
55 // !0: bit to set
56 int isAutofireKempston (void) {
57 return (optAutofire && afireDown && (afirePortBit&0xff00) == 0x0800 ? (afirePortBit&0x1f) : 0);
61 // !0: lo byte: bit to reset (0x01), hi byte: port
62 int isAutofireKeyboard (void) {
63 return (optAutofire && afireDown && (afirePortBit&0xff00) < 0x0800 ? afirePortBit : 0);
67 ////////////////////////////////////////////////////////////////////////////////
68 // force frame end
69 static void z80FrameEnd (void) {
70 if (tsmark_active) ++tsmark_frames;
71 ++z80_was_frame_end;
73 if (emuEvLastFrameCallTS < (uint32_t)z80.tstates) {
74 emuTimePassed((uint32_t)z80.tstates-emuEvLastFrameCallTS);
75 emuEvLastFrameCallTS = (uint32_t)z80.tstates;
78 zxRealiseScreen(machineInfo.tsperframe);
79 zxOldScreenTS = 0;
80 soundEndFrame();
81 autofireEndFrame();
83 z80.tstates %= machineInfo.tsperframe;
84 emuEvLastFrameCallTS %= (uint32_t)machineInfo.tsperframe;
86 if (++zxFlashTimer >= 16) { zxFlashTimer = 0; zxFlashState = !zxFlashState; }
88 if (zxWasUlaSnow) {
89 memset(zxUlaSnow, 0, sizeof(zxUlaSnow));
90 zxWasUlaSnow = 0;
93 emuTapeAdvanceFrame();
94 if (emuFrameStartTime == 0) {
95 if (!optPaused && optDrawFPS) {
96 emuFrameStartTime = timerGetMS();
97 emuFrameCount = 0;
99 } else {
100 ++emuFrameCount;
103 zxScreenCurrent ^= 1;
105 // autodetect "noflic"
106 if (optNoFlic < 0) {
107 if (emuCurrScreenBank != emuLastScreenBank) {
108 // different screens
109 if (!optNoFlicDetected) {
110 // detection
111 //fprintf(stderr, "+++ NFC warmup=%d\n", emuScreenBankSwitchCount);
112 if (++emuScreenBankSwitchCount >= 4) {
113 optNoFlicDetected = 1;
114 if (emuNoflicMsgCooldown == 0) {
115 cprintf("NOFLIC activated\n");
116 emuNoflicMsgCooldown = 13;
119 } else {
120 // cooldown time
121 //fprintf(stderr, "***NFC\n");
122 emuScreenBankSwitchCount = 6;
124 } else if (optNoFlicDetected) {
125 // same screens, detected noflic; perform cooldown
126 //fprintf(stderr, "::: NFC cooldown=%d\n", emuScreenBankSwitchCount);
127 if (--emuScreenBankSwitchCount <= 0) {
128 if (emuNoflicMsgCooldown == 0) {
129 cprintf("NOFLIC deactivated\n");
130 emuNoflicMsgCooldown = 13;
132 optNoFlicDetected = 0;
133 emuScreenBankSwitchCount = 0;
136 } else {
137 optNoFlicDetected = 0;
138 emuScreenBankSwitchCount = 0;
141 emuLastScreenBank = emuCurrScreenBank;
142 if (--emuNoflicMsgCooldown < 0) emuNoflicMsgCooldown = 0;
146 ////////////////////////////////////////////////////////////////////////////////
147 static inline void z80TSPassed (int ts) {
148 if (ts < 0) __builtin_trap();
149 if (ts) {
150 emuTimePassed((uint32_t)ts);
151 emuEvLastFrameCallTS += (uint32_t)ts;
156 int z80Step (void) {
157 int splus = 0;
158 int intrHit = 0;
160 if (zxRZX != NULL && z80.tstates >= machineInfo.tsperframe) {
161 //cprintf("RZX ERROR: too many tstates without interrupt! (%d)\n", z80.tstates);
162 //fprintf(stderr, "RZX ERROR: too many tstates without interrupt! (%d)\n", z80.tstates);
163 //lssnap_rzx_stop();
164 //lssnap_slt_clear();
165 //z80.tstates = 0;
166 z80.tstates = machineInfo.tsperframe-1;
167 if (emuEvLastFrameCallTS < (uint32_t)z80.tstates) {
168 emuTimePassed((uint32_t)z80.tstates-emuEvLastFrameCallTS);
169 emuEvLastFrameCallTS = (uint32_t)z80.tstates;
171 intrHit = 1;
174 if (zxRZX != NULL) {
175 // interrupt must be generated by RZX playback engine
176 int oldR, newR;
177 z80GenerateNMI = 0;
178 if (zxRZXRCount == -666) {
179 if (lssnap_rzx_first_frame() != 0) {
180 rzxerror:
181 lssnap_rzx_stop();
182 lssnap_slt_clear();
183 z80GenerateNMI = 0;
184 return 0;
186 //fprintf(stderr, "RZX FIRST FRAME: rcount=%d; ts=%d (%d)\n", zxRZXRCount, z80.tstates, machineInfo.tsperframe);
188 if (zxRZXRCount <= 0) {
189 // generate interrupt
190 //fprintf(stderr, "playing RZX: interrupt (tstates=%d)!\n", z80.tstates);
191 if (zxRZXRCount != -42) {
192 // force interrupt; -42 means "retrigger from RZX", play full frame
193 if (z80.tstates < machineInfo.tsperframe) z80.tstates = machineInfo.tsperframe; //???
194 intrHit = 1;
196 if (intrHit) {
197 z80FrameEnd();
198 z80.tstates %= machineInfo.intrlen; // so we will not go out of tsperframe too much
199 if (lssnap_rzx_next_frame() != 0) goto rzxerror;
200 //fprintf(stderr, "RZX NEXT FRAME: rcount=%d; ts=%d (%d)\n", zxRZXRCount, z80.tstates, machineInfo.tsperframe);
201 splus += zym_intr(&z80);
204 oldR = z80.regR&0x7f;
205 splus += zym_exec_step(&z80);
206 if (!dbtTickCounterPaused) dbgTickCounter += splus;
207 newR = z80.regR&0x7f;
208 if (newR < oldR) newR += 0x80;
209 if (zxRZXRCount >= 0) zxRZXRCount -= (newR-oldR);
210 z80GenerateNMI = 0;
211 z80TSPassed(splus);
212 //fprintf(stderr, " rcount=%d (%d) ts=%d (%d)\n", zxRZXRCount, newR-oldR, z80.tstates, machineInfo.tsperframe);
213 return splus;
216 // normal interrupt generation
217 int dointr = 1;
218 if (z80.tstates >= machineInfo.tsperframe) {
219 z80FrameEnd();
220 dointr = 1;
223 int tsres = 0;
225 if (z80GenerateNMI) {
226 z80GenerateNMI = 0;
227 if ((splus = zym_nmi(&z80)) != 0) {
228 // NMI accepted
229 dointr = 0; // don't accept normal interrupt then
230 if (zxModel == ZX_MACHINE_SCORPION) {
231 // page in ROM2
232 zxLastOut1ffd |= 0x02;
233 zxLastOut1ffd &= ~0x01; // disable RAM at page 0
234 emuRealizeMemoryPaging();
235 } else {
236 zxTRDOSpagein();
238 z80TSPassed(splus);
239 if (!dbtTickCounterPaused) dbgTickCounter += splus;
240 tsres += splus;
241 //HACKHACKHACK
242 if (debuggerActive) { dbgNMIHit = 1; return tsres; }
246 if (dointr) {
247 if (z80.tstates < machineInfo.intrlen) {
248 //int ttt = 0;
249 //if (z80.prev_was_EIDDR) { ttt = 1; printf("!!!zym_intr: prev_was_EIDDR=%d; iff1=%d; pc=#%04X; intrlen=%d\n", z80.prev_was_EIDDR, z80.iff1, z80.pc, machineInfo.intrlen); }
250 if ((splus = zym_intr(&z80)) != 0) {
251 //HACKHACKHACK
252 z80TSPassed(splus);
253 if (!dbtTickCounterPaused) dbgTickCounter += splus;
254 tsres += splus;
255 if (debuggerActive) { dbgIntrHit = 1; return tsres; }
257 //if (ttt) { printf("***zym_intr: prev_was_EIDDR=%d; iff1=%d; pc=#%04X; intrlen=%d\n", z80.prev_was_EIDDR, z80.iff1, z80.pc, machineInfo.intrlen); }
261 splus = zym_exec_step(&z80);
262 z80TSPassed(splus);
263 if (!dbtTickCounterPaused) dbgTickCounter += splus;
264 tsres += splus;
266 return tsres;