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 **************************************************************************/
21 #include "../libfusefdc/libfusefdc.h"
22 #include "libvideo/video.h"
26 #include "emuevents.h"
27 #include "zxscrdraw.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) {
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
) {
48 afireLeft
= optAutofireHold
;
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 ////////////////////////////////////////////////////////////////////////////////
69 static void z80FrameEnd (void) {
70 if (tsmark_active
) ++tsmark_frames
;
73 if (emuEvLastFrameCallTS
< (uint32_t)z80
.tstates
) {
74 emuTimePassed((uint32_t)z80
.tstates
-emuEvLastFrameCallTS
);
75 emuEvLastFrameCallTS
= (uint32_t)z80
.tstates
;
78 zxRealiseScreen(machineInfo
.tsperframe
);
83 z80
.tstates
%= machineInfo
.tsperframe
;
84 emuEvLastFrameCallTS
%= (uint32_t)machineInfo
.tsperframe
;
86 if (++zxFlashTimer
>= 16) { zxFlashTimer
= 0; zxFlashState
= !zxFlashState
; }
89 memset(zxUlaSnow
, 0, sizeof(zxUlaSnow
));
93 emuTapeAdvanceFrame();
94 if (emuFrameStartTime
== 0) {
95 if (!optPaused
&& optDrawFPS
) {
96 emuFrameStartTime
= timerGetMS();
103 zxScreenCurrent
^= 1;
105 // autodetect "noflic"
107 if (emuCurrScreenBank
!= emuLastScreenBank
) {
109 if (!optNoFlicDetected
) {
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;
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;
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();
150 emuTimePassed((uint32_t)ts
);
151 emuEvLastFrameCallTS
+= (uint32_t)ts
;
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);
164 //lssnap_slt_clear();
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
;
175 // interrupt must be generated by RZX playback engine
178 if (zxRZXRCount
== -666) {
179 if (lssnap_rzx_first_frame() != 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
; //???
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
);
212 //fprintf(stderr, " rcount=%d (%d) ts=%d (%d)\n", zxRZXRCount, newR-oldR, z80.tstates, machineInfo.tsperframe);
216 // normal interrupt generation
218 if (z80
.tstates
>= machineInfo
.tsperframe
) {
225 if (z80GenerateNMI
) {
227 if ((splus
= zym_nmi(&z80
)) != 0) {
229 dointr
= 0; // don't accept normal interrupt then
230 if (zxModel
== ZX_MACHINE_SCORPION
) {
232 zxLastOut1ffd
|= 0x02;
233 zxLastOut1ffd
&= ~0x01; // disable RAM at page 0
234 emuRealizeMemoryPaging();
239 if (!dbtTickCounterPaused
) dbgTickCounter
+= splus
;
242 if (debuggerActive
) { dbgNMIHit
= 1; return tsres
; }
247 if (z80
.tstates
< machineInfo
.intrlen
) {
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) {
253 if (!dbtTickCounterPaused
) dbgTickCounter
+= 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
);
263 if (!dbtTickCounterPaused
) dbgTickCounter
+= splus
;