Xemu [doxygen]  hyppo 0a42be3a057156924bc1b626a687bd6e27349c45 @ Sat 19 Mar 02:15:11 CET 2022
commodore_vic20.c
Go to the documentation of this file.
1 /* Test-case for a very simple and inaccurate Commodore VIC-20 emulator using SDL2 library.
2  Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu
3  Copyright (C)2016-2021 LGB (Gábor Lénárt) <lgblgblgb@gmail.com>
4 
5  This is the VIC-20 emulation. Note: the source is overcrowded with comments by intent :)
6  That it can useful for other people as well, or someone wants to contribute, etc ...
7 
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
21 
22 #include "xemu/emutools.h"
23 #include "xemu/emutools_files.h"
24 #include "xemu/emutools_hid.h"
25 #include "xemu/emutools_config.h"
26 #include "commodore_vic20.h"
27 #include "xemu/cpu65.h"
28 #include "xemu/via65c22.h"
29 #include "vic6561.h"
30 
31 #define SCREEN_HEIGHT (SCREEN_LAST_VISIBLE_SCANLINE - SCREEN_FIRST_VISIBLE_SCANLINE + 1)
32 #define SCREEN_WIDTH (SCREEN_LAST_VISIBLE_DOTPOS - SCREEN_FIRST_VISIBLE_DOTPOS + 1)
33 
34 
35 static const char *rom_fatal_msg = "This is one of the selected ROMs. Without it, Xemu won't work.\nInstall it, or use -romXXX CLI switches to specify another path, see the -h output for help.";
36 
37 
38 static Uint8 memory[0x10001]; // 64K (+1 for load check) address space of the 6502 CPU (some of it is ROM, undecoded, whatsoever ... it's simply the whole address space, *NOT* only RAM!)
39 static const Uint8 init_vic_palette_rgb[16 * 3] = { // VIC palette given by RGB components
40  0x00, 0x00, 0x00, // black
41  0xFF, 0xFF, 0xFF, // white
42  0xF0, 0x00, 0x00, // red
43  0x00, 0xF0, 0xF0, // cyan
44  0x60, 0x00, 0x60, // purple
45  0x00, 0xA0, 0x00, // green
46  0x00, 0x00, 0xF0, // blue
47  0xD0, 0xD0, 0x00, // yellow
48  0xC0, 0xA0, 0x00, // orange
49  0xFF, 0xA0, 0x00, // light orange
50  0xF0, 0x80, 0x80, // pink
51  0x00, 0xFF, 0xFF, // light cyan
52  0xFF, 0x00, 0xFF, // light purple
53  0x00, 0xFF, 0x00, // light green
54  0x00, 0xA0, 0xFF, // light blue
55  0xFF, 0xFF, 0x00 // light yellow
56 };
57 static Uint8 dummy_vic_access[1024]; // define 1K of "nothing" for VIC-I memory regions it cannot find memory there
58 
59 static char *emufile_p;
60 static int emufile_size;
61 static int frameskip = 0;
62 static int nmi_level = 0; // level of NMI (note: 6502 is _edge_ triggered on NMI, this is only used to check edges ...)
63 static struct Via65c22 via1, via2; // VIA-1 and VIA-2 emulation structures
64 
65 static Uint8 is_kpage_writable[64] = { // writable flag (for different memory expansions) for every kilobytes of the address space, this shows the default, unexpanded config!
66  1, // @ 0K (sum 1K), RAM, built-in (VIC-I can reach it)
67  0,0,0, // @ 1K -3K (sum 3K), place for 3K expansion [will be filled with RAM on memcfg request]
68  1,1,1,1, // @ 4K- 7K (sum 4K), RAM, built-in (VIC-I can reach it)
69  0,0,0,0,0,0,0,0,// @ 8K-15K (sum 8K), expansion block [will be filled with RAM on memcfg request]
70  0,0,0,0,0,0,0,0,// @16K-23K (sum 8K), expansion block [will be filled with RAM on memcfg request]
71  0,0,0,0,0,0,0,0,// @24K-31K (sum 8K), expansion block [will be filled with RAM on memcfg request]
72  0,0,0,0, // @32K-35K (sum 4K), character ROM (VIC-I can reach it)
73  0, // @36K (sum 1K), I/O block 0 (VIAs, VIC-I, ...)
74  1, // @37K (sum 1K), colour RAM (VIC-I can reach it directly), only 0.5K, but the position depends on the config ... [handled as a special case on READ - 4 bit wide only!]
75  0, // @38K (sum 1K), I/O block 2 (not used now, gives 0xFF on read)
76  0, // @39K (sum 1K), I/O block 3 (not used now, gives 0xFF on read)
77  0,0,0,0,0,0,0,0,// @40K-47K (sum 8K), expansion block (not available for BASIC even if it's RAM) [will be filled with RAM on memcfg request]
78  0,0,0,0,0,0,0,0,// @48K-55K (sum 8K), basic ROM
79  0,0,0,0,0,0,0,0 // @56K-63K (sum 8K), kernal ROM
80 };
81 static Uint8 *vic_address_space_hi4[16] = { // configure high 4 bits of VIC-I databus for 1K sized SRAM at $9400 on VIC-20
82  memory + 0x9400, memory + 0x9400, memory + 0x9400, memory + 0x9400,
83  memory + 0x9400, memory + 0x9400, memory + 0x9400, memory + 0x9400,
84  memory + 0x9400, memory + 0x9400, memory + 0x9400, memory + 0x9400,
85  memory + 0x9400, memory + 0x9400, memory + 0x9400, memory + 0x9400
86 };
87 static Uint8 *vic_address_space_lo8[16] = { // configure low 8 bits of VIC-I databus access of the VIC-20 memory
88  memory + 0x8000, memory + 0x8400, memory + 0x8800, memory + 0x8C00, // corresponds to the character ROM, access is OK
89  dummy_vic_access, dummy_vic_access, dummy_vic_access, dummy_vic_access, // I/O and colour RAM cannot be accessed (colour RAM is only on high4 ...)
90  memory, dummy_vic_access, dummy_vic_access, dummy_vic_access, // first 1K is OK, the others are not (not even with the +3K expansion)
91  memory + 0x1000, memory + 0x1400, memory + 0x1800, memory + 0x1C00 // 4K of internal RAM, OK
92 };
93 
94 static struct {
96  char *prg;
97  char *ramexp;
98  char *romchr;
99  char *rombasic;
100  char *romkernal;
101  char *romemu;
102  int syscon;
104 } configdb;
105 
106 #define VIRTUAL_SHIFT_POS 0x31
107 
108 static const struct KeyMappingDefault vic20_key_map[] = {
109  { SDL_SCANCODE_1, 0x00 }, // 1
110  { SDL_SCANCODE_3, 0x01 }, // 3
111  { SDL_SCANCODE_5, 0x02 }, // 5
112  { SDL_SCANCODE_7, 0x03 }, // 7
113  { SDL_SCANCODE_9, 0x04 }, // 9
114  //{ SDL_SCANCODE_+ 0x05 }, // PLUS
115  //{ SDL_SCANCODE_font 0x06 }, // FONT
116  { SDL_SCANCODE_BACKSPACE, 0x07 }, // DEL
117  //{ SDL_SCANCODE_// UNKNOWN KEY? 0x10 //
118  { SDL_SCANCODE_W, 0x11 }, // W
119  { SDL_SCANCODE_R, 0x12 }, // R
120  { SDL_SCANCODE_Y, 0x13 }, // Y
121  { SDL_SCANCODE_I, 0x14 }, // I
122  { SDL_SCANCODE_P, 0x15 }, // P
123  //{ SDL_SCANCODE_STAR // *
124  { SDL_SCANCODE_RETURN, 0x17 }, // RETURN
125  { SDL_SCANCODE_LCTRL, 0x20 }, // CTRL, only the left ctrl is mapped as vic-20 ctrl ...
126  { SDL_SCANCODE_A, 0x21 }, // A
127  { SDL_SCANCODE_D, 0x22 }, // D
128  { SDL_SCANCODE_G, 0x23 }, // G
129  { SDL_SCANCODE_J, 0x24 }, // J
130  { SDL_SCANCODE_L, 0x25 }, // L
131  { SDL_SCANCODE_SEMICOLON, 0x26 }, // ;
132  { SDL_SCANCODE_RIGHT, 0x27 }, { SDL_SCANCODE_LEFT, 0x27 | 8 }, // CURSOR RIGHT, _SHIFTED_: CURSOR LEFT!
133  { SDL_SCANCODE_END, 0x30 }, // RUN/STOP !! we use PC key 'END' for this!
134  { SDL_SCANCODE_LSHIFT, 0x31 }, // LEFT SHIFT
135  { SDL_SCANCODE_X, 0x32 }, // X
136  { SDL_SCANCODE_V, 0x33 }, // V
137  { SDL_SCANCODE_N, 0x34 }, // N
138  { SDL_SCANCODE_COMMA, 0x35 }, // ,
139  { SDL_SCANCODE_SLASH, 0x36 }, // /
140  { SDL_SCANCODE_DOWN, 0x37 }, { SDL_SCANCODE_UP, 0x37 | 8 }, // CURSOR DOWN, _SHIFTED_: CURSOR UP!
141  { SDL_SCANCODE_SPACE, 0x40 }, // SPACE
142  { SDL_SCANCODE_Z, 0x41 }, // Z
143  { SDL_SCANCODE_C, 0x42 }, // C
144  { SDL_SCANCODE_B, 0x43 }, // B
145  { SDL_SCANCODE_M, 0x44 }, // M
146  { SDL_SCANCODE_PERIOD, 0x45 }, // .
147  { SDL_SCANCODE_RSHIFT, 0x46 }, // RIGHT SHIFT
148  { SDL_SCANCODE_F1, 0x47 }, { SDL_SCANCODE_F2, 0x47 | 8 }, // F1, _SHIFTED_: F2!
149  { SDL_SCANCODE_LALT, 0x50 }, { SDL_SCANCODE_RALT, 0x50 }, // COMMODORE (may fav key!), PC sucks, no C= key :) - we map left and right ALT here ...
150  { SDL_SCANCODE_S, 0x51 }, // S
151  { SDL_SCANCODE_F, 0x52 }, // F
152  { SDL_SCANCODE_H, 0x53 }, // H
153  { SDL_SCANCODE_K, 0x54 }, // K
154  { SDL_SCANCODE_APOSTROPHE, 0x55 }, // : we map apostrophe here
155  { SDL_SCANCODE_EQUALS, 0x56 }, // =
156  { SDL_SCANCODE_F3, 0x57 }, { SDL_SCANCODE_F4, 0x57 | 8 }, // F3, _SHIFTED_: F4!
157  { SDL_SCANCODE_Q, 0x60 }, // Q
158  { SDL_SCANCODE_E, 0x61 }, // E
159  { SDL_SCANCODE_T, 0x62 }, // T
160  { SDL_SCANCODE_U, 0x63 }, // U
161  { SDL_SCANCODE_O, 0x64 }, // O
162  //{ SDL_SCANCODE_// @
163  // UNKNOWN KEY?!?! 0x66
164  { SDL_SCANCODE_F5, 0x67 }, { SDL_SCANCODE_F6, 0x67 | 8 }, // F5, _SHIFTED_: F6!
165  { SDL_SCANCODE_2, 0x70 }, // 2
166  { SDL_SCANCODE_4, 0x71 }, // 4
167  { SDL_SCANCODE_6, 0x72 }, // 6
168  { SDL_SCANCODE_8, 0x73 }, // 8
169  { SDL_SCANCODE_0, 0x74 }, // 0
170  { SDL_SCANCODE_MINUS, 0x75 }, // -
171  { SDL_SCANCODE_HOME, 0x76 }, // HOME
172  { SDL_SCANCODE_F7, 0x77 }, { SDL_SCANCODE_F8, 0x77 | 8 }, // F7, _SHIFTED_: F8!
173  // -- the following definitions are not VIC-20 keys, but emulator related stuffs
175  //{ SDL_SCANCODE_ESCAPE, 0x81 }, // RESTORE key
176  // **** this must be the last line: end of mapping table ****
177  { 0, -1 }
178 };
179 
180 
181 
182 
183 static inline void __mark_ram ( int start_k, int size_k )
184 {
185  printf("MEM: adding RAM $%04X-%04X" NL, start_k << 10, ((start_k + size_k) << 10) - 1);
186  while (size_k--)
187  is_kpage_writable[start_k++] = 1;
188 }
189 
190 
191 static char *vic20_get_memconfig_string ( void )
192 {
193  static char result[40];
194  sprintf(result, "%c1 %c8 %c16 %c24 %c40",
195  is_kpage_writable[1] ? '+' : '-',
196  is_kpage_writable[8] ? '+' : '-',
197  is_kpage_writable[16] ? '+' : '-',
198  is_kpage_writable[24] ? '+' : '-',
199  is_kpage_writable[40] ? '+' : '-'
200  );
201  return result;
202 }
203 
204 
205 static void emuprint ( const char *str )
206 {
207  snprintf(
208  (char*)memory + (memory[0xA017] | (memory[0xA018] << 8)),
209  memory[0xA019] | (memory[0xA01A] << 8),
210  "%s",
211  str
212  );
213 }
214 
215 
216 #define EMUPRINTF(...) do { \
217  char __buffer_for_conv__[8192]; \
218  snprintf(__buffer_for_conv__, sizeof __buffer_for_conv__, __VA_ARGS__); \
219  emuprint(__buffer_for_conv__); \
220 } while(0)
221 
222 
223 
224 static void execute_monitor_command ( void )
225 {
226  char *p;
227  memory[600] = 0; // close the input string, just in case (but it should have been anyway)
228  p = (char*)memory + 512;
229  while (*p <= 32)
230  p++;
231  if (p[0] == 'X') {
232  cpu65.a = 0; // do not continue ...
233  emuprint("\r");
234  return;
235  }
236  // unknown command
237  emuprint("?\r");
238 }
239 
240 
241 
242 static Uint8 inject_prg ( void )
243 {
244  int addr;
245  if (!emufile_p)
246  return configdb.bootmon; // No loaded program, use the pre-defined policy instead
247  addr = emufile_p[0] | (emufile_p[1] << 8);
248  printf("LOAD: injecting program into the memory from $%04X" NL, addr);
249  memcpy(memory + addr, emufile_p + 2, emufile_size - 2);
250  memory[addr - 1] = 0;
251  memory[0x2b] = addr & 0xFF;
252  memory[0x2c] = addr >> 8;
253  addr += emufile_size - 2;
254  memory[0x2d] = addr & 0xFF;
255  memory[0x2e] = addr >> 8;
256  return 128;
257 }
258 
259 
260 
261 static int is_our_rom ( void )
262 {
263  if (memcmp(memory + 0xA00C, "LGBXVIC20", 9))
264  return -1;
265  return memory[0xA015] | (memory[0xA016] << 8);
266 }
267 
268 
269 
270 // Need to be defined, if CPU65_TRAP_OPCODE is defined for the CPU emulator!
271 int cpu65_trap_callback ( const Uint8 opcode )
272 {
273  if (cpu65.pc >= 0xA000 && opcode == CPU65_TRAP_OPCODE) { // cpu65.pc always meant to be the position _after_ the trap opcode!
274  Uint8 trap = memory[cpu65.pc];
275  if (is_our_rom() < 0)
276  FATAL("Unknown ROM/RAM code at $%04X caused trap!", cpu65.pc - 1);
277  switch (trap) {
278  case 0:
279  cpu65.a = inject_prg();
280  EMUPRINTF("\rMONITOR: SYS %d\r", 0xA009);
281  break;
282  case 1:
283  EMUPRINTF("** XVIC20 MONITOR/LGB\rBM=$%04X-%04X S=$%04X\rEXP %s\r",
284  memory[0x281] | (memory[0x282] << 8),
285  memory[0x283] | (memory[0x284] << 8),
286  memory[648] << 8,
287  vic20_get_memconfig_string()
288  );
289  cpu65.a = 1;
290  break;
291  case 2:
292  cpu65.a = 1; // by default, set the continue flag ...
293  execute_monitor_command();
294  break;
295  default:
296  FATAL("Unknown CPU trap (%d) at $%04X", trap, cpu65.pc - 1);
297  }
298  cpu65.pc++; // jump over the trap number byte ...
299  return 1; // you must return with the CPU cycles used, but at least with value of 1!
300  } else
301  return 0; // ignore trap!! Return with zero means, the CPU emulator should execute the opcode anyway
302 }
303 
304 
306 {
307  ERROR_WINDOW("Unemulated NMOS 6502 opcode $%02X at PC=$%04X", cpu65.op, cpu65.pc - 1);
308  cpu65_reset();
309 }
310 
311 
312 
313 void clear_emu_events ( void )
314 {
315  hid_reset_events(1);
316 }
317 
318 
319 
320 // Called by CPU emulation code when any kind of memory byte must be written.
321 // Note: optimization is used, to make the *most common* type of write access easy. Even if the whole function is more complex, or longer/slower this way for other accesses!
323 {
324  // Write optimization, handle the most common case first: memory byte to be written is not special, ie writable RAM, not I/O, etc
325  if (XEMU_LIKELY(is_kpage_writable[addr >> 10])) { // writable flag for every Kbytes of 64K is checked (for different memory configurations, faster "decoding", etc)
326  memory[addr] = data;
327  return;
328  }
329  // ELSE: other kind of address space is tried to be written ...
330  // TODO check if I/O devices are fully decoded or there can be multiple mirror ranges
331  if ((addr & 0xFF00) == 0x9000) { // VIC-I register is written (decoded for a full 256 bytes long area)
332  cpu_vic_reg_write(addr & 0xF, data);
333  return;
334  }
335  if ((addr & 0xFFF0) == 0x9110) { // VIA-1 register is written
336  via_write(&via1, addr & 0xF, data);
337  return;
338  }
339  if ((addr & 0xFFF0) == 0x9120) { // VIA-2 register is written
340  via_write(&via2, addr & 0xF, data);
341  return;
342  }
343  // if other memory areas tried to be written (hit this point), write will be simply ignored
344 }
345 
346 
347 
348 // TODO: Use RMW write function in a proper way!
349 void cpu65_write_rmw_callback ( Uint16 addr, Uint8 old_data, Uint8 new_data )
350 {
351  if (XEMU_UNLIKELY(addr & 0x8000)) {
352  cpu65_write_callback(addr, old_data);
353  cpu65_write_callback(addr, new_data);
354  } else {
355  cpu65_write_callback(addr, new_data);
356  }
357 }
358 
359 
360 // Called by CPU emulation code when any kind of memory byte must be read.
361 // Note: optimization is used, to make the *most common* type of read access easy. Even if the whole function is more complex, or longer/slower this way for other accesses!
363 {
364  // Optimization: handle the most common case first!
365  // Check if our read is NOT about the (built-in) I/O area. If it's true, let's just use the memory array
366  // (even for undecoded areas, memory[] is intiailized with 0xFF values
367  if (XEMU_LIKELY((addr & 0xF800) != 0x9000))
368  return memory[addr];
369  // ELSE: it IS the I/O area or colour SRAM ... Let's see what we want!
370  // TODO check if I/O devices are fully decoded or there can be multiple mirror ranges
371  if ((addr & 0xFFF0) == 0x9000) // VIC-I register is read
372  return cpu_vic_reg_read(addr & 0xF);
373  if ((addr & 0xFFF0) == 0x9110) // VIA-1 register is read
374  return via_read(&via1, addr & 0xF);
375  if ((addr & 0xFFF0) == 0x9120) // VIA-2 register is read
376  return via_read(&via2, addr & 0xF);
377  if ((addr & 0xFC00) == 0x9400)
378  return memory[addr] | 0xF0; // colour SRAM is read, always return '1' for upper bits (only 4 bits "wide" memory chip!)
379  // if non of the above worked, let's just read our emulated address space
380  // which means some kind of ROM, or undecoded area (64K space is intialized with 0xFF bytes ...)
381  return memory[addr];
382 }
383 
384 
385 // HID needs this to be defined, it's up to the emulator if it uses or not ...
386 int emu_callback_key ( int pos, SDL_Scancode key, int pressed, int handled )
387 {
388  return 0;
389 }
390 
391 
392 static void update_emulator ( void )
393 {
394  if (!frameskip) {
395  // First: update VIC-20 screen ...
397  // Second: we must handle SDL events waiting for us in the event queue ...
399  // Third: Sleep ... Please read emutools.c source about this madness ... 40000 is (PAL) microseconds for a full frame to be produced
401  }
402  vic_vsync(!frameskip); // prepare for the next frame!
403 }
404 
405 
406 /* VIA emulation callbacks, called by VIA core. See main() near to via_init() calls for further information */
407 
408 
409 // VIA-1 generates NMI on VIC-20?
410 static void via1_setint ( int level )
411 {
412  if (nmi_level != level) {
413  printf("VIA-1: NMI edge: %d->%d" NL, nmi_level, level);
414  cpu65.nmiEdge = 1;
415  nmi_level = level;
416  }
417 }
418 
419 
420 // VIA-2 is used to generate IRQ on VIC-20
421 static void via2_setint ( int level )
422 {
423  cpu65.irqLevel = level;
424 }
425 
426 
427 
428 
429 static Uint8 via2_kbd_get_scan ( Uint8 mask )
430 {
431  return
432  ((via2.ORB & 1) ? 0xFF : kbd_matrix[0]) &
433  ((via2.ORB & 2) ? 0xFF : kbd_matrix[1]) &
434  ((via2.ORB & 4) ? 0xFF : kbd_matrix[2]) &
435  ((via2.ORB & 8) ? 0xFF : kbd_matrix[3]) &
436  ((via2.ORB & 16) ? 0xFF : kbd_matrix[4]) &
437  ((via2.ORB & 32) ? 0xFF : kbd_matrix[5]) &
438  ((via2.ORB & 64) ? 0xFF : kbd_matrix[6]) &
439  ((via2.ORB & 128) ? 0xFF : kbd_matrix[7])
440  ;
441 }
442 
443 
444 static Uint8 via1_ina ( Uint8 mask )
445 {
446  // joystick state (RIGHT direction is not handled here though)
447  return
448  hid_read_joystick_left (0, 1 << 4) |
449  hid_read_joystick_up (0, 1 << 2) |
450  hid_read_joystick_down (0, 1 << 3) |
451  hid_read_joystick_button(0, 1 << 5)
452  ;
453 }
454 
455 
456 static Uint8 via2_inb ( Uint8 mask )
457 {
458  // Port-B in VIA2 is used (temporary with DDR-B set to input) to scan joystick direction 'RIGHT'
459  return hid_read_joystick_right(0x7F, 0xFF);
460 }
461 
462 
463 
464 static int cycles;
465 
466 
467 static void emulation_loop ( void )
468 {
469  for (;;) { // our emulation loop ...
470  int opcyc;
471  opcyc = cpu65_step(); // execute one opcode (or accept IRQ, etc), return value is the used clock cycles
472  via_tick(&via1, opcyc); // run VIA-1 tasks for the same amount of cycles as the CPU
473  via_tick(&via2, opcyc); // -- "" -- the same for VIA-2
474  cycles += opcyc;
475  if (cycles >= CYCLES_PER_SCANLINE) { // if [at least!] 71 (on PAL) CPU cycles passed then render a VIC-I scanline, and maintain scanline value + texture/SDL update (at the end of a frame)
476  // render one (scan)line. Note: this is INACCURATE, we should do rendering per dot clock/cycle or something,
477  // but for a simple emulator like this, it's already acceptable solultion, I think!
478  // Note about frameskip: we render only every second (half) frame, no interlace (PAL VIC), not so correct, but we also save some resources this way
479  if (!frameskip)
480  vic_render_line();
481  if (scanline == LAST_SCANLINE) {
482  update_emulator();
483  frameskip = !frameskip;
484  return;
485  } else
486  scanline++;
487  cycles -= CYCLES_PER_SCANLINE;
488  }
489  }
490 }
491 
492 
493 int main ( int argc, char **argv )
494 {
495  xemu_pre_init(APP_ORG, TARGET_NAME, "The Inaccurate Commodore VIC-20 emulator from LGB");
497  { "bootmon", "Boot into monitor", &configdb.bootmon },
498  { "fullscreen", "Start in fullscreen mode", &configdb.fullscreen },
499  { "syscon", "Keep system console open (Windows-specific effect only)", &configdb.syscon }
500  );
502  { "prg", NULL, "Load a PRG file", &configdb.prg },
503  { "ramexp", NULL, "Comma separated list of installed RAM expansions at Kbyte(s)", &configdb.ramexp },
504  { "romchr", CHR_ROM_NAME, "Sets character ROM to use", &configdb.romchr },
505  { "rombasic", BASIC_ROM_NAME, "Sets BASIC ROM to use", &configdb.rombasic },
506  { "romkernal", KERNAL_ROM_NAME, "Sets KERNAL ROM to use", &configdb.romkernal },
507  { "romemu", EMU_ROM_NAME, "Sets EMU ROM to use", &configdb.romemu }
508  );
509  xemucfg_define_num_option("sdlrenderquality", RENDER_SCALE_QUALITY, "Setting SDL hint for scaling method/quality on rendering (0, 1, 2)", &configdb.sdlrenderquality, 0, 2);
510  if (xemucfg_parse_all(argc, argv))
511  return 1;
512  emufile_p = NULL;
513  emufile_size = 0;
514  printf(
515  "INFO: CPU clock frequency (calculated) %d Hz (wanted: %d Hz)" NL
516  "INFO: Texture resolution is %dx%d" NL
517  "INFO: Defined visible area is (%d,%d)-(%d,%d)" NL NL,
518  (int)((LAST_SCANLINE + 1) * CYCLES_PER_SCANLINE * (1000000.0 / (double)FULL_FRAME_USECS) * 2),
523  );
524  /* Initiailize SDL - note, it must be before loading ROMs, as it depends on path info from SDL! */
525  if (xemu_post_init(
526  TARGET_DESC APP_DESC_APPEND, // window title
527  1, // resizable window
528  SCREEN_WIDTH, SCREEN_HEIGHT, // texture sizes
529  SCREEN_WIDTH * 2, SCREEN_HEIGHT, // logical size (width is doubled for somewhat correct aspect ratio)
530  SCREEN_WIDTH * 2 * 2, SCREEN_HEIGHT * 2, // window size (doubled size, original would be too small)
531  SCREEN_FORMAT, // pixel format
532  16, // we have 16 colours
533  init_vic_palette_rgb, // initialize palette from this constant array
534  vic_palette, // initialize palette into this stuff
535  configdb.sdlrenderquality,// render scaling quality
536  USE_LOCKED_TEXTURE, // 1 = locked texture access
537  NULL // no emulator specific shutdown function
538  ))
539  return 1;
540  hid_init(
541  vic20_key_map,
543  SDL_ENABLE // enable HID joy events
544  );
545  // Program to load?
546  if (configdb.prg) {
547  emufile_size = xemu_load_file(configdb.prg, NULL, 3, 0x8000, "Cannot load user specified PRG with -prg");
548  if (emufile_size < 0) {
549  emufile_p = NULL;
550  emufile_size = 0;
551  } else
552  emufile_p = xemu_load_buffer_p;
553  }
554  // RAM expansion
555  if (configdb.ramexp && configdb.ramexp[0]) { // also allow empty string, which is discarded
556  int explist[5], r = xemucfg_integer_list_from_string(configdb.ramexp, explist, 5, ",");
557  if (r < 0)
558  FATAL("Invalid memory expansion list (not comma separated list, more than 5 elements, etc) syntax given with -ramexp");
559  else
560  while (r--)
561  switch (explist[r]) {
562  case 1:
563  __mark_ram( 1, 3);
564  break;
565  case 8:
566  __mark_ram( 8, 8);
567  break;
568  case 16:
569  __mark_ram(16, 8);
570  break;
571  case 24:
572  __mark_ram(24, 8);
573  break;
574  case 40:
575  __mark_ram(40, 8);
576  INFO_WINDOW("Warning, RAM from 40K (at $A000) is defined.\nThis may collide with the loaded EMU ROM there!");
577  break;
578  default:
579  FATAL("Unknown memory expansion element %d in -ramexp %s", explist[r], configdb.ramexp);
580  break;
581  }
582  }
583  /* Intialize memory and load ROMs */
584  memset(memory, 0xFF, sizeof memory);
585  memset(dummy_vic_access, 0xFF, sizeof dummy_vic_access); // define 1K of "nothing" for VIC-I memory regions what it can't access by hardware constraints
586  if (
587  xemu_load_file(configdb.romchr, memory + 0x8000, 0x1000, 0x1000, rom_fatal_msg) < 0 || // load chargen ROM
588  xemu_load_file(configdb.rombasic, memory + 0xC000, 0x2000, 0x2000, rom_fatal_msg) < 0 || // load basic ROM
589  xemu_load_file(configdb.romkernal, memory + 0xE000, 0x2000, 0x2000, rom_fatal_msg) < 0 || // load kernal ROM
590  xemu_load_file(configdb.romemu, memory + 0xA000, 0x2000, 0x2000, rom_fatal_msg) < 0 // load our "emulator monitor" ROM
591  )
592  return 1;
593  // Check our "emulator monitor" ROM ...
594  if (is_our_rom() < -1) {
595  ERROR_WINDOW("Unknown emulator ROM: %s", configdb.romemu);
596  return 1;
597  }
598  if (is_our_rom() != EMU_ROM_VERSION) {
599  ERROR_WINDOW("Bad emulator ROM %s version, we need v%d\nPlease upgrade the ROM image!", configdb.romemu, EMU_ROM_VERSION);
600  return 1;
601  }
602  // Continue with initializing ...
603  clear_emu_events(); // also resets the keyboard
604  cpu65_reset(); // reset CPU: it must be AFTER kernal is loaded at least, as reset also fetches the reset vector into PC ...
605  // Initiailize VIAs.
606  // Note: this is my unfinished VIA emulation skeleton, for my Commodore LCD emulator originally, ported from my JavaScript code :)
607  // it uses callback functions, which must be registered here, NULL values means unused functionality
608  via_init(&via1, "VIA-1", // from $9110 on VIC-20
609  NULL, // outa
610  NULL, // outb
611  NULL, // outsr
612  via1_ina, // ina
613  NULL, // inb
614  NULL, // insr
615  via1_setint // setint, called by via core, if interrupt level changed for whatever reason (ie: expired timer ...). It is wired to NMI on VIC20.
616  );
617  via_init(&via2, "VIA-2", // from $9120 on VIC-20
618  NULL, // outa [reg 1]
619  NULL, //via2_kbd_set_scan, // outb [reg 0], we wire port B as output to set keyboard scan, HOWEVER, we use ORB directly in get scan!
620  NULL, // outsr
621  via2_kbd_get_scan, // ina [reg 1], we wire port A as input to get the scan result, which was selected with port-A
622  via2_inb, // inb [reg 0], used with DDR set to input for joystick direction 'right' in VIC20
623  NULL, // insr
624  via2_setint // setint, same for VIA2 as with VIA1, but this is wired to IRQ on VIC20.
625  );
626  vic_init(vic_address_space_lo8, vic_address_space_hi4);
627  cycles = 0;
629  if (!configdb.syscon)
630  sysconsole_close(NULL);
631  xemu_timekeeping_start(); // we must call this once, right before the start of the emulation
632  XEMU_MAIN_LOOP(emulation_loop, 25, 1);
633  return 0;
634 }
xemu_pre_init
void xemu_pre_init(const char *app_organization, const char *app_name, const char *slogan)
Definition: emutools.c:651
cpu_vic_reg_write
void cpu_vic_reg_write(int addr, Uint8 data)
Definition: vic6561.c:107
USE_LOCKED_TEXTURE
#define USE_LOCKED_TEXTURE
Definition: commodore_65.h:26
via_tick
void via_tick(struct Via65c22 *via, int ticks)
Definition: via65c22.c:224
TARGET_DESC
#define TARGET_DESC
Definition: xemu-target.h:2
XEMUCFG_DEFINE_SWITCH_OPTIONS
#define XEMUCFG_DEFINE_SWITCH_OPTIONS(...)
Definition: emutools_config.h:123
cpu65_trap_callback
int cpu65_trap_callback(const Uint8 opcode)
Definition: commodore_vic20.c:271
Via65c22::ORB
Uint8 ORB
Definition: via65c22.h:30
vic_init
void vic_init(Uint8 **lo8_pointers, Uint8 **hi4_pointers)
Definition: vic6561.c:168
BASIC_ROM_NAME
#define BASIC_ROM_NAME
Definition: commodore_vic20.h:36
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
configdb_st::prg
char * prg
Definition: configdb.h:33
XEMU_MAIN_LOOP
#define XEMU_MAIN_LOOP(func, p1, p2)
Definition: emutools.h:58
REAL_CPU_SPEED
#define REAL_CPU_SPEED
Definition: commodore_vic20.h:27
CYCLES_PER_SCANLINE
#define CYCLES_PER_SCANLINE
Definition: vic6561.h:35
vic6561.h
xemucfg_integer_list_from_string
int xemucfg_integer_list_from_string(const char *value, int *result, int maxitems, const char *delims)
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
via65c22.h
vic_render_line
void vic_render_line(void)
Definition: vic6561.c:200
INFO_WINDOW
#define INFO_WINDOW(...)
Definition: xep128.h:114
vic_vsync
void vic_vsync(int relock_texture)
Definition: vic6561.c:155
KERNAL_ROM_NAME
#define KERNAL_ROM_NAME
Definition: commodore_vic20.h:35
addr
int addr
Definition: dma65.c:81
clear_emu_events
void clear_emu_events(void)
Definition: commodore_vic20.c:313
bootmon
int bootmon
Definition: commodore_vic20.c:95
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)
SCREEN_LAST_VISIBLE_DOTPOS
#define SCREEN_LAST_VISIBLE_DOTPOS
Definition: vic6561.h:25
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
main
int main(int argc, char **argv)
Definition: commodore_vic20.c:493
hid_init
void hid_init(const struct KeyMappingDefault *key_map_in, Uint8 virtual_shift_pos_in, int joy_enable)
Definition: emutools_hid.c:300
Via65c22
Definition: via65c22.h:21
SCREEN_WIDTH
#define SCREEN_WIDTH
Definition: commodore_vic20.c:32
hid_reset_events
void hid_reset_events(int burn)
Definition: emutools_hid.c:170
EMU_ROM_VERSION
#define EMU_ROM_VERSION
Definition: commodore_vic20.h:33
Uint8
uint8_t Uint8
Definition: fat32.c:51
xemu_set_full_screen
void xemu_set_full_screen(int setting)
Definition: emutools.c:311
configdb_st::sdlrenderquality
int sdlrenderquality
Definition: configdb.h:35
fullscreen
int fullscreen
Definition: commodore_vic20.c:95
TARGET_NAME
#define TARGET_NAME
Definition: xemu-target.h:1
emutools_files.h
SCREEN_FIRST_VISIBLE_SCANLINE
#define SCREEN_FIRST_VISIBLE_SCANLINE
Definition: vic6561.h:22
APP_ORG
#define APP_ORG
Definition: emutools.h:50
STD_XEMU_SPECIAL_KEYS
#define STD_XEMU_SPECIAL_KEYS
Definition: emutools_hid.h:108
via_init
void via_init(struct Via65c22 *via, const char *name, void(*outa)(Uint8 mask, Uint8 data), void(*outb)(Uint8 mask, Uint8 data), void(*outsr)(Uint8 data), Uint8(*ina)(Uint8 mask), Uint8(*inb)(Uint8 mask), Uint8(*insr)(void), void(*setint)(int level))
Definition: via65c22.c:77
SCREEN_FIRST_VISIBLE_DOTPOS
#define SCREEN_FIRST_VISIBLE_DOTPOS
Definition: vic6561.h:24
vic_palette
Uint32 vic_palette[16]
Definition: vic6561.c:51
prg
char * prg
Definition: commodore_vic20.c:96
SCREEN_HEIGHT
#define SCREEN_HEIGHT
Definition: commodore_vic20.c:31
SCREEN_LAST_VISIBLE_SCANLINE
#define SCREEN_LAST_VISIBLE_SCANLINE
Definition: vic6561.h:23
cpu65_write_rmw_callback
void cpu65_write_rmw_callback(Uint16 addr, Uint8 old_data, Uint8 new_data)
Definition: commodore_vic20.c:349
VIRTUAL_SHIFT_POS
#define VIRTUAL_SHIFT_POS
Definition: commodore_vic20.c:106
ERROR_WINDOW
#define ERROR_WINDOW(...)
Definition: xep128.h:116
XEMU_LIKELY
#define XEMU_LIKELY(__x__)
Definition: emutools_basicdefs.h:124
NL
#define NL
Definition: fat32.c:37
emutools_config.h
configdb
struct configdb_st configdb
Definition: configdb.c:34
compress_sd_image.r
def r
Definition: compress_sd_image.py:76
romchr
char * romchr
Definition: commodore_vic20.c:98
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
memory
Uint8 memory[0x100000]
Definition: commodore_65.c:43
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
cpu65_read_callback
Uint8 cpu65_read_callback(Uint16 addr)
Definition: commodore_vic20.c:362
rombasic
char * rombasic
Definition: commodore_vic20.c:99
cpu65.h
xemu_load_buffer_p
void * xemu_load_buffer_p
Definition: emutools_files.c:34
xemu_timekeeping_start
void xemu_timekeeping_start(void)
Definition: emutools.c:1122
via_read
Uint8 via_read(struct Via65c22 *via, int addr)
Definition: via65c22.c:177
FULL_FRAME_USECS
#define FULL_FRAME_USECS
Definition: commodore_vic20.h:25
configdb_st::syscon
int syscon
Definition: configdb.h:34
kbd_matrix
Uint8 kbd_matrix[16]
Definition: dave.c:30
cpu65_reset
void cpu65_reset(void)
Definition: cpu65.c:353
LAST_SCANLINE
#define LAST_SCANLINE
Definition: vic6561.h:34
configdb_st::fullscreen
int fullscreen
Definition: configdb.h:34
romkernal
char * romkernal
Definition: commodore_vic20.c:100
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
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
cpu65_write_callback
void cpu65_write_callback(Uint16 addr, Uint8 data)
Definition: commodore_vic20.c:322
CPU65_TRAP_OPCODE
#define CPU65_TRAP_OPCODE
Definition: xemu-target.h:3
mask
int mask
Definition: dma65.c:83
APP_DESC_APPEND
#define APP_DESC_APPEND
Definition: emutools.h:52
Uint16
uint16_t Uint16
Definition: fat32.c:50
ramexp
char * ramexp
Definition: commodore_vic20.c:97
emu_callback_key
int emu_callback_key(int pos, SDL_Scancode key, int pressed, int handled)
Definition: commodore_vic20.c:386
emutools_hid.h
via_write
void via_write(struct Via65c22 *via, int addr, Uint8 data)
Definition: via65c22.c:99
EMUPRINTF
#define EMUPRINTF(...)
Definition: commodore_vic20.c:216
romemu
char * romemu
Definition: commodore_vic20.c:101
sdlrenderquality
int sdlrenderquality
Definition: commodore_vic20.c:103
cpu65_step
int cpu65_step(void)
Definition: cpu65.c:796
FATAL
#define FATAL(...)
Definition: xep128.h:117
cpu_vic_reg_read
Uint8 cpu_vic_reg_read(int addr)
Definition: vic6561.c:94
KeyMappingDefault::pos
int pos
Definition: emutools_hid.h:26
commodore_vic20.h
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
CHR_ROM_NAME
#define CHR_ROM_NAME
Definition: commodore_vic20.h:34
frameskip
int frameskip
Definition: vic3.c:75
EMU_ROM_NAME
#define EMU_ROM_NAME
Definition: commodore_vic20.h:32
cpu65_illegal_opcode_callback
void cpu65_illegal_opcode_callback(void)
Definition: commodore_vic20.c:305
syscon
int syscon
Definition: commodore_vic20.c:102