48 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.";
50 static char emulator_addon_title[32] =
"";
52 static int cpu_mhz, cpu_cycles_per_tv_frame;
56 static Uint8 charrom[4096];
57 extern unsigned const char roms[];
58 static int mmu[3][4] = {
61 {0, 0, 0x30000, 0x30000}
63 static int *mmu_current = mmu[0];
64 static int *mmu_saved = mmu[0];
65 static Uint8 lcd_ctrl[4];
68 static Uint8 rtc_regs[16];
69 static int rtc_sel = 0;
78 static const Uint8 init_lcd_palette_rgb[6] = {
82 static Uint32 lcd_palette[2];
84 #define VIRTUAL_SHIFT_POS 0x82
86 { SDL_SCANCODE_BACKSPACE, 0x00 },
87 { SDL_SCANCODE_3, 0x01 },
88 { SDL_SCANCODE_5, 0x02 },
89 { SDL_SCANCODE_7, 0x03 },
90 { SDL_SCANCODE_9, 0x04 },
91 { SDL_SCANCODE_DOWN, 0x05 },
92 { SDL_SCANCODE_LEFT, 0x06 },
93 { SDL_SCANCODE_1, 0x07 },
94 { SDL_SCANCODE_RETURN, 0x10 },
95 { SDL_SCANCODE_W, 0x11 },
96 { SDL_SCANCODE_R, 0x12 },
97 { SDL_SCANCODE_Y, 0x13 },
98 { SDL_SCANCODE_I, 0x14 },
99 { SDL_SCANCODE_P, 0x15 },
100 { SDL_SCANCODE_RIGHTBRACKET, 0x16 },
101 { SDL_SCANCODE_HOME, 0x17 },
102 { SDL_SCANCODE_TAB, 0x20 },
103 { SDL_SCANCODE_A, 0x21 },
104 { SDL_SCANCODE_D, 0x22 },
105 { SDL_SCANCODE_G, 0x23 },
106 { SDL_SCANCODE_J, 0x24 },
107 { SDL_SCANCODE_L, 0x25 },
108 { SDL_SCANCODE_SEMICOLON, 0x26 },
109 { SDL_SCANCODE_F2, 0x27 },
110 { SDL_SCANCODE_F7, 0x30 },
111 { SDL_SCANCODE_4, 0x31 },
112 { SDL_SCANCODE_6, 0x32 },
113 { SDL_SCANCODE_8, 0x33 },
114 { SDL_SCANCODE_0, 0x34 },
115 { SDL_SCANCODE_UP, 0x35 },
116 { SDL_SCANCODE_RIGHT, 0x36 },
117 { SDL_SCANCODE_2, 0x37 },
118 { SDL_SCANCODE_F1, 0x40 },
119 { SDL_SCANCODE_Z, 0x41 },
120 { SDL_SCANCODE_C, 0x42 },
121 { SDL_SCANCODE_B, 0x43 },
122 { SDL_SCANCODE_M, 0x44 },
123 { SDL_SCANCODE_PERIOD, 0x45 },
124 { SDL_SCANCODE_ESCAPE, 0x46 },
125 { SDL_SCANCODE_SPACE, 0x47 },
126 { SDL_SCANCODE_F3, 0x50 },
127 { SDL_SCANCODE_S, 0x51 },
128 { SDL_SCANCODE_F, 0x52 },
129 { SDL_SCANCODE_H, 0x53 },
130 { SDL_SCANCODE_K, 0x54 },
131 { SDL_SCANCODE_APOSTROPHE, 0x55 },
132 { SDL_SCANCODE_EQUALS, 0x56 },
133 { SDL_SCANCODE_F8, 0x57 },
134 { SDL_SCANCODE_F5, 0x60 },
135 { SDL_SCANCODE_E, 0x61 },
136 { SDL_SCANCODE_T, 0x62 },
137 { SDL_SCANCODE_U, 0x63 },
138 { SDL_SCANCODE_O, 0x64 },
139 { SDL_SCANCODE_MINUS, 0x65 },
140 { SDL_SCANCODE_BACKSLASH, 0x66 },
141 { SDL_SCANCODE_Q, 0x67 },
142 { SDL_SCANCODE_LEFTBRACKET, 0x70 },
143 { SDL_SCANCODE_F4, 0x71 },
144 { SDL_SCANCODE_X, 0x72 },
145 { SDL_SCANCODE_V, 0x73 },
146 { SDL_SCANCODE_N, 0x74 },
147 { SDL_SCANCODE_COMMA, 0x75 },
148 { SDL_SCANCODE_SLASH, 0x76 },
149 { SDL_SCANCODE_F6, 0x77 },
151 { SDL_SCANCODE_END, 0x80 },
152 { SDL_SCANCODE_CAPSLOCK, 0x81},
153 { SDL_SCANCODE_LSHIFT, 0x82 }, { SDL_SCANCODE_RSHIFT, 0x82 },
154 { SDL_SCANCODE_LCTRL, 0x83 }, { SDL_SCANCODE_RCTRL, 0x83 },
155 { SDL_SCANCODE_LALT, 0x84 }, { SDL_SCANCODE_RALT, 0x84 },
175 #define GET_MEMORY(phys_addr) memory[phys_addr]
182 if (
addr >= 0xF980)
return 0;
183 if (
addr >= 0xF900)
return 0xFF;
194 if (
addr >= 0xF800) {
195 switch ((
addr - 0xF800) >> 7) {
200 case 4: mmu_current = mmu[2];
return;
201 case 5: mmu_current = mmu[1];
return;
202 case 6: mmu_current = mmu[0];
return;
203 case 7: mmu_current = mmu_saved;
return;
204 case 8: mmu_saved = mmu_current;
return;
205 case 9:
FATAL(
"MMU test mode is set, it would not work");
break;
206 case 10: mmu[1][0] =
data << 10;
return;
207 case 11: mmu[1][1] =
data << 10;
return;
208 case 12: mmu[1][2] =
data << 10;
return;
209 case 13: mmu[1][3] =
data << 10;
return;
210 case 14: mmu[2][1] =
data << 10;
return;
211 case 15: lcd_ctrl[
addr & 3] =
data;
return;
213 DEBUG(
"ERROR: should be not here!" NL);
216 maddr = (mmu_current[
addr >> 14] +
addr) & 0x3FFFF;
217 if (maddr < ram_size) {
221 DEBUG(
"MEM: out-of-RAM write addr=$%04X maddr=$%05X" NL,
addr, maddr);
232 static Uint8 portB1 = 0, portA2 = 0;
233 static int keytrans = 0;
234 static int powerstatus = 0;
239 keytrans = ((!(portB1 & 1)) && (
data & 1));
245 static void via2_setint(
int level) {}
260 return rtc_regs[rtc_sel] | (portA2 & 0x70);
267 static Uint8 via2_insr() {
return 0xFF; }
268 static Uint8 via1_insr()
285 static void via1_setint(
int level)
288 cpu65.irqLevel = level;
293 #define BG lcd_palette[0]
294 #define FG lcd_palette[1]
296 static void render_screen (
void )
298 int ps = lcd_ctrl[1] << 7;
302 if (lcd_ctrl[2] & 2) {
303 for (
y = 0;
y < 128;
y++) {
304 for (
x = 0;
x < 60;
x++) {
306 *(pix++) = (ch & 0x80) ?
FG :
BG;
307 *(pix++) = (ch & 0x40) ?
FG :
BG;
308 *(pix++) = (ch & 0x20) ?
FG :
BG;
309 *(pix++) = (ch & 0x10) ?
FG :
BG;
310 *(pix++) = (ch & 0x08) ?
FG :
BG;
311 *(pix++) = (ch & 0x04) ?
FG :
BG;
312 *(pix++) = (ch & 0x02) ?
FG :
BG;
313 *(pix++) = (ch & 0x01) ?
FG :
BG;
315 ps = (ps + 4) & 0x7FFF;
319 int cof = (lcd_ctrl[2] & 1) << 10;
320 int maxx = (lcd_ctrl[3] & 4) ? 60 : 80;
321 ps += lcd_ctrl[0] & 0x7F;
322 for (
y = 0;
y < 128;
y++) {
323 for (
x = 0;
x < maxx;
x++) {
326 ch = charrom[cof + ((ch & 0x7F) << 3) + (
y & 7)] ^ ((ch & 0x80) ? 0xFF : 0x00);
327 pix[0] = (ch & 0x80) ?
FG :
BG;
328 pix[1] = (ch & 0x40) ?
FG :
BG;
329 pix[2] = (ch & 0x20) ?
FG :
BG;
330 pix[3] = (ch & 0x10) ?
FG :
BG;
331 pix[4] = (ch & 0x08) ?
FG :
BG;
332 pix[5] = (ch & 0x04) ?
FG :
BG;
333 if (lcd_ctrl[3] & 4) {
334 pix[6] = (ch & 0x02) ?
FG :
BG;
335 pix[7] = (ch & 0x01) ?
FG :
BG;
349 if (!xemu_screenshot_png(
358 OSD(-1, -1,
"Screenshot has been taken");
364 static const struct menu_st menu_main[];
370 if (!pressed && pos == -2 && key == 0 && handled == SDL_BUTTON_RIGHT) {
380 static void update_rtc (
void )
383 rtc_regs[ 0] = t->tm_sec % 10;
384 rtc_regs[ 1] = t->tm_sec / 10;
385 rtc_regs[ 2] = t->tm_min % 10;
386 rtc_regs[ 3] = t->tm_min / 10;
387 rtc_regs[ 4] = t->tm_hour % 10;
388 rtc_regs[ 5] = (t->tm_hour / 10) | 8;
389 rtc_regs[ 6] = t->tm_wday;
390 rtc_regs[ 9] = t->tm_mday % 10;
391 rtc_regs[10] = t->tm_mday / 10;
392 rtc_regs[ 7] = (t->tm_mon + 1) % 10;
393 rtc_regs[ 8] = (t->tm_mon + 1) / 10;
394 rtc_regs[11] = (t->tm_year - 84) % 10;
395 rtc_regs[12] = (t->tm_year - 84) / 10;
400 static void update_emulator (
void )
403 static const Uint8 screen_sample1[] = { 0x20, 0x03, 0x0f, 0x0d, 0x0d, 0x0f, 0x04, 0x0f, 0x12, 0x05, 0x20, 0x0c, 0x03, 0x04, 0x20 };
404 static const Uint8 screen_sample2[] = { 0x12, 0x05, 0x01, 0x04, 0x19, 0x2e };
406 !memcmp(memory + 0x880, screen_sample1,
sizeof screen_sample1) &&
407 !memcmp(memory + 0x980, screen_sample2,
sizeof screen_sample2)
409 prg_inject.phase = 2;
410 DEBUGPRINT(
"BASIC: startup screen detected, injecting loaded basic program!" NL);
411 memory[0xA01] =
'R' -
'A' + 1;
412 memory[0xA02] =
'U' -
'A' + 1;
413 memory[0xA03] =
'N' -
'A' + 1;
415 memset(memory + 0x1000, 0, 0x8000);
416 memcpy(memory + 0x1001, prg_inject.data + 2, prg_inject.size - 2);
421 addr += prg_inject.size - 2;
435 static void shutdown_emu (
void )
438 #ifndef __EMSCRIPTEN__
439 FILE *f = fopen(
"memory.dump",
"wb");
441 fwrite(memory,
sizeof memory, 1, f);
453 static void rom_list (
void )
456 for (
int addr = 0x20000;
addr < 0x40000;
addr += 0x4000) {
457 if (!memcmp(memory +
addr + 8,
"Commodore LCD", 13)) {
459 int pos =
addr + 13 + 8;
460 while (memory[pos]) {
462 memcpy(
name, memory + pos + 6, memory[pos] - 6);
464 DEBUGPRINT(
"\t($%02X $%02X $%02X) START=$%04X : \"%s\"" NL,
465 memory[pos + 1], memory[pos + 2], memory[pos + 3],
466 memory[pos + 4] | (memory[pos + 5] <<8),
484 static void load_program_for_inject (
const char *file_name,
int new_address )
486 prg_inject.phase = 0;
489 memset(prg_inject.data, 0,
sizeof prg_inject.data);
490 prg_inject.size =
xemu_load_file(file_name, prg_inject.data, 8,
sizeof(prg_inject.data) - 4,
"Cannot load program");
491 if (prg_inject.size <= 0)
493 int old_address = prg_inject.data[0] | (prg_inject.data[1] << 8);
494 DEBUGPRINT(
"PRG: program \"%s\" load_addr=$%04X, new_load_addr=$%04X" NL, file_name, old_address, new_address);
495 if (old_address != new_address) {
498 if (prg_inject.data[i] == 0 && prg_inject.data[i + 1] == 0)
502 while (prg_inject.data[i])
505 new_address += i - o;
506 DEBUGPRINT(
"BASIC: re-linking line (%d) $%04X -> $%04X" NL,
507 prg_inject.data[o + 2] | (prg_inject.data[o + 3] << 8),
508 prg_inject.data[o] | (prg_inject.data[o + 1] << 8),
511 prg_inject.data[o] = new_address & 0xFF;
512 prg_inject.data[o + 1] = new_address >> 8;
516 prg_inject.phase = 1;
520 static void update_addon_title (
void )
522 sprintf(emulator_addon_title,
"(%dMHz, %dK RAM)", cpu_mhz, ram_size >> 10);
526 static void set_ram_size (
int kbytes )
528 ram_size = kbytes << 10;
529 DEBUGPRINT(
"MEM: RAM size is set to %dKbytes." NL, kbytes);
530 update_addon_title();
533 static void set_cpu_speed (
int mhz )
536 cpu_cycles_per_tv_frame = mhz * 1000000 / 25;
537 DEBUGPRINT(
"CPU: setting CPU to %dMHz, %d CPU cycles per full 1/25sec frame." NL, mhz, cpu_cycles_per_tv_frame);
538 update_addon_title();
543 static int cycles, viacyc;
546 static void emulation_loop (
void )
552 if (viacyc >= cpu_mhz) {
553 int steps = viacyc / cpu_mhz;
554 viacyc = viacyc % cpu_mhz;
561 cycles -= cpu_cycles_per_tv_frame;
570 static void ui_cb_clock_mhz (
const struct menu_st *m,
int *query )
576 static void ui_cb_ramsize (
const struct menu_st *m,
int *query )
584 static const struct menu_st menu_display[] = {
591 static const struct menu_st menu_clock[] = {
600 static const struct menu_st menu_ramsize[] = {
611 static const struct menu_st menu_main[] = {
615 #ifdef XEMU_FILES_SCREENSHOT_SUPPORT
622 #ifdef HAVE_XEMU_EXEC_API
627 #ifdef HAVE_XEMU_EXEC_API
652 int main (
int argc,
char **argv )
657 {
"keepram",
"Deactivate ROM patch for clear RAM and also save/restore RAM", &
configdb.keep_ram },
658 {
"syscon",
"Keep system console open (Windows-specific effect only)", &
configdb.
syscon },
662 #ifdef SDL_HINT_RENDER_SCALE_QUALITY
665 {
"zoom", 1,
"Window zoom value (1-4)", &
configdb.zoom, 1, 4 },
666 {
"ram", 128,
"Sets RAM size in KBytes (32-128)", &
configdb.ram_size_kbytes, 32, 128 },
667 {
"clock", 1,
"Sets CPU speed in MHz (integer only) 1-16", &
configdb.clock_mhz, 1, 16 }
670 {
"rom102",
"#clcd-u102.rom",
"Selects 'U102' ROM to use", &
configdb.rom102_fn },
671 {
"rom103",
"#clcd-u103.rom",
"Selects 'U103' ROM to use", &
configdb.rom103_fn },
672 {
"rom104",
"#clcd-u104.rom",
"Selects 'U104' ROM to use", &
configdb.rom104_fn },
673 {
"rom105",
"#clcd-u105.rom",
"Selects 'U105' ROM to use", &
configdb.rom105_fn },
674 {
"romchr",
"#clcd-chargen.rom",
"Selects character ROM to use", &
configdb.romchr_fn },
676 {
"prg", NULL,
"Inject BASIC program on entering to BASIC", &
configdb.prg_inject_fn },
677 {
"gui", NULL,
"Select GUI type for usage. Specify some insane str to get a list", &
configdb.
gui_selection }
685 set_ram_size(
configdb.ram_size_kbytes);
696 init_lcd_palette_rgb,
698 #ifdef SDL_HINT_RENDER_SCALE_QUALITY
715 memset(charrom, 0xFF,
sizeof charrom);
725 #ifdef ROM_HACK_COLD_START
728 DEBUGPRINT(
"ROM-HACK: forcing cold start condition with ROM patching!" NL);
732 DEBUGPRINT(
"ROM-HACK: SKIP cold start condition forcing!" NL);
736 #ifdef ROM_HACK_NEW_ROM_SEARCHING
741 DEBUG(
"ROM HACK: modifying ROM searching MMU table" NL);
750 load_program_for_inject(
configdb.prg_inject_fn, 0x1001);
755 via_init(&via1,
"VIA#1", via1_outa, via1_outb, via1_outsr, via1_ina, via1_inb, via1_insr, via1_setint);
756 via_init(&via2,
"VIA#2", via2_outa, via2_outb, via2_outsr, via2_ina, via2_inb, via2_insr, via2_setint);