Xemu [doxygen]  hyppo 0a42be3a057156924bc1b626a687bd6e27349c45 @ Sat 19 Mar 02:15:11 CET 2022
hypervisor.c
Go to the documentation of this file.
1 /* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator
2  Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu
3  Copyright (C)2016-2022 LGB (Gábor Lénárt) <lgblgblgb@gmail.com>
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
18 
19 #include "xemu/emutools.h"
20 #include "xemu/emutools_files.h"
21 #include "mega65.h"
22 #include "hypervisor.h"
23 #include "xemu/cpu65.h"
24 #include "vic4.h"
25 #include "dma65.h"
26 #include "memory_mapper.h"
27 #include "io_mapper.h"
28 #include "xemu/emutools_config.h"
29 #include "configdb.h"
30 #include "rom.h"
31 #define XEMU_MEGA65_HDOS_H_ALLOWED
32 #include "hdos.h"
33 
34 #include <sys/types.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <errno.h>
38 
39 
40 int in_hypervisor; // mega65 hypervisor mode
44 
45 static int resolver_ok = 0;
46 
47 static char hypervisor_monout[0x10000];
48 static char *hypervisor_monout_p = hypervisor_monout;
49 
50 static int hypervisor_serial_out_asciizer;
51 
52 static int hypervisor_is_first_call;
53 static int execution_range_check_gate;
54 static int trap_current;
55 static int current_hdos_func = -1;
56 
57 static int hypervisor_queued_trap = -1;
58 
59 static char *debug_file_storage = NULL;
60 struct debug_info_st {
61  const char *src_fn; // source file name
62  int src_ln; // source line number
63  const char *sym_name; // symbol name
64  int sym_offs; // offset relative to the symbol
65  int exec; // @ this addr, executable? [out-of-bound PC check]
66  const char *line; // source line (minus symbol definition)
67 };
68 // Will be malloc'ed to have 0x4000 entries for the space of $8000-$BFFF of hypervisor
69 // RAM, info about each bytes there
70 static struct debug_info_st *debug_info = NULL;
71 
72 
73 
74 int hypervisor_queued_enter ( int trapno )
75 {
76  if (!in_hypervisor) {
77  DEBUG("HYPERVISOR: no need to queue trap, can be executed now #$%02X" NL, trapno);
78  hypervisor_enter(trapno);
79  return 0;
80  } else if (hypervisor_queued_trap < 0) {
81  hypervisor_queued_trap = trapno;
82  DEBUG("HYPERVISOR: queueing trap #$%02X" NL, trapno);
83  return 0;
84  } else {
85  DEBUGPRINT("HYPERVISOR: cannot queue trap #$%02X, already have a queued one #$%02X" NL, trapno, hypervisor_queued_trap);
86  return 1;
87  }
88 }
89 
90 
91 // Very same as hypervisor_enter() - actually it calls that
92 // **BUT** we check here, if next opcode is NOP.
93 // it should be, as on real hardware this is a relability problem.
94 // (sometimes one byte is skipped on execution after a trap caused by writing D640-D67F)
96 {
98  static int do_warn = 1;
99  if (do_warn) {
100  WARNING_WINDOW("DMA operation would trigger hypervisor trap.\nThis is totally ignored!\nThere will be no future warning before you restart Xemu");
101  do_warn = 0;
102  }
103  return;
104  }
105  static int do_nop_check = 1;
106  if (do_nop_check) {
107  // FIXME: for real there should be a memory reading function independent to the one used by the CPU, since
108  // this has some side effects to just fetch a byte to check something, which is otherwise used normally to fetch CPU opcodes and such
109  Uint8 skipped_byte = cpu65_read_callback(cpu65.pc);
110  if (XEMU_UNLIKELY(skipped_byte != 0xEA && skipped_byte != 0xB8)) { // $EA = opcode for NOP, $B8 = opcode for CLV
111  char msg[256];
112  snprintf(msg, sizeof msg,
113  "Writing hypervisor trap $%02X must be followed by NOP/CLV\n"
114  "but found opcode $%02X at PC=$%04X\n\n"
115  "This will cause problems on a real MEGA65!"
116  ,
117  0xD640 + trapno, skipped_byte, cpu65.pc
118  );
119  if (QUESTION_WINDOW(
120  "Ignore now|Ignore all",
121  msg
122  )) {
123  do_nop_check = 0;
124  INFO_WINDOW("There will be no further warnings on this issue\nuntil you restart Xemu");
125  }
126  }
127  }
128  hypervisor_enter(trapno);
129 }
130 
131 
132 void hypervisor_enter ( int trapno )
133 {
134  trap_current = trapno;
135  // Sanity checks
136  if (XEMU_UNLIKELY(trapno > 0x7F || trapno < 0))
137  FATAL("FATAL: got invalid trap number %d", trapno);
139  FATAL("FATAL: already in hypervisor mode while calling hypervisor_enter()");
140  // First, save machine status into hypervisor registers, TODO: incomplete, can be buggy!
141  D6XX_registers[0x40] = cpu65.a;
142  D6XX_registers[0x41] = cpu65.x;
143  D6XX_registers[0x42] = cpu65.y;
144  D6XX_registers[0x43] = cpu65.z;
145  D6XX_registers[0x44] = cpu65.bphi >> 8; // "B" register
146  D6XX_registers[0x45] = cpu65.s;
147  D6XX_registers[0x46] = cpu65.sphi >> 8; // stack page register
148  D6XX_registers[0x47] = cpu65_get_pf();
149  D6XX_registers[0x48] = cpu65.pc & 0xFF;
150  D6XX_registers[0x49] = cpu65.pc >> 8;
151  D6XX_registers[0x4A] = ((map_offset_low >> 16) & 0x0F) | ((map_mask & 0x0F) << 4);
152  D6XX_registers[0x4B] = ( map_offset_low >> 8) & 0xFF ;
153  D6XX_registers[0x4C] = ((map_offset_high >> 16) & 0x0F) | ( map_mask & 0xF0);
154  D6XX_registers[0x4D] = ( map_offset_high >> 8) & 0xFF ;
155  D6XX_registers[0x4E] = map_megabyte_low >> 20;
156  D6XX_registers[0x4F] = map_megabyte_high >> 20;
159  D6XX_registers[0x52] = vic_iomode;
160  D6XX_registers[0x53] = dma_registers[5]; // GS $D653 - Hypervisor DMAgic source MB
161  D6XX_registers[0x54] = dma_registers[6]; // GS $D654 - Hypervisor DMAgic destination MB
162  D6XX_registers[0x55] = dma_registers[0]; // GS $D655 - Hypervisor DMAGic list address bits 0-7
163  D6XX_registers[0x56] = dma_registers[1]; // GS $D656 - Hypervisor DMAGic list address bits 15-8
164  D6XX_registers[0x57] = (dma_registers[2] & 15) | ((dma_registers[4] & 15) << 4); // GS $D657 - Hypervisor DMAGic list address bits 23-16
165  D6XX_registers[0x58] = dma_registers[4] >> 4; // GS $D658 - Hypervisor DMAGic list address bits 27-24
166  // Now entering into hypervisor mode
167  in_hypervisor = 1; // this will cause apply_memory_config to map hypervisor RAM, also for checks later to out-of-bound execution of hypervisor RAM, etc ...
169  memory_set_cpu_io_port_ddr_and_data(0x3F, 0x35); // sets all-RAM + I/O config up!
170  cpu65.pf_d = 0; // clear decimal mode ... according to Paul, punnishment will be done, if it's removed :-)
171  cpu65.pf_i = 1; // disable IRQ in hypervisor mode
172  cpu65.pf_e = 1; // 8 bit stack in hypervisor mode
173  cpu65.sphi = 0xBE00; // set a nice shiny stack page
174  cpu65.bphi = 0xBF00; // ... and base page (aka zeropage)
175  cpu65.s = 0xFF;
176  // Set mapping for the hypervisor
177  map_mask = (map_mask & 0xF) | 0x30; // mapping: 0011XXXX (it seems low region map mask is not changed by hypervisor entry)
178  map_megabyte_high = 0xFF << 20;
179  map_offset_high = 0xF0000;
180  memory_set_vic3_rom_mapping(0); // for VIC-III rom mapping disable in hypervisor mode
181  memory_set_do_map(); // now the memory mapping is changed
182  machine_set_speed(0); // set machine speed (hypervisor always runs at M65 fast ... ??) FIXME: check this!
183  cpu65.pc = 0x8000 | (trapno << 2); // load PC with the address assigned for the given trap number
184  DEBUG("HYPERVISOR: entering into hypervisor mode, trap=$%02X (A=$%02X) @ $%04X -> $%04X" NL, trapno, cpu65.a, D6XX_registers[0x48] | (D6XX_registers[0x49] << 8), cpu65.pc);
185  if (trapno == TRAP_DOS) {
186  current_hdos_func = cpu65.a & 0x7E;
187  hdos_enter(current_hdos_func);
188  } else
189  current_hdos_func = -1;
190  if (XEMU_UNLIKELY(trapno == TRAP_RESET)) {
194  DEBUGPRINT("HYPERVISOR: setting video standard change banning" NL);
195  }
196  }
198  if (!configdb.allowfreezer) {
199  // If freezer is not enabled I warn the user, also return from the hypervisor trap now, without doing anything
200  WARNING_WINDOW("FREEZER is not enabled in Xemu currently.");
201  // Leave hypervisor mode now, do not allow Hyppo to get this trap.
202  if (trapno == TRAP_FREEZER_USER_CALL) {
203  D6XX_registers[0x47] &= ~CPU65_PF_C; // clear carry flag (bit zero)
204  D6XX_registers[0x40] = 0xFF; // set A register to $FF
205  }
207  } else if (configdb.hyperdebugfreezer) {
209  }
210  }
211 #ifdef TRAP_XEMU
212  if (XEMU_UNLIKELY(trapno == TRAP_XEMU)) {
213  // Xemu's own trap.
215  hypervisor_leave(); // leave hypervisor mode, do not allow Hyppo to take control on this trap
216  }
217 #endif
218 }
219 
220 
221 static void extract_version_string ( char *target, int target_max_size )
222 {
223  static const char marker[] = "GIT: ";
224  int a = 0;
225  // Note: memmem() would be handy, but a bit problematic, some platforms don't have it,
226  // others may require special macros to be defined to be able to use it.
227  for (;;) {
228  if (a == 0x4000 - 0x10) {
229  strcpy(target, "???");
230  return;
231  }
232  if (!memcmp(hypervisor_ram + a, marker, strlen(marker)))
233  break;
234  a++;
235  }
236  a += strlen(marker);
237  int l = 0;
238  while (a < 0x4000 && hypervisor_ram[a + l])
239  l++;
240  if (l >= target_max_size)
241  l = target_max_size - 1;
242  memcpy(target, hypervisor_ram + a, l);
243  target[l] = '\0';
244 }
245 
246 
247 // Actual (CPU level opcode execution) emulation of MEGA65 should start with calling this function (surely after initialization of every subsystems etc).
249 {
250  static int init_done = 0;
251  if (XEMU_UNLIKELY(!init_done)) {
252  init_done = 1;
253  hyppo_version_string[0] = '\0';
255  }
256  in_hypervisor = 0;
257  hypervisor_queued_trap = -1;
258  hypervisor_is_first_call = 1;
259  execution_range_check_gate = 0;
260  extract_version_string(hyppo_version_string, sizeof hyppo_version_string);
261  DEBUGPRINT("HYPERVISOR: HYPPO version \"%s\" (%s) starting with TRAP reset (#$%02X)" NL, hyppo_version_string, hickup_is_overriden ? "OVERRIDEN" : "built-in", TRAP_RESET);
263 }
264 
265 
266 static inline void first_leave ( void )
267 {
268  DEBUGPRINT("HYPERVISOR: first return after RESET, start of processing workarounds." NL);
269  execution_range_check_gate = 1;
270  // Workarounds: ROM override
271  // returns with new PC for reset vector _OR_ negative value if no ROM override was needed
272  int new_pc = rom_do_override(main_ram + 0x20000);
273  if (new_pc >= 0) {
274  // if ROM was forced here, PC hypervisor would return is invalid (valid for the _original_ ROM which was overriden here!), thus we must set it now!
275  DEBUGPRINT("ROM: force ROM re-apply policy, PC change: $%04X -> $%04X" NL, cpu65.pc, new_pc);
276  if (new_pc < 0x8000)
277  WARNING_WINDOW("ROM override has a suspect reset address! ($%04X)", new_pc);
278  cpu65.pc = new_pc;
279  // Since we have overriden ROM, we also must take the responsibility to do what Hyppo would also do:
280  // uploading chargen from the loaded ROM into the "char WOM".
281  memcpy(char_wom, main_ram + 0x2D000, 0x1000);
282  } else {
283  DEBUGPRINT("ROM: no custom force-ROM policy, PC remains at $%04X" NL, cpu65.pc);
284  }
285  // Workaround: set DMA version based on ROM version
287  // Workaround: force video standard
288  if (configdb.init_videostd >= 0) {
289  DEBUGPRINT("VIC: setting %s mode as boot-default based on request" NL, configdb.init_videostd ? "NTSC" : "PAL");
291  vic_registers[0x6F] |= 0x80;
292  else
293  vic_registers[0x6F] &= 0x7F;
294  }
296  DEBUGPRINT("HYPERVISOR: first return after RESET, end of processing workarounds." NL);
297 }
298 
299 
301 {
302  if (!in_hypervisor) {
303  DEBUGPRINT("HYPERVISOR: hypervisor-only reset was requested by Xemu." NL);
305  last_reset_type = "HYPPO";
306  return 0;
307  }
308  DEBUGPRINT("HYPERVISOR: hypervisor-only reset requested by Xemu **FAILED**: already in hypervisor mode!" NL);
309  return 1;
310 }
311 
312 
313 void hypervisor_leave ( void )
314 {
315  // Sanity check
317  FATAL("FATAL: not in hypervisor mode while calling hypervisor_leave()");
318  DEBUG("HYPERVISOR: leaving hypervisor mode @ $%04X -> $%04X" NL, cpu65.pc, D6XX_registers[0x48] | (D6XX_registers[0x49] << 8));
319  // First, restore machine status from hypervisor registers
320  cpu65.a = D6XX_registers[0x40];
321  cpu65.x = D6XX_registers[0x41];
322  cpu65.y = D6XX_registers[0x42];
323  cpu65.z = D6XX_registers[0x43];
324  cpu65.bphi = D6XX_registers[0x44] << 8; // "B" register
325  cpu65.s = D6XX_registers[0x45];
326  cpu65.sphi = D6XX_registers[0x46] << 8; // stack page register
328  cpu65.pf_e = D6XX_registers[0x47] & CPU65_PF_E; // cpu65_set_pf() does NOT set 'E' bit by design, so we do at our own
329  cpu65.pc = D6XX_registers[0x48] | (D6XX_registers[0x49] << 8);
330  if (current_hdos_func >= 0)
331  hdos_leave(current_hdos_func);
332  map_offset_low = ((D6XX_registers[0x4A] & 0xF) << 16) | (D6XX_registers[0x4B] << 8);
333  map_offset_high = ((D6XX_registers[0x4C] & 0xF) << 16) | (D6XX_registers[0x4D] << 8);
334  map_mask = (D6XX_registers[0x4A] >> 4) | (D6XX_registers[0x4C] & 0xF0);
335  map_megabyte_low = D6XX_registers[0x4E] << 20;
336  map_megabyte_high = D6XX_registers[0x4F] << 20;
338  vic_iomode = D6XX_registers[0x52] & 3;
339  if (vic_iomode == VIC_BAD_IOMODE)
340  vic_iomode = VIC3_IOMODE; // I/O mode "2" (binary: 10) is not used, I guess
341  dma_registers[5] = D6XX_registers[0x53]; // GS $D653 - Hypervisor DMAgic source MB
342  dma_registers[6] = D6XX_registers[0x54]; // GS $D654 - Hypervisor DMAgic destination MB
343  dma_registers[0] = D6XX_registers[0x55]; // GS $D655 - Hypervisor DMAGic list address bits 0-7
344  dma_registers[1] = D6XX_registers[0x56]; // GS $D656 - Hypervisor DMAGic list address bits 15-8
345  dma_registers[2] = D6XX_registers[0x57] & 15; //
346  dma_registers[4] = (D6XX_registers[0x57] >> 4) | (D6XX_registers[0x58] << 4);
347  // Now leaving hypervisor mode ...
348  in_hypervisor = 0;
349  machine_set_speed(0); // restore speed ...
350  memory_set_vic3_rom_mapping(vic_registers[0x30]); // restore possible active VIC-III mapping
351  memory_set_do_map(); // restore mapping ...
352  if (XEMU_UNLIKELY(hypervisor_is_first_call)) {
353  if (trap_current != TRAP_RESET)
354  ERROR_WINDOW("First hypervisor TRAP is not RESET?!");
355  first_leave();
356  }
357  // Catch the event when it's a reset TRAP but not part of the initial call (not "cold" reset)
358  if (XEMU_UNLIKELY(trap_current == TRAP_RESET && !hypervisor_is_first_call)) {
359  // If Xemu does a "hyppo level reset" only, we can have problems with knowing the
360  // exact ROM type, thus, make sure, to do it here.
361  // FIXME: really, should we care about this minor detail so much?! ;)
363  rom_detect_date(main_ram + 0x20000);
364  }
365  hypervisor_is_first_call = 0;
366  if (XEMU_UNLIKELY(trap_current == TRAP_RESET)) {
368  DEBUGPRINT("HYPERVISOR: clearing video standard change banning" NL);
370  }
371  }
372  if (XEMU_UNLIKELY(hypervisor_queued_trap >= 0)) {
373  // Not so much used currently ...
374  DEBUG("HYPERVISOR: processing queued trap on leaving hypervisor: trap #$%02X" NL, hypervisor_queued_trap);
375  hypervisor_enter(hypervisor_queued_trap);
376  hypervisor_queued_trap = -1;
377  }
378 }
379 
380 
382 {
383  if (hypervisor_monout_p >= hypervisor_monout - 1 + sizeof hypervisor_monout)
384  return;
385  int flush = (chr == 0x0A || chr == 0x0D || chr == 0x8A || chr == 0x8D);
386  if (hypervisor_monout_p == hypervisor_monout && flush)
387  return;
388  if (flush) {
389  *hypervisor_monout_p = 0;
390  if (hypervisor_serial_out_asciizer) {
391  unsigned char *p = (unsigned char*)hypervisor_monout;
392  while (*p) {
393  if (*p >= 0x61 && *p <= 0x7A)
394  *p -= 0x20;
395  else if (*p >= 0xC1 && *p <= 0xDA)
396  *p -= 0x80;
397  else if (*p < 0x20 || *p >= 0x80)
398  *p = '?';
399  p++;
400  }
401  }
402  fprintf(stderr, "Hypervisor serial output: \"%s\"." NL, hypervisor_monout);
403  DEBUG("MEGA65: Hypervisor serial output: \"%s\"." NL, hypervisor_monout);
404  hypervisor_monout_p = hypervisor_monout;
405  return;
406  }
407  *(hypervisor_monout_p++) = (char)chr;
408 }
409 
410 
411 void hypervisor_debug_invalidate ( const char *reason )
412 {
413  DEBUGPRINT("HYPERVISOR: disabling debug ability: %s" NL, reason);
414  if (resolver_ok) {
415  resolver_ok = 0;
416  INFO_WINDOW("Hypervisor debug feature is asked to be disabled: %s", reason);
417  }
418 }
419 
420 
421 int hypervisor_debug_init ( const char *fn, int hypervisor_debug, int use_hypervisor_serial_out_asciizer )
422 {
423  resolver_ok = 0;
425  hypervisor_serial_out_asciizer = use_hypervisor_serial_out_asciizer;
426  if (debug_file_storage) {
427  free(debug_file_storage);
428  debug_file_storage = NULL;
429  }
430  if (debug_info) {
431  free(debug_info);
432  debug_info = NULL;
433  }
434  if (!fn || !*fn) {
435  DEBUG("HYPERDEBUG: feature is not enabled, null file name for list file" NL);
436  return 1;
437  }
438  debug_info = xemu_malloc(sizeof(struct debug_info_st) * 0x4000);
439  // Load REP file into a memory buffer. Yes, it's maybe a waste of memory a bit, but we can
440  // use then to reference various parts of it with pointers, with some mangling though (like inserting field
441  // terminator '\0's to have valid C-strings). It also eliminates the need to build up storage for symbols, lines
442  // and managing that. Though a bit ugly solution, I admit.
443  int ret = xemu_load_file(fn, NULL, 10, 4 << 20, "Failed to load the REP file"); // hopefully 4Mbyte max size is enough
444  if (ret < 0)
445  return -1;
446  debug_file_storage = xemu_realloc(xemu_load_buffer_p, ret + 1); // reallocate buffer to have space for the '\0' terminator
447  xemu_load_buffer_p = NULL;
448  debug_file_storage[ret] = '\0'; // terminate the file content, to form a valid C-string
449  if (strlen(debug_file_storage) != ret) {
450  ERROR_WINDOW("The loaded REP file has NULL character inside, thus it's invalid!");
451  goto failure;
452  }
453  DEBUGPRINT("HYPERDEBUG: loaded REP file with %d bytes" NL, ret);
454  static const char unknown_source_name[] = "<UNKNOWN_SRC>";
455  static const char unknown_line[] = "<UNKNOWN_LINE>";
456  for (int a = 0; a < 0x4000; a++) {
457  debug_info[a].src_fn = unknown_source_name;
458  debug_info[a].src_ln = 0;
459  debug_info[a].sym_name = NULL;
460  debug_info[a].sym_offs = 0;
461  debug_info[a].exec = 0;
462  debug_info[a].line = unknown_line;
463  }
464  // The big loop, which walks through the file and parses it
465  static const char valid_label_characters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
466  const char *current_source_file_name = unknown_source_name, *unresolved_symbol = NULL;
467  int total_symbols = 0, source_lines = 0, sym_cols = 0;
468  for (char *r = debug_file_storage ;*r ;) {
469  // search the end of line marker, with trying to guess line ending policy
470  //DEBUGPRINT("HYPERDEBUG: processing line at offset %d" NL, (int)(r - debug_file_storage));
471  char *nl = r;
472  while (*nl && *nl != '\r' && *nl != '\n')
473  nl++;
474  if (*nl) {
475  *nl++ = '\0'; // terminate string here
476  while (*nl == '\r' || *nl == '\n') // skip single or multiple '\r'/'\n' combinations (various OS line endings)
477  nl++;
478  } // now 'nl' pointer points to the start of the next line, and current line at 'r' should be '\0' terminated C-string
479  if (*r == ';') { // comment at the beginning of the line, must be acme's own, about the Source file name
480  char *p = strchr(r, ':'); // acme syntax: "Source: FILENAME", thus search for ':'
481  if (p) {
482  p++;
483  if (*p == ' ')
484  p++; // skip space after ':'
485  char *q = p + strlen(p) - 1;
486  // do not use the path part of the file name
487  while (q >= p && *q != '/' && *q != '\\')
488  q--;
489  current_source_file_name = q > p ? q + 1 : p;
490  } else
491  DEBUGPRINT("HYPERDEBUG: unknown acme-comment line: %s" NL, r);
492  goto next_line;
493  }
494  if (strlen(r) < 32)
495  goto next_line;
496  r[31] = '\0'; // terminate the 'acme' apart from the 'user' part of the line
497  int line_number, addr;
498  // try to sscanf() the acme part of the line. We can say things on the meaning of the line
499  // based on the return value (how many entities could got). The first is line number in the
500  // current source, second is hex memory address (if there is no address, it means, that
501  // no code is generated by that line in the REP file, though, we still want to check
502  // if there is a label definition within the 'user' part of the line there).
503  switch (sscanf(r, "%d %x", &line_number, &addr)) {
504  case 0:
505  goto next_line;
506  case 1:
507  addr = -1; // no address in this line (still interesting for labels, but should be resolved later on the next known address line ...)
508  break;
509  case 2:
510  if (addr < 0x8000 || addr >= 0xC000) {
511  ERROR_WINDOW("Bad REP file contains address outside of $8000...$BFFF area!");
512  goto failure;
513  }
514  addr -= 0x8000; // !!! *** normalize addr to be from 0-3FFF from now, instead of the 8000-BFFF range *** !!!
515  break;
516  default:
517  FATAL("%s(): impossible sscanf() result", __func__);
518  }
519  r += 32; // now "r" points to the "user" part of the line
520  while (*r && *r <= 0x20)// skip spaces/tabs/odd things though
521  r++;
522  // Now search if there is some "label-like" entity in the 'user' part of the line
523  char *l = r;
524  while (strchr(valid_label_characters, *l))
525  l++;
526  if (*l == ':') {
527  // wow we seems to have found a label!
528  *l = '\0'; // terminate string here for the label
529  if (addr == -1) {
530  if (unresolved_symbol) {
531  DEBUG("HYPERDEBUG: warning: multiple unresolved syms collide ignoring %s in favour of %s" NL, r, unresolved_symbol);
532  sym_cols++;
533  } else
534  unresolved_symbol = r;
535  } else
536  debug_info[addr].sym_name = r;
537  total_symbols++;
538  r = l + 1;
539  }
540  if (addr != -1) {
541  debug_info[addr].src_fn = current_source_file_name;
542  debug_info[addr].src_ln = line_number;
543  debug_info[addr].exec = 1;
544  if (unresolved_symbol) {
545  // We have address now, so we can resolve the symbol was alone in a line before (thus was unresolved)
546  debug_info[addr].sym_name = unresolved_symbol;
547  unresolved_symbol = NULL;
548  }
549  while (*r && *r <= 0x20) // skip spaces/tabs/odd things
550  r++;
551  debug_info[addr].line = r; // store the remaining line content
552  source_lines++;
553  }
554  next_line:
555  r = nl;
556  }
557  DEBUGPRINT("HYPERDEBUG: imported %d symbols (%d collisions) and %d source lines" NL, total_symbols, sym_cols, source_lines);
558  // for debug info entries not having any symbol, let's populate those with offsets compared to the last known one
559  if (!debug_info[0].sym_name) {
560  // to make sure we have known symbol at the very first byte, put something, if there wasn't such a thing
561  static const char first_fake_label[] = "<HYPPO_BASE>";
562  debug_info[0].sym_name = first_fake_label;
563  }
564  for (int a = 0, last_symi = 0; a < 0x4000; a++) {
565  if (!debug_info[a].sym_name) {
566  debug_info[a].sym_name = debug_info[last_symi].sym_name;
567  debug_info[a].sym_offs = a - last_symi;
568  } else
569  last_symi = a;
570  }
571  // Now, it's really the end
572  resolver_ok = 1;
574  return 0;
575 failure:
576  if (debug_file_storage) {
577  free(debug_file_storage);
578  debug_file_storage = NULL;
579  }
580  if (debug_info) {
581  free(debug_info);
582  debug_info = NULL;
583  }
584  return -1;
585 }
586 
587 
589 {
590  if (resolver_ok && !hypervisor_is_debugged) {
593  DEBUGPRINT("HYPERDEBUG: late-enable process now." NL);
594  }
595 }
596 
597 
598 void hypervisor_debug ( void )
599 {
601  return;
602  // TODO: better hypervisor upgrade check, maybe with checking the exact range hyppo/hickup uses for upgrade outside of the "normal" hypervisor mem range
603  if (XEMU_UNLIKELY((cpu65.pc & 0xFF00) == 0x3000)) { // this area is used by HICKUP upgrade
604  DEBUG("HYPERDEBUG: allowed to run outside of hypervisor memory, no debug info, PC = $%04X" NL, cpu65.pc);
605  return;
606  }
607  static const unsigned int io_mode_xlat[4] = {2, 3, 0, 4};
608  static Uint16 prev_sp = 0xBEFF;
609  static int prev_pc = 0;
610  static int do_execution_range_check = 1;
611  static int previous_within_hypervisor_ram = 1; // in case of "cold boot" we start in hypervisor, do not give false alarms because of that
612  const int within_hypervisor_ram = (cpu65.pc >= 0x8000 && cpu65.pc < 0xC000);
613  if (XEMU_UNLIKELY(previous_within_hypervisor_ram != within_hypervisor_ram)) {
614  DEBUG("HYPERDEBUG: execution %s hypervisor RAM at PC=$%04X (PC before=$%04X)" NL, within_hypervisor_ram ? "RETURNS to" : "LEAVES", cpu65.pc, prev_pc);
615  if (within_hypervisor_ram && cpu65.sphi != 0x0100)
616  DEBUG("HYPERDEBUG: warning, execution in hypervisor mode leaves the hypervisor RAM with SPHI != $01 but $%02X" NL, cpu65.sphi >> 8);
617  if (within_hypervisor_ram && cpu65.bphi != 0x0000)
618  DEBUG("HYPERDEBUG: warning, execution in hypervisor mode leaves the hypervisor RAM with BPHI != $00 but $%02X" NL, cpu65.bphi >> 8);
619  previous_within_hypervisor_ram = within_hypervisor_ram;
620  }
621  if (XEMU_LIKELY(within_hypervisor_ram)) {
622  if (XEMU_UNLIKELY(cpu65.sphi != 0xBE00))
623  DEBUG("HYPERDEBUG: warning, execution in hypervisor memory without SPHI == $BE but $%02X" NL, cpu65.sphi >> 8);
624  if (XEMU_UNLIKELY(cpu65.bphi != 0xBF00))
625  DEBUG("HYPERDEBUG: warning, execution in hypervisor memory without BPHI == $BF but $%02X" NL, cpu65.bphi >> 8);
626  if (XEMU_UNLIKELY(vic_iomode != 3)) // "3" means VIC-4 I/O mode. See "io_mode_xlat" definition above.
627  DEBUG("HYPERDEBUG: warning, execution in hypervisor memory with VIC I/O mode of %d" NL, io_mode_xlat[vic_iomode]);
628  }
629  const Uint16 now_sp = cpu65.sphi | cpu65.s;
630  int sp_diff = (int)prev_sp - (int)now_sp;
631  if (XEMU_UNLIKELY(sp_diff)) {
632  if (sp_diff < 0)
633  sp_diff = -sp_diff;
634  // the theory: a single opcode should not change the effective stack address more than 2 positions
635  // However, since stack page itself can be changed, difference >= $100 should not be reported.
636  // Also, I may need to check for opcode TXS to avoid false alarm on that (FIXME)
637  if (sp_diff > 2 && sp_diff < 0x100)
638  DEBUG("HYPERVISOR: warning, possible underflow of stack! SP has changed: $%04X -> $%04X @ PC=$%04X", prev_sp, now_sp, cpu65.pc);
639  prev_sp = now_sp;
640  }
641  prev_pc = cpu65.pc;
642  if (XEMU_UNLIKELY(!within_hypervisor_ram && do_execution_range_check && execution_range_check_gate)) {
643  DEBUG("HYPERDEBUG: execution outside of the hypervisor memory, PC = $%04X" NL, cpu65.pc);
644  char msg[128];
645  sprintf(msg, "Hypervisor fatal error: execution outside of the hypervisor memory, PC=$%04X SP=$%04X", cpu65.pc, cpu65.sphi | cpu65.s);
646  switch (QUESTION_WINDOW("Reset|Exit Xemu|Ignore now|Ingore all", msg)) {
647  case 0:
649  break;
650  case 1:
651  XEMUEXIT(1);
652  break;
653  case 2:
654  break;
655  case 3:
656  do_execution_range_check = 0;
657  break;
658  }
659  return;
660  }
661  if (!resolver_ok)
662  return; // no debug info loaded, cannot continue
663  // Xemu's CPU emulation executes prefixes separately. So for out-of-order checks we must take this account and do not
664  // false alarming on MEGA65 prefixed opcodes. This however introduces some ugly output in the hyperdebug output later, but anyway ...
665  if (XEMU_UNLIKELY(within_hypervisor_ram && !debug_info[cpu65.pc - 0x8000].exec && !cpu65.prefix)) {
666  DEBUG("HYPERDEBUG: execution address not found in list file (out-of-order code?), PC = $%04X" NL, cpu65.pc);
667  FATAL("Hypervisor fatal error: execution address not found in list file (out-of-order code?), PC = $%04X", cpu65.pc);
668  return;
669  }
670  // WARNING: as it turned out, using stdio I/O to log every opcodes even "only" at ~3.5MHz rate makes emulation _VERY_ slow ...
672  const Uint8 pf = cpu65_get_pf();
673  fprintf(
674  debug_fp ? debug_fp : stdout,
675  "HYPERDEBUG: PC=%04X SP=%04X B=%02X A=%02X X=%02X Y=%02X Z=%02X P=%c%c%c%c%c%c%c%c IO=%u @ %s:%d %s+$%X | %s" NL,
676  cpu65.pc, now_sp, cpu65.bphi >> 8, cpu65.a, cpu65.x, cpu65.y, cpu65.z,
677  (pf & CPU65_PF_N) ? 'N' : 'n',
678  (pf & CPU65_PF_V) ? 'V' : 'v',
679  (pf & CPU65_PF_E) ? 'E' : 'e',
680  '-',
681  (pf & CPU65_PF_D) ? 'D' : 'd',
682  (pf & CPU65_PF_I) ? 'I' : 'i',
683  (pf & CPU65_PF_Z) ? 'Z' : 'z',
684  (pf & CPU65_PF_C) ? 'C' : 'c',
685  io_mode_xlat[vic_iomode],
686  within_hypervisor_ram ? debug_info[cpu65.pc - 0x8000].src_fn : "<NOT>",
687  within_hypervisor_ram ? debug_info[cpu65.pc - 0x8000].src_ln : 0,
688  within_hypervisor_ram ? debug_info[cpu65.pc - 0x8000].sym_name : "<NOT>",
689  within_hypervisor_ram ? debug_info[cpu65.pc - 0x8000].sym_offs : 0,
690  within_hypervisor_ram ? debug_info[cpu65.pc - 0x8000].line : (cpu65.prefix ? "<PREFIXED-OP>" : "?")
691  );
692  }
693 }
configdb_st::hyperdebugfreezer
int hyperdebugfreezer
Definition: configdb.h:76
dma_init_set_rev
void dma_init_set_rev(unsigned int revision, Uint8 *rom_ver_signature)
Definition: dma65.c:439
vic4.h
configdb.h
map_megabyte_high
int map_megabyte_high
Definition: memory_mapper.c:142
CPU65_PF_E
#define CPU65_PF_E
Definition: cpu65.h:25
machine_set_speed
void machine_set_speed(int verbose)
Definition: mega65.c:101
hdos_init
void hdos_init(const int do_virt, const char *virtroot)
Definition: hdos.c:863
rom_do_override
int rom_do_override(Uint8 *rom)
Definition: rom.c:271
emutools.h
char_wom
Uint8 char_wom[0x2000]
Definition: memory_mapper.c:67
TRAP_FREEZER_RESTORE_PRESS
#define TRAP_FREEZER_RESTORE_PRESS
Definition: hypervisor.h:26
rom_detect_date
void rom_detect_date(const Uint8 *rom)
Definition: rom.c:82
configdb_st::init_videostd
int init_videostd
Definition: configdb.h:70
hypervisor_debug_init
int hypervisor_debug_init(const char *fn, int hypervisor_debug, int use_hypervisor_serial_out_asciizer)
Definition: hypervisor.c:421
debug_info_st::line
const char * line
Definition: hypervisor.c:66
CPU65_PF_Z
#define CPU65_PF_Z
Definition: cpu65.h:29
hdos_enter
void hdos_enter(const Uint8 func_no)
Definition: hdos.c:646
memory_set_vic3_rom_mapping
void memory_set_vic3_rom_mapping(Uint8 value)
Definition: memory_mapper.c:689
cpu_cycles_per_step
int cpu_cycles_per_step
Definition: mega65.c:78
hypervisor_ram
Uint8 hypervisor_ram[0x4000]
Definition: memory_mapper.c:69
CPU65_PF_D
#define CPU65_PF_D
Definition: cpu65.h:27
map_offset_high
int map_offset_high
Definition: memory_mapper.c:142
hypervisor_queued_enter
int hypervisor_queued_enter(int trapno)
Definition: hypervisor.c:74
WARNING_WINDOW
#define WARNING_WINDOW(...)
Definition: xep128.h:115
configdb_st::hdosvirt
int hdosvirt
Definition: configdb.h:72
INFO_WINDOW
#define INFO_WINDOW(...)
Definition: xep128.h:114
io_mapper.h
addr
int addr
Definition: dma65.c:81
fn
const char * fn
Definition: roms.c:42
mega65.h
hdos_notify_system_start_end
void hdos_notify_system_start_end(void)
Definition: hdos.c:855
hdos_notify_system_start_begin
void hdos_notify_system_start_begin(void)
Definition: hdos.c:847
TRAP_XEMU
#define TRAP_XEMU
Definition: hypervisor.h:23
debug_fp
FILE * debug_fp
Definition: configuration.c:87
Uint8
uint8_t Uint8
Definition: fat32.c:51
TRAP_FREEZER_USER_CALL
#define TRAP_FREEZER_USER_CALL
Definition: hypervisor.h:24
main_ram
Uint8 main_ram[512<< 10]
Definition: memory_mapper.c:47
CPU65_PF_C
#define CPU65_PF_C
Definition: cpu65.h:30
dma_registers
Uint8 dma_registers[16]
Definition: dma65.c:52
map_offset_low
int map_offset_low
Definition: memory_mapper.c:142
hyppo_version_string
char hyppo_version_string[64]
Definition: hypervisor.c:41
debug_info_st::sym_name
const char * sym_name
Definition: hypervisor.c:63
emutools_files.h
memory_get_cpu_io_port
Uint8 memory_get_cpu_io_port(int addr)
Definition: memory_mapper.c:763
xemu_malloc
void * xemu_malloc(size_t size)
Definition: emutools.c:226
DEBUGPRINT
#define DEBUGPRINT(...)
Definition: emutools_basicdefs.h:171
VIC3_IOMODE
#define VIC3_IOMODE
Definition: vic4.h:24
map_mask
int map_mask
Definition: memory_mapper.c:142
hypervisor_leave
void hypervisor_leave(void)
Definition: hypervisor.c:313
TRAP_RESET
#define TRAP_RESET
Definition: hypervisor.h:25
hdos_leave
void hdos_leave(const Uint8 func_no)
Definition: hdos.c:758
ERROR_WINDOW
#define ERROR_WINDOW(...)
Definition: xep128.h:116
debug_info_st::src_ln
int src_ln
Definition: hypervisor.c:62
memory_mapper.h
rom_clear_reports
void rom_clear_reports(void)
Definition: rom.c:48
VIC4_IOMODE
#define VIC4_IOMODE
Definition: vic4.h:26
XEMU_LIKELY
#define XEMU_LIKELY(__x__)
Definition: emutools_basicdefs.h:124
hdos.h
memory_set_do_map
void memory_set_do_map(void)
Definition: memory_mapper.c:774
map_megabyte_low
int map_megabyte_low
Definition: memory_mapper.c:142
hypervisor_debug_invalidate
void hypervisor_debug_invalidate(const char *reason)
Definition: hypervisor.c:411
vic4_disallow_video_std_change
int vic4_disallow_video_std_change
Definition: vic4.c:90
NL
#define NL
Definition: fat32.c:37
CPU65_PF_V
#define CPU65_PF_V
Definition: cpu65.h:24
CPU65_PF_I
#define CPU65_PF_I
Definition: cpu65.h:28
emutools_config.h
hypervisor.h
configdb
struct configdb_st configdb
Definition: configdb.c:34
compress_sd_image.r
def r
Definition: compress_sd_image.py:76
configdb_st::allowfreezer
int allowfreezer
Definition: configdb.h:74
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
rom.h
configdb_st::hdosdir
char * hdosdir
Definition: configdb.h:45
debug_info_st::exec
int exec
Definition: hypervisor.c:65
trap_for_xemu
void trap_for_xemu(const int func_no)
Definition: hdos.c:148
cpu65.h
xemu_load_buffer_p
void * xemu_load_buffer_p
Definition: emutools_files.c:34
cpu65_read_callback
Uint8 cpu65_read_callback(Uint16 addr)
Definition: commodore_65.c:711
XEMUEXIT
#define XEMUEXIT(n)
Definition: emutools_basicdefs.h:246
hypervisor_debug
void hypervisor_debug(void)
Definition: hypervisor.c:598
hypervisor_serial_monitor_push_char
void hypervisor_serial_monitor_push_char(Uint8 chr)
Definition: hypervisor.c:381
debug_info_st::sym_offs
int sym_offs
Definition: hypervisor.c:64
D6XX_registers
Uint8 D6XX_registers[0x100]
Definition: io_mapper.c:38
TRAP_DOS
#define TRAP_DOS
Definition: hypervisor.h:22
VIC_BAD_IOMODE
#define VIC_BAD_IOMODE
Definition: vic4.h:25
hypervisor_is_debugged
int hypervisor_is_debugged
Definition: hypervisor.c:43
vic_iomode
int vic_iomode
Definition: vic4.c:44
cpu65_get_pf
Uint8 cpu65_get_pf(void)
Definition: cpu65.c:331
in_hypervisor
int in_hypervisor
Definition: hypervisor.c:40
Uint16
uint16_t Uint16
Definition: fat32.c:50
dma65.h
configdb_st::dmarev
int dmarev
Definition: configdb.h:35
hypervisor_start_machine
void hypervisor_start_machine(void)
Definition: hypervisor.c:248
QUESTION_WINDOW
#define QUESTION_WINDOW(items, msg)
Definition: xep128.h:124
DEBUG
#define DEBUG(...)
Definition: emutools_basicdefs.h:167
xemu_realloc
void * xemu_realloc(void *p, size_t size)
Definition: emutools.c:235
hypervisor_enter
void hypervisor_enter(int trapno)
Definition: hypervisor.c:132
last_reset_type
const char * last_reset_type
Definition: mega65.c:86
CPU65_PF_N
#define CPU65_PF_N
Definition: cpu65.h:23
FATAL
#define FATAL(...)
Definition: xep128.h:117
cpu65_set_pf
void cpu65_set_pf(const Uint8 st)
Definition: cpu65.c:313
debug_info_st::src_fn
const char * src_fn
Definition: hypervisor.c:61
dma_is_in_use
int dma_is_in_use(void)
Definition: dma65.c:303
hypervisor_enter_via_write_trap
void hypervisor_enter_via_write_trap(int trapno)
Definition: hypervisor.c:95
vic_registers
Uint8 vic_registers[0x80]
Definition: vic4.c:43
debug_info_st
Definition: hypervisor.c:60
hypervisor_debug_late_enable
void hypervisor_debug_late_enable(void)
Definition: hypervisor.c:588
XEMU_UNLIKELY
#define XEMU_UNLIKELY(__x__)
Definition: emutools_basicdefs.h:125
hickup_is_overriden
int hickup_is_overriden
Definition: hypervisor.c:42
hypervisor_level_reset
int hypervisor_level_reset(void)
Definition: hypervisor.c:300
memory_set_cpu_io_port_ddr_and_data
void memory_set_cpu_io_port_ddr_and_data(Uint8 p0, Uint8 p1)
Definition: memory_mapper.c:755