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