1 /*
2  * vicii.c - A cycle-exact event-driven MOS6569 (VIC-II) emulation.
3  *
4  * Written by
5  *  Ettore Perazzoli <ettore@comm2000.it>
6  *  Andreas Boose <viceteam@t-online.de>
7  *
8  * This file is part of VICE, the Versatile Commodore Emulator.
9  * See README for copyright notice.
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24  *  02111-1307  USA.
25  *
26  */
27 
28 #include "vice.h"
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 
34 #include "videoarch.h"
35 
36 #include "c64cart.h"
37 #include "c64cartmem.h"
38 #include "clkguard.h"
39 #include "lib.h"
40 #include "log.h"
41 #include "machine.h"
42 #include "maincpu.h"
43 #include "mem.h"
44 #include "monitor.h"
45 #include "raster-line.h"
46 #include "raster-modes.h"
47 #include "resources.h"
48 #include "screenshot.h"
49 #include "types.h"
50 #include "vicii-chip-model.h"
51 #include "vicii-cmdline-options.h"
52 #include "vicii-color.h"
53 #include "vicii-draw.h"
54 #include "vicii-draw-cycle.h"
55 #include "vicii-fetch.h"
56 #include "vicii-irq.h"
57 #include "vicii-mem.h"
58 #include "vicii-resources.h"
59 #include "vicii-timing.h"
60 #include "vicii.h"
61 #include "viciitypes.h"
62 #include "vsync.h"
63 #include "video.h"
64 #include "viewport.h"
65 
66 
vicii_set_phi1_addr_options(uint16_t mask,uint16_t offset)67 void vicii_set_phi1_addr_options(uint16_t mask, uint16_t offset)
68 {
69     vicii.vaddr_mask_phi1 = mask;
70     vicii.vaddr_offset_phi1 = offset;
71 
72     VICII_DEBUG_REGISTER(("Set phi1 video addr mask=%04x, offset=%04x", mask, offset));
73 }
74 
vicii_set_phi2_addr_options(uint16_t mask,uint16_t offset)75 void vicii_set_phi2_addr_options(uint16_t mask, uint16_t offset)
76 {
77     vicii.vaddr_mask_phi2 = mask;
78     vicii.vaddr_offset_phi2 = offset;
79 
80     VICII_DEBUG_REGISTER(("Set phi2 video addr mask=%04x, offset=%04x", mask, offset));
81 }
82 
vicii_set_phi1_chargen_addr_options(uint16_t mask,uint16_t value)83 void vicii_set_phi1_chargen_addr_options(uint16_t mask, uint16_t value)
84 {
85     vicii.vaddr_chargen_mask_phi1 = mask;
86     vicii.vaddr_chargen_value_phi1 = value;
87 
88     VICII_DEBUG_REGISTER(("Set phi1 chargen addr mask=%04x, value=%04x", mask, value));
89 }
90 
vicii_set_phi2_chargen_addr_options(uint16_t mask,uint16_t value)91 void vicii_set_phi2_chargen_addr_options(uint16_t mask, uint16_t value)
92 {
93     vicii.vaddr_chargen_mask_phi2 = mask;
94     vicii.vaddr_chargen_value_phi2 = value;
95 
96     VICII_DEBUG_REGISTER(("Set phi2 chargen addr mask=%04x, value=%04x", mask, value));
97 }
98 
vicii_set_chargen_addr_options(uint16_t mask,uint16_t value)99 void vicii_set_chargen_addr_options(uint16_t mask, uint16_t value)
100 {
101     vicii.vaddr_chargen_mask_phi1 = mask;
102     vicii.vaddr_chargen_value_phi1 = value;
103     vicii.vaddr_chargen_mask_phi2 = mask;
104     vicii.vaddr_chargen_value_phi2 = value;
105 
106     VICII_DEBUG_REGISTER(("Set chargen addr mask=%04x, value=%04x", mask, value));
107 }
108 
109 /* ---------------------------------------------------------------------*/
110 
111 vicii_t vicii;
112 
113 static void vicii_set_geometry(void);
114 
clk_overflow_callback(CLOCK sub,void * unused_data)115 static void clk_overflow_callback(CLOCK sub, void *unused_data)
116 {
117     if (vicii.light_pen.trigger_cycle < CLOCK_MAX) {
118         vicii.light_pen.trigger_cycle -= sub;
119     }
120 }
121 
vicii_change_timing(machine_timing_t * machine_timing,int border_mode)122 void vicii_change_timing(machine_timing_t *machine_timing, int border_mode)
123 {
124     vicii_timing_set(machine_timing, border_mode);
125 
126     if (vicii.initialized) {
127         vicii_set_geometry();
128         raster_mode_change();
129     }
130 }
131 
vicii_handle_pending_alarms_external(int num_write_cycles)132 void vicii_handle_pending_alarms_external(int num_write_cycles)
133 {
134     return;
135 }
136 
vicii_handle_pending_alarms_external_write(void)137 void vicii_handle_pending_alarms_external_write(void)
138 {
139     return;
140 }
141 
142 /* return pixel aspect ratio for current video mode
143  * based on http://codebase64.com/doku.php?id=base:pixel_aspect_ratio
144  */
vicii_get_pixel_aspect(void)145 static float vicii_get_pixel_aspect(void)
146 {
147     int video;
148     resources_get_int("MachineVideoStandard", &video);
149     switch (video) {
150         case MACHINE_SYNC_PAL:
151             return 0.93650794f;
152         case MACHINE_SYNC_PALN:
153             return 0.90769231f;
154         case MACHINE_SYNC_NTSC:
155             return 0.75000000f;
156         case MACHINE_SYNC_NTSCOLD:
157             return 0.76171875f;
158         default:
159             return 1.0f;
160     }
161 }
162 
163 /* return type of monitor used for current video mode */
vicii_get_crt_type(void)164 static int vicii_get_crt_type(void)
165 {
166     int video;
167     resources_get_int("MachineVideoStandard", &video);
168     switch (video) {
169         case MACHINE_SYNC_PAL:
170         case MACHINE_SYNC_PALN:
171             return 1; /* PAL */
172         default:
173             return 0; /* NTSC */
174     }
175 }
176 
vicii_set_geometry(void)177 static void vicii_set_geometry(void)
178 {
179     unsigned int width, height;
180 
181     width = vicii.screen_leftborderwidth + VICII_SCREEN_XPIX + vicii.screen_rightborderwidth;
182     height = vicii.last_displayed_line - vicii.first_displayed_line + 1;
183 
184     raster_set_geometry(&vicii.raster,
185                         width, height, /* canvas dimensions */
186                         width, vicii.screen_height, /* screen dimensions */
187                         VICII_SCREEN_XPIX, VICII_SCREEN_YPIX, /* gfx dimensions */
188                         VICII_SCREEN_TEXTCOLS, VICII_SCREEN_TEXTLINES, /* text dimensions */
189                         vicii.screen_leftborderwidth, VICII_NO_BORDER_FIRST_DISPLAYED_LINE, /* gfx position */
190                         0, /* gfx area doesn't move */
191                         vicii.first_displayed_line,
192                         vicii.last_displayed_line,
193                         0, /* extra offscreen border left */
194                         0) /* extra offscreen border right */;
195 
196     vicii.raster.display_ystart = 0;
197     vicii.raster.display_ystop = vicii.screen_height;
198     vicii.raster.display_xstart = 0;
199     vicii.raster.display_xstop = width;
200     vicii.raster.dont_cache_all = 1;
201 
202     vicii.raster.geometry->pixel_aspect_ratio = vicii_get_pixel_aspect();
203     vicii.raster.viewport->crt_type = vicii_get_crt_type();
204 }
205 
init_raster(void)206 static int init_raster(void)
207 {
208     raster_t *raster;
209 
210     raster = &vicii.raster;
211 
212     raster->sprite_status = NULL;
213     raster_line_changes_init(raster);
214 
215     /* We only use the dummy mode for "drawing" to raster.
216        Report only 1 video mode and set the idle mode to it. */
217     if (raster_init(raster, 1) < 0) {
218         return -1;
219     }
220     raster_modes_set_idle_mode(raster->modes, VICII_DUMMY_MODE);
221 
222     resources_touch("VICIIVideoCache");
223 
224     vicii_set_geometry();
225 
226     if (vicii_color_update_palette(raster->canvas) < 0) {
227         log_error(vicii.log, "Cannot load palette.");
228         return -1;
229     }
230 
231 #if 0
232     raster_set_title(raster, machine_name);
233 #else
234     /* hack */
235     if (machine_class != VICE_MACHINE_C64SC) {
236         raster_set_title(raster, machine_name);
237     } else {
238         raster_set_title(raster, "C64 (x64sc)");
239     }
240 #endif
241 
242     if (raster_realize(raster) < 0) {
243         return -1;
244     }
245 
246     return 0;
247 }
248 
vicii_new_sprites_init(void)249 static void vicii_new_sprites_init(void)
250 {
251     int i;
252 
253     for (i = 0; i < VICII_NUM_SPRITES; i++) {
254         vicii.sprite[i].data = 0;
255         vicii.sprite[i].mc = 0;
256         vicii.sprite[i].mcbase = 0;
257         vicii.sprite[i].pointer = 0;
258         vicii.sprite[i].exp_flop = 1;
259         vicii.sprite[i].x = 0;
260     }
261 
262     vicii.sprite_display_bits = 0;
263     vicii.sprite_dma = 0;
264 }
265 
266 /* Initialize the VIC-II emulation.  */
vicii_init(unsigned int flag)267 raster_t *vicii_init(unsigned int flag)
268 {
269     if (flag != VICII_STANDARD) {
270         return NULL;
271     }
272 
273     vicii.log = log_open("VIC-II");
274 
275     vicii_chip_model_init();
276 
277     vicii_irq_init();
278 
279     if (init_raster() < 0) {
280         return NULL;
281     }
282 
283     vicii_powerup();
284 
285     vicii_draw_init();
286     vicii_draw_cycle_init();
287     vicii_new_sprites_init();
288 
289     vicii.vmli = 0;
290 
291     vicii.initialized = 1;
292 
293     clk_guard_add_callback(maincpu_clk_guard, clk_overflow_callback, NULL);
294 
295     return &vicii.raster;
296 }
297 
vicii_get_canvas(void)298 struct video_canvas_s *vicii_get_canvas(void)
299 {
300     return vicii.raster.canvas;
301 }
302 
303 /* Reset the VIC-II chip.  */
vicii_reset(void)304 void vicii_reset(void)
305 {
306     raster_reset(&vicii.raster);
307 
308     vicii.raster_line = 0;
309     vicii.raster_cycle = 6;
310     /* this should probably be updated through some function */
311     vicii.cycle_flags = 0;
312     vicii.start_of_frame = 0;
313     vicii.raster_irq_triggered = 0;
314 
315     vicii.light_pen.state = 0;
316     vicii.light_pen.triggered = 0;
317     vicii.light_pen.x = vicii.light_pen.y = vicii.light_pen.x_extra_bits = 0;
318     vicii.light_pen.trigger_cycle = CLOCK_MAX;
319 
320     /* Remove all the IRQ sources.  */
321     vicii.regs[0x1a] = 0;
322 
323     vicii.vborder = 1;
324     vicii.set_vborder = 1;
325     vicii.main_border = 1;
326 }
327 
vicii_reset_registers(void)328 void vicii_reset_registers(void)
329 {
330     uint16_t i;
331 
332     if (!vicii.initialized) {
333         return;
334     }
335 
336     for (i = 0; i <= 0x3f; i++) {
337         vicii_store(i, 0);
338     }
339 }
340 
341 /* This /should/ put the VIC-II in the same state as after a powerup, if
342    `reset_vicii()' is called afterwards.  But FIXME, as we are not really
343    emulating everything correctly here; just $D011.  */
vicii_powerup(void)344 void vicii_powerup(void)
345 {
346     memset(vicii.regs, 0, sizeof(vicii.regs));
347 
348     vicii.irq_status = 0;
349     vicii.raster_irq_line = 0;
350     vicii.ram_base_phi1 = mem_ram;
351     vicii.ram_base_phi2 = mem_ram;
352 
353     vicii.vaddr_mask_phi1 = 0xffff;
354     vicii.vaddr_mask_phi2 = 0xffff;
355     vicii.vaddr_offset_phi1 = 0;
356     vicii.vaddr_offset_phi2 = 0;
357 
358     vicii.allow_bad_lines = 0;
359     vicii.sprite_sprite_collisions = vicii.sprite_background_collisions = 0;
360     vicii.clear_collisions = 0x00;
361     vicii.idle_state = 0;
362     vicii.vcbase = 0;
363     vicii.vc = 0;
364     vicii.bad_line = 0;
365     vicii.light_pen.state = 0;
366     vicii.light_pen.x = vicii.light_pen.y = vicii.light_pen.x_extra_bits = vicii.light_pen.triggered = 0;
367     vicii.light_pen.trigger_cycle = CLOCK_MAX;
368     vicii.vbank_phi1 = 0;
369     vicii.vbank_phi2 = 0;
370 
371     vicii_reset();
372 
373     vicii.ysmooth = 0;
374 }
375 
376 /* ---------------------------------------------------------------------*/
377 
378 /* This hook is called whenever video bank must be changed.  */
vicii_set_vbanks(int vbank_p1,int vbank_p2)379 static inline void vicii_set_vbanks(int vbank_p1, int vbank_p2)
380 {
381     vicii.vbank_phi1 = vbank_p1;
382     vicii.vbank_phi2 = vbank_p2;
383 }
384 
385 /* Phi1 and Phi2 accesses */
vicii_set_vbank(int num_vbank)386 void vicii_set_vbank(int num_vbank)
387 {
388     int tmp = num_vbank << 14;
389     vicii_set_vbanks(tmp, tmp);
390 }
391 
392 /* Phi1 accesses */
vicii_set_phi1_vbank(int num_vbank)393 void vicii_set_phi1_vbank(int num_vbank)
394 {
395     vicii_set_vbanks(num_vbank << 14, vicii.vbank_phi2);
396 }
397 
398 /* Phi2 accesses */
vicii_set_phi2_vbank(int num_vbank)399 void vicii_set_phi2_vbank(int num_vbank)
400 {
401     vicii_set_vbanks(vicii.vbank_phi1, num_vbank << 14);
402 }
403 
404 /* ---------------------------------------------------------------------*/
405 
406 /* Change the base of RAM seen by the VIC-II.  */
vicii_set_ram_bases(uint8_t * base_p1,uint8_t * base_p2)407 static inline void vicii_set_ram_bases(uint8_t *base_p1, uint8_t *base_p2)
408 {
409     vicii.ram_base_phi1 = base_p1;
410     vicii.ram_base_phi2 = base_p2;
411 }
412 
vicii_set_ram_base(uint8_t * base)413 void vicii_set_ram_base(uint8_t *base)
414 {
415     vicii_set_ram_bases(base, base);
416 }
417 
vicii_set_phi1_ram_base(uint8_t * base)418 void vicii_set_phi1_ram_base(uint8_t *base)
419 {
420     vicii_set_ram_bases(base, vicii.ram_base_phi2);
421 }
422 
vicii_set_phi2_ram_base(uint8_t * base)423 void vicii_set_phi2_ram_base(uint8_t *base)
424 {
425     vicii_set_ram_bases(vicii.ram_base_phi1, base);
426 }
427 
vicii_update_memory_ptrs_external(void)428 void vicii_update_memory_ptrs_external(void)
429 {
430 }
431 
432 /* Redraw the current raster line.  This happens after the last cycle
433    of each line.  */
vicii_raster_draw_handler(void)434 void vicii_raster_draw_handler(void)
435 {
436     int in_visible_area;
437 
438     in_visible_area = (vicii.raster.current_line
439                        >= (unsigned int)vicii.first_displayed_line
440                        && vicii.raster.current_line
441                        <= (unsigned int)vicii.last_displayed_line);
442 
443     /* handle wrap if the first few lines are displayed in the visible lower border */
444     if ((unsigned int)vicii.last_displayed_line >= vicii.screen_height) {
445         in_visible_area |= vicii.raster.current_line
446                            <= ((unsigned int)vicii.last_displayed_line - vicii.screen_height);
447     }
448 
449     raster_line_emulate(&vicii.raster);
450 
451     if (vicii.raster.current_line == 0) {
452         /* no vsync here for NTSC  */
453         if ((unsigned int)vicii.last_displayed_line < vicii.screen_height) {
454             raster_skip_frame(&vicii.raster,
455                               vsync_do_vsync(vicii.raster.canvas,
456                                              vicii.raster.skip_frame));
457         }
458 
459     }
460 
461     /* vsync for NTSC */
462     if ((unsigned int)vicii.last_displayed_line >= vicii.screen_height
463         && vicii.raster.current_line == vicii.last_displayed_line - vicii.screen_height + 1) {
464         raster_skip_frame(&vicii.raster,
465                           vsync_do_vsync(vicii.raster.canvas,
466                                          vicii.raster.skip_frame));
467     }
468 }
469 
vicii_set_canvas_refresh(int enable)470 void vicii_set_canvas_refresh(int enable)
471 {
472     raster_set_canvas_refresh(&vicii.raster, enable);
473 }
474 
vicii_shutdown(void)475 void vicii_shutdown(void)
476 {
477     raster_shutdown(&vicii.raster);
478 }
479 
vicii_screenshot(screenshot_t * screenshot)480 void vicii_screenshot(screenshot_t *screenshot)
481 {
482     uint16_t screen_addr;             /* Screen start address.  */
483     uint8_t *screen_base_phi2;       /* Pointer to screen memory.  */
484     uint8_t *char_base;              /* Pointer to character memory.  */
485     uint8_t *bitmap_low_base;        /* Pointer to bitmap memory (low part).  */
486     uint8_t *bitmap_high_base;       /* Pointer to bitmap memory (high part).  */
487     int tmp, bitmap_bank;
488 
489     screen_addr = vicii.vbank_phi2 + ((vicii.regs[0x18] & 0xf0) << 6);
490 
491     screen_addr = (screen_addr & vicii.vaddr_mask_phi2)
492                   | vicii.vaddr_offset_phi2;
493 
494     tmp = (vicii.regs[0x18] & 0xe) << 10;
495     tmp = (tmp + vicii.vbank_phi1);
496     tmp &= vicii.vaddr_mask_phi1;
497     tmp |= vicii.vaddr_offset_phi1;
498 
499     bitmap_bank = tmp & 0xe000;
500     bitmap_low_base = vicii.ram_base_phi1 + bitmap_bank;
501 
502     if (export.ultimax_phi2) {
503         if ((screen_addr & 0x3fff) >= 0x3000) {
504             screen_base_phi2 = ultimax_romh_phi2_ptr((uint16_t)(0x1000 + (screen_addr & 0xfff)));
505         } else {
506             screen_base_phi2 = vicii.ram_base_phi2 + screen_addr;
507         }
508     } else {
509         if ((screen_addr & vicii.vaddr_chargen_mask_phi2)
510             != vicii.vaddr_chargen_value_phi2) {
511             screen_base_phi2 = vicii.ram_base_phi2 + screen_addr;
512         } else {
513             screen_base_phi2 = mem_chargen_rom_ptr + (screen_addr & 0xc00);
514         }
515     }
516 
517     if (export.ultimax_phi1) {
518         if ((tmp & 0x3fff) >= 0x3000) {
519             char_base = ultimax_romh_phi1_ptr((uint16_t)(0x1000 + (tmp & 0xfff)));
520         } else {
521             char_base = vicii.ram_base_phi1 + tmp;
522         }
523 
524         if (((bitmap_bank + 0x1000) & 0x3fff) >= 0x3000) {
525             bitmap_high_base = ultimax_romh_phi1_ptr(0x1000);
526         } else {
527             bitmap_high_base = bitmap_low_base + 0x1000;
528         }
529     } else {
530         if ((tmp & vicii.vaddr_chargen_mask_phi1)
531             != vicii.vaddr_chargen_value_phi1) {
532             char_base = vicii.ram_base_phi1 + tmp;
533         } else {
534             char_base = mem_chargen_rom_ptr + (tmp & 0x0800);
535         }
536 
537         if (((bitmap_bank + 0x1000) & vicii.vaddr_chargen_mask_phi1)
538             != vicii.vaddr_chargen_value_phi1) {
539             bitmap_high_base = bitmap_low_base + 0x1000;
540         } else {
541             bitmap_high_base = mem_chargen_rom_ptr;
542         }
543     }
544 
545     raster_screenshot(&vicii.raster, screenshot);
546 
547     screenshot->chipid = "VICII";
548     screenshot->video_regs = vicii.regs;
549     screenshot->screen_ptr = screen_base_phi2;
550     screenshot->chargen_ptr = char_base;
551     screenshot->bitmap_ptr = NULL;
552     screenshot->bitmap_low_ptr = bitmap_low_base;
553     screenshot->bitmap_high_ptr = bitmap_high_base;
554     screenshot->color_ram_ptr = mem_color_ram_vicii;
555 }
556 
vicii_async_refresh(struct canvas_refresh_s * refresh)557 void vicii_async_refresh(struct canvas_refresh_s *refresh)
558 {
559     raster_async_refresh(&vicii.raster, refresh);
560 }
561 
562 /* ---------------------------------------------------------------------*/
563 
fetch_phi1_type(int addr)564 static const char *fetch_phi1_type(int addr)
565 {
566     addr = (addr & vicii.vaddr_mask_phi1) | vicii.vaddr_offset_phi1;
567 
568     if (export.ultimax_phi1) {
569         if ((addr & 0x3fff) >= 0x3000) {
570             return "Cart";
571         } else {
572             return "RAM";
573         }
574     } else {
575         if ((addr & vicii.vaddr_chargen_mask_phi1) == vicii.vaddr_chargen_value_phi1) {
576             return "CharROM";
577         } else {
578             return "RAM";
579         }
580     }
581 }
582 
583 
vicii_dump(void)584 int vicii_dump(void)
585 {
586     static const char *mode_name[] = {
587         "Standard Text",
588         "Multicolor Text",
589         "Hires Bitmap",
590         "Multicolor Bitmap",
591         "Extended Text",
592         "Illegal Text",
593         "Invalid Bitmap 1",
594         "Invalid Bitmap 2"
595     };
596 
597     int video_mode, m_mcm, m_bmm, m_ecm, v_bank, v_vram;
598     int i, bits, bits2;
599 
600     video_mode = ((vicii.regs[0x11] & 0x60) | (vicii.regs[0x16] & 0x10)) >> 4;
601 
602     m_ecm = (video_mode & 4) >> 2;  /* 0 standard, 1 extended */
603     m_bmm = (video_mode & 2) >> 1;  /* 0 text, 1 bitmap */
604     m_mcm = video_mode & 1;         /* 0 hires, 1 multi */
605 
606     v_bank = vicii.vbank_phi1;
607 
608     mon_out("Raster cycle/line: %d/%d IRQ: %d\n", vicii.raster_cycle, vicii.raster_line, vicii.raster_irq_line);
609     mon_out("Mode: %s (ECM/BMM/MCM=%d/%d/%d)\n", mode_name[video_mode], m_ecm, m_bmm, m_mcm);
610     mon_out("Colors: Border: %x BG: %x ", vicii.regs[0x20], vicii.regs[0x21]);
611     if (m_ecm) {
612         mon_out("BG1: %x BG2: %x BG3: %x\n", vicii.regs[0x22], vicii.regs[0x23], vicii.regs[0x24]);
613     } else if (m_mcm && !m_bmm) {
614         mon_out("MC1: %x MC2: %x\n", vicii.regs[0x22], vicii.regs[0x23]);
615     } else {
616         mon_out("\n");
617     }
618 
619     mon_out("Scroll X/Y: %d/%d, RC %d, Idle: %d, ", vicii.regs[0x16] & 0x07, vicii.regs[0x11] & 0x07, vicii.rc, vicii.idle_state);
620     mon_out("%dx%d\n", 39 + ((vicii.regs[0x16] >> 3) & 1), 24 + ((vicii.regs[0x11] >> 3) & 1));
621 
622     mon_out("VC $%03x, VCBASE $%03x, VMLI %2d, Phi1 $%02x\n", vicii.vc, vicii.vcbase, vicii.vmli, vicii.last_read_phi1);
623 
624     v_vram = ((vicii.regs[0x18] >> 4) * 0x0400) + vicii.vbank_phi2;
625     mon_out("Video $%04x, ", v_vram);
626     if (m_bmm) {
627         i = ((vicii.regs[0x18] >> 3) & 1) * 0x2000 + v_bank;
628         mon_out("Bitmap $%04x (%s)\n", i, fetch_phi1_type(i));
629     } else {
630         i = (((vicii.regs[0x18] >> 1) & 0x7) * 0x800) + v_bank;
631         mon_out("Charset $%04x (%s)\n", i, fetch_phi1_type(i));
632     }
633 
634     mon_out("\nSprites: S.0 S.1 S.2 S.3 S.4 S.5 S.6 S.7");
635     mon_out("\nEnabled:");
636     bits = vicii.regs[0x15];
637     for (i = 0; i < 8; i++) {
638         mon_out("%4s", (bits & 1) ? "yes" : "no");
639         bits >>= 1;
640     }
641 
642     mon_out("\nDMA/dis:");
643     bits = vicii.sprite_dma;
644     bits2 = vicii.sprite_display_bits;
645     for (i = 0; i < 8; i++) {
646         mon_out(" %c/%c", (bits & 1) ? 'D' : ' ', (bits2 & 1) ? 'd' : ' ');
647         bits >>= 1;
648         bits2 >>= 1;
649     }
650 
651     mon_out("\nPointer:");
652     for (i = 0; i < 8; i++) {
653         mon_out(" $%02x", vicii.sprite[i].pointer);
654     }
655 
656     mon_out("\nMC:     ");
657     for (i = 0; i < 8; i++) {
658         mon_out(" $%02x", vicii.sprite[i].mc);
659     }
660 
661     mon_out("\nMCBASE: ");
662     for (i = 0; i < 8; i++) {
663         mon_out(" $%02x", vicii.sprite[i].mcbase);
664     }
665 
666     mon_out("\nX-Pos:  ");
667     for (i = 0; i < 8; i++) {
668         mon_out("$%03x", vicii.sprite[i].x);
669     }
670     mon_out("\nY-Pos:  ");
671     for (i = 0; i < 8; i++) {
672         mon_out("%4d", vicii.regs[1 + (i << 1)]);
673     }
674     mon_out("\nX/Y-Exp:");
675     bits = vicii.regs[0x1d];
676     bits2 = vicii.regs[0x17];
677     for (i = 0; i < 8; i++) {
678         mon_out(" %c/%c", (bits & 1) ? 'X' : ' ', (bits2 & 1) ? (vicii.sprite[i].exp_flop ? 'Y' : 'y') : ' ');
679         bits >>= 1;
680         bits2 >>= 1;
681     }
682     mon_out("\nPri./MC:");
683     bits = vicii.regs[0x1b];
684     bits = vicii.regs[0x1c];
685     for (i = 0; i < 8; i++) {
686         mon_out(" %c/%c", (bits & 1) ? 'b' : 's', (bits2 & 1) ? '*' : ' ');
687         bits >>= 1;
688     }
689     mon_out("\nColor:  ");
690     for (i = 0; i < 8; i++) {
691         mon_out("   %x", vicii.regs[i + 0x27]);
692     }
693     if (vicii.regs[0x1c]) {
694         mon_out("\nMulti Color 1: %x  Multi Color 2: %x", vicii.regs[0x25], vicii.regs[0x26]);
695     }
696     mon_out("\n");
697 
698     return 0;
699 }
700