Xemu [doxygen]  hyppo 0a42be3a057156924bc1b626a687bd6e27349c45 @ Sat 19 Mar 02:15:11 CET 2022
primo.c
Go to the documentation of this file.
1 /* Test-case for a very simple Primo (a Hungarian U880 - Z80
2  compatible clone CPU - based 8 bit computer) emulator.
3 
4  Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu
5  Copyright (C)2016-2021 LGB (Gábor Lénárt) <lgblgblgb@gmail.com>
6 
7  NOTE: Primo's CPU is U880, but for the simplicity I still call it Z80, as
8  it's [unlicensed] clone anyway.
9 
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
23 
24 #include "xemu/emutools.h"
25 #include "xemu/emutools_files.h"
26 #include "xemu/emutools_hid.h"
27 #include "xemu/emutools_config.h"
28 #include "xemu/emutools_gui.h"
29 #include "xemu/z80.h"
30 #include "xemu/z80_dasm.h"
31 #include "primo.h"
32 
33 #include <ctype.h>
34 #include <strings.h>
35 
36 
38 
39 #define ROM_Z80_PC_LOAD_TRIGGER 0x3994
40 
41 struct {
42  Uint8 *wr_p[4], *rd_p[4]; // write and read pointers for the 4*16K=64K Z80 address space
43  Uint8 *vid16k; // memory pointer to the 16K of RAM what video circuit sees on a given Primo model
44  Uint8 main [0x10000]; // main memory area of traditional Primo, 16K ROM + max of 48K RAM
45  Uint8 exprom [ 0x8000]; // expansion 2*16K ROM with the "memory paging addon" (replacing 16K ROM if paged)
46  Uint8 expram [ 0x4000]; // expansion 16K RAM with the "memory paging addon" (replacing 16K ROM if paged)
47  Uint8 wrwaste[ 0x4000]; // 16K of "wasteland", writes to ROM redirected here, saving "if"'s in the code
48  Uint8 rdempty[ 0x4000]; // 16K of "undecoded" nothing, in case of smaller RAM capacity than 48K (saves "if"s in the code)
49 } memory;
50 static Uint32 primo_palette_white;
51 static Uint32 primo_palette[0x100];
52 static int border_top, border_bottom, border_left, border_right;
53 static int primo_screen = 0;
54 static int nmi_enabled = 0;
55 static int nmi_status = 0;
56 static void (*renderer)(void);
57 
58 static int primo_model_set;
59 static const char *primo_model_set_str;
60 
61 static int cpu_clocks_per_scanline;
62 static int cpu_clocks_per_audio_sample;
63 static int cpu_clock;
64 static int cpu_clock_wanted;
65 static int scanline;
66 static Uint64 all_cycles_spent;
67 
68 static int emu_loop_notification;
69 
70 #define EMU_LOOP_NMI_NOTIFY 1
71 #define EMU_LOOP_LOAD_NOTIFY 2
72 #define EMU_LOOP_UPDATE_NOTIFY 4
73 #define EMU_LOOP_DISASM_NOTIFY 8
74 
75 #define VBLANK_ON 32
76 #define VBLANK_OFF 0
77 #define VBLANK_START_SCANLINE 256
78 #define PAL_LINE_FREQ 15625
79 
80 static int vblank = VBLANK_OFF;
81 
82 #define JOY_CLOCKING_TIMEOUT_MICROSECS 272
83 
84 static struct {
85  int step;
86  int clock;
87  Uint64 last_clocked;
89  int instance;
90 } joy;
91 
92 #define AUDIO_SAMPLING_FREQ (PAL_LINE_FREQ * 2)
93 #define AUDIO_PULSE_SAMPLES_MAX_PASS (AUDIO_SAMPLING_FREQ / 32)
94 #define VOLUME8 0x10
95 
96 static int beeper;
97 static Uint64 beeper_last_changed;
98 static SDL_AudioDeviceID audio;
99 static int audio_enabled = 1;
100 
101 static struct {
103  double clock_mhz;
104  char *model_name;
105  char *rom_fn;
106  char *pri_name;
107  char *exprom_fn;
109 } configdb;
110 
111 
112 
113 static int primo_read_joy ( int on, int off )
114 {
115  switch (joy.step) {
116  case 0: return hid_read_joystick_left (on, off);
117  case 1: return hid_read_joystick_down (on, off);
118  case 2: return hid_read_joystick_right (on, off);
119  case 3: return hid_read_joystick_up (on, off);
120  case 4: return hid_read_joystick_button (on, off);
121  default:return off;
122  }
123 }
124 
125 
127 {
128  return memory.rd_p[addr >> 14][addr & 0x3FFF];
129 }
130 
131 static Uint8 disasm_read_callback ( Uint16 addr )
132 {
133  return memory.rd_p[addr >> 14][addr & 0x3FFF];
134 }
135 
137 {
138  memory.wr_p[addr >> 14][addr & 0x3FFF] = value;
139 }
140 
141 
142 int serial = 2 | 8 | 16 | 32;
143 
144 
146 {
147  if ((port16 & 0xFF) < 0x40) {
148  static int random_2 = 0;
149  random_2 ^= 2;
150  //DEBUGPRINT("VBLANK = %d" NL, vblank);
151  // See the comments at the "matrix" definition below
152  return (((~kbd_matrix[(port16 >> 3) & 7]) >> (port16 & 7)) & 1) | vblank;
153  } else if ((port16 & 0xFF) < 0x80) {
154  DEBUG("JOY: read state at step=%d" NL, joy.step);
155  return primo_read_joy(0, 1) | serial;
156  } else
157  return 0xFF;
158 }
159 
160 
161 // Call this, if "enable NMI" or VBLANK signals change!
162 static void manage_nmi ( void )
163 {
164  // NMI signal on the CPU in the Primo is created from VBLANK, and a gate signal to allow to mask the unmaskable :) interrupt (NMI)
165  // VBLANK is a looong signal, the reason that we can use this way, that Z80 has an edge triggered NMI input, not level (unlike INT)
166  int new_status = nmi_enabled && vblank;
167  if (new_status && !nmi_status) { // rising edge (note: in the emulator we only use high-active signal, which can be different than the real hw)
168  emu_loop_notification |= EMU_LOOP_NMI_NOTIFY;
169  DEBUG("NMI: triggering edge" NL);
170  }
171  nmi_status = new_status;
172 }
173 
174 
175 
176 
178 {
179  if ((port16 & 0xFF) < 0x40) {
180  primo_screen = (value & 8) ? 0x2000 : 0x0000;
181  nmi_enabled = value & 128;
182  manage_nmi();
183  if ((value & 16) != beeper) {
184  static int rounding_error = 0;
185  Uint64 no_of_samples = (rounding_error + all_cycles_spent - beeper_last_changed) / cpu_clocks_per_audio_sample;
186  rounding_error = (rounding_error + all_cycles_spent - beeper_last_changed) % cpu_clocks_per_audio_sample;
187  if (no_of_samples <= AUDIO_PULSE_SAMPLES_MAX_PASS && no_of_samples > 0 && audio && audio_enabled) {
188  Uint8 samples[no_of_samples];
189  memset(samples, value & 16 ? 0x80 + VOLUME8 : 0x80 - VOLUME8, no_of_samples);
190  int ret = SDL_QueueAudio(audio, samples, no_of_samples); // last param are (number of) BYTES not samles. But we use 1 byte/samle audio ...
191  if (ret)
192  DEBUGPRINT("AUDIO: DATA: ERROR: %s" NL, SDL_GetError());
193  else
194  DEBUG("AUDIO: DATA: queued" NL);
195  } /* else
196  DEBUG("AUDIO: DATA: rejected! samples=%d" NL, (int)no_of_samples); */
197  beeper = value & 16;
198  beeper_last_changed = all_cycles_spent;
199  }
200  if ((value & 64) != joy.clock) {
201  joy.clock = value & 64;
202  if (joy.clock) {
203  joy.step = (all_cycles_spent - joy.last_clocked >= joy.clocking_timeout) ? 0 : (joy.step + 1) & 7;
204  joy.last_clocked = all_cycles_spent;
205  if (joy.step > 4) // FIXME: should we do this??? or real HW would always depends on timed reset of counter?
206  joy.step = 0;
207  }
208  }
209  } else if ((port16 & 0xFF) < 0x80) {
210  serial = value & (2 | 8 | 16 | 32);
211  } else if ((port16 & 0xFF) == 0xFD) {
212  static Uint8* const rd_tab[] = { memory.main, memory.expram, memory.exprom, memory.exprom + 0x4000 };
213  static Uint8* const wr_tab[] = { memory.wrwaste, memory.expram, memory.wrwaste , memory.wrwaste };
214  memory.rd_p[0] = rd_tab[value & 3];
215  memory.wr_p[0] = wr_tab[value & 3];
216  }
217 }
218 
219 
221 {
222  return 0xFF;
223 }
224 
225 
226 void z80ex_reti_cb ( void )
227 {
228 }
229 
230 
231 
232 #define VIRTUAL_SHIFT_POS 0x03
233 
234 
235 /* Primo for real does not have the notion if "keyboard matrix", well or we
236  can say it has 1*64 matrix (not like eg C64 with 8*8). Since the current
237  Xemu HID structure is more about a "real" matrix with "sane" dimensions,
238  I didn't want to hack it over, instead we use a more-or-less artificial
239  matrix, and we map that to the Primo I/O port request on port reading.
240  Since, HID assumes the high nibble of the "position" is the "row" and
241  low nibble can be only 0-7 we have values like:
242  $00 - $07, $10 - $17, $20 - $27, ...
243  ALSO: Primo uses bit '1' for pressed, so we also invert value in
244  the port read function above.
245  FIXME: better map, missing keys, some differences between Primo models!
246 */
247 static const struct KeyMappingDefault primo_key_map[] = {
248  { SDL_SCANCODE_Y, 0x00 }, // scan 0 Y
249  { SDL_SCANCODE_UP, 0x01 }, // scan 1 UP-ARROW
250  { SDL_SCANCODE_S, 0x02 }, // scan 2 S
251  { SDL_SCANCODE_LSHIFT, 0x03 }, { SDL_SCANCODE_RSHIFT, 0x03 }, // scan 3 SHIFT
252  { SDL_SCANCODE_E, 0x04 }, // scan 4 E
253  //{ SDL_SCANCODE_UPPER, 0x05 }, // scan 5 UPPER
254  { SDL_SCANCODE_W, 0x06 }, // scan 6 W
255  { SDL_SCANCODE_LCTRL, 0x07 }, // scan 7 CTR
256  { SDL_SCANCODE_D, 0x10 }, // scan 8 D
257  { SDL_SCANCODE_3, 0x11 }, // scan 9 3 #
258  { SDL_SCANCODE_X, 0x12 }, // scan 10 X
259  { SDL_SCANCODE_2, 0x13 }, // scan 11 2 "
260  { SDL_SCANCODE_Q, 0x14 }, // scan 12 Q
261  { SDL_SCANCODE_1, 0x15 }, // scan 13 1 !
262  { SDL_SCANCODE_A, 0x16 }, // scan 14 A
263  { SDL_SCANCODE_DOWN, 0x17 }, // scan 15 DOWN-ARROW
264  { SDL_SCANCODE_C, 0x20 }, // scan 16 C
265  //{ SDL_SCANCODE_----, 0x21 }, // scan 17 ----
266  { SDL_SCANCODE_F, 0x22 }, // scan 18 F
267  //{ SDL_SCANCODE_----, 0x23 }, // scan 19 ----
268  { SDL_SCANCODE_R, 0x24 }, // scan 20 R
269  //{ SDL_SCANCODE_----, 0x25 }, // scan 21 ----
270  { SDL_SCANCODE_T, 0x26 }, // scan 22 T
271  { SDL_SCANCODE_7, 0x27 }, // scan 23 7 /
272  { SDL_SCANCODE_H, 0x30 }, // scan 24 H
273  { SDL_SCANCODE_SPACE, 0x31 }, // scan 25 SPACE
274  { SDL_SCANCODE_B, 0x32 }, // scan 26 B
275  { SDL_SCANCODE_6, 0x33 }, // scan 27 6 &
276  { SDL_SCANCODE_G, 0x34 }, // scan 28 G
277  { SDL_SCANCODE_5, 0x35 }, // scan 29 5 %
278  { SDL_SCANCODE_V, 0x36 }, // scan 30 V
279  { SDL_SCANCODE_4, 0x37 }, // scan 31 4 $
280  { SDL_SCANCODE_N, 0x40 }, // scan 32 N
281  { SDL_SCANCODE_8, 0x41 }, // scan 33 8 (
282  { SDL_SCANCODE_Z, 0x42 }, // scan 34 Z
283  //{ SDL_SCANCODE_PLUS, 0x43 }, // scan 35 + ?
284  { SDL_SCANCODE_U, 0x44 }, // scan 36 U
285  { SDL_SCANCODE_0, 0x45 }, // scan 37 0
286  { SDL_SCANCODE_J, 0x46 }, // scan 38 J
287  //{ SDL_SCANCODE_>, 0x47 }, // scan 39 > <
288  { SDL_SCANCODE_L, 0x50 }, // scan 40 L
289  { SDL_SCANCODE_MINUS, 0x51 }, // scan 41 - i
290  { SDL_SCANCODE_K, 0x52 }, // scan 42 K
291  { SDL_SCANCODE_PERIOD, 0x53 }, // scan 43 . :
292  { SDL_SCANCODE_M, 0x54 }, // scan 44 M
293  { SDL_SCANCODE_9, 0x55 }, // scan 45 9 ;
294  { SDL_SCANCODE_I, 0x56 }, // scan 46 I
295  { SDL_SCANCODE_COMMA, 0x57 }, // scan 47 ,
296  //{ SDL_SCANCODE_U", 0x60 }, // scan 48 U"
297  { SDL_SCANCODE_APOSTROPHE, 0x61 }, // scan 49 ' #
298  { SDL_SCANCODE_P, 0x62 }, // scan 50 P
299  //{ SDL_SCANCODE_u', 0x63 }, // scan 51 u' u"
300  { SDL_SCANCODE_O, 0x64 }, // scan 52 O
301  { SDL_SCANCODE_HOME, 0x65 }, // scan 53 CLS
302  //{ SDL_SCANCODE_----, 0x66 }, // scan 54 ----
303  { SDL_SCANCODE_RETURN, 0x67 }, // scan 55 RETURN
304  //{ SDL_SCANCODE_----, 0x70 }, // scan 56 ----
305  { SDL_SCANCODE_LEFT, 0x71 }, // scan 57 LEFT-ARROW
306  //{ SDL_SCANCODE_E', 0x72 }, // scan 58 E'
307  //{ SDL_SCANCODE_o', 0x73 }, // scan 59 o'
308  //{ SDL_SCANCODE_A', 0x74 }, // scan 60 A'
309  { SDL_SCANCODE_RIGHT, 0x75 }, // scan 61 RIGHT-ARROW
310  //{ SDL_SCANCODE_O:, 0x76 }, // scan 62 O:
311  { SDL_SCANCODE_ESCAPE, 0x77 }, // scan 63 BRK
313  // **** this must be the last line: end of mapping table ****
314  { 0, -1 }
315 };
316 
317 
318 
319 void clear_emu_events ( void )
320 {
321  hid_reset_events(1);
322 }
323 
324 
325 
326 static void set_border_geometry ( int xres, int yres )
327 {
328  border_left = (SCREEN_WIDTH - xres) / 2;
329  border_right = (SCREEN_WIDTH - xres) - border_left;
330  border_top = (SCREEN_HEIGHT - yres) / 2;
331  border_bottom = (SCREEN_HEIGHT - yres) - border_top;
332  if (XEMU_UNLIKELY(border_left < 0 || border_top < 0))
333  FATAL("Internal error: too small target texture!");
334 }
335 
336 
337 #ifdef XEMU_FILES_SCREENSHOT_SUPPORT
338 static int register_screenshot_request = 0;
339 static inline void do_pending_screenshot ( void )
340 {
342  return;
344  if (!xemu_screenshot_png(
345  NULL, NULL,
346  2,
347  2,
348  NULL, // allow function to figure it out ;)
349  SCREEN_WIDTH,
352  )) {
353  const char *p = strrchr(xemu_screenshot_full_path, DIRSEP_CHR);
354  if (p)
355  OSD(-1, -1, "%s", p + 1);
356  }
357 }
358 #endif
359 
360 
361 // ABSOLUTELY LAME implementation!
362 // We render the screen "at once" :-O
363 
364 static void render_primo_bw_screen ( void )
365 {
366  int tail;
368  Uint8 *scr = memory.vid16k + primo_screen + 0x800;
369  for (int y = 0; y < border_top; y++) {
370  for (int x = 0; x < 256 + border_left + border_right; x++)
371  *pix++ = primo_palette[0];
372  pix += tail;
373  }
374  for (int y = 0; y < 192; y++) {
375  for (int x = 0; x < border_left; x++)
376  *pix++ = primo_palette[0];
377  for (int x = 0; x < 32; x++)
378  for (int z = 0, b = *scr++; z < 8; z++, b <<= 1)
379  *pix++ = b & 0x80 ? primo_palette_white : primo_palette[0];
380  for (int x = 0; x < border_right; x++)
381  *pix++ = primo_palette[0];
382  pix += tail;
383  }
384  for (int y = 0; y < border_bottom; y++) {
385  for (int x = 0; x < 256 + border_left + border_right; x++)
386  *pix++ = primo_palette[0];
387  pix += tail;
388  }
389 #ifdef XEMU_FILES_SCREENSHOT_SUPPORT
390  do_pending_screenshot();
391 #endif
393 }
394 
395 
396 
397 static void render_primo_c_screen ( void )
398 {
399  int tail;
401  Uint8 *scr = memory.vid16k + primo_screen + 0x500;
402  //DEBUGPRINT("B0=%02X B1=%02X B2=%02X B3=%02X B4=%02X" NL,
403  // scr[-0x800], scr[-0x800+1], scr[0x800+2], scr[0x800+3], scr[0x800+4]
404  //);
405 #if 0
406  case // 4x4 mode.
407  yres = 200;
408  attrw = 4;
409  attrh = 4;
410  case // 6x6 mode
411  yres = 216;
412  attrw = 6;
413  attrh = 6;
414  case // 6x9 mode
415  yres = 225;
416  attrw = 6;
417  attrh = 9;
418 #endif
419  // for now, just use the settings of the default mode, fixed
420  int yres = 216;
421  int attrw = 6;
422  int attrh = 6;
423  int attrstartpos = 1;
424  int attrincelstart = 2;
425  // that was it for the defaults
426  int attrline = 0;
427  scr = memory.vid16k + primo_screen + 0x2000 - yres * 32;
428  Uint8 *col = scr - (yres / attrh) * 32;
429  Uint8 *pal_bg_sel = memory.vid16k + primo_screen + 32;
430  Uint8 *pal_fg_sel = memory.vid16k + primo_screen + 32 + 16;
431  set_border_geometry(256, yres);
432  for (int y = 0; y < border_top; y++) {
433  for (int x = 0; x < 256 + border_left + border_right; x++)
434  *pix++ = primo_palette[0];
435  pix += tail;
436  }
437  Uint32 foregrounds[64], backgrounds[64];
438  for (int y = 0; y < yres; y++) {
439  if (!attrline)
440  for (int x = 0; x < 64; ) { // decode a full line of attribute (we may not use ALL!!!)
441  int b = *col++;
442  foregrounds[x] = primo_palette[pal_fg_sel[b >> 4]];
443  backgrounds[x] = primo_palette[pal_bg_sel[b >> 4]];
444  x++;
445  foregrounds[x] = primo_palette[pal_fg_sel[b & 15]];
446  backgrounds[x] = primo_palette[pal_bg_sel[b & 15]];
447  x++;
448  }
449  for (int x = 0; x < border_left; x++)
450  *pix++ = primo_palette[0];
451  int attrpos = attrstartpos;
452  int attrcountincel = attrincelstart;
453  for (int x = 0; x < 32; x++)
454  for (int z = 0, b = *scr++; z < 8; z++, b <<= 1) {
455  *pix++ = b & 0x80 ? foregrounds[attrpos] : backgrounds[attrpos];
456  attrcountincel++;
457  if (attrcountincel == attrw) {
458  attrcountincel = 0;
459  attrpos++;
460  }
461  }
462  for (int x = 0; x < border_right; x++)
463  *pix++ = primo_palette[0];
464  attrline++;
465  if (attrline == attrh)
466  attrline = 0;
467  pix += tail;
468  }
469  for (int y = 0; y < border_bottom; y++) {
470  for (int x = 0; x < 256 + border_left + border_right; x++)
471  *pix++ = primo_palette[0];
472  pix += tail;
473  }
474 #ifdef XEMU_FILES_SCREENSHOT_SUPPORT
475  do_pending_screenshot();
476 #endif
478 }
479 
480 
481 
482 
483 void emu_quit_callback ( void )
484 {
485  DEBUGPRINT("Exiting @ PC=$%04X" NL, Z80_PC);
486 }
487 
488 
489 
490 static int pri_apply ( Uint8 *data, int size, int wet_run )
491 {
492  int i = 0;
493  while (i < size) {
494  int btype = data[i];
495  DEBUGPRINT("PRI: block_type=$%02X @ %d" NL, btype, i);
496  if (btype == 0xC9)
497  return 0; // return, OK, no Z80 PC specified
498  if (i >= size - 2)
499  break;
500  int baddr = data[i + 1] | (data[i + 2] << 8);
501  if (btype == 0xC3)
502  return baddr; // return OK, Z80 PC specified!
503  if (i >= size - 4)
504  break;
505  int bsize = data[i + 3] | (data[i + 4] << 8);
506  if (btype == 0xD1) {
507  baddr += memory.main[0x40A4] | (memory.main[0x40A5] << 8);
508  if (wet_run) {
509  memory.main[0x40F9] = (baddr + bsize + 1) & 0xFF;
510  memory.main[0x40FA] = (baddr + bsize + 1) >> 8;
511  }
512  } else if (btype != 0xD5 && btype != 0xD9) {
513  ERROR_WINDOW("Invalid PRI file, unknown block type $%02X @ %d", btype, i);
514  return -1;
515  }
516  if (i >= size - bsize)
517  break;
518  DEBUGPRINT(" ... ADDR=%04X SIZE=%04X" NL, baddr, bsize);
519  i += 5;
520  while (bsize--) {
521  if (wet_run) // I don't use direct memory.main access here, as PRI could overwrite ROM etc ...
522  z80ex_mwrite_cb(baddr++, data[i]);
523  i++;
524  }
525 #if 0
526  if (wet_run)
527  memcpy(memory.main + baddr, data + i + 5, bsize);
528  i += 5 + bsize;
529 #endif
530  }
531  ERROR_WINDOW("Invalid PRI file, probably truncated");
532  return -1;
533 }
534 
535 
536 
537 static int pri_load ( const char *file_name, int wet_run )
538 {
539  if (!file_name || !*file_name)
540  return -1;
541  int file_size = xemu_load_file(file_name, NULL, 10, 0xC000, "Cannot open/use PRI file");
542  if (file_size < 0)
543  return file_size;
544  int ret = pri_apply(xemu_load_buffer_p, file_size, wet_run);
545  free(xemu_load_buffer_p);
546  return ret;
547 }
548 
549 
550 static void set_title_model_details ( void )
551 {
552  static char title_str_id[64];
553  sprintf(title_str_id, "(model %s, %.2fMHz)", primo_model_set_str, (float)cpu_clock / 1000000.0);
554  window_title_info_addon = title_str_id;
555 }
556 
557 
558 static int set_cpu_hz ( int hz )
559 {
560  cpu_clock_wanted = hz;
561  cpu_clocks_per_scanline = (hz / PAL_LINE_FREQ) & ~1; // 15625 Hz = 312.5 * 50, PAL "scanline frequency", how many Z80 cycles we need for that. Also make it to an even number
562  cpu_clock = cpu_clocks_per_scanline * PAL_LINE_FREQ; // to reflect the possible situation when it's not a precise divider above
563  DEBUGPRINT("CLOCK: CPU: clock speed set to %.2f MHz (%d CPU cycles per scanline)" NL, cpu_clock / 1000000.0, cpu_clocks_per_scanline);
564  joy.clocking_timeout = (int)(((double)JOY_CLOCKING_TIMEOUT_MICROSECS / 1000000.0) * (double)cpu_clock);
565  DEBUGPRINT("CLOCK: JOY: clocking timeout is %d microseconds, %d CPU cycles" NL, JOY_CLOCKING_TIMEOUT_MICROSECS, (int)joy.clocking_timeout);
566  cpu_clocks_per_audio_sample = cpu_clock / AUDIO_SAMPLING_FREQ;
567  DEBUGPRINT("CLOCK: AUDIO: %d CPU clocks per audio sample at sampling frequency of %d Hz" NL, cpu_clocks_per_audio_sample, AUDIO_SAMPLING_FREQ);
568  set_title_model_details();
569  return cpu_clock;
570 }
571 
572 
573 static void emulation_loop ( void )
574 {
575  static int cycles = 0;
576  static int frameskip = 0;
577  Uint64 all_cycles_old = all_cycles_spent;
578  goto first_shot;
579  for (;;) { // our emulation loop ...
580  int op_cycles = z80ex_step();
581  cycles -= op_cycles;
582  all_cycles_spent += op_cycles;
583  first_shot:
584  if (XEMU_UNLIKELY(emu_loop_notification)) {
585  DEBUG("EMU: notification" NL);
586  // DESIGN: for fast emulation loop, we have a "notification" system. In general,
587  // there are no notifications. So this way we have to check only a single variable,
588  // and do more "if"-s only if we have something. So the usual scenario (not having
589  // notification) would require to check a single condition -> faster emulation. Still,
590  // we can expand the range of notifications without affecting the emulation speed
591  // this way, if there are no notifications, which should be the majority of the emulator
592  // time. NOTE: debugger support/etc, should use notification system as well!
593  if (emu_loop_notification & EMU_LOOP_NMI_NOTIFY) { // Notification for triggering NMI
594  // check if Z80ex accepted NMI. Though a real Z80 should always do this (NMI is not maskable),
595  // Z80ex is constructed in a way, that it actually *returns* after z80ex_step() even if it was
596  // a prefix byte, like FD,DD,CB,ED ... Surely, Z80 would not allow this, that's why z80ex_nmi()
597  // may report zero, that is NMI *now* cannot be accepted. Then we simply try again on the next
598  // run. Note: the construction of this z80ex_step() function actually GOOD because of more fine
599  // grained timing, so it's not only "a problem"! Actually it's IMPOSSIBLE to write an accurate
600  // Z80 emulation without this behaviour! Since, a real Z80 would react for a loong opocde
601  // sequence of FD/DD bytes to block even NMIs till its end, which would block the execution
602  // of the emulator updates etc as well, probably.
603  op_cycles = z80ex_nmi();
604  if (op_cycles) {
605  DEBUG("NMI accepted" NL);
606  cycles -= op_cycles;
607  all_cycles_spent += op_cycles;
608  emu_loop_notification &= ~EMU_LOOP_NMI_NOTIFY; // clear notification, it's done
609  } else {
610  DEBUG("NMI not accepted" NL);
611  }
612  }
613  if (emu_loop_notification & EMU_LOOP_LOAD_NOTIFY) { // Notification for checking PC to trigger PRI loading!
615  emu_loop_notification &= ~EMU_LOOP_LOAD_NOTIFY; // clear notification, it's done
616  int ret = pri_load(configdb.pri_name, 1);
617  if (ret > 0) {
618  DEBUGPRINT("PRI: loaded, Z80 PC change $%04X -> $%04X" NL, Z80_PC, ret);
619  Z80_PC = ret;
620  } else if (!ret)
621  DEBUGPRINT("PRI: loaded, no Z80 PC change requested" NL);
622  else
623  DEBUGPRINT("PRI: cannot load program" NL);
624  }
625  }
626  // This is not very much used currently, but maybe in the future:
627  if (emu_loop_notification & EMU_LOOP_UPDATE_NOTIFY) { // update notification for the emulator core
628  emu_loop_notification &= ~EMU_LOOP_UPDATE_NOTIFY; // clear notification, it's done
629  break; // end of the emulation loop!
630  }
631  if (emu_loop_notification & EMU_LOOP_DISASM_NOTIFY) {
632  char buf[256];
633  int t1, t2;
634  z80ex_dasm(buf, sizeof buf, 0, &t1, &t2, disasm_read_callback, Z80_PC);
635  DEBUGPRINT("%04X %s" NL, Z80_PC, buf);
636  }
637  }
638  if (XEMU_UNLIKELY(cycles <= 0)) {
639  // We run at least enough CPU cycles for a scanline, by decrementing cycles with the number
640  // of cycles Z80 spent to execute code at each step. The value "cycles" must be intialized
641  // with the desired number of CPU cycles for a scanline, which is calculated by the set_cpu_hz()
642  // function to configure CPU clock speed.
643  if (scanline == 311) {
644  cycles += cpu_clocks_per_scanline / 2; // last scanline is only a "half" according to the PAL standard. Or such ...
645  scanline++;
646  } else if (scanline == 312) { // we were at the last scanline of a half-frame.
647  cycles += cpu_clocks_per_scanline;
648  scanline = 0;
649  vblank = VBLANK_OFF;
650  manage_nmi();
651  //DEBUGPRINT("VIDEO: new frame!" NL);
652  frameskip = !frameskip;
653  if (!frameskip)
654  break; // end of the emulation loop!
655  } else {
656  cycles += cpu_clocks_per_scanline;
657  scanline++;
658  if (scanline == VBLANK_START_SCANLINE) {
659  vblank = VBLANK_ON;
660  manage_nmi();
661  }
662  }
663  if (XEMU_UNLIKELY(cycles <= 0)) {
664  FATAL("Emulation problem, underflow of cpu cycles / scanline tracking counter!");
665  }
666  }
667  }
668  renderer();
671  xemu_timekeeping_delay((Uint64)(1000000L * (Uint64)(all_cycles_spent - all_cycles_old) / (Uint64)cpu_clock));
672 }
673 
674 
675 void emu_dropfile_callback ( const char *fn )
676 {
677  static int choice = 0;
678  if (choice != 1)
679  choice = QUESTION_WINDOW("Load as PRI now|Load as PRI always|Cancel for now", "What should I do with the dropped file?");
680  if (choice > 1)
681  return;
682  emu_loop_notification |= EMU_LOOP_LOAD_NOTIFY;
683  emu_loop_notification &= ~EMU_LOOP_NMI_NOTIFY;
684  xemucfg_set_str(&configdb.pri_name, fn);
685  primo_screen = 0;
686  nmi_status = 0;
687  vblank = VBLANK_OFF;
688  memory.rd_p[0] = memory.main;
689  memory.wr_p[0] = memory.wrwaste;
690  z80ex_reset();
691  OSD(-1, -1, "File has been dropped");
692  //DEBUGPRINT("DROPFILE: %s" NL, fn);
693 }
694 
695 
696 static const char primo_model_name_0[] = "A32";
697 static const char primo_model_name_1[] = "A48";
698 static const char primo_model_name_2[] = "A64";
699 static const char primo_model_name_3[] = "B32";
700 static const char primo_model_name_4[] = "B48";
701 static const char primo_model_name_5[] = "B64";
702 static const char primo_model_name_6[] = "C";
703 static const char *primo_model_names[] = { primo_model_name_0, primo_model_name_1, primo_model_name_2, primo_model_name_3, primo_model_name_4, primo_model_name_5, primo_model_name_6, NULL };
704 
705 
706 static int set_model ( const char *model_id, int do_load_rom )
707 {
708  int id = 0;
709  while (primo_model_names[id] && strcasecmp(model_id, primo_model_names[id]))
710  id++;
711  if (!primo_model_names[id]) {
712  ERROR_WINDOW("Unknown Primo model requested: %s", model_id);
713  return -1;
714  }
715  char model_desc[16];
716  sprintf(model_desc, "Primo-%c%s", toupper(primo_model_names[id][0]), primo_model_names[id] + 1);
717  DEBUGPRINT("PRIMO: trying to initialize to model: %s" NL, model_desc);
718  if (do_load_rom) {
719  if (configdb.rom_fn) {
720  DEBUGPRINT("ROM: trying to load forced (by config) ROM, regardless of the selected model" NL);
721  if (xemu_load_file(configdb.rom_fn, memory.main, 0x4000, 0x4000, "This is the selected primo ROM. Without it, Xemu won't work.\nInstall it, or use -rom CLI switch to specify another path.") < 0)
722  return -1;
723  } else {
724  char rom_file[64];
725  char rom_err[128];
726  sprintf(rom_file, "#primo-%c%s.rom", tolower(primo_model_names[id][0]), primo_model_names[id] + 1);
727  sprintf(rom_err, "Cannot load default %s ROM file.\nYou can try to force one with the -rom CLI option.", model_desc);
728  DEBUGPRINT("ROM: trying to load model dependent default ROM file" NL);
729  if (xemu_load_file(rom_file, memory.main, 0x4000, 0x4000, rom_err) < 0)
730  return -1;
731  }
732  }
733  memory.rd_p[0] = memory.main;
734  memory.wr_p[0] = memory.wrwaste;
735  memory.rd_p[1] = memory.wr_p[1] = memory.main + 0x4000;
736  memory.rd_p[2] = memory.rd_p[3] = memory.rdempty;
737  memory.wr_p[2] = memory.wr_p[3] = memory.wrwaste;
738  renderer = render_primo_bw_screen; // default for all A and B models here
739  set_border_geometry(256, 192); // for colour primo, it's more complex, and handled in the render_primo_c_screen function, actually. this is the default for all other primo's, though
740  switch (id) {
741  case 0: // Primo-A32
742  memory.vid16k = memory.main + 0x4000;
743  break;
744  case 1: // Primo-A48
745  memory.wr_p[2] = memory.rd_p[2] = memory.main + 0x8000;
746  memory.vid16k = memory.main + 0x8000;
747  break;
748  case 2: // Primo-A64
749  memory.wr_p[2] = memory.rd_p[2] = memory.main + 0x8000;
750  memory.wr_p[3] = memory.rd_p[3] = memory.main + 0xC000;
751  memory.vid16k = memory.main + 0xC000;
752  break;
753  case 3: // Primo-B32
754  memory.vid16k = memory.main + 0x4000;
755  break;
756  case 4: // Primo-B48
757  memory.wr_p[2] = memory.rd_p[2] = memory.main + 0x8000;
758  memory.vid16k = memory.main + 0x8000;
759  break;
760  case 5: // Primo-B64
761  memory.wr_p[2] = memory.rd_p[2] = memory.main + 0x8000;
762  memory.wr_p[3] = memory.rd_p[3] = memory.main + 0xC000;
763  memory.vid16k = memory.main + 0xC000;
764  break;
765  case 6: // Primo-C
766  memory.wr_p[2] = memory.rd_p[2] = memory.main + 0x8000;
767  memory.wr_p[3] = memory.rd_p[3] = memory.main + 0xC000;
768  memory.vid16k = memory.main + 0xC000;
769  renderer = render_primo_c_screen;
770  break;
771  default:
772  FATAL("Unknown primo model ID #%d", id);
773  }
774  primo_model_set = id;
775  primo_model_set_str = primo_model_names[id];
776  set_title_model_details();
777  return 0;
778 }
779 
780 static void primo_reset ( void )
781 {
782  emu_loop_notification &= ~EMU_LOOP_NMI_NOTIFY;
783  emu_loop_notification &= ~EMU_LOOP_LOAD_NOTIFY;
784  z80ex_reset();
785 }
786 
787 
788 static void ui_cb_set_model ( const struct menu_st *m, int *query )
789 {
790  XEMUGUI_RETURN_CHECKED_ON_QUERY(query, !strcasecmp(m->user_data, primo_model_set_str));
791  if (!set_model(m->user_data, 1))
792  primo_reset();
793 }
794 
795 
796 static void ui_load_pri ( void )
797 {
798  static char fnbuf[PATH_MAX] = "";
799  static char dir[PATH_MAX] = "";
802  "Select PRI file to load",
803  dir,
804  fnbuf,
805  sizeof fnbuf
806  )) {
807  if (pri_load(fnbuf, 0) > 0) {
808  primo_reset();
809  emu_loop_notification |= EMU_LOOP_LOAD_NOTIFY;
810  xemucfg_set_str(&configdb.pri_name, fnbuf);
811  }
812  }
813 }
814 
815 
816 static void primo_reset_asked ( void )
817 {
818  if (ARE_YOU_SURE("Are you sure to HARD RESET your Primo?", i_am_sure_override | ARE_YOU_SURE_DEFAULT_YES))
819  primo_reset();
820 }
821 
822 
823 static void ui_cb_set_cpu_clock ( const struct menu_st *m, int *query )
824 {
825  XEMUGUI_RETURN_CHECKED_ON_QUERY(query, VOIDPTR_TO_INT(m->user_data) == cpu_clock_wanted);
826  set_cpu_hz(VOIDPTR_TO_INT(m->user_data));
827 }
828 
829 
830 static void ui_sound ( const struct menu_st *m, int *query )
831 {
832  XEMUGUI_RETURN_CHECKED_ON_QUERY(query, audio_enabled);
833  if (!audio) {
834  ERROR_WINDOW("Cannot enable audio.");
835  return;
836  }
837  audio_enabled = !audio_enabled;
838 }
839 
840 
841 
842 static const struct menu_st menu_cpu_clock[] = {
843  { "2.5MHz (default)", XEMUGUI_MENUID_CALLABLE |
844  XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_set_cpu_clock, (void*)2500000 },
845  { "3.5MHz", XEMUGUI_MENUID_CALLABLE |
846  XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_set_cpu_clock, (void*)3500000 },
847  { "7MHz", XEMUGUI_MENUID_CALLABLE |
848  XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_set_cpu_clock, (void*)7000000 },
849  { NULL }
850 };
851 static const struct menu_st menu_models[] = {
852  { primo_model_name_0, XEMUGUI_MENUID_CALLABLE |
853  XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_set_model, primo_model_name_0 },
854  { primo_model_name_1, XEMUGUI_MENUID_CALLABLE |
855  XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_set_model, primo_model_name_1 },
856  { primo_model_name_2, XEMUGUI_MENUID_CALLABLE |
857  XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_set_model, primo_model_name_2 },
858  { primo_model_name_3, XEMUGUI_MENUID_CALLABLE |
859  XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_set_model, primo_model_name_3 },
860  { primo_model_name_4, XEMUGUI_MENUID_CALLABLE |
861  XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_set_model, primo_model_name_4 },
862  { primo_model_name_5, XEMUGUI_MENUID_CALLABLE |
863  XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_set_model, primo_model_name_5 },
864  { primo_model_name_6, XEMUGUI_MENUID_CALLABLE |
865  XEMUGUI_MENUFLAG_QUERYBACK, ui_cb_set_model, primo_model_name_6 },
866  { NULL }
867 };
868 static const struct menu_st menu_display[] = {
869  { "Fullscreen", XEMUGUI_MENUID_CALLABLE, xemugui_cb_windowsize, (void*)0 },
870  { "Window - 100%", XEMUGUI_MENUID_CALLABLE, xemugui_cb_windowsize, (void*)1 },
871  { "Window - 200%", XEMUGUI_MENUID_CALLABLE |
873  { "Enable OSD kbd debug", XEMUGUI_MENUID_CALLABLE |
875  { NULL }
876 };
877 static const struct menu_st menu_main[] = {
878  { "Display", XEMUGUI_MENUID_SUBMENU, NULL, menu_display },
879  { "Set Primo model", XEMUGUI_MENUID_SUBMENU, NULL, menu_models },
880  { "CPU clock", XEMUGUI_MENUID_SUBMENU, NULL, menu_cpu_clock },
881  { "Reset Primo", XEMUGUI_MENUID_CALLABLE, xemugui_cb_call_user_data, primo_reset_asked },
882  { "Load PRI file", XEMUGUI_MENUID_CALLABLE, xemugui_cb_call_user_data, ui_load_pri },
883  { "Browse system folder", XEMUGUI_MENUID_CALLABLE, xemugui_cb_native_os_prefdir_browser, NULL },
884 #ifdef XEMU_FILES_SCREENSHOT_SUPPORT
886 #endif
887 #ifdef XEMU_ARCH_WIN
888  { "System console", XEMUGUI_MENUID_CALLABLE |
889  XEMUGUI_MENUFLAG_QUERYBACK, xemugui_cb_sysconsole, NULL },
890 #endif
891  { "Sound emulation", XEMUGUI_MENUID_CALLABLE |
892  XEMUGUI_MENUFLAG_QUERYBACK, ui_sound, NULL },
894 #ifdef HAVE_XEMU_EXEC_API
895  { "Help (on-line)", XEMUGUI_MENUID_CALLABLE, xemugui_cb_web_help_main, NULL },
896 #endif
898  { NULL }
899 };
900 
901 
902 // HID needs this to be defined, it's up to the emulator if it uses or not ...
903 int emu_callback_key ( int pos, SDL_Scancode key, int pressed, int handled )
904 {
905  if (!pressed && pos == -2 && key == 0 && handled == SDL_BUTTON_RIGHT) {
906  DEBUGGUI("UI: handler has been called." NL);
907  if (xemugui_popup(menu_main)) {
908  DEBUGPRINT("UI: oops, POPUP does not worked :(" NL);
909  }
910  }
911  return 0;
912 }
913 
914 
915 
916 
917 
918 int main ( int argc, char **argv )
919 {
920  xemu_pre_init(APP_ORG, TARGET_NAME, "The Unknown Primo emulator from LGB");
922  { "rom", NULL, "Select ROM to use", &configdb.rom_fn },
923  { "exprom", NULL, "ROM expansion file selector (max 32K size)", &configdb.exprom_fn },
924  { "pri", NULL, "Loads a PRI file", &configdb.pri_name },
925  { "model", "b64", "Set Primo model: a32, a48, a64, b32, b48, b64, c", &configdb.model_name },
926  { "gui", NULL, "Select GUI type for usage. Specify some insane str to get a list", &configdb.gui_selection }
927  );
929  { "fullscreen", "Start in fullscreen mode", &configdb.fullscreen },
930  { "syscon", "Keep system console open (Windows-specific effect only)", &configdb.syscon },
931  { "disasm", "Disassemble every CPU step (uber-spammy, uber-slow!)", &configdb.disasm },
932  { "besure", "Skip asking \"are you sure?\" on RESET or EXIT", &i_am_sure_override }
933  );
934  xemucfg_define_float_option("clock", (double)DEFAULT_CPU_CLOCK / 1000000.0, "Selects CPU frequency (1.00-16.00 in MHz)", &configdb.clock_mhz, 1.0, 16.0);
935  xemucfg_define_num_option("sdlrenderquality", RENDER_SCALE_QUALITY, "Setting SDL hint for scaling method/quality on rendering (0, 1, 2)", &configdb.sdlrenderquality, 0, 2);
936  if (xemucfg_parse_all(argc, argv))
937  return 1;
938  /* Initiailize SDL - note, it must be before loading ROMs, as it depends on path info from SDL! */
939  if (xemu_post_init(
940  TARGET_DESC APP_DESC_APPEND, // window title
941  1, // resizable window
942  SCREEN_WIDTH, SCREEN_HEIGHT, // texture sizes
943  SCREEN_WIDTH, SCREEN_HEIGHT, // logical size (width is doubled for somewhat correct aspect ratio)
944  SCREEN_WIDTH * 3, SCREEN_HEIGHT * 3, // window size (tripled in size, original would be too small)
945  SCREEN_FORMAT, // pixel format
946  0, // Prepare for colour primo, we have many colours, we want to generate at our own, later
947  NULL, // -- "" --
948  NULL, // -- "" --
949  configdb.sdlrenderquality,// render scaling quality
950  USE_LOCKED_TEXTURE, // 1 = locked texture access
951  NULL // no emulator specific shutdown function
952  ))
953  return 1;
954  for (int a = 0; a < 0x100; a++) // generate (colour primo's) palette
955  primo_palette[a] = SDL_MapRGBA(sdl_pix_fmt, (a >> 5) * 0xFF / 7, ((a >> 2) & 7) * 0xFF / 7, ((a << 1) & 7) * 0xFF / 7, 0xFF);
956  primo_palette_white = SDL_MapRGBA(sdl_pix_fmt, 0xFF, 0xFF, 0xFF, 0xFF); // colour primo scheme seems to have no white :-O So we do an extra entry for non-colour primo's only colour :)
957  hid_init(
958  primo_key_map,
960  SDL_ENABLE // enable joystick HID events
961  );
964  SDL_AudioSpec audio_want, audio_have;
965  SDL_memset(&audio_want, 0, sizeof audio_want);
966  audio_want.freq = AUDIO_SAMPLING_FREQ;
967  audio_want.format = AUDIO_U8;
968  audio_want.channels = 1;
969  audio_want.samples = 4096;
970  audio_want.callback = NULL;
971  audio = SDL_OpenAudioDevice(NULL, 0, &audio_want, &audio_have, 0);
972  DEBUGPRINT("AUDIO: dev=#%d driver=\"%s\" sampling at %d Hz" NL, audio, SDL_GetCurrentAudioDriver(), audio_have.freq);
973  /* Intialize memory and load ROMs */
974  memset(&memory, 0xFF, sizeof memory);
975  //memory.rd_p[0] = memory.main;
976  //memory.wr_p[0] = memory.wrwaste;
977  //memory.rd_p[1] = memory.wr_p[1] = memory.main + 0x4000;
978  //memory.rd_p[2] = memory.wr_p[2] = memory.main + 0x8000;
979  //memory.rd_p[3] = memory.wr_p[3] = memory.main + 0xC000;
980  //memory.vid16k = memory.main + 0xC000;
981  //border_left = (SCREEN_WIDTH - 256) / 2;
982  //border_right = (SCREEN_WIDTH - 256) - border_left;
983  //border_top = (SCREEN_HEIGHT - 192) / 2;
984  //border_bottom = (SCREEN_HEIGHT - 192) - border_top;
985  //if (border_left < 0 || border_top < 0)
986  // FATAL("Internal error: too small target texture!");
987  //DEBUGPRINT("Video: %dx%d pixels, border top,bottom,left,right=%d,%d,%d,%d" NL,
988  // SCREEN_WIDTH, SCREEN_HEIGHT,
989  // border_top, border_bottom, border_left, border_right
990  //);
991  if (set_model(configdb.model_name, 1))
992  FATAL("Bad Primo model specified and/or ROM not found (more details: the previous message)");
993  //if (xemu_load_file(xemucfg_get_str("rom"), memory.main, 0x4000, 0x4000, "This is the selected primo ROM. Without it, Xemu won't work.\nInstall it, or use -rom CLI switch to specify another path.") < 0)
994  // return 1;
995  if (configdb.exprom_fn)
996  xemu_load_file(configdb.exprom_fn, memory.exprom, 1, sizeof memory.exprom, "Invalid selected expansion ROM");
997  // Continue with initializing ...
998  clear_emu_events(); // also resets the keyboard
999  z80ex_init();
1000  beeper = 0;
1001  beeper_last_changed = 0;
1002  joy.step = 0;
1003  joy.clock = 0;
1004  joy.last_clocked = 0;
1005  //set_cpu_hz(DEFAULT_CPU_CLOCK);
1006  set_cpu_hz((int)(configdb.clock_mhz * 1000000.0));
1007  //set_cpu_hz((int)(xemucfg_get_float("clock") * 1000000.0));
1008  scanline = 0;
1010  if (!configdb.syscon)
1011  sysconsole_close(NULL);
1012  emu_loop_notification = 0;
1013  all_cycles_spent = 0;
1014  if (configdb.pri_name)
1015  emu_loop_notification |= EMU_LOOP_LOAD_NOTIFY;
1016  if (configdb.disasm)
1017  emu_loop_notification |= EMU_LOOP_DISASM_NOTIFY;
1018  SDL_PauseAudioDevice(audio, 0);
1019  xemu_timekeeping_start(); // we must call this once, right before the start of the emulation
1020  XEMU_MAIN_LOOP(emulation_loop, 25, 1);
1021  return 0;
1022 }
z80_dasm.h
VBLANK_ON
#define VBLANK_ON
Definition: primo.c:75
xemu_pre_init
void xemu_pre_init(const char *app_organization, const char *app_name, const char *slogan)
Definition: emutools.c:651
z80ex_init
void z80ex_init(void)
Definition: z80ex.c:175
serial
int serial
Definition: primo.c:142
USE_LOCKED_TEXTURE
#define USE_LOCKED_TEXTURE
Definition: commodore_65.h:26
TARGET_DESC
#define TARGET_DESC
Definition: xemu-target.h:2
z80ex
Z80EX_CONTEXT z80ex
Definition: primo.c:37
XEMUCFG_DEFINE_SWITCH_OPTIONS
#define XEMUCFG_DEFINE_SWITCH_OPTIONS(...)
Definition: emutools_config.h:123
exprom
Uint8 exprom[0x8000]
Definition: primo.c:45
menu_st
Definition: emutools_gui.h:65
rom_fn
char * rom_fn
Definition: primo.c:105
rdempty
Uint8 rdempty[0x4000]
Definition: primo.c:48
hid_read_joystick_down
int hid_read_joystick_down(int on, int off)
Definition: emutools_hid.c:461
xemu_timekeeping_delay
void xemu_timekeeping_delay(int td_em)
Definition: emutools.c:405
emutools.h
sysconsole_close
void sysconsole_close(const char *waitmsg)
Definition: emutools.c:1393
XEMU_MAIN_LOOP
#define XEMU_MAIN_LOOP(func, p1, p2)
Definition: emutools.h:58
fullscreen
int fullscreen
Definition: primo.c:102
rd_p
Uint8 * rd_p[4]
Definition: primo.c:42
syscon
int syscon
Definition: primo.c:102
z80ex_step
int z80ex_step(void)
Definition: z80ex.c:47
vid16k
Uint8 * vid16k
Definition: primo.c:43
menu_st::user_data
const void * user_data
Definition: emutools_gui.h:69
SCREEN_WIDTH
#define SCREEN_WIDTH
Definition: vic3.h:29
z80ex_pread_cb
Z80EX_BYTE z80ex_pread_cb(Z80EX_WORD port16)
Definition: primo.c:145
xemugui_file_selector
int xemugui_file_selector(int dialog_mode, const char *dialog_title, char *default_dir, char *selected, int path_max_size)
Definition: emutools_gui.c:126
xemugui_popup
int xemugui_popup(const struct menu_st desc[])
Definition: emutools_gui.c:135
EMU_LOOP_DISASM_NOTIFY
#define EMU_LOOP_DISASM_NOTIFY
Definition: primo.c:73
register_screenshot_request
int register_screenshot_request
Definition: enterprise128.c:54
xemucfg_define_num_option
void xemucfg_define_num_option(const char *optname, const int defval, const char *help, int *storage, int min, int max)
xemu_update_screen
void xemu_update_screen(void)
Definition: emutools.c:1184
i_am_sure_override
int i_am_sure_override
Definition: emutools.c:74
clock
int clock
Definition: primo.c:86
xemugui_cb_osd_key_debugger
void xemugui_cb_osd_key_debugger(const struct menu_st *m, int *query)
Definition: popular_user_funcs.c:169
wrwaste
Uint8 wrwaste[0x4000]
Definition: primo.c:47
addr
int addr
Definition: dma65.c:81
DEFAULT_CPU_CLOCK
#define DEFAULT_CPU_CLOCK
Definition: enterprise128.h:34
sdl_pix_fmt
SDL_PixelFormat * sdl_pix_fmt
Definition: emutools.c:80
fn
const char * fn
Definition: roms.c:42
hid_handle_all_sdl_events
void hid_handle_all_sdl_events(void)
Definition: emutools_hid.c:613
xemucfg_parse_all
int xemucfg_parse_all(int argc, char **argv)
VBLANK_OFF
#define VBLANK_OFF
Definition: primo.c:76
XEMUGUI_MENUID_CALLABLE
#define XEMUGUI_MENUID_CALLABLE
Definition: emutools_gui.h:30
emutools_gui.h
id
int id
Definition: joystick.c:52
hid_read_joystick_left
int hid_read_joystick_left(int on, int off)
Definition: emutools_hid.c:467
m65-memcontent-generator.data
data
Definition: m65-memcontent-generator.py:119
osd_init_with_defaults
int osd_init_with_defaults(void)
Definition: osd.c:131
Uint32
uint32_t Uint32
Definition: fat32.c:49
last_clocked
Uint64 last_clocked
Definition: primo.c:87
Z80EX_WORD
unsigned short Z80EX_WORD
Definition: z80ex.h:51
z80ex_reset
void z80ex_reset(void)
Definition: z80ex.c:156
hid_init
void hid_init(const struct KeyMappingDefault *key_map_in, Uint8 virtual_shift_pos_in, int joy_enable)
Definition: emutools_hid.c:300
AUDIO_SAMPLING_FREQ
#define AUDIO_SAMPLING_FREQ
Definition: primo.c:92
primo.h
hid_reset_events
void hid_reset_events(int burn)
Definition: emutools_hid.c:170
XEMUGUI_FSEL_FLAG_STORE_DIR
#define XEMUGUI_FSEL_FLAG_STORE_DIR
Definition: emutools_gui.h:28
Uint8
uint8_t Uint8
Definition: fat32.c:51
xemu_set_full_screen
void xemu_set_full_screen(int setting)
Definition: emutools.c:311
_z80_cpu_context
Definition: z80ex.h:143
expram
Uint8 expram[0x4000]
Definition: primo.c:46
VBLANK_START_SCANLINE
#define VBLANK_START_SCANLINE
Definition: primo.c:77
configdb_st::sdlrenderquality
int sdlrenderquality
Definition: configdb.h:35
wr_p
Uint8 * wr_p[4]
Definition: primo.c:42
xemugui_init
int xemugui_init(const char *name)
Definition: emutools_gui.c:82
z80ex_pwrite_cb
void z80ex_pwrite_cb(Z80EX_WORD port16, Z80EX_BYTE value)
Definition: primo.c:177
XEMUGUI_MENUFLAG_QUERYBACK
#define XEMUGUI_MENUFLAG_QUERYBACK
Definition: emutools_gui.h:40
TARGET_NAME
#define TARGET_NAME
Definition: xemu-target.h:1
emutools_files.h
z80.h
APP_ORG
#define APP_ORG
Definition: emutools.h:50
x
int x
Definition: console.c:27
STD_XEMU_SPECIAL_KEYS
#define STD_XEMU_SPECIAL_KEYS
Definition: emutools_hid.h:108
DEBUGPRINT
#define DEBUGPRINT(...)
Definition: emutools_basicdefs.h:171
VOIDPTR_TO_INT
#define VOIDPTR_TO_INT(x)
Definition: emutools_basicdefs.h:270
VOLUME8
#define VOLUME8
Definition: primo.c:94
xemu_start_pixel_buffer_access
Uint32 * xemu_start_pixel_buffer_access(int *texture_tail)
Definition: emutools.c:1153
main
Uint8 main[0x10000]
Definition: primo.c:44
z80ex_mread_cb
Z80EX_BYTE z80ex_mread_cb(Z80EX_WORD addr, int m1_state)
Definition: primo.c:126
gui_selection
char * gui_selection
Definition: primo.c:108
dir
DIR * dir
Definition: cpmfs.c:46
Z80EX_BYTE
unsigned char Z80EX_BYTE
Definition: z80ex.h:49
ERROR_WINDOW
#define ERROR_WINDOW(...)
Definition: xep128.h:116
EMU_LOOP_NMI_NOTIFY
#define EMU_LOOP_NMI_NOTIFY
Definition: primo.c:70
instance
int instance
Definition: primo.c:89
NL
#define NL
Definition: fat32.c:37
emutools_config.h
xemugui_cb_set_integer_to_one
void xemugui_cb_set_integer_to_one(const struct menu_st *m, int *query)
Definition: popular_user_funcs.c:187
configdb
struct configdb_st configdb
Definition: configdb.c:34
XEMUGUI_FSEL_OPEN
#define XEMUGUI_FSEL_OPEN
Definition: emutools_gui.h:26
KeyMappingDefault
Definition: emutools_hid.h:24
xemu_post_init
int xemu_post_init(const char *window_title, int is_resizable, int texture_x_size, int texture_y_size, int logical_x_size, int logical_y_size, int win_x_size, int win_y_size, Uint32 pixel_format, int n_colours, const Uint8 *colours, Uint32 *store_palette, int render_scale_quality, int locked_texture_update, void(*shutdown_callback)(void))
Definition: emutools.c:908
xemu_load_file
int xemu_load_file(const char *filename, void *store_to, int min_size, int max_size, const char *cry)
Definition: emutools_files.c:674
clocking_timeout
Uint64 clocking_timeout
Definition: primo.c:88
xemu_load_buffer_p
void * xemu_load_buffer_p
Definition: emutools_files.c:34
step
int step
Definition: primo.c:85
exprom_fn
char * exprom_fn
Definition: primo.c:107
ARE_YOU_SURE_DEFAULT_YES
#define ARE_YOU_SURE_DEFAULT_YES
Definition: emutools.h:125
xemu_timekeeping_start
void xemu_timekeeping_start(void)
Definition: emutools.c:1122
JOY_CLOCKING_TIMEOUT_MICROSECS
#define JOY_CLOCKING_TIMEOUT_MICROSECS
Definition: primo.c:82
VIRTUAL_SHIFT_POS
#define VIRTUAL_SHIFT_POS
Definition: primo.c:232
z80ex_intread_cb
Z80EX_BYTE z80ex_intread_cb(void)
Definition: primo.c:220
sdlrenderquality
int sdlrenderquality
Definition: primo.c:102
z80ex_mwrite_cb
void z80ex_mwrite_cb(Z80EX_WORD addr, Z80EX_BYTE value)
Definition: primo.c:136
configdb_st::syscon
int syscon
Definition: configdb.h:34
size
int size
Definition: inject.c:37
kbd_matrix
Uint8 kbd_matrix[16]
Definition: dave.c:30
emu_dropfile_callback
void emu_dropfile_callback(const char *fn)
Definition: primo.c:675
L
#define L
Definition: macros.h:30
z80ex_reti_cb
void z80ex_reti_cb(void)
Definition: primo.c:226
ARE_YOU_SURE
int ARE_YOU_SURE(const char *s, int flags)
Definition: emutools.c:1202
configdb_st::fullscreen
int fullscreen
Definition: configdb.h:34
SCREEN_HEIGHT
#define SCREEN_HEIGHT
Definition: vic3.h:30
Z80_PC
#define Z80_PC
Definition: z80ex.h:121
disasm
int disasm
Definition: primo.c:102
emu_quit_callback
void emu_quit_callback(void)
Definition: primo.c:483
emu_callback_key
int emu_callback_key(int pos, SDL_Scancode key, int pressed, int handled)
Definition: primo.c:903
xemucfg_define_float_option
void xemucfg_define_float_option(const char *optname, const double defval, const char *help, double *storage, double min, double max)
SCREEN_FORMAT
#define SCREEN_FORMAT
Definition: commodore_65.h:25
hid_read_joystick_button
int hid_read_joystick_button(int on, int off)
Definition: emutools_hid.c:479
hid_read_joystick_up
int hid_read_joystick_up(int on, int off)
Definition: emutools_hid.c:455
OSD
#define OSD(...)
Definition: xep128.h:100
XEMUCFG_DEFINE_STR_OPTIONS
#define XEMUCFG_DEFINE_STR_OPTIONS(...)
Definition: emutools_config.h:119
RENDER_SCALE_QUALITY
#define RENDER_SCALE_QUALITY
Definition: commodore_65.h:27
XEMUGUI_MENUID_SUBMENU
#define XEMUGUI_MENUID_SUBMENU
Definition: emutools_gui.h:31
y
int y
Definition: console.c:27
XEMUGUI_RETURN_CHECKED_ON_QUERY
#define XEMUGUI_RETURN_CHECKED_ON_QUERY(query, status)
Definition: emutools_gui.h:55
value
int value
Definition: dma65.c:90
clock_mhz
double clock_mhz
Definition: primo.c:103
APP_DESC_APPEND
#define APP_DESC_APPEND
Definition: emutools.h:52
Uint16
uint16_t Uint16
Definition: fat32.c:50
xemugui_iteration
int xemugui_iteration(void)
Definition: emutools_gui.c:120
xemugui_cb_call_quit_if_sure
void xemugui_cb_call_quit_if_sure(const struct menu_st *m, int *query)
Definition: popular_user_funcs.c:79
xemugui_cb_call_user_data
void xemugui_cb_call_user_data(const struct menu_st *m, int *query)
Definition: popular_user_funcs.c:29
emutools_hid.h
model_name
char * model_name
Definition: primo.c:104
xemugui_cb_windowsize
void xemugui_cb_windowsize(const struct menu_st *m, int *query)
Definition: popular_user_funcs.c:97
z80ex_dasm
int z80ex_dasm(char *output, int output_size, unsigned flags, int *t_states, int *t_states2, z80ex_dasm_readbyte_cb readbyte_cb, Z80EX_WORD addr)
Definition: z80ex_dasm.c:42
QUESTION_WINDOW
#define QUESTION_WINDOW(items, msg)
Definition: xep128.h:124
PAL_LINE_FREQ
#define PAL_LINE_FREQ
Definition: primo.c:78
DEBUG
#define DEBUG(...)
Definition: emutools_basicdefs.h:167
EMU_LOOP_UPDATE_NOTIFY
#define EMU_LOOP_UPDATE_NOTIFY
Definition: primo.c:72
EMU_LOOP_LOAD_NOTIFY
#define EMU_LOOP_LOAD_NOTIFY
Definition: primo.c:71
configdb_st::gui_selection
char * gui_selection
Definition: configdb.h:43
xemugui_cb_about_window
void xemugui_cb_about_window(const struct menu_st *m, int *query)
Definition: popular_user_funcs.c:53
FATAL
#define FATAL(...)
Definition: xep128.h:117
z80ex_nmi
int z80ex_nmi(void)
Definition: z80ex.c:192
window_title_info_addon
char * window_title_info_addon
Definition: emutools.c:91
joy
SDL_Joystick * joy
Definition: joystick.c:35
clear_emu_events
void clear_emu_events(void)
Definition: primo.c:319
XEMU_UNLIKELY
#define XEMU_UNLIKELY(__x__)
Definition: emutools_basicdefs.h:125
hid_read_joystick_right
int hid_read_joystick_right(int on, int off)
Definition: emutools_hid.c:473
scanline
int scanline
Definition: vic6561.c:52
DEBUGGUI
#define DEBUGGUI
Definition: emutools_gui.h:22
ROM_Z80_PC_LOAD_TRIGGER
#define ROM_Z80_PC_LOAD_TRIGGER
Definition: primo.c:39
XEMUGUI_MENUFLAG_SEPARATOR
#define XEMUGUI_MENUFLAG_SEPARATOR
Definition: emutools_gui.h:38
frameskip
int frameskip
Definition: vic3.c:75
xemucfg_set_str
void xemucfg_set_str(char **ptr, const char *value)
buf
Uint8 buf[512]
Definition: fat32.c:155
memory
struct @34 memory
Definition: commodore_65.c:43
pri_name
char * pri_name
Definition: primo.c:106
DIRSEP_CHR
#define DIRSEP_CHR
Definition: emutools_basicdefs.h:142