Xemu [doxygen]  hyppo 0a42be3a057156924bc1b626a687bd6e27349c45 @ Sat 19 Mar 02:15:11 CET 2022
commodore_geos.c
Go to the documentation of this file.
1 /* Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu
2  Copyright (C)2016-2021 LGB (Gábor Lénárt) <lgblgblgb@gmail.com>
3 
4  This is an odd emulator, emulating a Commodore 64 like machine only for the
5  level needed for a special version of GEOS to be able to run on it.
6  You should have a really special one with own disk drive etc, since there
7  is no hardware support for drive emulation etc, but it's built in the emulator
8  with a kind of CPU trap! The purpose: know GEOS better and slowly replace
9  more and more functions on C/emulator level, so at one point it's possible
10  to write a very own version of GEOS without *any* previously used code in
11  the real GEOS. Then it can be even ported to other architectures GEOS wasn't
12  mean to run ever.
13  ---------------------------------------------------------------------------------
14  One interesting plan: write a GEOS emulator which does not use VIC-II bitmapped
15  screen anymore, but the GEOS functions mean to be targeted a "modern UI toolkit",
16  ie GTK, so a dozens years old (unmodified) GEOS app would be able to run on a PC
17  with modern look and feel, ie anti-aliased fonts, whatever ...
18  ---------------------------------------------------------------------------------
19 
20 This program is free software; you can redistribute it and/or modify
21 it under the terms of the GNU General Public License as published by
22 the Free Software Foundation; either version 2 of the License, or
23 (at your option) any later version.
24 
25 This program is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 GNU General Public License for more details.
29 
30 You should have received a copy of the GNU General Public License
31 along with this program; if not, write to the Free Software
32 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
33 
34 #include "xemu/emutools.h"
35 #include "xemu/emutools_files.h"
36 #include "commodore_geos.h"
37 #include "xemu/cpu65.h"
38 #include "xemu/cia6526.h"
39 #include "xemu/emutools_hid.h"
40 #include "xemu/emutools_config.h"
41 #include "xemu/c64_kbd_mapping.h"
42 #include "geos.h"
43 
44 
45 #define DISK_IMAGE_SIZE 819200
46 //static Uint8 disk_image[DISK_IMAGE_SIZE + 1];
47 
48 /*
49  -- Port pin (bit) $A000 to $BFFF $D000 to $DFFF $E000 to $FFFF
50  -- 2 1 0 Read Write Read Write Read Write
51  -- -------------- ---------------- ---------------- ----------------
52 0 -- 0 0 0 RAM RAM RAM RAM RAM RAM
53 1 -- 0 0 1 RAM RAM CHAR-ROM RAM RAM RAM
54 2 -- 0 1 0 RAM RAM CHAR-ROM RAM KERNAL-ROM RAM
55 3 -- 0 1 1 BASIC-ROM RAM CHAR-ROM RAM KERNAL-ROM RAM
56 4 -- 1 0 0 RAM RAM RAM RAM RAM RAM
57 5 -- 1 0 1 RAM RAM I/O I/O RAM RAM
58 6 -- 1 1 0 RAM RAM I/O I/O KERNAL-ROM RAM
59 7 -- 1 1 1 BASIC-ROM RAM I/O I/O KERNAL-ROM RAM
60 */
61 
62 #define CPU_PORT_DEFAULT_VALUE0 0xFF
63 #define CPU_PORT_DEFAULT_VALUE1 0xFF
64 
65 #define BASIC_ROM_OFFSET 0x10000
66 #define KERNAL_ROM_OFFSET 0x12000
67 #define CHAR_ROM_OFFSET 0x14000
68 #define IO_OFFSET 0x15000
69 
71 #define IO_VIRT_ADDR (memory + IO_OFFSET)
72 
73 #define MAP_RAM memory
74 #define MAP_BASIC memory + BASIC_ROM_OFFSET - 0xA000
75 #define MAP_KERNAL memory + KERNAL_ROM_OFFSET - 0xE000
76 #define MAP_CHRROM memory + CHAR_ROM_OFFSET - 0xD000
77 #define MAP_IO memory + IO_OFFSET - 0xD000
78 #define MAP_RAM_TWICE MAP_RAM,MAP_RAM
79 #define MAP_RAM_10_TIMES MAP_RAM_TWICE,MAP_RAM_TWICE,MAP_RAM_TWICE,MAP_RAM_TWICE,MAP_RAM_TWICE
80 #define MAP_BASIC_TWICE MAP_BASIC,MAP_BASIC
81 #define MAP_KERNAL_TWICE MAP_KERNAL,MAP_KERNAL
82 
83 #define KERNAL_PATCH_ADDR 0xE388
84 #define PATCH_P memory[KERNAL_PATCH_ADDR - 0xE000 + KERNAL_ROM_OFFSET]
85 #define PATCH_OLD_BYTE 0x6C
86 #define PATCH_NEW_BYTE CPU65_TRAP_OPCODE
87 
88 
89 static Uint8 *memcfgs[8][2][16] = {
90  // 0000-9FFF A000-BFFF CXXX DXXX E000-FFFF 0000-9FFF A000-BFFF CXXX DXXX E000-FFFF
91  // READ READ READ READ READ WRITE WRITE WRITE WRITE WRITE
92  // ---------------- --------------- ------- ---------- ---------------- ---------------- ------------- ------- ------- -------------
101 };
102 
103 #define GET_READ_P(a) (memcfgs[cpu_port_memconfig][0][(a)>>12] + (a))
104 #define GET_WRITE_P(a) (memcfgs[cpu_port_memconfig][1][(a)>>12] + (a))
105 #define IS_P_IO(p) ((p) >= IO_VIRT_ADDR)
106 
107 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.";
108 
109 static const char *memconfig_descriptions[8] = {
110  "ALL RAM [v1]", "CHAR+RAM", "CHAR+KERNAL", "ALL *ROM*",
111  "ALL RAM [v2]", "IO+RAM", "IO+KERNAL", "BASIC+IO+KERNAL"
112 };
113 
114 static struct Cia6526 cia1, cia2; // CIA emulation structures for the two CIAs
115 static int vic2_16k_bank;
116 static int scanline;
118 static Uint8 vic2_registers[0x40]; // though not all of them really exists
119 static int cpu_port_memconfig = 7;
120 static Uint32 palette[16];
121 static int compare_raster; // raster compare (9 bits width) data
122 static int vic2_interrupt_status; // Interrupt status of VIC
123 static Uint8 *vic2_sprite_pointers;
124 static int warp = 1; // warp speed for initially to faster "boot" for C64
125 static int geos_loaded = 0;
126 static int nmi_level; // please read the comment at nmi_set() below
127 
128 
129 static const Uint8 init_vic2_palette_rgb[16 * 3] = { // VIC2 palette given by RGB components
130  0x00, 0x00, 0x00,
131  0xFF, 0xFF, 0xFF,
132  0x74, 0x43, 0x35,
133  0x7C, 0xAC, 0xBA,
134  0x7B, 0x48, 0x90,
135  0x64, 0x97, 0x4F,
136  0x40, 0x32, 0x85,
137  0xBF, 0xCD, 0x7A,
138  0x7B, 0x5B, 0x2F,
139  0x4f, 0x45, 0x00,
140  0xa3, 0x72, 0x65,
141  0x50, 0x50, 0x50,
142  0x78, 0x78, 0x78,
143  0xa4, 0xd7, 0x8e,
144  0x78, 0x6a, 0xbd,
145  0x9f, 0x9f, 0x9f
146 };
147 
148 static struct {
152 } configdb;
153 
154 #define CHECK_PIXEL_POINTER
155 
156 #ifdef CHECK_PIXEL_POINTER
157 /* Temporary hack to be used in renders. Asserts out-of-texture accesses */
158 static Uint32 *pixel_pointer_check_base;
159 static Uint32 *pixel_pointer_check_end;
160 static const char *pixel_pointer_check_modn;
161 static inline void PIXEL_POINTER_CHECK_INIT( Uint32 *base, int tail, const char *module )
162 {
163  pixel_pointer_check_base = base;
164  pixel_pointer_check_end = base + (320 + tail) * 200;
165  pixel_pointer_check_modn = module;
166 }
167 static inline void PIXEL_POINTER_CHECK_ASSERT ( Uint32 *p )
168 {
169  if (p < pixel_pointer_check_base)
170  FATAL("FATAL ASSERT: accessing texture (%p) under the base limit (%p).\nIn program module: %s", p, pixel_pointer_check_base, pixel_pointer_check_modn);
171  if (p >= pixel_pointer_check_end)
172  FATAL("FATAL ASSERT: accessing texture (%p) above the upper limit (%p).\nIn program module: %s", p, pixel_pointer_check_end, pixel_pointer_check_modn);
173 }
174 static inline void PIXEL_POINTER_FINAL_ASSERT ( Uint32 *p )
175 {
176  if (p != pixel_pointer_check_end)
177  FATAL("FATAL ASSERT: final texture pointer (%p) is not the same as the desired one (%p),\nIn program module %s", p, pixel_pointer_check_end, pixel_pointer_check_modn);
178 }
179 #else
180 # define PIXEL_POINTER_CHECK_INIT(base,tail,mod)
181 # define PIXEL_POINTER_CHECK_ASSERT(p)
182 # define PIXEL_POINTER_FINAL_ASSERT(p)
183 #endif
184 
185 
186 
187 
188 
189 static void vic2_interrupt_checker ( void )
190 {
191  int vic_irq_old = cpu65.irqLevel & 2;
192  int vic_irq_new;
193  if (vic2_interrupt_status & vic2_registers[0x1A]) {
194  vic2_interrupt_status |= 128;
195  vic_irq_new = 2;
196  } else {
197  vic2_interrupt_status &= 127;
198  vic_irq_new = 0;
199  }
200  if (vic_irq_old != vic_irq_new) {
201  DEBUG("VIC2: interrupt change %s -> %s" NL, vic_irq_old ? "active" : "inactive", vic_irq_new ? "active" : "inactive");
202  if (vic_irq_new)
203  cpu65.irqLevel |= 2;
204  else
205  cpu65.irqLevel &= ~2;
206  }
207 }
208 
209 
210 
212 {
213  // C65 seems to use raster interrupt to generate the usual periodic IRQ
214  // (which was done with CIA on C64) in raster line 511. However as
215  // raster line 511 can never be true, I really don't know what to do.
216  // To be able C65 ROM to work, I assume that raster 511 is raster 0.
217  // It's possible that this is an NTSC/PAL issue, as raster can be "negative"
218  // according to the specification in case of NTSC. I really don't know ...
219  if (scanline == compare_raster)
220  vic2_interrupt_status |= 1;
221  else
222  vic2_interrupt_status &= 0xFE;
223  vic2_interrupt_checker();
224 }
225 
226 
227 
228 
230 {
231  addr &= 0x3F;
232  DEBUG("VIC3: write reg $%02X with data $%02X" NL, addr, data);
233  if (addr > 0x2E)
234  return;
235  vic2_registers[addr] = data;
236  switch (addr) {
237  case 0x11:
238  compare_raster = (compare_raster & 0xFF) | ((data & 128) ? 0x100 : 0);
239  DEBUG("VIC2: compare raster is now %d" NL, compare_raster);
240  break;
241  case 0x12:
242  compare_raster = (compare_raster & 0xFF00) | data;
243  DEBUG("VIC2: compare raster is now %d" NL, compare_raster);
244  break;
245  case 0x19:
246  vic2_interrupt_status = vic2_interrupt_status & (~data) & 15;
247  vic2_interrupt_checker();
248  break;
249  case 0x1A:
250  vic2_registers[0x1A] &= 15;
251  break;
252  }
253 }
254 
255 
256 
257 
259 {
260  Uint8 result;
261  addr &= 0x3F;
262  switch (addr) {
263  case 0x11:
264  result = (vic2_registers[0x11] & 0x7F) | ((scanline & 256) ? 0x80 : 0);
265  break;
266  case 0x12:
267  result = scanline & 0xFF;
268  break;
269  case 0x16:
270  result = vic2_registers[addr] | (128 + 64); // unused bits
271  break;
272  case 0x19:
273  result = vic2_interrupt_status | (64 + 32 + 16); // unused bits
274  break;
275  case 0x1A:
276  result = vic2_registers[addr] | 0xF0; // unused bits
277  break;
278  case 0x18:
279  result = vic2_registers[addr] | 1; // unused bit
280  break;
281  default:
282  result = vic2_registers[addr];
283  if (addr >= 0x20 && addr < 0x2F)
284  result |= 0xF0; // unused bits
285  break;
286  }
287  DEBUG("VIC2: read reg $%02X with result $%02X" NL, addr, result);
288  return result;
289 }
290 
291 
292 
293 
294 /* At-frame-at-once (thus incorrect implementation)
295  for "normal" text VIC mode.
296  Character map memory if fixed :-/ */
297 static inline void vic2_render_screen_text ( Uint32 *p, int tail )
298 {
299  Uint32 bg;
300  Uint8 *vidp, *chrg, *colp = colour_sram;
301  int x = 0, y = 0, charline = 0;
302  // Currently, only text (no MCM, ECM) is supported,
303  // and fixed chargen address.
304  // Fixed character info, heh ... FIXME
305  chrg = memory + CHAR_ROM_OFFSET;
306  // Note: VIC2 sees ROM at some addresses thing is not emulated yet!
307  vidp = memory + ((vic2_registers[0x18] & 0xF0) << 6) + vic2_16k_bank;
308  vic2_sprite_pointers = vidp + 1016;
309  // Target SDL pixel related format for the background colour
310  bg = palette[vic2_registers[0x21] & 15];
311  PIXEL_POINTER_CHECK_INIT(p, tail, "vic2_render_screen_text");
312  for (;;) {
313  Uint8 chrdata = chrg[((*(vidp++)) << 3) + charline];
314  Uint8 coldata = *(colp++);
315  Uint32 fg = palette[coldata & 15];
316  // FIXME: no ECM, MCM stuff ...
317  PIXEL_POINTER_CHECK_ASSERT(p + 7);
318  *(p++) = chrdata & 128 ? fg : bg;
319  *(p++) = chrdata & 64 ? fg : bg;
320  *(p++) = chrdata & 32 ? fg : bg;
321  *(p++) = chrdata & 16 ? fg : bg;
322  *(p++) = chrdata & 8 ? fg : bg;
323  *(p++) = chrdata & 4 ? fg : bg;
324  *(p++) = chrdata & 2 ? fg : bg;
325  *(p++) = chrdata & 1 ? fg : bg;
326  if (x == 39) {
327  p += tail;
328  x = 0;
329  if (charline == 7) {
330  if (y == 24)
331  break;
332  y++;
333  charline = 0;
334  } else {
335  charline++;
336  vidp -= 40;
337  colp -= 40;
338  }
339  } else
340  x++;
341  }
342  PIXEL_POINTER_FINAL_ASSERT(p);
343 }
344 
345 
346 
347 // VIC2 bitmap mode, now only HIRES mode (no MCM yet)
348 // Note: VIC2 sees ROM at some addresses thing is not emulated yet!
349 static inline void vic2_render_screen_bmm ( Uint32 *p, int tail )
350 {
351  int x = 0, y = 0, charline = 0;
352  Uint8 *vidp, *chrp;
353  vidp = memory + ((vic2_registers[0x18] & 0xF0) << 6) + vic2_16k_bank;
354  vic2_sprite_pointers = vidp + 1016;
355  chrp = memory + ((vic2_registers[0x18] & 8) ? 8192 : 0) + vic2_16k_bank;
356  PIXEL_POINTER_CHECK_INIT(p, tail, "vic2_render_screen_bmm");
357  for (;;) {
358  Uint8 data = *(vidp++);
359  Uint32 bg = palette[data & 15];
360  Uint32 fg = palette[data >> 4];
361  data = *chrp;
362  chrp += 8;
363  PIXEL_POINTER_CHECK_ASSERT(p);
364  p[0] = data & 128 ? fg : bg;
365  p[1] = data & 64 ? fg : bg;
366  p[2] = data & 32 ? fg : bg;
367  p[3] = data & 16 ? fg : bg;
368  p[4] = data & 8 ? fg : bg;
369  p[5] = data & 4 ? fg : bg;
370  p[6] = data & 2 ? fg : bg;
371  p[7] = data & 1 ? fg : bg;
372  p += 8;
373  if (x == 39) {
374  p += tail;
375  x = 0;
376  if (charline == 7) {
377  if (y == 24)
378  break;
379  y++;
380  charline = 0;
381  chrp -= 7;
382  } else {
383  charline++;
384  vidp -= 40;
385  chrp -= 319;
386  }
387  } else
388  x++;
389  }
390  PIXEL_POINTER_FINAL_ASSERT(p);
391 }
392 
393 #define SPRITE_X_START_SCREEN 24
394 #define SPRITE_Y_START_SCREEN 50
395 
396 
397 /* Extremely incorrect sprite emulation! BUGS:
398  * Sprites cannot be behind the background (sprite priority)
399  * Multicolour sprites are not supported
400  * No sprite-background collision detection
401  * No sprite-sprite collision detection
402  * This is a simple, after-the-rendered-frame render-sprites one-by-one algorithm
403  * This also requires to give up direct rendering if a sprite is enabled
404  * Very ugly, quick&dirty hack, not so optimal either, even without the other mentioned bugs ...
405 */
406 static void vic2_render_sprite ( int sprite_no, int sprite_mask, Uint8 *data, Uint32 *p, int tail )
407 {
408  int sprite_y = vic2_registers[sprite_no * 2 + 1] - SPRITE_Y_START_SCREEN;
409  int sprite_x = ((vic2_registers[sprite_no * 2] | ((vic2_registers[16] & sprite_mask) ? 0x100 : 0)) - SPRITE_X_START_SCREEN) * 1;
410  Uint32 colour = palette[vic2_registers[39 + sprite_no] & 15];
411  int expand_x = vic2_registers[29] & sprite_mask;
412  int expand_y = vic2_registers[23] & sprite_mask;
413  int lim_y = sprite_y + ((expand_y) ? 42 : 21);
414  int y;
415  p += (320 + tail) * sprite_y;
416  for (y = sprite_y; y < lim_y; y += (expand_y ? 2 : 1), p += (320 + tail) * (expand_y ? 2 : 1))
417  if (y < 0 || y >= 200)
418  data += 3; // skip one line (three bytes) of sprite data if outside of screen
419  else {
420  int mask, a, x = sprite_x;
421  for (a = 0; a < 3; a++) {
422  for (mask = 128; mask; mask >>= 1) {
423  if (*data & mask) {
424  if (x >= 0 && x < 320) {
425  p[x] = colour;
426  if (expand_y && y < 200)
427  p[x + 320 + tail] = colour;
428  }
429  x++;
430  if (expand_x && x >= 0 && x < 320) {
431  p[x] = colour;
432  if (expand_y && y < 200)
433  p[x + 320 + tail] = colour;
434  x++;
435  }
436  } else
437  x += expand_x ? 2 : 1;
438  }
439  data++;
440  }
441  }
442 }
443 
444 
445 
446 
447 /* This is the one-frame-at-once (highly incorrect implementation, that is)
448  renderer. It will call legacy VIC2 text mode render (optionally with
449  80 columns mode, though, ECM, MCM, hardware attributes are not supported),
450  VIC2 legacy HIRES mode (MCM is not supported), or bitplane modes (V400,
451  H1280, odd scanning/interlace is not supported). Sprites, screen positioning,
452  etc is not supported */
453 void vic2_render_screen ( void )
454 {
455  int tail_sdl;
456  Uint32 *p_sdl = xemu_start_pixel_buffer_access(&tail_sdl);
457  int sprites = vic2_registers[0x15];
458  if (vic2_registers[0x11] & 32)
459  vic2_render_screen_bmm(p_sdl, tail_sdl);
460  else
461  vic2_render_screen_text(p_sdl, tail_sdl);
462  if (sprites) { // Render sprites. VERY BAD. We ignore sprite priority as well (cannot be behind the background)
463  int a;
464  for (a = 7; a >= 0; a--) {
465  int mask = 1 << a;
466  if (sprites & mask)
467  vic2_render_sprite(a, mask, memory + vic2_16k_bank + (vic2_sprite_pointers[a] << 6), p_sdl, tail_sdl); // sprite_pointers are set by the renderer functions above!
468  }
469  }
471 }
472 
473 
474 
475 static void cia1_setint_cb ( int level )
476 {
477  DEBUG("%s: IRQ level changed to %d" NL, cia1.name, level);
478  if (level)
479  cpu65.irqLevel |= 1;
480  else
481  cpu65.irqLevel &= ~1;
482 }
483 
484 
485 static inline void nmi_set ( int level, int mask )
486 {
487  // NMI is a low active _EDGE_ triggered 65xx input ... In my emulator though, the signal
488  // is "high active", and also we must form the "edge" ourselves from "level". NMI level is
489  // set as a 2bit number, on bit 0, CIA2, on bit 1, keyboard RESTORE key. Thus having zero
490  // value for level means (in the emu!) that not RESTORE key is pressed neither CIA2 has active
491  // IRQ output, non-zero value means some activation. Well, if I am not confused enough here,
492  // this should mean that nmi_level zero->non-zero transit should produce the edge (which should
493  // be the falling edge in the real hardware anyway ... but the rising here. heh, I should follow
494  // the signal level of the hardware in my emulator, honestly ...)
495  int nmi_new_level;
496  if (level)
497  nmi_new_level = nmi_level | mask;
498  else
499  nmi_new_level = nmi_level & (~mask);
500  if ((!nmi_level) && nmi_new_level) {
501  DEBUG("NMI edge is emulated towards the CPU (%d->%d)" NL, nmi_level, nmi_new_level);
502  cpu65.nmiEdge = 1; // the "NMI edge" trigger is deleted by the CPU emulator itself (it's not a level trigger)
503  }
504  nmi_level = nmi_new_level;
505 }
506 
507 
508 static void cia2_setint_cb ( int level )
509 {
510  nmi_set(level, 1);
511 }
512 
513 
514 void clear_emu_events ( void )
515 {
516  hid_reset_events(1);
517 }
518 
519 
520 static Uint8 cia1_in_b ( void )
521 {
522  return c64_keyboard_read_on_CIA1_B(
523  cia1.PRA | (~cia1.DDRA),
524  cia1.PRB | (~cia1.DDRB),
525  joystick_emu == 1 ? c64_get_joy_state() : 0xFF
526  );
527 }
528 
529 
530 static Uint8 cia1_in_a ( void )
531 {
532  return c64_keyboard_read_on_CIA1_A(
533  cia1.PRB | (~cia1.DDRB),
534  cia1.PRA | (~cia1.DDRA),
535  joystick_emu == 2 ? c64_get_joy_state() : 0xFF
536  );
537 }
538 
539 
540 static void cia2_out_a ( Uint8 data )
541 {
542  vic2_16k_bank = ((~(data | (~cia2.DDRA))) & 3) << 14;
543  DEBUG("VIC2: 16K BANK is set to $%04X (CIA mask=$%02X)" NL, vic2_16k_bank, cia2.DDRA);
544 }
545 
546 
547 
548 // Just for easier test to have a given port value for CIA input ports
549 static Uint8 cia_port_in_dummy ( void )
550 {
551  return 0xFF;
552 }
553 
554 
555 
556 static void cpu_port_write ( int addr, Uint8 data )
557 {
558  memory[addr] = data;
559  if (addr) {
560  if (cpu_port_memconfig == (data & 7))
561  DEBUG("MEM: memory configuration is the SAME: %d %s @ PC = $%04X" NL, cpu_port_memconfig, memconfig_descriptions[cpu_port_memconfig], cpu65.pc);
562  else {
563  DEBUG("MEM: memory configuration is CHANGED : %d %s (from %d %s) @ PC = $%02X" NL,
564  data & 7, memconfig_descriptions[data & 7],
565  cpu_port_memconfig, memconfig_descriptions[cpu_port_memconfig],
566  cpu65.pc
567  );
568  cpu_port_memconfig = data & 7;
569  }
570  }
571 }
572 
573 
574 static void geosemu_init ( void )
575 {
576  hid_init(
577  c64_key_map,
579  SDL_ENABLE // joy HID events enabled
580  );
581  joystick_emu = 1;
582  nmi_level = 0;
583  // *** Init memory space
584  memset(memory, 0xFF, sizeof memory);
585  cpu_port_write(0, CPU_PORT_DEFAULT_VALUE0);
586  cpu_port_write(1, CPU_PORT_DEFAULT_VALUE1);
587  // *** Load ROM image
588  if (
589  xemu_load_file(configdb.rombasic, memory + BASIC_ROM_OFFSET, 8192, 8192, rom_fatal_msg) < 0 ||
590  xemu_load_file(configdb.romkernal, memory + KERNAL_ROM_OFFSET, 8192, 8192, rom_fatal_msg) < 0 ||
591  xemu_load_file(configdb.romchar, memory + CHAR_ROM_OFFSET, 4096, 4096, rom_fatal_msg) < 0
592  )
593  XEMUEXIT(1);
594  // *** Patching ROM for custom GEOS loader
595  if (PATCH_P != PATCH_OLD_BYTE)
596  FATAL("FATAL: ROM problem, patching point does not contain the expected value!");
598  // *** Initialize VIC2 ... sort of :)
599  memset(colour_sram, 0xFF, sizeof colour_sram);
600  memset(vic2_registers, 0, sizeof vic2_registers);
601  vic2_16k_bank = 0;
602  scanline = 0;
603  vic2_interrupt_status = 0;
604  compare_raster = 0;
605  // *** CIAs
606  cia_init(&cia1, "CIA-1",
607  NULL, // callback: OUTA
608  NULL, // callback: OUTB
609  NULL, // callback: OUTSR
610  cia1_in_a, // callback: INA ~ joy#2
611  cia1_in_b, // callback: INB ~ keyboard
612  NULL, // callback: INSR
613  cia1_setint_cb // callback: SETINT
614  );
615  cia_init(&cia2, "CIA-2",
616  cia2_out_a, // callback: OUTA ~ eg VIC-II bank
617  NULL, // callback: OUTB
618  NULL, // callback: OUTSR
619  cia_port_in_dummy, // callback: INA
620  NULL, // callback: INB
621  NULL, // callback: INSR
622  cia2_setint_cb // callback: SETINT ~ that would be NMI in our case
623  );
624  // Initialize Disk Image
625  // TODO
626  // *** RESET CPU, also fetches the RESET vector into PC
627  cpu65_reset();
628  DEBUG("INIT: end of initialization!" NL);
629 }
630 
631 
632 
633 static Uint8 io_read ( int addr )
634 {
635  DEBUG("IO: reading $%04X @ PC=$%04X" NL, addr, cpu65.pc);
636  if (addr < 0xD400) // D000-D3FF VIC-II
637  return vic2_read_reg(addr);
638  if (addr < 0xD800) // D400-D7FF SID (not emulated here)
639  return 0xFF;
640  if (addr < 0xDC00) // D800-DBFF Colour SRAM (1K, 4 bit)
641  return colour_sram[addr & 0x3FF];
642  if (addr < 0xDD00) // DC00-DCFF CIA-1
643  return cia_read(&cia1, addr & 15);
644  if (addr < 0xDE00) // DD00-DDFF CIA-2
645  return cia_read(&cia2, addr & 15);
646  return 0xFF; // DE00-DFFF the rest, I/O-1 and I/O-2 exp area
647 }
648 
649 
650 
651 static void io_write ( int addr, Uint8 data )
652 {
653  DEBUG("IO: writing $%04X with $%02X @ PC=$%04X" NL, addr, data, cpu65.pc);
654  if (addr < 0xD400) { // D000-D3FF VIC-II
656  return;
657  }
658  if (addr < 0xD800) // D400-D7FF SID (not emulated here)
659  return;
660  if (addr < 0xDC00) { // D800-DBFF Colour SRAM (1K, 4 bit)
661  colour_sram[addr & 0x3FF] = data | 0xF0; // 4 upper bits are always set (the SRAM has only 4 bits)
662  return;
663  }
664  if (addr < 0xDD00) { // DC00-DCFF CIA-1
665  cia_write(&cia1, addr & 15, data);
666  return;
667  }
668  if (addr < 0xDE00) { // DD00-DDFF CIA-2
669  cia_write(&cia2, addr & 15, data);
670  return;
671  }
672  // DE00-DFFF the rest, I/O-1 and I/O-2 exp area
673 }
674 
675 
676 
677 int cpu65_trap_callback ( const Uint8 opcode )
678 {
679  Uint8 *pc_p = GET_READ_P(cpu65.pc);
680  if (pc_p != 1 + &PATCH_P) {
681  if (warp)
682  FATAL("FATAL: CPU trap at unknown address in warp mode (pre-GEOS loading) PC=$%04X OP=$%02X" NL, cpu65.pc, opcode);
683  if (pc_p >= memory + 0x10000)
684  FATAL("FATAL: unknown CPU trap not in the RAM PC=$%04X OP=$%02X" NL, cpu65.pc, opcode);
685  if (geos_loaded != 2)
686  FATAL("FATAL: unknown CPU without GEOS loaded PC=$%04X OP=$%02X" NL, cpu65.pc, opcode);
687  geos_cpu_trap(opcode);
688  return 1;
689  }
690  warp = 0; // turn warp speed off
691  // Try to load a custom GEOS kernal directly into the RAM
692  if (geos_loaded) {
693  cpu65.pc = memory[0x300] | (memory[0x301] << 8);
694  return 1;
695  }
696  if (!geos_load_kernal(configdb.geoskernal)) {
697  geos_loaded = 2; // GEOS was OK!!!!
698  return 1; // if no error, return with '1' (as not zero) to signal CPU emulator that trap should not be executed
699  }
700  geos_loaded = 1;
701  // In case if we cannot load some GEOS kernal stuff, continue in "C64 mode" ... :-/
702  // Some ugly method to produce custom "startup screen" :)
703  inject_screencoded_message(41, "**** Can't load GEOS, boot as C64 ****");
704  cpu65.pc = memory[0x300] | (memory[0x301] << 8);
705  return 1; // do NOT execute the trap op
706 }
707 
708 
710 {
711  ERROR_WINDOW("Unemulated NMOS 6502 opcode $%02X at PC=$%04X", cpu65.op, cpu65.pc - 1);
712  cpu65_reset();
713 }
714 
715 
716 
717 // This function is called by the 65C02 emulator in case of reading a byte (regardless of data or code)
719 {
720  Uint8 *p = GET_READ_P(addr);
721  return IS_P_IO(p) ? io_read(addr) : *p;
722 }
723 
724 
725 
726 // This function is called by the 65C02 emulator in case of writing a byte
728 {
729  Uint8 *p = GET_WRITE_P(addr);
730  if (IS_P_IO(p))
731  io_write(addr, data);
732  else if (addr > 1)
733  *p = data;
734  else
735  cpu_port_write(addr, data);
736 }
737 
738 
739 
740 // Called in case of an RMW (read-modify-write) opcode write access.
741 // Original NMOS 6502 would write the old_data first, then new_data.
742 // It has no inpact in case of normal RAM, but it *does* with an I/O register in some cases!
743 void cpu65_write_rmw_callback ( Uint16 addr, Uint8 old_data, Uint8 new_data )
744 {
745  Uint8 *p = GET_WRITE_P(addr);
746  if (IS_P_IO(p)) {
747  io_write(addr, old_data);
748  io_write(addr, new_data);
749  } else if (addr > 1)
750  *p = new_data;
751  else
752  cpu_port_write(addr, new_data);
753 }
754 
755 
756 
757 static void shutdown_callback ( void )
758 {
759  DEBUG("SHUTDOWN: @ PC=$%04X" NL, cpu65.pc);
760 }
761 
762 
763 // HID needs this to be defined, it's up to the emulator if it uses or not ...
764 int emu_callback_key ( int pos, SDL_Scancode key, int pressed, int handled )
765 {
766  if (pressed) {
767  if (key == SDL_SCANCODE_F10) {
768  cpu_port_write(0, CPU_PORT_DEFAULT_VALUE0);
769  cpu_port_write(1, CPU_PORT_DEFAULT_VALUE1);
770  cpu65_reset();
771  nmi_level = 0;
772  DEBUG("RESET!" NL);
773  } else if (key == SDL_SCANCODE_KP_ENTER)
775  }
776  return 0;
777 }
778 
779 
780 static void update_emulator ( void )
781 {
783  nmi_set(IS_RESTORE_PRESSED(), 2); // Custom handling of the restore key ...
784  // Screen rendering: begin
786  // Screen rendering: end
787  xemu_timekeeping_delay(40000);
788  // Ugly CIA trick to maintain realtime TOD in CIAs :)
789  const struct tm *t = xemu_get_localtime();
790  const Uint8 sec10ths = xemu_get_microseconds() / 100000;
791  cia_ugly_tod_updater(&cia1, t, sec10ths, 0);
792  cia_ugly_tod_updater(&cia2, t, sec10ths, 0);
793 }
794 
795 
796 
797 int main ( int argc, char **argv )
798 {
799  int cycles, frameskip;
800  xemu_pre_init(APP_ORG, TARGET_NAME, "The Unexplained Commodore GEOS emulator from LGB");
801  xemucfg_define_switch_option("fullscreen", "Start in fullscreen mode", &configdb.fullscreen);
802  xemucfg_define_str_option("geosimg", NULL, "Select GEOS disk image to use (NOT USED YET!)", &configdb.geosimg);
803  xemucfg_define_str_option("geoskernal", "#geos-kernal.bin", "Select GEOS KERNAL to use", &configdb.geoskernal);
804  xemucfg_define_str_option("drive", NULL, "Path/name of a D81 GEOS will see as the disk drive", &configdb.drive);
805  xemucfg_define_str_option("rombasic", "#c64-basic.rom", "Select BASIC ROM to use", &configdb.rombasic);
806  xemucfg_define_str_option("romchar", "#c64-chargen.rom", "Select CHARACTER ROM to use", &configdb.romchar);
807  xemucfg_define_str_option("romkernal", "#c64-kernal.rom", "Select KERNAL ROM to use", &configdb.romkernal);
808  xemucfg_define_switch_option("syscon", "Keep system console open (Windows-specific effect only)", &configdb.syscon);
809  xemucfg_define_num_option("sdlrenderquality", RENDER_SCALE_QUALITY, "Setting SDL hint for scaling method/quality on rendering (0, 1, 2)", &configdb.sdlrenderquality, 0, 2);
810  if (xemucfg_parse_all(argc, argv))
811  return 1;
812  /* Initiailize SDL - note, it must be before loading ROMs, as it depends on path info from SDL! */
813  if (xemu_post_init(
814  TARGET_DESC APP_DESC_APPEND, // window title
815  1, // resizable window
816  SCREEN_WIDTH, SCREEN_HEIGHT, // texture sizes
817  SCREEN_WIDTH * 2, SCREEN_HEIGHT * 2,// logical size (used with keeping aspect ratio by the SDL render stuffs)
818  SCREEN_WIDTH * 2, SCREEN_HEIGHT * 2,// window size
819  SCREEN_FORMAT, // pixel format
820  16, // we have 16 colours
821  init_vic2_palette_rgb, // initialize palette from this constant array
822  palette, // initialize palette into this stuff
823  configdb.sdlrenderquality, // render scaling quality
824  USE_LOCKED_TEXTURE, // 1 = locked texture access
825  shutdown_callback // registered shutdown function
826  ))
827  return 1;
828  // Initialize
829  geosemu_init();
830  cycles = 0;
831  frameskip = 0;
833  if (!configdb.syscon)
834  sysconsole_close(NULL);
835  // Start!!
837  for (;;) {
838  int opcyc = cpu65_step();
839  cia_tick(&cia1, opcyc);
840  cia_tick(&cia2, opcyc);
841  cycles += opcyc;
842  if (cycles >= 63) {
843 #if 0
844  vic2_registers[0] = 80;
845  vic2_registers[1] = 80;
846  vic2_registers[16] = 0;
847  vic2_registers[21] = 0xFF;
848 #endif
849  scanline++;
850  //DEBUG("VIC3: new scanline (%d)!" NL, scanline);
851  cycles -= 63;
852  if (scanline == 312) {
853  //DEBUG("VIC3: new frame!" NL);
854  frameskip = !frameskip;
855  scanline = 0;
856  if (!frameskip && !warp) // well, let's only render every full frames (~ie 25Hz)
857  update_emulator();
858  }
859  //DEBUG("RASTER=%d COMPARE=%d" NL,scanline,compare_raster);
860  //vic_interrupt();
862  }
863  }
864  return 0;
865 }
xemu_pre_init
void xemu_pre_init(const char *app_organization, const char *app_name, const char *slogan)
Definition: emutools.c:651
xemucfg_define_str_option
void xemucfg_define_str_option(const char *optname, const char *defval, const char *help, char **storage)
MAP_IO
#define MAP_IO
Definition: commodore_geos.c:77
USE_LOCKED_TEXTURE
#define USE_LOCKED_TEXTURE
Definition: commodore_65.h:26
geos_cpu_trap
void geos_cpu_trap(Uint8 opcode)
Definition: geos.c:116
cia6526.h
TARGET_DESC
#define TARGET_DESC
Definition: xemu-target.h:2
vic2_check_raster_interrupt
void vic2_check_raster_interrupt(void)
Definition: commodore_geos.c:211
PATCH_P
#define PATCH_P
Definition: commodore_geos.c:84
cia_ugly_tod_updater
void cia_ugly_tod_updater(struct Cia6526 *cia, const struct tm *t, Uint8 sec10, int hour_offset)
Definition: cia6526.c:270
cia_read
Uint8 cia_read(struct Cia6526 *cia, int addr)
Definition: cia6526.c:121
MAP_RAM_TWICE
#define MAP_RAM_TWICE
Definition: commodore_geos.c:78
SPRITE_X_START_SCREEN
#define SPRITE_X_START_SCREEN
Definition: commodore_geos.c:393
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
GET_READ_P
#define GET_READ_P(a)
Definition: commodore_geos.c:103
sdlrenderquality
int sdlrenderquality
Definition: commodore_geos.c:150
cpu65_read_callback
Uint8 cpu65_read_callback(Uint16 addr)
Definition: commodore_geos.c:718
memory
Uint8 memory[IO_OFFSET+1]
Definition: commodore_geos.c:70
xemu_get_localtime
struct tm * xemu_get_localtime(void)
Definition: emutools.c:187
SCREEN_WIDTH
#define SCREEN_WIDTH
Definition: vic3.h:29
colour
Uint32 colour
Definition: vera.c:67
c64_key_map
const struct KeyMappingDefault c64_key_map[]
Definition: c64_kbd_mapping.c:33
KERNAL_ROM_OFFSET
#define KERNAL_ROM_OFFSET
Definition: commodore_geos.c:66
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
commodore_geos.h
joystick_emu
int joystick_emu
Definition: c64_kbd_mapping.c:137
rombasic
char * rombasic
Definition: commodore_geos.c:151
geos_load_kernal
int geos_load_kernal(const char *kernal_image_name)
Definition: geos.c:89
inject_screencoded_message
void inject_screencoded_message(int addr, const char *s)
Definition: geos.c:48
addr
int addr
Definition: dma65.c:81
hid_handle_all_sdl_events
void hid_handle_all_sdl_events(void)
Definition: emutools_hid.c:613
MAP_CHRROM
#define MAP_CHRROM
Definition: commodore_geos.c:76
CPU_PORT_DEFAULT_VALUE1
#define CPU_PORT_DEFAULT_VALUE1
Definition: commodore_geos.c:63
io_write
void io_write(int addr, Uint8 data)
Definition: commodore_65.c:546
xemucfg_parse_all
int xemucfg_parse_all(int argc, char **argv)
SPRITE_Y_START_SCREEN
#define SPRITE_Y_START_SCREEN
Definition: commodore_geos.c:394
m65-memcontent-generator.data
data
Definition: m65-memcontent-generator.py:119
Uint32
uint32_t Uint32
Definition: fat32.c:49
hid_init
void hid_init(const struct KeyMappingDefault *key_map_in, Uint8 virtual_shift_pos_in, int joy_enable)
Definition: emutools_hid.c:300
romchar
char * romchar
Definition: commodore_geos.c:151
cia_tick
void cia_tick(struct Cia6526 *cia, int ticks)
Definition: cia6526.c:281
PATCH_OLD_BYTE
#define PATCH_OLD_BYTE
Definition: commodore_geos.c:85
hid_reset_events
void hid_reset_events(int burn)
Definition: emutools_hid.c:170
cia2
struct Cia6526 cia1 cia2
Definition: commodore_65.c:44
CPU_PORT_DEFAULT_VALUE0
#define CPU_PORT_DEFAULT_VALUE0
Definition: commodore_geos.c:62
Uint8
uint8_t Uint8
Definition: fat32.c:51
Cia6526::DDRA
Uint8 DDRA
Definition: cia6526.h:46
xemu_set_full_screen
void xemu_set_full_screen(int setting)
Definition: emutools.c:311
Cia6526
Definition: cia6526.h:37
c64_toggle_joy_emu
void c64_toggle_joy_emu(void)
Definition: c64_kbd_mapping.c:140
vic2_render_screen
void vic2_render_screen(void)
Definition: commodore_geos.c:453
configdb_st::sdlrenderquality
int sdlrenderquality
Definition: configdb.h:35
drive
char * drive
Definition: commodore_geos.c:151
vic2_write_reg
void vic2_write_reg(int addr, Uint8 data)
Definition: commodore_geos.c:229
TARGET_NAME
#define TARGET_NAME
Definition: xemu-target.h:1
emutools_files.h
geos.h
CHAR_ROM_OFFSET
#define CHAR_ROM_OFFSET
Definition: commodore_geos.c:67
APP_ORG
#define APP_ORG
Definition: emutools.h:50
x
int x
Definition: console.c:27
PATCH_NEW_BYTE
#define PATCH_NEW_BYTE
Definition: commodore_geos.c:86
cpu65_trap_callback
int cpu65_trap_callback(const Uint8 opcode)
Definition: commodore_geos.c:677
VIRTUAL_SHIFT_POS
#define VIRTUAL_SHIFT_POS
Definition: commodore_lcd.c:84
xemu_start_pixel_buffer_access
Uint32 * xemu_start_pixel_buffer_access(int *texture_tail)
Definition: emutools.c:1153
xemu_get_microseconds
unsigned int xemu_get_microseconds(void)
Definition: emutools.c:205
c64_get_joy_state
Uint8 c64_get_joy_state(void)
Definition: c64_kbd_mapping.c:151
MAP_RAM_10_TIMES
#define MAP_RAM_10_TIMES
Definition: commodore_geos.c:79
ERROR_WINDOW
#define ERROR_WINDOW(...)
Definition: xep128.h:116
MAP_KERNAL_TWICE
#define MAP_KERNAL_TWICE
Definition: commodore_geos.c:81
main
int main(int argc, char **argv)
Definition: commodore_geos.c:797
vic2_read_reg
Uint8 vic2_read_reg(int addr)
Definition: commodore_geos.c:258
cpu65_write_callback
void cpu65_write_callback(Uint16 addr, Uint8 data)
Definition: commodore_geos.c:727
NL
#define NL
Definition: fat32.c:37
emutools_config.h
configdb
struct configdb_st configdb
Definition: configdb.c:34
IS_P_IO
#define IS_P_IO(p)
Definition: commodore_geos.c:105
base
int base
Definition: dma65.c:82
romkernal
char * romkernal
Definition: commodore_geos.c:151
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
cpu65.h
XEMUEXIT
#define XEMUEXIT(n)
Definition: emutools_basicdefs.h:246
cia_init
void cia_init(struct Cia6526 *cia, const char *name, void(*outa)(Uint8 data), void(*outb)(Uint8 data), void(*outsr)(Uint8 data), Uint8(*ina)(void), Uint8(*inb)(void), Uint8(*insr)(void), void(*setint)(int level))
Definition: cia6526.c:98
colour_sram
Uint8 colour_sram[1024]
Definition: commodore_geos.c:117
xemu_timekeeping_start
void xemu_timekeeping_start(void)
Definition: emutools.c:1122
syscon
int syscon
Definition: commodore_geos.c:149
configdb_st::syscon
int syscon
Definition: configdb.h:34
MAP_RAM
#define MAP_RAM
Definition: commodore_geos.c:73
io_read
Uint8 io_read(int addr)
Definition: commodore_65.c:472
cpu65_reset
void cpu65_reset(void)
Definition: cpu65.c:353
configdb_st::fullscreen
int fullscreen
Definition: configdb.h:34
SCREEN_HEIGHT
#define SCREEN_HEIGHT
Definition: vic3.h:30
fullscreen
int fullscreen
Definition: commodore_geos.c:149
SCREEN_FORMAT
#define SCREEN_FORMAT
Definition: commodore_65.h:25
RENDER_SCALE_QUALITY
#define RENDER_SCALE_QUALITY
Definition: commodore_65.h:27
y
int y
Definition: console.c:27
mask
int mask
Definition: dma65.c:83
APP_DESC_APPEND
#define APP_DESC_APPEND
Definition: emutools.h:52
IO_OFFSET
#define IO_OFFSET
Definition: commodore_geos.c:68
Uint16
uint16_t Uint16
Definition: fat32.c:50
emutools_hid.h
emu_callback_key
int emu_callback_key(int pos, SDL_Scancode key, int pressed, int handled)
Definition: commodore_geos.c:764
MAP_BASIC_TWICE
#define MAP_BASIC_TWICE
Definition: commodore_geos.c:80
geosimg
char * geosimg
Definition: commodore_geos.c:151
xemucfg_define_switch_option
void xemucfg_define_switch_option(const char *optname, const char *help, int *storage)
DEBUG
#define DEBUG(...)
Definition: emutools_basicdefs.h:167
GET_WRITE_P
#define GET_WRITE_P(a)
Definition: commodore_geos.c:104
cpu65_step
int cpu65_step(void)
Definition: cpu65.c:796
clear_emu_events
void clear_emu_events(void)
Definition: commodore_geos.c:514
FATAL
#define FATAL(...)
Definition: xep128.h:117
IS_RESTORE_PRESSED
#define IS_RESTORE_PRESSED()
Definition: c64_kbd_mapping.h:33
cpu65_write_rmw_callback
void cpu65_write_rmw_callback(Uint16 addr, Uint8 old_data, Uint8 new_data)
Definition: commodore_geos.c:743
BASIC_ROM_OFFSET
#define BASIC_ROM_OFFSET
Definition: commodore_geos.c:65
c64_kbd_mapping.h
frameskip
int frameskip
Definition: vic3.c:75
geoskernal
char * geoskernal
Definition: commodore_geos.c:151
cia_write
void cia_write(struct Cia6526 *cia, int addr, Uint8 data)
Definition: cia6526.c:169
cpu65_illegal_opcode_callback
void cpu65_illegal_opcode_callback(void)
Definition: commodore_geos.c:709