forked from redcode/SpecEmu
-
Notifications
You must be signed in to change notification settings - Fork 0
/
RunFrame.asm
307 lines (237 loc) · 13.4 KB
/
RunFrame.asm
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
; ##########################################################################
Run_Frame:
mov al, FrameSkipCounter
mov FrameSkipLoop, al
.if FULLSPEEDMODE == TRUE
mov FrameSkipLoop, FULLSPEEDFRAMECOUNT
.endif
.if TapePlaying && FastTapeLoading && (RealTapeMode == FALSE)
mov FrameSkipLoop, AUTOTAPEFRAMESKIP
.endif
; #########################################################################
Emu_ReInit:
mov PortUpdatePending, TRUE
; #########################################################################
; INT low code
align 16
mov eax, totaltstates
.while eax < MACHINE.InterruptCycles
.if (currentMachine.iff1 == TRUE) && (EI_Last == FALSE) ; EI_Last will be false if last opcode was LD A,I or LD A,R so we can still handle the PV flag bug for those in 'z80_Interrupt' proc
call z80_Interrupt
je @F ; jump forward if INT accepted
.endif
call Exec_Opcode
@@: call Exec_Extras
mov eax, totaltstates
.endw
;--------------------------------------------------------------------------------
; frame code
align 16
.while totaltstates < 69888/2
call Exec_Opcode
call Exec_Extras
.endw
; update keyboard and joystick port states at frame midpoint
call UpdatePortState ; preserves all registers
.if currentMachine.nmi
call z80_NMI
call Exec_Extras ; take NMI timings into effect
.endif
mov eax, totaltstates
align 16
.while eax < MACHINE.FrameCycles
call Exec_Opcode
call Exec_Extras
mov eax, totaltstates
.endw
;--------------------------------------------------------------------------------
push totaltstates
mov totaltstates, 71000
RENDERCYCLES
pop totaltstates
mov eax, MACHINE.FrameCycles
sub totaltstates, eax
.if AutoPlayTapes
.if AutoTapeStarted
.if (LoadTapeType == Type_TZX) && (TZXPause > 0) && (SL_AND_32_64 == TRUE)
.elseif (LoadTapeType == Type_PZX) && (PZX.Pause > 0) && (SL_AND_32_64 == TRUE)
.else
.if AutoTapeStopFrames == 0
mov TapePlaying, FALSE
mov AutoTapeStarted, FALSE
.else
dec AutoTapeStopFrames
.endif
.endif
.endif
.endif
call InitUpdateScreen
inc FramesPerSecond
inc GlobalFramesCounter
shr AY_FloatingRegister, 1
invoke DRAM_Fade
dec FrameSkipLoop
jnz Emu_ReInit
ret
align 16
Exec_Opcode: push totaltstates
RUNZ80INSTR ; run current Z80 opcode
mov ebx, totaltstates
pop eax
sub ebx, eax
mov cl, CPU_Speed
.if (cl != 0) && (TapePlaying == FALSE) && (RealTapeMode == FALSE)
.if cl == -1
shl ebx, 1
.else
shr ebx, cl
cmp ebx, 1
adc ebx, 0
.endif
add eax, ebx
mov totaltstates, eax
.endif
mov Z80TState, bl ; Ts timing of opcode for tape renderer, etc.
add AYTimer, bl
add SampleTimer, bl
add RealTapeTimer, bl
add uSpeechTimer, ebx
ret
align 16
Exec_Extras: .if RealTapeMode
mov al, MACHINE.REALTAPEPERIOD.CurrentCyclesPerSample
.if RealTapeTimer >= al
sub RealTapeTimer, al
invoke Get_Real_Tape_Bit
.endif
.else
ifc TapePlaying then call PlayTape
.endif
ifc SaveTapeType ne Type_NONE then call WriteTapePulse
IFDEF WANTSOUND
.if MuteSound == FALSE
movzx eax, BeepVal
inc BeeperSubCount
add BeeperSubTotal, eax
invoke Sample_AY
.endif
ENDIF
ret
; #########################################################################
align 16
z80_NMI: ; -- takes 11 T-States to respond
mov currentMachine.nmi, FALSE
lea esi, RegisterBase
mov eax, 11
mov Z80TState, al
add AYTimer, al
add SampleTimer, al
add RealTapeTimer, al
add uSpeechTimer, eax
sub eax, 6 ; Z80CALL adds back 6 cycles
add Reg_totaltstates, eax
inc Reg_R
; reset refresh counter entry for current value of R
movzx eax, Reg_R
and eax, 7Fh
mov [currentMachine.refresh_counters+eax], 0
.if HALTED
mov HALTED, FALSE
inc Reg_PC ; step past HALT instruction
.endif
; CBI95_NMI_enabled set by commandline switch
.if (CBI_Enabled == TRUE) && (CBI95_NMI_enabled == TRUE)
and CBI_Port_252, not (1 shl 7) ; enable CBI I / O interface
and CBI_Port_255, not (1 shl 7) ; page in the CBI EPROM
invoke Z80Call_MEMPTR, 102
mov currentMachine.iff1, FALSE ; IFF1 = 0; IFF2 unchanged
ret
.endif
.if DivIDEEnabled
.if DivIDE.Mapped == FALSE
invoke Z80Call_MEMPTR, 102
mov currentMachine.iff1, FALSE ; IFF1 = 0; IFF2 unchanged
.endif
ret
.endif
.if PLUSD_Enabled
invoke Z80Call_MEMPTR, 102
mov currentMachine.iff1, FALSE ; IFF1 = 0; IFF2 unchanged
ret
.endif
.if HardwareMode == HW_PENTAGON128
;Beta has the "Magic Button". When pressed, the execution continues as
;long as we're in ROM. It prevents TRDOS' own routines being interrupted.
;With first memory access outside - ROM A(15,14) != "00" & MEMRQ="0",
;Beta is paged in and NMI is triggered. So the NMI execution starts in
;Beta ROM. It saves the memory snapshot to the disk then.
;The saved snapshot can be loaded back by GOTO "name" CODE at the TRDOS
;prompt.
;I just checked this against the original Beta v5.03 schematics.
.if zPC >= 4000h
call TrDos_Page_In
invoke Z80Call_MEMPTR, 102
mov currentMachine.iff1, FALSE ; IFF1 = 0; IFF2 unchanged
.endif
ret
.endif
invoke Z80Call_MEMPTR, 102
mov currentMachine.iff1, FALSE ; IFF1 = 0; IFF2 unchanged
.if (MultifaceEnabled == TRUE) && (MultifacePaged == FALSE)
mov Multiface_LockOut, FALSE ; enable Multiface device
call PageInMultiface
.endif
ret
align 16
z80_Interrupt: .if HardwareMode == HW_TC2048
test Timex_Port_FF, 64
retcc nz ; return non-zero if INTs are disabled in hardware
.endif
lea esi, RegisterBase
inc Reg_R
; reset refresh counter entry for current value of R
movzx eax, Reg_R
and eax, 7Fh
mov [currentMachine.refresh_counters+eax], 0
.if HALTED
mov HALTED, FALSE
inc Reg_PC ; step past HALT instruction
.endif
.if IFF2_Read
; LD A,I or LD A,R was just executed, copying IFF2 to the PV flag
; if an interrupt was just accepted during that opcode, clear the PV flag as IFF2 would have been cleared before it was transferred
and Reg_F, NOT FLAG_V
mov IFF2_Read, FALSE
.endif
DISABLEINTS ; disable interrupts when accepting an interrupt
movzx eax, Reg_IntMode
cmp eax, 2
jc IntMode01
mov bh, Reg_I
mov bl, 255
call MemGetWord
mov bx, ax ; IM 2 int vector > bx
mov eax, 19 ; 19 cycles for IM 2
jmp IntModeEx
IntMode01: mov eax, 13 ; 13 cycles for IM 0 and IM 1 modes (tested on real 48K machine)
mov bx, 56 ; IM 0/1 int vector > bx
IntModeEx: mov Z80TState, al
add AYTimer, al
add SampleTimer, al
add RealTapeTimer, al
add uSpeechTimer, eax
sub eax, 6 ; Z80CALL adds back 6 cycles
add Reg_totaltstates, eax
invoke Z80Call_MEMPTR, bx
xor eax, eax ; set zero flag to signal INT accepted
ret
; ##########################################################################
align 16
Z80Call_MEMPTR proc uses esi ebx edi,
z80addr: WORD
lea esi, RegisterBase
mov ax, z80addr
mov Reg_MemPtr, ax
Z80CALL
ret
Z80Call_MEMPTR endp