1 /* spectrum.c: Generic Spectrum routines
2 Copyright (c) 1999-2016 Philip Kendall, Darren Salt
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
18 Author contact information:
19
20 E-mail: philip-fuse@shadowmagic.org.uk
21
22 */
23
24 #include <config.h>
25
26 #include <libspectrum.h>
27
28 #include "compat.h"
29 #include "debugger/debugger.h"
30 #include "display.h"
31 #include "event.h"
32 #include "keyboard.h"
33 #include "infrastructure/startup_manager.h"
34 #include "loader.h"
35 #include "machine.h"
36 #include "memory_pages.h"
37 #include "module.h"
38 #include "peripherals/printer.h"
39 #include "peripherals/ula.h"
40 #include "phantom_typist.h"
41 #include "psg.h"
42 #include "profile.h"
43 #include "rzx.h"
44 #include "settings.h"
45 #include "sound.h"
46 #include "spectrum.h"
47 #include "tape.h"
48 #include "timer/timer.h"
49 #include "ui/ui.h"
50 #include "ui/uijoystick.h"
51 #include "z80/z80.h"
52
53 /* 1040 KB of RAM */
54 libspectrum_byte RAM[ SPECTRUM_RAM_PAGES ][0x4000];
55
56 /* How many tstates have elapsed since the last interrupt? (or more
57 precisely, since the ULA last pulled the /INT line to the Z80 low) */
58 libspectrum_dword tstates;
59
60 /* Contention patterns */
61 static int contention_pattern_65432100[] = { 5, 4, 3, 2, 1, 0, 0, 6 };
62 static int contention_pattern_76543210[] = { 5, 4, 3, 2, 1, 0, 7, 6 };
63
64 /* Event */
65 int spectrum_frame_event;
66
67 /* Debugger variable prefix */
68 static const char * const debugger_type_string = "spectrum";
69
70 /* Debugger variable for frame count */
71 static const char * const frame_count_name = "frames";
72
73 /* Count of frames since last reset */
74 static libspectrum_dword frames_since_reset;
75
76 static void
spectrum_reset(int hard_reset)77 spectrum_reset( int hard_reset )
78 {
79 frames_since_reset = 0;
80 }
81
82 static module_info_t module_info = {
83 /* .reset = */ spectrum_reset,
84 /* .romcs = */ NULL,
85 /* .snapshot_enabled = */ NULL,
86 /* .snapshot_from = */ NULL,
87 /* .snapshot_to = */ NULL
88 };
89
90 static void
spectrum_frame_event_fn(libspectrum_dword last_tstates,int type,void * user_data)91 spectrum_frame_event_fn( libspectrum_dword last_tstates, int type,
92 void *user_data )
93 {
94 if( rzx_playback ) event_force_events();
95 rzx_frame();
96 psg_frame();
97 spectrum_frame();
98 z80_interrupt();
99 ui_joystick_poll();
100 timer_estimate_speed();
101 debugger_add_time_events();
102 ui_event();
103 ui_error_frame();
104 }
105
106 static libspectrum_dword
get_frame_count(void)107 get_frame_count( void )
108 {
109 return frames_since_reset;
110 }
111
112 static int
spectrum_init(void * context)113 spectrum_init( void *context )
114 {
115 spectrum_frame_event = event_register( spectrum_frame_event_fn,
116 "End of frame" );
117
118 module_register( &module_info );
119
120 debugger_system_variable_register( debugger_type_string,
121 frame_count_name, get_frame_count, NULL );
122
123 return 0;
124 }
125
126 void
spectrum_register_startup(void)127 spectrum_register_startup( void )
128 {
129 startup_manager_module dependencies[] = {
130 STARTUP_MANAGER_MODULE_DEBUGGER,
131 STARTUP_MANAGER_MODULE_EVENT,
132 STARTUP_MANAGER_MODULE_SETUID,
133 };
134 startup_manager_register( STARTUP_MANAGER_MODULE_SPECTRUM, dependencies,
135 ARRAY_SIZE( dependencies ), spectrum_init, NULL,
136 NULL );
137 }
138
139 int
spectrum_frame(void)140 spectrum_frame( void )
141 {
142 libspectrum_dword frame_length;
143
144 /* Reduce the t-state count of both the processor and all the events
145 scheduled to occur. Done slightly differently if RZX playback is
146 occurring */
147 frame_length = rzx_playback ? tstates
148 : machine_current->timings.tstates_per_frame;
149
150 event_frame( frame_length );
151 debugger_breakpoint_reduce_tstates( frame_length );
152 tstates -= frame_length;
153 if( z80.interrupts_enabled_at >= 0 )
154 z80.interrupts_enabled_at -= frame_length;
155
156 if( sound_enabled ) sound_frame();
157
158 if( display_frame() ) return 1;
159 if( profile_active ) profile_frame( frame_length );
160 printer_frame();
161
162 /* Add an interrupt unless they're being generated by .rzx playback */
163 if( !rzx_playback )
164 event_add( machine_current->timings.tstates_per_frame,
165 spectrum_frame_event );
166
167 loader_frame( frame_length );
168 phantom_typist_frame();
169
170 frames_since_reset++;
171
172 return 0;
173 }
174
175 libspectrum_byte
spectrum_contend_delay_none(libspectrum_dword time)176 spectrum_contend_delay_none( libspectrum_dword time )
177 {
178 return 0;
179 }
180
181 static libspectrum_byte
contend_delay_common(libspectrum_dword time,int * timings,int offset)182 contend_delay_common( libspectrum_dword time, int* timings, int offset )
183 {
184 int line, tstates_through_line;
185
186 line =
187 (libspectrum_signed_dword)( time - machine_current->line_times[ 0 ] ) /
188 machine_current->timings.tstates_per_line;
189
190 /* Work out where we are in this line, remembering that line_times[0] holds
191 the first pixel we display, not the start of where the Spectrum produced
192 the left border */
193 tstates_through_line = time - machine_current->line_times[ 0 ] +
194 ( machine_current->timings.left_border - DISPLAY_BORDER_WIDTH_COLS * 4 );
195
196 tstates_through_line %= machine_current->timings.tstates_per_line;
197
198 /* No contention in the upper and lower borders */
199 if( line < DISPLAY_BORDER_HEIGHT ||
200 line >= DISPLAY_BORDER_HEIGHT + DISPLAY_HEIGHT ) return 0;
201
202 /* Or in the left border */
203 if( tstates_through_line < machine_current->timings.left_border - offset )
204 return 0;
205
206 /* Or the right border or retrace */
207 if( tstates_through_line >= machine_current->timings.left_border +
208 machine_current->timings.horizontal_screen -
209 offset )
210 return 0;
211
212 /* We now know the ULA is reading the screen, so put in the appropriate
213 delay */
214 return timings[ tstates_through_line % 8 ];
215 }
216
217 libspectrum_byte
spectrum_contend_delay_65432100(libspectrum_dword time)218 spectrum_contend_delay_65432100( libspectrum_dword time )
219 {
220 return contend_delay_common( time, contention_pattern_65432100, 1 );
221 }
222
223 libspectrum_byte
spectrum_contend_delay_76543210(libspectrum_dword time)224 spectrum_contend_delay_76543210( libspectrum_dword time )
225 {
226 return contend_delay_common( time, contention_pattern_76543210, 4 );
227 }
228
229 /* What happens if we read from an unattached port? */
230 libspectrum_byte
spectrum_unattached_port(void)231 spectrum_unattached_port( void )
232 {
233 int line, tstates_through_line, column;
234
235 /* Return 0xff (idle bus) if we're in the top border */
236 if( tstates < machine_current->line_times[ DISPLAY_BORDER_HEIGHT ] )
237 return 0xff;
238
239 /* Work out which line we're on, relative to the top of the screen */
240 line = ( (libspectrum_signed_dword)tstates -
241 machine_current->line_times[ DISPLAY_BORDER_HEIGHT ] ) /
242 machine_current->timings.tstates_per_line;
243
244 /* Idle bus if we're in the lower border */
245 if( line >= DISPLAY_HEIGHT ) return 0xff;
246
247 /* Work out where we are in this line, remembering that line_times[] holds
248 the first pixel we display, not the start of where the Spectrum produced
249 the left border */
250 tstates_through_line = tstates -
251 machine_current->line_times[ DISPLAY_BORDER_HEIGHT + line ] +
252 ( machine_current->timings.left_border - DISPLAY_BORDER_WIDTH_COLS * 4 );
253
254 /* Idle bus if we're in the left border */
255 if( tstates_through_line < machine_current->timings.left_border )
256 return 0xff;
257
258 /* Or the right border or retrace */
259 if( tstates_through_line >= machine_current->timings.left_border +
260 machine_current->timings.horizontal_screen )
261 return 0xff;
262
263 column = ( ( tstates_through_line -
264 machine_current->timings.left_border ) / 8 ) * 2;
265
266 switch( tstates_through_line % 8 ) {
267
268 /* The pattern of bytes returned here is the same as documented by
269 Ramsoft in their 'Floating bus technical guide' at
270 http://web.archive.org/web/20080509193736/http://www.ramsoft.bbk.org/floatingbus.html
271
272 However, the timings used are based on the first byte being
273 returned at 14338 (48K) and 14364 (128K) respectively, not
274 14347 and 14368 as used by Ramsoft.
275
276 In contrast to previous versions of this code, Arkanoid and
277 Sidewize now work. */
278
279 /* Attribute bytes */
280 case 5: column++;
281 case 3:
282 return RAM[ memory_current_screen ][ display_attr_start[line] + column ];
283
284 /* Screen data */
285 case 4: column++;
286 case 2:
287 return RAM[ memory_current_screen ][ display_line_start[line] + column ];
288
289 /* Idle bus */
290 case 0: case 1: case 6: case 7:
291 return 0xff;
292
293 }
294
295 return 0; /* Keep gcc happy */
296 }
297
298 libspectrum_byte
spectrum_unattached_port_none(void)299 spectrum_unattached_port_none( void )
300 {
301 return 0xff;
302 }
303