Xemu [doxygen]  hyppo 0a42be3a057156924bc1b626a687bd6e27349c45 @ Sat 19 Mar 02:15:11 CET 2022
vic4.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  Copyright (C)2020-2022 Hernán Di Pietro <hernan.di.pietro@gmail.com>
5 
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
19 
20 #include "xemu/emutools.h"
21 #include "mega65.h"
22 #include "xemu/cpu65.h"
23 #include "vic4.h"
24 #include "vic4_palette.h"
25 #include "memory_mapper.h"
26 #include "hypervisor.h"
27 #include "configdb.h"
28 #include "xemu/f011_core.h"
29 #include "xemu/emutools_files.h"
30 #include "io_mapper.h"
31 
32 
33 #define SPRITE_SPRITE_COLLISION
34 #define SPRITE_FG_COLLISION
35 
36 
37 const char *iomode_names[4] = { "VIC2", "VIC3", "BAD!", "VIC4" };
38 
39 // (SDL) target texture rendering pointers
40 static Uint32 *current_pixel; // current_pixel pointer to the rendering target (one current_pixel: 32 bit)
41 static Uint32 *pixel_start; // points to the end and start of the buffer
42 static Uint32 *pixel_raster_start; // first pixel of current raster
43 Uint8 vic_registers[0x80]; // VIC-4 registers
44 int vic_iomode; // VIC2/VIC3/VIC4 mode
45 static int compare_raster; // raster compare (9 bits width) data
46 static int logical_raster = 0;
47 static int interrupt_status; // Interrupt status of VIC
48 static int blink_phase = 0; // blinking attribute helper, state.
49 Uint8 c128_d030_reg; // C128-like register can be only accessed in VIC-II mode but not in others, quite special!
50 static Uint8 reg_d018_screen_addr = 0; // Legacy VIC-II $D018 screen address register
51 static int vic_hotreg_touched = 0; // If any "legacy" registers were touched
52 static int vic4_sideborder_touched = 0; // If side-border register were touched
53 static int border_x_left= 0; // Side border left
54 static int border_x_right= 0; // Side border right
55 static int xcounter = 0, ycounter = 0; // video counters
56 static int char_row = 0, display_row = 0;
57 // FIXME: really, it's 2048 now, since in H320, GOTOX value is multiplied with 2 and may overflow this array even if it's not so much used this way, we want avoid crash ...
58 // FIXME: should be rethought!!!!
59 static Uint8 is_fg[2048]; // this cache helps in sprite rendering, zero means background state, other value: foreground
60 #ifdef SPRITE_SPRITE_COLLISION
61 static Uint8 is_sprite[1024];
62 #endif
63 static float char_x_step = 0.0;
64 static int enable_bg_paint = 1;
65 //static int display_row_count = 0;
66 #define display_row_count vic_registers[0x7B]
67 static int max_rasters = PHYSICAL_RASTERS_DEFAULT;
68 static int visible_area_height = SCREEN_HEIGHT_VISIBLE_DEFAULT;
69 static int vicii_first_raster = 7; // Default for NTSC
70 static Uint8 *bitplane_bank_p = main_ram;
71 static Uint8 *bitplane_p[8];
72 static Uint32 red_colour, black_colour; // used by "drive LED", and cross-hair (only the red) for debug pixel read
73 static Uint8 vic_pixel_readback_result[4];
74 static Uint8 vic_color_register_mask = 0xFF;
75 static Uint32 *used_palette; // normally the same value as "palette" from vic4_palette.c but GOTOX RRB token can modify this! So this should be used
76 static int EFFECTIVE_V400;
77 
78 // TODO: not really implemented just here ...
79 static int etherbuffer_is_io_mapped = 0;
80 
81 // --- these things are altered by vic4_open_frame_access() ONLY at every fame ONLY based on PAL or NTSC selection
82 Uint8 videostd_id = 0xFF; // 0=PAL, 1=NTSC [give some insane value by default to force the change at the fist frame after starting Xemu]
83 const char *videostd_name = "<UNDEF>"; // PAL or NTSC, however initially is not yet set
84 int videostd_frametime = NTSC_FRAME_TIME; // time in microseconds for a frame to produce
85 float videostd_1mhz_cycles_per_scanline = 32.0; // have some value to jumpstart emulation, it will be overriden sooner or later XXX FIXME: why it does not work with zero value when it's overriden anyway?!
87 static const char NTSC_STD_NAME[] = "NTSC";
88 static const char PAL_STD_NAME[] = "PAL";
91 
92 // VIC-IV Modeline Parameters
93 // ----------------------------------------------------
94 #define DISPLAY_HEIGHT ((max_rasters-1)-20)
95 #define TEXT_HEIGHT_200 400
96 #define TEXT_HEIGHT_400 400
97 #define CHARGEN_Y_SCALE_200 2
98 #define CHARGEN_Y_SCALE_400 1
99 #define chargen_y_pixels 0
100 #define TOP_BORDERS_HEIGHT_200 (DISPLAY_HEIGHT - TEXT_HEIGHT_200)
101 #define TOP_BORDERS_HEIGHT_400 (DISPLAY_HEIGHT - TEXT_HEIGHT_400)
102 #define SINGLE_TOP_BORDER_200 (TOP_BORDERS_HEIGHT_200 >> 1)
103 #define SINGLE_TOP_BORDER_400 (TOP_BORDERS_HEIGHT_400 >> 1)
104 // TODO: move as many things as possible from vic4.h to here which is only used by vic4.c, to avoid confusion ...
105 
106 
107 static const Uint8 reverse_byte_table[] = {
108  0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, //0
109  0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, //8
110  0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, //16
111  0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, //24
112  0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, //32
113  0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, //40
114  0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, //48
115  0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, //56
116  0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, //64
117  0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, //72
118  0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, //80
119  0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, //88
120  0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, //96
121  0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, //104
122  0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, //112
123  0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, //120
124  0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, //128
125  0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
126  0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
127  0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
128  0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
129  0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
130  0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
131  0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
132  0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
133  0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
134  0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
135  0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
136  0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
137  0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
138  0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
139  0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
140 };
141 
142 
143 void vic_reset ( void )
144 {
146  interrupt_status = 0;
147  compare_raster = 0;
148  // *** Just a check to try all possible regs (in VIC2,VIC3 and VIC4 modes), it should not panic ...
149  // It may also sets/initializes some internal variables sets by register writes, which would cause a crash on screen rendering without prior setup!
150  for (int i = 0; i < 0x140; i++) {
151  vic_write_reg(i, 0);
152  (void)vic_read_reg(i);
153  }
154  // to deactivate the pixel readback crosshair by default, ie X/Y pos that never meet
155  vic_registers[0x7D] = 0xFF;
156  vic_registers[0x7E] = 0xFF;
157  vic_registers[0x7F] = 0xFF;
158  // turn off possible remained sprite collision info
159  vic_registers[0x1E] = 0;
160  vic_registers[0x1F] = 0;
161 }
162 
163 
164 static inline void vic4_reset_display_counters ( void )
165 {
166  xcounter = 0;
167  display_row = 0;
168  char_row = 0;
169  ycounter = 0;
170 }
171 
172 
173 void vic_init ( void )
174 {
175  vic_pixel_readback_result[0] = 0xFF; // "hyperram access count" or what, not so much emulated
176  // Needed to render "drive LED" feature + debug pixel-read back cross-hair (only the red colour)
177  red_colour = SDL_MapRGBA(sdl_pix_fmt, 0xFF, 0x00, 0x00, 0xFF);
178  black_colour = SDL_MapRGBA(sdl_pix_fmt, 0x00, 0x00, 0x00, 0xFF);
179  // Init VIC4 stuffs
181  vic_reset();
182  c128_d030_reg = 0xFE; // this may be set to 2MHz in the previous step, so be sure to set to FF here, BUT FIX: bit 0 should be inverted!!
184  vic4_reset_display_counters();
185  DEBUG("VIC4: has been initialized." NL);
186 }
187 
188 
189 // Debug pixel-read back feature of MEGA65
190 static XEMU_INLINE void pixel_readback ( void )
191 {
192  // FIXME: this is surely wrong, and we should not use texture coords directly. We must fix this somehow (offsets?)
193  const int pix_readback_x = (vic_registers[0x7D] | ((vic_registers[0x7F] & 0x0F) << 8)) - 8 + 2;
194  const int pix_readback_y = vic_registers[0x7E] | ((vic_registers[0x7F] & 0xF0) << 4);
195  if (XEMU_UNLIKELY(pix_readback_y >= 0 && pix_readback_x >= 0 && pix_readback_y < max_rasters && pix_readback_x < TEXTURE_WIDTH)) {
196  const Uint32 texpixcol = xemu_frame_pixel_access_p[TEXTURE_WIDTH * pix_readback_y + pix_readback_x];
197  // the array will be used on reading $D70D indexed by the top two bits of $D07C, element "0" is "hyperram access count" and not handled here
198  // FIXME Warning: this code assumes that R/G/B SDL components are exactly 8 bit
199  vic_pixel_readback_result[1] = (texpixcol >> sdl_pix_fmt->Rshift) & 0xFF; // red channel
200  vic_pixel_readback_result[2] = (texpixcol >> sdl_pix_fmt->Gshift) & 0xFF; // green channel
201  vic_pixel_readback_result[3] = (texpixcol >> sdl_pix_fmt->Bshift) & 0xFF; // blue channel
202  // draw red coloured cross-hair
203  Uint32 *p = xemu_frame_pixel_access_p + TEXTURE_WIDTH * pix_readback_y;
204  for (int a = 0; a < TEXTURE_WIDTH; a++)
205  *p++ = red_colour;
206  p = xemu_frame_pixel_access_p + pix_readback_x;
207  for (int a = 0; a < max_rasters; a++) {
208  *p = red_colour;
209  p+= TEXTURE_WIDTH;
210  }
211  }
212 }
213 
214 
215 // Pair of vic4_open_frame_access() and the place when screen is updated at SDL level, finally.
216 // Do NOT call this function from vic4.c! It must be used by the emulator's main loop!
218 {
219  DEBUG("FRAME CLOSED" NL);
220  // Debug pixel-read back feature of MEGA65
221  pixel_readback();
222 #ifdef XEMU_FILES_SCREENSHOT_SUPPORT
223  // Screenshot
225  unsigned int x1, y1, x2, y2;
226  xemu_get_viewport(&x1, &y1, &x2, &y2);
228  if (!xemu_screenshot_png(
229  NULL, NULL,
230  1, 1, // no ratio/zoom correction is applied
231  pixel_start + y1 * TEXTURE_WIDTH + x1, // pixel pointer corresponding to the top left corner of the viewport
232  x2 - x1 + 1, // width
233  y2 - y1 + 1, // height
234  TEXTURE_WIDTH // full width (ie, width of the texture)
235  )) {
236  const char *p = strrchr(xemu_screenshot_full_path, DIRSEP_CHR);
237  if (p)
238  OSD(-1, -1, "%s", p + 1);
239  }
240  }
241 #endif
242  // Render "drive LED" if it was requested at all
244  unsigned int x_origin, y_origin; // top right corner of the viewport
245  xemu_get_viewport(NULL, &y_origin, &x_origin, NULL);
246  for (unsigned int y = 0; y < 8; y++)
247  for (unsigned int x = 0; x < 8; x++)
248  *(pixel_start + x_origin - 10 + x + (y + 2 + y_origin) * (TEXTURE_WIDTH)) = (x > 1 && x < 7 && y > 1 && y < 7) ? red_colour : black_colour;
249  }
250  // FINALLY ....
252  D7XX[0xFA]++; // D7FA: elapsed number of frames counter
253 }
254 
255 // The hardware allows a sideborder value of 16383 as a remnant of old MEGA65 design.
256 // In practical terms, any sideborder exceeding display_width / 2 will cover the entire
257 // character generator (effective 400 since since dw is fixed to 800px wide). Since our
258 // scanline renderer takes borders into account and any bizarre value will crash emulator
259 // due to offlimits pixel buffer access, we clamp the maximum practical sideborder value
260 // to TEXTURE_WIDTH/2, even when program code can set any of the 13-bit value range through
261 // the $D05C/$D05D registers.
262 static inline unsigned int vic4_single_side_border_clamped( void )
263 {
265 }
266 
267 
268 static void vic4_update_sideborder_dimensions ( void )
269 {
270  if (REG_CSEL) { // 40-columns?
271  border_x_left = FRAME_H_FRONT + vic4_single_side_border_clamped();
272  if (!REG_H640)
273  border_x_right = FRAME_H_FRONT + TEXTURE_WIDTH - vic4_single_side_border_clamped() - 1;
274  else // 80-col mode
275  border_x_right = FRAME_H_FRONT + TEXTURE_WIDTH - vic4_single_side_border_clamped();
276  } else { // 38-columns
277  border_x_right = FRAME_H_FRONT + TEXTURE_WIDTH - vic4_single_side_border_clamped() - 18;
278  if (!REG_H640)
279  border_x_left = FRAME_H_FRONT + vic4_single_side_border_clamped() + 14;
280  else // 78-col mode
281  border_x_left = FRAME_H_FRONT + vic4_single_side_border_clamped() + 15;
282  }
283 
284  DEBUGPRINT("VIC4: set border left=%d, right=%d, textxpos=%d" NL, border_x_left, border_x_right, CHARGEN_X_START);
285 }
286 
287 
288 static void vic4_update_vertical_borders( void )
289 {
290  // FIXME: it seems we need this line here! Otherwise EFFECTIVE_V400 may not reflect what
291  // it should be, if just updated in vic4_open_frame_access(). This seems to fix the OpenROMs
292  // issue that the bottom half of the screen is invisible, since the wrong condition below
293  // is taken for setting display_row_count based on V400 from EFFECTIVE_V400 ...
294  EFFECTIVE_V400 = (REG_V400 && REG_CHRYSCL == 0 && (vic_registers[0x51] & 0x40)) ? 0 : !!REG_V400;
295  if (REG_CSEL) { // 40-columns?
296  if (!REG_H640)
298  else // 80-col mode
300  } else { // 38-columns
301  if (!REG_H640)
303  else // 78-col mode
305  }
306  if (!EFFECTIVE_V400) { // Standard mode (200-lines)
307  display_row_count = 24;
308  if (REG_RSEL) { // 25-row
309  SET_BORDER_Y_TOP(RASTER_CORRECTION + SINGLE_TOP_BORDER_200 - (2 * vicii_first_raster));
311  } else {
312  SET_BORDER_Y_TOP(RASTER_CORRECTION + SINGLE_TOP_BORDER_200 - (2 * vicii_first_raster) + 8);
314  }
315  SET_CHARGEN_Y_START(RASTER_CORRECTION + SINGLE_TOP_BORDER_200 - (2 * vicii_first_raster) - 6 + REG_VIC2_YSCROLL * 2);
316  } else { // V400
317  display_row_count = 49;
318  if (REG_RSEL) { // 25-line+V400
319  SET_BORDER_Y_TOP(RASTER_CORRECTION + SINGLE_TOP_BORDER_400 - (2 * vicii_first_raster));
321  } else {
322  SET_BORDER_Y_TOP(RASTER_CORRECTION + SINGLE_TOP_BORDER_400 - (2 * vicii_first_raster) + 8);
324  }
325  SET_CHARGEN_Y_START(RASTER_CORRECTION + SINGLE_TOP_BORDER_400 - (2 * vicii_first_raster) - 6 + (REG_VIC2_YSCROLL * 2));
326  }
327 
328  // This offset is present in recent versions of VIC-IV VHDL
330 
331  DEBUGPRINT("VIC4: set border top=%d, bottom=%d, textypos=%d, display_row_count=%d vic_ii_first_raster=%d EFFECTIVE_V400=%d REG_V400=%d" NL, BORDER_Y_TOP, BORDER_Y_BOTTOM,
332  CHARGEN_Y_START, display_row_count, vicii_first_raster, EFFECTIVE_V400, REG_V400);
333 }
334 
335 
336 static void vic4_interpret_legacy_mode_registers ( void )
337 {
338  // See https://github.com/MEGA65/mega65-core/blob/257d78aa6a21638cb0120fd34bc0e6ab11adfd7c/src/vhdl/viciv.vhdl#L1277
339  vic4_update_sideborder_dimensions();
340  vic4_update_vertical_borders();
341 
342  Uint8 width = REG_H640 ? 80 : 40;
343  REG_CHRCOUNT = width;
344  SET_LINESTEP_BYTES(width);// * (REG_16BITCHARSET ? 2 : 1));
345 
346  REG_SCRNPTR_B0 = 0;
347  REG_SCRNPTR_B1 &= 0xC0;
348  REG_SCRNPTR_B1 |= REG_H640 ? ((reg_d018_screen_addr & 14) << 2) : (reg_d018_screen_addr << 2);
349  REG_SCRNPTR_B2 = 0;
350  vic_registers[0x63] &= 0b11110000; // clear VIC-IV precise screen addr bits 31-24 (from post bits 3-0) as it does not make sense; MEGA65 does not have enough fast RAM to use it
351 
352  REG_SPRPTR_B0 = 0xF8;
353  REG_SPRPTR_B1 = (reg_d018_screen_addr << 2) | 0x3;
354  if (REG_H640 | EFFECTIVE_V400)
355  REG_SPRPTR_B1 |= 4;
356  vic_registers[0x6E] &= 128; // hmmm, clearing VIC-IV sprite pointer bits 22-16 (bits 0-6 of this reg)
357 
358  REG_SPRPTR_B1 = (~last_dd00_bits << 6) | (REG_SPRPTR_B1 & 0x3F);
359  REG_SCRNPTR_B1 = (~last_dd00_bits << 6) | (REG_SCRNPTR_B1 & 0x3F);
360  REG_CHARPTR_B1 = (~last_dd00_bits << 6) | (REG_CHARPTR_B1 & 0x3F);
361 
363  DEBUGPRINT("VIC4: 16bit=%d, chrcount=%d, linestep=%d bytes, charxscale=%d, ras_src=%d "
364  "screen_ram=$%06x, charset/bitmap=$%06x, sprite=$%06x" NL,
367 }
368 
369 
370 // Must be called before using the texture at all, otherwise crash will happen, or nothing at all.
371 // Access must be closed with vic4_close_frame_access().
372 // Do NOT call this function from vic4.c! It must be used by the emulator's main loop!
374 {
375  int tail_sdl;
376  current_pixel = pixel_start = xemu_start_pixel_buffer_access(&tail_sdl);
377  if (XEMU_UNLIKELY(tail_sdl))
378  FATAL("tail_sdl is not zero!");
379  // The V400 hack ...
380  // V400 + Yscale=0 + Bit6 of $D051 is handled as V200 ...
381  EFFECTIVE_V400 = (REG_V400 && REG_CHRYSCL == 0 && (vic_registers[0x51] & 0x40)) ? 0 : !!REG_V400;
382  // Now check the video mode: NTSC or PAL
383  // Though it can be changed any time, this kind of information really only can be applied
384  // at frame level. Thus we check here, if during the previous frame there was change
385  // and apply the video mode set for our just started new frame.
386  Uint8 new_mode = (configdb.force_videostd >= 0) ? configdb.force_videostd : !!(vic_registers[0x6F] & 0x80);
387  if (XEMU_UNLIKELY(new_mode != videostd_id)) {
388  // We have video mode change!
389  videostd_id = new_mode;
390  // signal that video standard has been changed, it's handled in the main emulation stuff then (reacted with recalculated timing change, and such)
391  // ... including the task of resetting this signal variable!
392  videostd_changed = 1;
393  const char *new_name;
394  if (videostd_id) {
395  // --- NTSC ---
396  new_name = NTSC_STD_NAME;
398  videostd_1mhz_cycles_per_scanline = 1000000.0 / (float)(NTSC_LINE_FREQ);
399  max_rasters = PHYSICAL_RASTERS_NTSC;
400  visible_area_height = SCREEN_HEIGHT_VISIBLE_NTSC;
401  vicii_first_raster = 7;
402  REG_SPRITE_Y_ADJUST = 24;
403  } else {
404  // --- PAL ---
405  new_name = PAL_STD_NAME;
407  videostd_1mhz_cycles_per_scanline = 1000000.0 / (float)(PAL_LINE_FREQ);
408  max_rasters = PHYSICAL_RASTERS_PAL;
409  visible_area_height = SCREEN_HEIGHT_VISIBLE_PAL;
410  vicii_first_raster = 0;
412  }
413  DEBUGPRINT("VIC: switching video standard from %s to %s (1MHz line cycle count is %f, frame time is %dusec, max raster is %d, visible area height is %d)" NL, videostd_name, new_name, videostd_1mhz_cycles_per_scanline, videostd_frametime, max_rasters, visible_area_height);
414  videostd_name = new_name;
416  vicii_first_raster = vic_registers[0x6F] & 0x1F;
417  if (!in_hypervisor) {
418  // FIXME: later it can be a problem that a very brief transition to hypervisor mode and back (ie a quick trap) may be triggered within a single frame without
419  // ever hitting this point (?!)
420  vic4_update_sideborder_dimensions();
421  vic4_update_vertical_borders();
422  }
423  }
424  // handle this via vic_readjust_sdl_viewport variable (not directly above) so external stuff (like UI) can also
425  // force to adjust viewport, not just the PAL/NTSC change itself (ie: fullborder/clipped border change)
428  if (configdb.fullborders) // XXX FIXME what should be the correct values for full borders and without that?!
430  else
431  xemu_set_viewport(48, 0, TEXTURE_WIDTH - 48, visible_area_height - 1, XEMU_VIEWPORT_ADJUST_LOGICAL_SIZE);
432  }
433 }
434 
435 
436 static void interrupt_checker ( void )
437 {
438  int vic_irq_old = cpu65.irqLevel & 2;
439  int vic_irq_new;
440  if ((interrupt_status & vic_registers[0x1A])) {
441  interrupt_status |= 128;
442  vic_irq_new = 2;
443  } else {
444  interrupt_status &= 127;
445  vic_irq_new = 0;
446  }
447  if (vic_irq_old != vic_irq_new) {
448  DEBUG("VIC4: interrupt change %s -> %s" NL, vic_irq_old ? "active" : "inactive", vic_irq_new ? "active" : "inactive");
449  if (vic_irq_new)
450  cpu65.irqLevel |= 2;
451  else
452  cpu65.irqLevel &= ~2;
453  }
454 }
455 
456 
457 static inline void check_raster_interrupt ( int nraster )
458 {
459  if (nraster == compare_raster)
460  interrupt_status |= 1;
461  else
462  interrupt_status &= 0xFE;
463  interrupt_checker();
464 }
465 
466 
467 static inline void calculate_char_x_step ( void )
468 {
469  char_x_step = (REG_CHARXSCALE / 120.0f) / (REG_H640 ? 1 : 2);
470 }
471 
472 
473 // FIXME: preliminary DAT support. For real, these should be mostly calculated at writing
474 // DAT X/Y registers, bitplane selection registers etc (also true for the actual renderer!),
475 // would give much better emulator performace. Though for now, that's a naive preliminary
476 // way to support DAT at all!
477 static XEMU_INLINE Uint8 *get_dat_addr ( unsigned int bpn )
478 {
479  unsigned int x = vic_registers[0x3C];
480  unsigned int y = vic_registers[0x3D] + ((x << 1) & 0x100);
481  unsigned int h640 = (vic_registers[0x31] & 128);
482  unsigned int and_mask, bit_shifter;
483  x &= 0x7F;
484  //DEBUGPRINT("VIC-IV: DAT: accessing DAT for bitplane #%u at X,Y of %u,%u in H%u mode" NL, bpn, x, y, h640 ? 640 : 320);
485  // In V400 modes, odd/even scanlines should be considered as well!
486  if (EFFECTIVE_V400) {
487  if ((y & 1)) {
488  and_mask = h640 ? 12 << 4 : 14 << 4;
489  bit_shifter = 12 - 4;
490  } else {
491  and_mask = h640 ? 12 : 14 ;
492  bit_shifter = 12;
493  }
494  y >>= 1;
495  } else {
496  and_mask = h640 ? 12 : 14;
497  bit_shifter = 12;
498  }
499  return
500  bitplane_bank_p + // MEGA65 feature to support relocatable bitplane bank by the DAT! (this is a pointer, not an integer!)
501  ((vic_registers[0x33 + bpn] & and_mask) << bit_shifter) + // bitplane address
502  ((bpn & 1) ? 0x10000 : 0) + // odd/even bitplane selection
503  (((y >> 3) * (h640 ? 640 : 320)) + (x << 3) + (y & 7)) // position within the bitplane given by the X/Y info
504  ;
505 }
506 
507 
508 /* DESIGN of vic_read_reg() and vic_write_reg() functions:
509  addr = 00-7F, VIC-IV registers 00-7F (ALWAYS, regardless of current I/O mode!)
510  addr = 80-FF, VIC-III registers 00-7F (ALWAYS, regardless of current I/O mode!) [though for VIC-III, many registers are ignored after the last one]
511  addr = 100-13F, VIC-II registers 00-3F (ALWAYS, regardless of current I/O mode!)
512  NOTES:
513  * on a real VIC-II last used register is $2E. However we need the KEY register ($2F) and the C128-style 2MHz mode ($30) on M65 too.
514  * ALL cases must be handled!! from 000-13F for both of reading/writing funcs, otherwise Xemu will panic! this is a safety stuff
515  * on write, later an M65-alike solution is needed: ie "hot registers" for VIC-II,VIC-III also writes VIC-IV specific registers then
516  * currently MANY things are not handled, it will be the task of "move to VIC-IV internals" project ...
517  * the purpose of ugly "tons of case" implementation that it should compile into a simple jump-table, which cannot be done faster too much ...
518  * do not confuse these "vic reg mode" ranges with the vic_iomode variable, not so much direct connection between them! vic_iomode referred
519  for the I/O mode used on the "classic $D000 area" and DMA I/O access only
520 */
521 
522 static const char vic_registers_internal_mode_names[] = {'4', '3', '2'};
523 
524 #define CASE_VIC_2(n) case n+0x100
525 #define CASE_VIC_3(n) case n+0x080
526 #define CASE_VIC_4(n) case n
527 #define CASE_VIC_ALL(n) CASE_VIC_2(n): CASE_VIC_3(n): CASE_VIC_4(n)
528 #define CASE_VIC_3_4(n) CASE_VIC_3(n): CASE_VIC_4(n)
529 
530 
531 /* - If HOTREG register is enabled, VICIV will trigger recalculation of border and such on next raster,
532  on any "legacy" register write. For the VIC-IV such "hot" registers are:
533 
534  -- @IO:C64 $D011 VIC-II control register
535  -- @IO:C64 $D016 VIC-II control register
536  -- @IO:C64 $D018 VIC-II RAM addresses
537  -- @IO:C65 $D031 VIC-III Control Register B
538 */
539 void vic_write_reg ( unsigned int addr, Uint8 data )
540 {
541  //DEBUGPRINT("VIC%c: write reg $%02X (internally $%03X) with data $%02X" NL, XEMU_LIKELY(addr < 0x180) ? vic_registers_internal_mode_names[addr >> 7] : '?', addr & 0x7F, addr, data);
542  // IMPORTANT NOTE: writing of vic_registers[] happens only *AFTER* this switch/case construct! This means if you need to do this before, you must do it manually at the right "case"!!!!
543  // if you do so, you can even use "return" instead of "break" to save the then-redundant write of the register
544  switch (addr) {
545  CASE_VIC_ALL(0x00): CASE_VIC_ALL(0x01): CASE_VIC_ALL(0x02): CASE_VIC_ALL(0x03): CASE_VIC_ALL(0x04): CASE_VIC_ALL(0x05): CASE_VIC_ALL(0x06): CASE_VIC_ALL(0x07):
546  CASE_VIC_ALL(0x08): CASE_VIC_ALL(0x09): CASE_VIC_ALL(0x0A): CASE_VIC_ALL(0x0B): CASE_VIC_ALL(0x0C): CASE_VIC_ALL(0x0D): CASE_VIC_ALL(0x0E): CASE_VIC_ALL(0x0F):
547  CASE_VIC_ALL(0x10):
548  break; // Sprite coordinates: simple write the VIC reg in all I/O modes.
549  CASE_VIC_ALL(0x11):
550  if (vic_registers[0x11] ^ data)
551  vic_hotreg_touched = 1;
552  compare_raster = (compare_raster & 0xFF) | ((data & 0x80) << 1);
553  DEBUG("VIC: compare raster is now %d" NL, compare_raster);
554  break;
555  CASE_VIC_ALL(0x12):
556  compare_raster = (compare_raster & 0xFF00) | data;
557  DEBUG("VIC: compare raster is now %d" NL, compare_raster);
558  break;
559  CASE_VIC_ALL(0x13): CASE_VIC_ALL(0x14):
560  return; // FIXME: writing light-pen registers?????
561  CASE_VIC_ALL(0x15): // sprite enabled
562  break;
563  CASE_VIC_ALL(0x16): // control-reg#2, we allow write even if non-used bits here
564  if (vic_registers[0x16] ^ data)
565  vic_hotreg_touched = 1;
566  break;
567  CASE_VIC_ALL(0x17): // sprite-Y expansion
568  break;
569  CASE_VIC_ALL(0x18): // memory pointers.
570  // (See vic4_interpret_legacy_mode_registers () for later REG_SCRNPTR_ adjustments)
571  // Reads are mapped to extended registers.
572  // So we just store the D018 Legacy Screen Address to be referenced elsewhere.
573  //
574  if (vic_registers[0x18] ^ data) {
575  REG_CHARPTR_B2 = 0;
576  REG_CHARPTR_B1 = (data & 14) << 2;
577  REG_CHARPTR_B0 = 0;
578  REG_SCRNPTR_B2 &= 0xF0;
579  reg_d018_screen_addr = (data & 0xF0) >> 4;
580  vic_hotreg_touched = 1;
581  }
582  data &= 0xFE;
583  break;
584  CASE_VIC_ALL(0x19):
585  interrupt_status = interrupt_status & (~data) & 0xF;
586  interrupt_checker();
587  break;
588  CASE_VIC_ALL(0x1A):
589  data &= 0xF;
590  break;
591  CASE_VIC_ALL(0x1B): // sprite data priority
592  CASE_VIC_ALL(0x1C): // sprite multicolour
593  CASE_VIC_ALL(0x1D): // sprite-X expansion
594  break;
595  CASE_VIC_ALL(0x1E): // sprite-sprite collision
596  vic_registers[0x1E] = 0;
597  return;
598  CASE_VIC_ALL(0x1F): // sprite-data collision
599  vic_registers[0x1F] = 0;
600  return;
601  CASE_VIC_2(0x20): CASE_VIC_2(0x21): CASE_VIC_2(0x22): CASE_VIC_2(0x23): CASE_VIC_2(0x24): CASE_VIC_2(0x25): CASE_VIC_2(0x26): CASE_VIC_2(0x27):
602  CASE_VIC_2(0x28): CASE_VIC_2(0x29): CASE_VIC_2(0x2A): CASE_VIC_2(0x2B): CASE_VIC_2(0x2C): CASE_VIC_2(0x2D): CASE_VIC_2(0x2E):
603  data &= 0xF; // colour-related registers are 4 bit only for VIC-II
604  break;
605  // Colour registers of VIC seems to be always 8 in VIC-III. It's may be in conflict with c65manual.txt
606  // But anyway, this seems to be MEGA65's way.
607  // Previously this was "gated" with D031.5, however that was not correct!
608  CASE_VIC_3(0x20): CASE_VIC_3(0x21): CASE_VIC_3(0x22): CASE_VIC_3(0x23): CASE_VIC_3(0x24): CASE_VIC_3(0x25): CASE_VIC_3(0x26): CASE_VIC_3(0x27):
609  CASE_VIC_3(0x28): CASE_VIC_3(0x29): CASE_VIC_3(0x2A): CASE_VIC_3(0x2B): CASE_VIC_3(0x2C): CASE_VIC_3(0x2D): CASE_VIC_3(0x2E):
610  CASE_VIC_4(0x20): CASE_VIC_4(0x21): CASE_VIC_4(0x22): CASE_VIC_4(0x23): CASE_VIC_4(0x24): CASE_VIC_4(0x25): CASE_VIC_4(0x26): CASE_VIC_4(0x27):
611  CASE_VIC_4(0x28): CASE_VIC_4(0x29): CASE_VIC_4(0x2A): CASE_VIC_4(0x2B): CASE_VIC_4(0x2C): CASE_VIC_4(0x2D): CASE_VIC_4(0x2E):
612  break; // colour-related registers are full 8 bit for VIC-IV and VIC-III
613  CASE_VIC_ALL(0x2F): // the KEY register, it must be handled in ALL VIC modes, to be able to set VIC I/O mode
614  do {
615  int vic_new_iomode;
616  etherbuffer_is_io_mapped = 0;
617  if (data == 0x96 && vic_registers[0x2F] == 0xA5) {
618  vic_new_iomode = VIC3_IOMODE;
619  vic_color_register_mask = 0xFF;
620  } else if (data == 0x53 && vic_registers[0x2F] == 0x47) {
621  vic_new_iomode = VIC4_IOMODE;
622  vic_color_register_mask = 0xFF;
623  } else if (data == 0x54 && vic_registers[0x2F] == 0x45) {
624  // this I/O mode is the same as VIC4 I/O mode _but_ with ethernet buffer also mapped
625  DEBUGPRINT("VIC: warning, unimplemented ethernet I/O mode is set!" NL);
626  vic_new_iomode = VIC4_IOMODE;
627  vic_color_register_mask = 0xFF;
628  etherbuffer_is_io_mapped = 1;
629  } else {
630  vic_new_iomode = VIC2_IOMODE;
631  vic_color_register_mask = 0x0F;
632  }
633  if (vic_new_iomode != vic_iomode) {
634  DEBUG("VIC: changing I/O mode %d(%s) -> %d(%s)" NL, vic_iomode, iomode_names[vic_iomode], vic_new_iomode, iomode_names[vic_new_iomode]);
635  vic_iomode = vic_new_iomode;
636  }
637  } while(0);
638  break;
639  CASE_VIC_2(0x30): // this register is _SPECIAL_, and exists only in VIC-II (C64) I/O mode: C128-style "2MHz fast" mode ...
640  DEBUGPRINT("VIC: Write 0xD030 in VIC-II I/O mode with data $%02x @ PC=$%04X (hypervisor mode: %d)" NL, data, cpu65.old_pc, !!in_hypervisor);
643  return; // it IS important to have return here, since it's not a "real" VIC-4 mode register's view in another mode!!
644  /* --- NO MORE VIC-II REGS FROM HERE --- */
645  CASE_VIC_3_4(0x30):
647  check_if_rom_palette(!(data & 4));
648  break;
649  CASE_VIC_3_4(0x31):
650  // (!) NOTE:
651  // According to Paul, speed change should trigger "HOTREG" touched notification but no VIC legacy register "interpret"
652  // So probably we need a separate (cpu_speed_hotreg) var?
653  if ((vic_registers[0x31] & 0xBF) ^ (data & 0xBF))
654  vic_hotreg_touched = 1;
655  vic_registers[0x31] = data; // we need this work-around, since reg-write happens _after_ this switch statement, but machine_set_speed above needs it ...
657  calculate_char_x_step();
658  break; // we did the write, but we need to trigger vichot_reg if should
659 
660  CASE_VIC_3_4(0x32): CASE_VIC_3_4(0x33): CASE_VIC_3_4(0x34): CASE_VIC_3_4(0x35): CASE_VIC_3_4(0x36): CASE_VIC_3_4(0x37): CASE_VIC_3_4(0x38):
661  CASE_VIC_3_4(0x39): CASE_VIC_3_4(0x3A): CASE_VIC_3_4(0x3B): CASE_VIC_3_4(0x3C): CASE_VIC_3_4(0x3D): CASE_VIC_3_4(0x3E): CASE_VIC_3_4(0x3F):
662  break;
663  // DAT read/write bitplanes port
664  CASE_VIC_3_4(0x40): CASE_VIC_3_4(0x41): CASE_VIC_3_4(0x42): CASE_VIC_3_4(0x43): CASE_VIC_3_4(0x44): CASE_VIC_3_4(0x45): CASE_VIC_3_4(0x46):
665  CASE_VIC_3_4(0x47):
666  *get_dat_addr(addr & 7) = data; // write pixels via the DAT!
667  break;
668  /* --- NO MORE VIC-III REGS FROM HERE --- */
669  CASE_VIC_4(0x48): CASE_VIC_4(0x49): CASE_VIC_4(0x4A): CASE_VIC_4(0x4B):
670  CASE_VIC_4(0x4C): CASE_VIC_4(0x4D): CASE_VIC_4(0x4E): CASE_VIC_4(0x4F):
671  break;
672  CASE_VIC_4(0x50):
673  // Writing to XPOS register is no-op
674  return;
675  CASE_VIC_4(0x51):
676  // Writing to XPOS register (high bits) is no-op, BUT the two top bits are writable!
677  vic_registers[0x51] = (data & 0xC0) | (vic_registers[0x51] & 0x3F);
678  return;
679  CASE_VIC_4(0x52): CASE_VIC_4(0x53):
680  break;
681  CASE_VIC_4(0x54):
682  vic_registers[0x54] = data; // we need this work-around, since reg-write happens _after_ this switch statement, but machine_set_speed above needs it ...
684  return; // since we DID the write, it's OK to return here and not using "break"
685  CASE_VIC_4(0x55): CASE_VIC_4(0x56): CASE_VIC_4(0x57): break;
686  CASE_VIC_4(0x58): CASE_VIC_4(0x59):
687  DEBUGPRINT("VIC: Write $%04x LINESTEP: $%02x" NL, addr, data);
688  break;
689  CASE_VIC_4(0x5A):
690  //DEBUGPRINT("WRITE $%04x CHARXSCALE: $%02x" NL, addr, data);
691  vic_registers[0x5A] = data; // write now and calculate step
692  calculate_char_x_step();
693  return;
694  CASE_VIC_4(0x5B):
695  break;
696  CASE_VIC_4(0x5C):
697  vic4_sideborder_touched = 1;
698  break;
699 
700  CASE_VIC_4(0x5D):
701  DEBUGPRINT("VIC: Write $%04x SIDEBORDER/HOTREG: $%02x" NL, addr, data);
702 
703  if ((vic_registers[0x5D] & 0x1F) ^ (data & 0x1F)) // sideborder MSB (0..5) modified ?
704  vic4_sideborder_touched = 1;
705  break;
706 
707  CASE_VIC_4(0x5E):
708  DEBUGPRINT("VIC: Write $%04x CHARCOUNT: $%02x" NL, addr, data);
709  break;
710  CASE_VIC_4(0x5F):
711  break;
712  CASE_VIC_4(0x60): CASE_VIC_4(0x61): CASE_VIC_4(0x62): CASE_VIC_4(0x63):
713  DEBUG("VIC: Write SCREENADDR byte 0xD0%02x: $%02x" NL, addr, data);
714  break;
715  CASE_VIC_4(0x64):
716  CASE_VIC_4(0x65): CASE_VIC_4(0x66): CASE_VIC_4(0x67): /*CASE_VIC_4(0x68): CASE_VIC_4(0x69): CASE_VIC_4(0x6A):*/ CASE_VIC_4(0x6B): /*CASE_VIC_4(0x6C):
717  CASE_VIC_4(0x6D): CASE_VIC_4(0x6E):*//*CASE_VIC_4(0x70):*/ CASE_VIC_4(0x71): CASE_VIC_4(0x72): CASE_VIC_4(0x73): CASE_VIC_4(0x74):
718  CASE_VIC_4(0x75): CASE_VIC_4(0x76): CASE_VIC_4(0x77): CASE_VIC_4(0x78): CASE_VIC_4(0x79): CASE_VIC_4(0x7A): CASE_VIC_4(0x7B): /*CASE_VIC_4(0x7C):*/
719  CASE_VIC_4(0x7D): CASE_VIC_4(0x7E): CASE_VIC_4(0x7F):
720  break;
721 
722  CASE_VIC_4(0x68): CASE_VIC_4(0x69): CASE_VIC_4(0x6A):
723  break;
724  CASE_VIC_4(0x6C): CASE_VIC_4(0x6D): CASE_VIC_4(0x6E):
725  vic_registers[addr & 0x7F] = data;
726  break;
727  CASE_VIC_4(0x6F):
728  // If video standard change was disallowed, we keep bit7 as is, regardless of the write
730  data = (vic_registers[0x6F] & 0x80) | (data & 0x7F);
731  // We trigger video setup at next frame automatically, no need do anything further here
732  break;
733 
734  CASE_VIC_4(0x70): // VIC-IV palette selection register
735  altpalette = ((data & 0x03) << 8) + vic_palettes;
736  spritepalette = ((data & 0x0C) << 6) + vic_palettes;
737  palette = ((data & 0x30) << 4) + vic_palettes;
738  palregaccofs = ((data & 0xC0) << 2);
739  check_if_rom_palette(!(vic_registers[0x30] & 4));
740  break;
741  CASE_VIC_4(0x7C):
742  if ((data & 7) <= 2) {
743  // The lower 3 bits of $7C set's the number of "128K slice" of the main RAM to be used with bitplanes
744  bitplane_bank_p = main_ram + ((data & 7) << 17);
745  DEBUG("VIC4: bitmap bank offset is $%X" NL, (unsigned int)(bitplane_bank_p - main_ram));
746  } else
747  DEBUGPRINT("VIC4: bitplane selection 128K-bank tried to set over 2. Refused to do so." NL);
748  break;
749  /* --- NON-EXISTING REGISTERS --- */
750  CASE_VIC_2(0x31): CASE_VIC_2(0x32): CASE_VIC_2(0x33): CASE_VIC_2(0x34): CASE_VIC_2(0x35): CASE_VIC_2(0x36): CASE_VIC_2(0x37): CASE_VIC_2(0x38):
751  CASE_VIC_2(0x39): CASE_VIC_2(0x3A): CASE_VIC_2(0x3B): CASE_VIC_2(0x3C): CASE_VIC_2(0x3D): CASE_VIC_2(0x3E): CASE_VIC_2(0x3F):
752  DEBUG("VIC2: this register does not exist for this mode, ignoring write." NL);
753  return; // not existing VIC-II registers, do not write!
754  CASE_VIC_3(0x48): CASE_VIC_3(0x49): CASE_VIC_3(0x4A): CASE_VIC_3(0x4B): CASE_VIC_3(0x4C): CASE_VIC_3(0x4D): CASE_VIC_3(0x4E): CASE_VIC_3(0x4F):
755  CASE_VIC_3(0x50): CASE_VIC_3(0x51): CASE_VIC_3(0x52): CASE_VIC_3(0x53): CASE_VIC_3(0x54): CASE_VIC_3(0x55): CASE_VIC_3(0x56): CASE_VIC_3(0x57):
756  CASE_VIC_3(0x58): CASE_VIC_3(0x59): CASE_VIC_3(0x5A): CASE_VIC_3(0x5B): CASE_VIC_3(0x5C): CASE_VIC_3(0x5D): CASE_VIC_3(0x5E): CASE_VIC_3(0x5F):
757  CASE_VIC_3(0x60): CASE_VIC_3(0x61): CASE_VIC_3(0x62): CASE_VIC_3(0x63): CASE_VIC_3(0x64): CASE_VIC_3(0x65): CASE_VIC_3(0x66): CASE_VIC_3(0x67):
758  CASE_VIC_3(0x68): CASE_VIC_3(0x69): CASE_VIC_3(0x6A): CASE_VIC_3(0x6B): CASE_VIC_3(0x6C): CASE_VIC_3(0x6D): CASE_VIC_3(0x6E): CASE_VIC_3(0x6F):
759  CASE_VIC_3(0x70): CASE_VIC_3(0x71): CASE_VIC_3(0x72): CASE_VIC_3(0x73): CASE_VIC_3(0x74): CASE_VIC_3(0x75): CASE_VIC_3(0x76): CASE_VIC_3(0x77):
760  CASE_VIC_3(0x78): CASE_VIC_3(0x79): CASE_VIC_3(0x7A): CASE_VIC_3(0x7B): CASE_VIC_3(0x7C): CASE_VIC_3(0x7D): CASE_VIC_3(0x7E): CASE_VIC_3(0x7F):
761  DEBUG("VIC3: this register does not exist for this mode, ignoring write." NL);
762  return; // not existing VIC-III registers, do not write!
763  /* --- FINALLY, IF THIS IS HIT, IT MEANS A MISTAKE SOMEWHERE IN MY CODE --- */
764  default:
765  FATAL("Xemu: invalid VIC internal register numbering on write: $%X", addr);
766  }
767  vic_registers[addr & 0x7F] = data;
768  if (REG_HOTREG) {
769  if (vic_hotreg_touched) {
770  //DEBUGPRINT("VIC: vic_hotreg_touched triggered (WRITE $D0%02x, $%02x)" NL, addr & 0x7F, data );
771  vic4_interpret_legacy_mode_registers();
772  vic_hotreg_touched = 0;
773  vic4_sideborder_touched = 0;
774  }
775  }
776  if (vic4_sideborder_touched) {
777  //DEBUGPRINT("VIC: vic4_sideborder_touched triggered (WRITE $D0%02x, $%02x)" NL, addr & 0x7F, data );
778  vic4_update_sideborder_dimensions();
779  vic4_sideborder_touched = 0;
780  }
781 }
782 
783 
784 Uint8 vic_read_reg ( int unsigned addr )
785 {
786  Uint8 result = vic_registers[addr & 0x7F]; // read the answer by default (mostly this will be), allow to override/modify in the switch construct if needed
787  switch (addr) {
788  CASE_VIC_ALL(0x00): CASE_VIC_ALL(0x01): CASE_VIC_ALL(0x02): CASE_VIC_ALL(0x03): CASE_VIC_ALL(0x04): CASE_VIC_ALL(0x05): CASE_VIC_ALL(0x06): CASE_VIC_ALL(0x07):
789  CASE_VIC_ALL(0x08): CASE_VIC_ALL(0x09): CASE_VIC_ALL(0x0A): CASE_VIC_ALL(0x0B): CASE_VIC_ALL(0x0C): CASE_VIC_ALL(0x0D): CASE_VIC_ALL(0x0E): CASE_VIC_ALL(0x0F):
790  CASE_VIC_ALL(0x10):
791  break; // Sprite coordinates
792  CASE_VIC_ALL(0x11):
793  result = (result & 0x7F) | ((logical_raster & 0x100) >> 1);
794  break;
795  CASE_VIC_ALL(0x12):
796  result = logical_raster & 0xFF;
797  break;
798  CASE_VIC_ALL(0x13): CASE_VIC_ALL(0x14):
799  break; // light-pen registers
800  CASE_VIC_ALL(0x15): // sprite enabled
801  break;
802  CASE_VIC_ALL(0x16): // control-reg#2
803  result |= 0xC0;
804  break;
805  CASE_VIC_ALL(0x17): // sprite-Y expansion
806  break;
807  CASE_VIC_ALL(0x18): // memory pointers
808  result |= 1;
809  // Always mapped to VIC-IV extended "precise" registers
810  // result = ((REG_SCRNPTR_B1 & 60) << 2) | ((REG_CHARPTR_B1 & 60) >> 2);
811  // DEBUGPRINT("READ 0x81: $%02x" NL, result);
812  break;
813  CASE_VIC_ALL(0x19):
814  result = interrupt_status | (64 + 32 + 16);
815  break;
816  CASE_VIC_ALL(0x1A):
817  result |= 0xF0;
818  break;
819  CASE_VIC_ALL(0x1B): // sprite data priority
820  CASE_VIC_ALL(0x1C): // sprite multicolour
821  CASE_VIC_ALL(0x1D): // sprite-X expansion
822  break;
823  CASE_VIC_ALL(0x1E): // sprite-sprite collision
824  CASE_VIC_ALL(0x1F): // sprite-data collision
825  vic_registers[addr & 0x7F] = 0; // 1E and 1F registers are cleared on read!
826  // FIXME: needs to re-check interrupts!
827  break;
828  CASE_VIC_2(0x20): CASE_VIC_2(0x21): CASE_VIC_2(0x22): CASE_VIC_2(0x23): CASE_VIC_2(0x24): CASE_VIC_2(0x25): CASE_VIC_2(0x26): CASE_VIC_2(0x27):
829  CASE_VIC_2(0x28): CASE_VIC_2(0x29): CASE_VIC_2(0x2A): CASE_VIC_2(0x2B): CASE_VIC_2(0x2C): CASE_VIC_2(0x2D): CASE_VIC_2(0x2E):
830  // XXX check this!!! I'm not sure if MEGA65 really does this, even though on C64, unused top 4 bits are read as '1'!
831  result |= 0xF0; // colour-related registers are 4 bit only for VIC-II
832  break;
833  CASE_VIC_3(0x20): CASE_VIC_3(0x21): CASE_VIC_3(0x22): CASE_VIC_3(0x23): CASE_VIC_3(0x24): CASE_VIC_3(0x25): CASE_VIC_3(0x26): CASE_VIC_3(0x27):
834  CASE_VIC_3(0x28): CASE_VIC_3(0x29): CASE_VIC_3(0x2A): CASE_VIC_3(0x2B): CASE_VIC_3(0x2C): CASE_VIC_3(0x2D): CASE_VIC_3(0x2E):
835  CASE_VIC_4(0x20): CASE_VIC_4(0x21): CASE_VIC_4(0x22): CASE_VIC_4(0x23): CASE_VIC_4(0x24): CASE_VIC_4(0x25): CASE_VIC_4(0x26): CASE_VIC_4(0x27):
836  CASE_VIC_4(0x28): CASE_VIC_4(0x29): CASE_VIC_4(0x2A): CASE_VIC_4(0x2B): CASE_VIC_4(0x2C): CASE_VIC_4(0x2D): CASE_VIC_4(0x2E):
837  break; // colour-related registers are full 8 bit for VIC-IV and VIC-III
838  CASE_VIC_ALL(0x2F): // the KEY register
839  break;
840  CASE_VIC_2(0x30): // this register is _SPECIAL_, and exists only in VIC-II (C64) I/O mode: C128-style "2MHz fast" mode ...
841  result = c128_d030_reg; // ... so we override "result" read before the "switch" statement!
842  break;
843  /* --- NO MORE VIC-II REGS FROM HERE --- */
844  CASE_VIC_3_4(0x30):
845  break;
846  CASE_VIC_3_4(0x31):
847  break;
848  CASE_VIC_3_4(0x32): CASE_VIC_3_4(0x33): CASE_VIC_3_4(0x34): CASE_VIC_3_4(0x35): CASE_VIC_3_4(0x36): CASE_VIC_3_4(0x37): CASE_VIC_3_4(0x38):
849  CASE_VIC_3_4(0x39): CASE_VIC_3_4(0x3A): CASE_VIC_3_4(0x3B): CASE_VIC_3_4(0x3C): CASE_VIC_3_4(0x3D): CASE_VIC_3_4(0x3E): CASE_VIC_3_4(0x3F):
850  break;
851  // DAT read/write bitplanes port
852  CASE_VIC_3_4(0x40): CASE_VIC_3_4(0x41): CASE_VIC_3_4(0x42): CASE_VIC_3_4(0x43): CASE_VIC_3_4(0x44): CASE_VIC_3_4(0x45): CASE_VIC_3_4(0x46):
853  CASE_VIC_3_4(0x47):
854  result = *get_dat_addr(addr & 7); // read pixels via the DAT!
855  break;
856  /* --- NO MORE VIC-III REGS FROM HERE --- */
857  CASE_VIC_4(0x48): CASE_VIC_4(0x49): CASE_VIC_4(0x4A): CASE_VIC_4(0x4B): CASE_VIC_4(0x4C): CASE_VIC_4(0x4D): CASE_VIC_4(0x4E): CASE_VIC_4(0x4F):
858  CASE_VIC_4(0x50):
859  // XPOS low byte
860  break;
861  CASE_VIC_4(0x51):
862  // XPOS high bits + others
863  // FIXME XXX super ugly hack to have something XPOS register changing. (some programs wait that to be changed)
864  // Note, that bit 6 and 7 is different and not part of the XPOS info.
865  result = (result & 0xC0) | ((result + 1) & 0x3F);
866  vic_registers[0x51] = result;
867  break;
868  CASE_VIC_4(0x52): CASE_VIC_4(0x53):
869  break;
870  CASE_VIC_4(0x54):
871  break;
872  CASE_VIC_4(0x55): CASE_VIC_4(0x56): CASE_VIC_4(0x57): CASE_VIC_4(0x58): CASE_VIC_4(0x59): CASE_VIC_4(0x5A): CASE_VIC_4(0x5B): CASE_VIC_4(0x5C):
873  CASE_VIC_4(0x5D): CASE_VIC_4(0x5E): CASE_VIC_4(0x5F): CASE_VIC_4(0x60): CASE_VIC_4(0x61): CASE_VIC_4(0x62): CASE_VIC_4(0x63): CASE_VIC_4(0x64):
874  CASE_VIC_4(0x65): CASE_VIC_4(0x66): CASE_VIC_4(0x67): CASE_VIC_4(0x68): CASE_VIC_4(0x69): CASE_VIC_4(0x6A): CASE_VIC_4(0x6B): CASE_VIC_4(0x6C):
875  CASE_VIC_4(0x6D):
876  break;
877  CASE_VIC_4(0x6E):
878  break;
879  CASE_VIC_4(0x6F): CASE_VIC_4(0x70): CASE_VIC_4(0x71): CASE_VIC_4(0x72): CASE_VIC_4(0x73): CASE_VIC_4(0x74):
880  CASE_VIC_4(0x75): CASE_VIC_4(0x76): CASE_VIC_4(0x77): CASE_VIC_4(0x78): CASE_VIC_4(0x79): CASE_VIC_4(0x7A): CASE_VIC_4(0x7B): CASE_VIC_4(0x7C):
881  break;
882  CASE_VIC_4(0x7D):
883  result = vic_pixel_readback_result[vic_registers[0x7C] >> 6];
884  break;
885  CASE_VIC_4(0x7E): CASE_VIC_4(0x7F):
886  break;
887  /* --- NON-EXISTING REGISTERS --- */
888  CASE_VIC_2(0x31): CASE_VIC_2(0x32): CASE_VIC_2(0x33): CASE_VIC_2(0x34): CASE_VIC_2(0x35): CASE_VIC_2(0x36): CASE_VIC_2(0x37): CASE_VIC_2(0x38):
889  CASE_VIC_2(0x39): CASE_VIC_2(0x3A): CASE_VIC_2(0x3B): CASE_VIC_2(0x3C): CASE_VIC_2(0x3D): CASE_VIC_2(0x3E): CASE_VIC_2(0x3F):
890  DEBUG("VIC2: this register does not exist for this mode, $FF for read answer." NL);
891  result = 0xFF; // not existing VIC-II registers
892  break;
893  CASE_VIC_3(0x48): CASE_VIC_3(0x49): CASE_VIC_3(0x4A): CASE_VIC_3(0x4B): CASE_VIC_3(0x4C): CASE_VIC_3(0x4D): CASE_VIC_3(0x4E): CASE_VIC_3(0x4F):
894  CASE_VIC_3(0x50): CASE_VIC_3(0x51): CASE_VIC_3(0x52): CASE_VIC_3(0x53): CASE_VIC_3(0x54): CASE_VIC_3(0x55): CASE_VIC_3(0x56): CASE_VIC_3(0x57):
895  CASE_VIC_3(0x58): CASE_VIC_3(0x59): CASE_VIC_3(0x5A): CASE_VIC_3(0x5B): CASE_VIC_3(0x5C): CASE_VIC_3(0x5D): CASE_VIC_3(0x5E): CASE_VIC_3(0x5F):
896  CASE_VIC_3(0x60): CASE_VIC_3(0x61): CASE_VIC_3(0x62): CASE_VIC_3(0x63): CASE_VIC_3(0x64): CASE_VIC_3(0x65): CASE_VIC_3(0x66): CASE_VIC_3(0x67):
897  CASE_VIC_3(0x68): CASE_VIC_3(0x69): CASE_VIC_3(0x6A): CASE_VIC_3(0x6B): CASE_VIC_3(0x6C): CASE_VIC_3(0x6D): CASE_VIC_3(0x6E): CASE_VIC_3(0x6F):
898  CASE_VIC_3(0x70): CASE_VIC_3(0x71): CASE_VIC_3(0x72): CASE_VIC_3(0x73): CASE_VIC_3(0x74): CASE_VIC_3(0x75): CASE_VIC_3(0x76): CASE_VIC_3(0x77):
899  CASE_VIC_3(0x78): CASE_VIC_3(0x79): CASE_VIC_3(0x7A): CASE_VIC_3(0x7B): CASE_VIC_3(0x7C): CASE_VIC_3(0x7D): CASE_VIC_3(0x7E): CASE_VIC_3(0x7F):
900  DEBUG("VIC3: this register does not exist for this mode, $FF for read answer." NL);
901  result = 0xFF;
902  break; // not existing VIC-III registers
903  /* --- FINALLY, IF THIS IS HIT, IT MEANS A MISTAKE SOMEWHERE IN MY CODE --- */
904  default:
905  FATAL("Xemu: invalid VIC internal register numbering on read: $%X", addr);
906  }
907  DEBUG("VIC%c: read reg $%02X (internally $%03X) with result $%02X" NL, XEMU_LIKELY(addr < 0x180) ? vic_registers_internal_mode_names[addr >> 7] : '?', addr & 0x7F, addr, result);
908  return result;
909 }
910 
911 #undef CASE_VIC_2
912 #undef CASE_VIC_3
913 #undef CASE_VIC_4
914 #undef CASE_VIC_ALL
915 #undef CASE_VIC_3_4
916 
917 
918 
919 #ifdef SPRITE_SPRITE_COLLISION
920 # warning "Sprite-sprite collision is an experimental feature (SPRITE_SPRITE_COLLISION is defined)!"
921 # define DO_SPRITE_SPRITE_COLLISION(pos,cond) do { \
922  if (cond) { \
923  const Uint8 sp = is_sprite[pos]; \
924  is_sprite[pos] = sp | sprbmask; \
925  if (sp) \
926  vic_registers[0x1E] |= sp | sprbmask; \
927  } \
928  } while (0)
929 #ifndef SPRITE_ANY_COLLISION
930 #define SPRITE_ANY_COLLISION
931 #endif
932 #else
933  // dummy macro for the case when SPRITE_SPRITE_COLLISION was not requested
934 # define DO_SPRITE_SPRITE_COLLISION(pos,cond)
935 #endif
936 
937 
938 #ifdef SPRITE_FG_COLLISION
939 # warning "Sprite-foreground collision is an experimental feature (SPRITE_FG_COLLISION is defined)!"
940 # define DO_SPRITE_FG_COLLISION(pos,cond) do { \
941  if (is_fg[pos] && (cond)) \
942  vic_registers[0x1F] |= sprbmask; \
943  } while (0)
944 #ifndef SPRITE_ANY_COLLISION
945 #define SPRITE_ANY_COLLISION
946 #endif
947 #else
948  // dummy macro for the case when SPRITE_FG_COLLISION was not requested
949 # define DO_SPRITE_FG_COLLISION(pos,cond)
950 #endif
951 
952 
953 static XEMU_INLINE void vic4_draw_sprite_row_16color ( const int sprnum, int x_display_pos, const Uint8* row_data_ptr, const int xscale, const int do_tiling )
954 {
955  const int totalBytes = SPRITE_EXTWIDTH(sprnum) ? 8 : 3;
956  //const int palindexbase = sprnum * 16 + 128 * (SPRITE_BITPLANE_ENABLE(sprnum) >> sprnum);
957  // pal16 is a pointer corrected by "palindexbase" already, so ready to be indexed with the 4 bit (16) colour
958  const Uint32 *pal16 = spritepalette + (sprnum * 16 + 128 * (SPRITE_BITPLANE_ENABLE(sprnum) >> sprnum));
959  // in 16 colour sprite mode, sprite colour register gives the transparent colour index
960  // We always use the lower 4 bit only at this very specific case, that's the reason for SPRITE_COLOR_4BIT() macro and not SPRITE_COLOR() [which can be 4/8 bit depending on curretn VIC mode)
961  const Uint8 transparency_palette_index = SPRITE_COLOR_4BIT(sprnum);
962 # ifdef SPRITE_ANY_COLLISION
963  const Uint8 sprbmask = 1 << sprnum;
964 # endif
965  do {
966  for (int byte = 0; byte < totalBytes; byte++) {
967  const Uint8 c0 = (*(row_data_ptr + byte)) >> 4;
968  const Uint8 c1 = (*(row_data_ptr + byte)) & 0xF;
969  for (int p = 0; p < xscale && x_display_pos < border_x_right; p++, x_display_pos++) {
970  if (c0 != transparency_palette_index && x_display_pos >= border_x_left && (
971  !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && !is_fg[x_display_pos])
972  )) {
973  *(pixel_raster_start + x_display_pos) = pal16[c0];
974  DO_SPRITE_SPRITE_COLLISION(x_display_pos, 1);
975  DO_SPRITE_FG_COLLISION(x_display_pos, 1);
976  }
977  }
978  for (int p = 0; p < xscale && x_display_pos < border_x_right; p++, x_display_pos++) {
979  if (c1 != transparency_palette_index && x_display_pos >= border_x_left && (
980  !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && !is_fg[x_display_pos])
981  )) {
982  *(pixel_raster_start + x_display_pos) = pal16[c1];
983  DO_SPRITE_SPRITE_COLLISION(x_display_pos, 1);
984  DO_SPRITE_FG_COLLISION(x_display_pos, 1);
985  }
986  }
987  }
988  } while (XEMU_UNLIKELY(do_tiling && x_display_pos < border_x_right));
989 }
990 
991 
992 static XEMU_INLINE void vic4_draw_sprite_row_multicolor ( const int sprnum, int x_display_pos, const Uint8* row_data_ptr, const int xscale, const int do_tiling )
993 {
994  const int totalBytes = SPRITE_EXTWIDTH(sprnum) ? 8 : 3;
995  const Uint8 mcm_spr_pal_indices[4] = { 0, SPRITE_MULTICOLOR_1, SPRITE_COLOR(sprnum), SPRITE_MULTICOLOR_2 }; // entry zero is not used
996 # ifdef SPRITE_ANY_COLLISION
997  const Uint8 sprbmask = 1 << sprnum;
998 # endif
999  do {
1000  for (int byte = 0; byte < totalBytes; byte++) {
1001  const Uint8 row_data = *row_data_ptr++;
1002  for (int shift = 6; shift >= 0; shift -= 2) {
1003  const int mcm_pixel_value = (row_data >> shift) & 3;
1004  const Uint32 sdl_pixel = spritepalette[mcm_spr_pal_indices[mcm_pixel_value]];
1005  for (int p = 0; p < xscale && x_display_pos < border_x_right; p++, x_display_pos += 2) {
1006  if (mcm_pixel_value) {
1007  if (x_display_pos >= border_x_left && (
1008  !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && !is_fg[x_display_pos])
1009  )) {
1010  *(pixel_raster_start + x_display_pos) = sdl_pixel;
1011  DO_SPRITE_SPRITE_COLLISION(x_display_pos, mcm_pixel_value & 2);
1012  DO_SPRITE_FG_COLLISION(x_display_pos, mcm_pixel_value & 2);
1013  }
1014  if (x_display_pos + 1 >= border_x_left && (
1015  !SPRITE_IS_BACK(sprnum) || (SPRITE_IS_BACK(sprnum) && !is_fg[x_display_pos + 1])
1016  )) {
1017  *(pixel_raster_start + x_display_pos + 1) = sdl_pixel;
1018  DO_SPRITE_SPRITE_COLLISION(x_display_pos + 1, mcm_pixel_value & 2);
1019  DO_SPRITE_FG_COLLISION(x_display_pos + 1, mcm_pixel_value & 2);
1020  }
1021  }
1022  }
1023  }
1024  }
1025  } while (XEMU_UNLIKELY(do_tiling && x_display_pos < border_x_right));
1026 }
1027 
1028 
1029 static XEMU_INLINE void vic4_draw_sprite_row_mono ( const int sprnum, int x_display_pos, const Uint8 *row_data_ptr, const int xscale, const int do_tiling )
1030 {
1031  const int totalBytes = SPRITE_EXTWIDTH(sprnum) ? 8 : 3;
1032  const Uint32 sdl_pixel = spritepalette[SPRITE_COLOR(sprnum)];
1033 # ifdef SPRITE_ANY_COLLISION
1034  const Uint8 sprbmask = 1 << sprnum;
1035 # endif
1036  do {
1037  for (int byte = 0; byte < totalBytes; byte++) {
1038  for (int xbit = 0; xbit < 8; xbit++) {
1039  const Uint8 sprite_bit = *row_data_ptr & (0x80 >> xbit);
1040  for (int p = 0; p < xscale && x_display_pos < border_x_right; p++, x_display_pos++) {
1041  if (x_display_pos >= border_x_left && sprite_bit && (
1042  !SPRITE_IS_BACK(sprnum) ||
1043  (SPRITE_IS_BACK(sprnum) && !is_fg[x_display_pos])
1044  )) {
1045  *(pixel_raster_start + x_display_pos) = sdl_pixel;
1046  DO_SPRITE_SPRITE_COLLISION(x_display_pos, 1);
1047  DO_SPRITE_FG_COLLISION(x_display_pos, 1);
1048  }
1049  }
1050  }
1051  row_data_ptr++;
1052  }
1053  } while (XEMU_UNLIKELY(do_tiling && x_display_pos < border_x_right));
1054 }
1055 
1056 
1057 static XEMU_INLINE void vic4_do_sprites ( void )
1058 {
1059  // Fetch and sequence sprites.
1060  //
1061  // NOTE about Text/Bitmap Graphics Background/foreground semantics:
1062  // In multicolor mode (MCM=1), the bit combinations "00" and "01" belong to the background
1063  // and "10" and "11" to the foreground whereas in standard mode (MCM=0),
1064  // cleared pixels belong to the background and set pixels to the foreground.
1065  const int reg_tiling = REG_SPRTILEN;
1066  for (int sprnum = 7; sprnum >= 0; sprnum--) {
1067  if (REG_SPRITE_ENABLE & (1 << sprnum)) {
1068  const int y_adjust = SPRITE_V400(sprnum) ? 0 : (REG_SPRITE_Y_ADJUST - 2);
1069  const int spriteHeight = SPRITE_EXTHEIGHT(sprnum) ? REG_SPRHGHT : 21;
1070  const int x_display_pos = (REG_SPR640 ? 1 : 2) * SPRITE_POS_X(sprnum) + (REG_SPR640 ? 1 : SPRITE_FIRST_X);
1071  const int y_display_pos = ((SPRITE_V400(sprnum) ? 1 : 2) * (SPRITE_POS_Y(sprnum) - y_adjust)) ;
1072 
1073  int sprite_row_in_raster = ycounter - y_display_pos;
1074 
1075  if (!SPRITE_V400(sprnum))
1076  sprite_row_in_raster = sprite_row_in_raster >> 1;
1077 
1078  if (SPRITE_VERT_2X(sprnum))
1079  sprite_row_in_raster = sprite_row_in_raster >> 1;
1080 
1081  if (sprite_row_in_raster >= 0 && sprite_row_in_raster < spriteHeight) {
1082  const int widthBytes = SPRITE_EXTWIDTH(sprnum) ? 8 : 3;
1083  // Mask-out bits 0-3, 23-19 if SPRPTR16 enabled
1084  const Uint32 sprite_pointer_addr = SPRITE_16BITPOINTER ? (SPRITE_POINTER_ADDR & 0x8FFFF0) : SPRITE_POINTER_ADDR;
1085  const Uint8 *sprite_data_pointer = main_ram + sprite_pointer_addr + sprnum * ((SPRITE_16BITPOINTER >> 7) + 1);
1086  const Uint32 sprite_data_addr = SPRITE_16BITPOINTER ?
1087  64 * ((*(sprite_data_pointer + 1) << 8) | (*sprite_data_pointer))
1088  : ((64 * (*sprite_data_pointer)) | ( ((~last_dd00_bits) & 0x3)) << 14);
1089 
1090  //DEBUGPRINT("VIC: Sprite %d data at $%08X " NL, sprnum, sprite_data_addr);
1091  const Uint8 *sprite_data = main_ram + sprite_data_addr;
1092  const Uint8 *row_data = sprite_data + widthBytes * sprite_row_in_raster;
1093  const int xscale = (REG_SPR640 ? 1 : 2) * (SPRITE_HORZ_2X(sprnum) ? 2 : 1);
1094  const int do_tiling = reg_tiling & (1 << sprnum);
1095  if (SPRITE_MULTICOLOR(sprnum))
1096  vic4_draw_sprite_row_multicolor(sprnum, x_display_pos, row_data, xscale, do_tiling);
1097  else if (SPRITE_16COLOR(sprnum))
1098  vic4_draw_sprite_row_16color(sprnum, x_display_pos, row_data, xscale, do_tiling);
1099  else
1100  vic4_draw_sprite_row_mono(sprnum, x_display_pos, row_data, xscale, do_tiling);
1101  }
1102  }
1103  }
1104 }
1105 
1106 
1107 // Render a monochrome character cell row
1108 // flip = 00 Dont flip, 01 = flip vertical, 10 = flip horizontal, 11 = flip both
1109 static XEMU_INLINE void vic4_render_mono_char_row ( Uint8 char_byte, const int glyph_width, const Uint8 bg_color, Uint8 fg_color, Uint8 vic3attr )
1110 {
1111  Uint32* active_palette = used_palette;
1112  if (XEMU_UNLIKELY(vic3attr)) {
1113  if(!VIC3_ATTR_BLINK(vic3attr) || blink_phase) {
1114  if (XEMU_UNLIKELY(VIC3_ATTR_BOLD(vic3attr) && VIC3_ATTR_REVERSE(vic3attr)))
1115  used_palette = altpalette;
1116  else if (VIC3_ATTR_REVERSE(vic3attr))
1117  char_byte = ~char_byte;
1118  if (VIC3_ATTR_BOLD(vic3attr))
1119  fg_color |= 0x10;
1120  if (char_row == 7 && VIC3_ATTR_UNDERLINE(vic3attr))
1121  char_byte = 0xFF;
1122  } else if (VIC3_ATTR_BLINK(vic3attr) && vic3attr == 0x1) {
1123  char_byte = 0;
1124  }
1125  }
1126  const Uint32 sdl_fg_color = used_palette[fg_color];
1127  if (XEMU_LIKELY(enable_bg_paint)) {
1128  const Uint32 sdl_bg_color = used_palette[bg_color];
1129  for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) {
1130  const Uint8 char_pixel = (char_byte & (0x80 >> (int)cx));
1131  *(current_pixel++) = char_pixel ? sdl_fg_color : sdl_bg_color;
1132  is_fg[xcounter++] = char_pixel;
1133  }
1134  } else {
1135  for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) {
1136  const Uint8 char_pixel = (char_byte & (0x80 >> (int)cx));
1137  if (char_pixel)
1138  *current_pixel = sdl_fg_color;
1139  current_pixel++;
1140  is_fg[xcounter++] = char_pixel;
1141  }
1142  }
1143  used_palette = active_palette;
1144 }
1145 
1146 
1147 static XEMU_INLINE void vic4_render_multicolor_char_row ( const Uint8 char_byte, const int glyph_width, const Uint8 color_source[4] )
1148 {
1149  for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) {
1150  const Uint8 bitsel = 2 * (int)(cx / 2);
1151  const Uint8 bit_pair = (char_byte & (0x80 >> bitsel)) >> (6-bitsel) | (char_byte & (0x40 >> bitsel)) >> (6-bitsel);
1152  if (XEMU_LIKELY(bit_pair || enable_bg_paint))
1153  *current_pixel = used_palette[color_source[bit_pair]];
1154  current_pixel++;
1155  is_fg[xcounter++] = (bit_pair & 2);
1156  }
1157 }
1158 
1159 
1160 // 8-bytes per row
1161 static XEMU_INLINE void vic4_render_fullcolor_char_row ( const Uint8* char_row, const int glyph_width, const Uint32 bg_sdl_color, const Uint32 fg_sdl_color, const int hflip )
1162 {
1163  for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) {
1164  const Uint8 char_data = char_row[XEMU_LIKELY(!hflip) ? (int)cx : glyph_width - 1 - (int)cx];
1165  if (char_data == 0xFF)
1166  *current_pixel = fg_sdl_color;
1167  else if (XEMU_LIKELY(char_data))
1168  *current_pixel = used_palette[char_data];
1169  else if (XEMU_LIKELY(enable_bg_paint))
1170  *current_pixel = bg_sdl_color;
1171  current_pixel++;
1172  is_fg[xcounter++] = char_data;
1173  }
1174 }
1175 
1176 
1177 // 16-color (Nybl) mode (4-bit per pixel / 16 pixel wide characters)
1178 static XEMU_INLINE void vic4_render_16color_char_row ( const Uint8* char_row, const int glyph_width, const Uint32 bg_sdl_color, const Uint32 *palette16, const int hflip )
1179 {
1180  for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) {
1181  Uint8 char_data;
1182  if (XEMU_LIKELY(!hflip)) {
1183  char_data = char_row[((int)cx) / 2];
1184  if (((int)cx) & 1)
1185  char_data >>= 4;
1186  else
1187  char_data &= 0xf;
1188  } else {
1189  char_data = char_row[glyph_width / 2 - 1 - (((int)cx) / 2)];
1190  if (((int)cx) & 1)
1191  char_data &= 0xf;
1192  else
1193  char_data >>= 4;
1194  }
1195  is_fg[xcounter++] = char_data;
1196  if (char_data)
1197  *current_pixel = palette16[char_data];
1198  else if (enable_bg_paint)
1199  *current_pixel = bg_sdl_color;
1200  current_pixel++;
1201  }
1202 }
1203 
1204 
1205 static XEMU_INLINE void set_bitplane_pointers ( void )
1206 {
1207  // Get Bitplane source addresses
1208  /* TODO: Cache the following reads & EA calculation */
1209  int and_mask, bit_shifter;
1210  if (EFFECTIVE_V400) {
1211  if (!(ycounter & 1)) {
1212  and_mask = (REG_H640 ? 12 : 14);
1213  bit_shifter = 12;
1214  } else {
1215  and_mask = (REG_H640 ? 12 << 4 : 14 << 4);
1216  bit_shifter = 12 - 4;
1217  }
1218  } else {
1219  and_mask = (REG_H640 ? 12 : 14);
1220  bit_shifter = 12;
1221  }
1222  bitplane_p[0] = bitplane_bank_p + ((vic_registers[0x33] & and_mask) << bit_shifter);
1223  bitplane_p[1] = bitplane_bank_p + ((vic_registers[0x34] & and_mask) << bit_shifter) + 0x10000;
1224  bitplane_p[2] = bitplane_bank_p + ((vic_registers[0x35] & and_mask) << bit_shifter);
1225  bitplane_p[3] = bitplane_bank_p + ((vic_registers[0x36] & and_mask) << bit_shifter) + 0x10000;
1226  bitplane_p[4] = bitplane_bank_p + ((vic_registers[0x37] & and_mask) << bit_shifter);
1227  bitplane_p[5] = bitplane_bank_p + ((vic_registers[0x38] & and_mask) << bit_shifter) + 0x10000;
1228  bitplane_p[6] = bitplane_bank_p + ((vic_registers[0x39] & and_mask) << bit_shifter);
1229  bitplane_p[7] = bitplane_bank_p + ((vic_registers[0x3A] & and_mask) << bit_shifter) + 0x10000;
1230 }
1231 
1232 
1233 // Render a bitplane-mode character cell row
1234 static XEMU_INLINE void vic4_render_bitplane_char_row ( const Uint32 offset, const int glyph_width )
1235 {
1236  const Uint8 bpe_mask = vic_registers[0x32] & (REG_H640 ? 15 : 255);
1237  for (float cx = 0; cx < glyph_width && xcounter < border_x_right; cx += char_x_step) {
1238  const Uint8 bitsel = 0x80 >> ((int)cx);
1239  *(current_pixel++) = palette[(( // Do not try this at home ...
1240  ((*(bitplane_p[0] + offset) & bitsel) ? 1 : 0) |
1241  ((*(bitplane_p[1] + offset) & bitsel) ? 2 : 0) |
1242  ((*(bitplane_p[2] + offset) & bitsel) ? 4 : 0) |
1243  ((*(bitplane_p[3] + offset) & bitsel) ? 8 : 0) |
1244  ((*(bitplane_p[4] + offset) & bitsel) ? 16 : 0) |
1245  ((*(bitplane_p[5] + offset) & bitsel) ? 32 : 0) |
1246  ((*(bitplane_p[6] + offset) & bitsel) ? 64 : 0) |
1247  ((*(bitplane_p[7] + offset) & bitsel) ? 128 : 0)
1248  ) & bpe_mask) ^ vic_registers[0x3B]
1249  ];
1250  is_fg[xcounter++] = (*(bitplane_p[2] + offset) & bitsel);
1251  }
1252 }
1253 
1254 
1255 static XEMU_INLINE void vic4_render_bitplane_raster ( void )
1256 {
1257  // FIXME: do not call this function here, but from actual register writes only
1258  // which can affect the result of this function!!
1259  set_bitplane_pointers();
1260  Uint32 offset = display_row * REG_CHRCOUNT * 8 + char_row;
1261  int line_char_index = 0;
1262  while (line_char_index < REG_CHRCOUNT) {
1263  vic4_render_bitplane_char_row(offset, 8);
1264  offset += 8;
1265  line_char_index++;
1266  }
1267  if (!EFFECTIVE_V400 || (ycounter & 1)) {
1268  if (++char_row > 7) {
1269  char_row = 0;
1270  display_row++;
1271  }
1272  }
1273  while (xcounter++ < border_x_right)
1274  *current_pixel++ = palette[REG_SCREEN_COLOR];
1275 }
1276 
1277 
1278 // TODO: make this register-write time event rather than calling by the scanline renderer again and again ...
1279 static XEMU_INLINE Uint8 *get_charset_effective_addr ( void )
1280 {
1281  //const Uint8 *row_data_base_addr = main_ram + (REG_BMM ? VIC2_BITMAP_ADDR : get_charset_effective_addr());
1283  // Note: in theory on C65 there is a bit for choose between two charsets (rather than only lower/upper case)
1284  // See: https://github.com/lgblgblgb/xemu/issues/213
1285  // However it seems even MEGA65 does not support this.
1286  // FIXME: how we can be sure, there won't be any out-of-bound access for the relative small WOM then?
1287  if (!REG_BMM && (addr == 0x1000 || addr == 0x9000 || addr == 0x1800 || addr == 0x9800))
1288  return char_wom + (addr & 0xFFF);
1289  // FIXME XXX this is a fixed constant for checking.
1290  if (XEMU_UNLIKELY(addr > 0x60000)) // this is valid since we still have got some extra unused RAM left to go beyond actual RAM while bulding the frame
1291  return main_ram + 0x60000; // give some unused ram array of emulaton, thus whatever high value set by user as ADDR, won't overflow during the frame
1292  else
1293  return main_ram + addr;
1294 }
1295 
1296 
1297 // The character rendering engine. Most features are shared between
1298 // all graphic modes. Basically, the VIC-IV supports the following character
1299 // color modes:
1300 //
1301 // - Monochrome (Bg/Fg)
1302 // - VICII Multicolor
1303 // - 16-color
1304 // - 256-color
1305 //
1306 // It's interesting to see that the four modes can be selected in
1307 // bitmap or text modes.
1308 //
1309 // VIC-III Extended attributes are applied to characters if properly set,
1310 // except in Multicolor modes.
1311 static XEMU_INLINE void vic4_render_char_raster ( void )
1312 {
1313  int line_char_index = 0;
1314  enable_bg_paint = 1;
1315  const Uint8 *row_data_base_addr = get_charset_effective_addr(); // FIXME: is it OK that I moved here, before the loop?
1316  if (display_row <= display_row_count) {
1317  Uint8 *colour_ram_current_ptr = colour_ram + COLOUR_RAM_OFFSET + (display_row * LINESTEP_BYTES);
1318  Uint8 *screen_ram_current_ptr = main_ram + SCREEN_ADDR + (display_row * LINESTEP_BYTES);
1319  // Account for Chargen X-displacement
1320  for (Uint32 *p = current_pixel; p < current_pixel + (CHARGEN_X_START - border_x_left); p++)
1321  *p = palette[REG_SCREEN_COLOR];
1322  current_pixel += (CHARGEN_X_START - border_x_left);
1323  xcounter += (CHARGEN_X_START - border_x_left);
1324  const int xcounter_start = xcounter;
1325  Uint8 char_fetch_offset = 0;
1326  // Chargen starts here.
1327  while (line_char_index < REG_CHRCOUNT) {
1328  Uint16 color_data = *(colour_ram_current_ptr++);
1329  Uint16 char_value = *(screen_ram_current_ptr++);
1330  if (REG_16BITCHARSET) {
1331  color_data = (color_data << 8) | (*(colour_ram_current_ptr++));
1332  char_value = char_value | (*(screen_ram_current_ptr++) << 8);
1333  if (XEMU_UNLIKELY(SXA_GOTO_X(color_data))) {
1334  // ---- Start of the GOTOX re-positioning functionality implementation, tricky one ----
1335  xcounter = (char_value & 0x3FF); // first, extract the goto 'X' value as an usigned number
1336  // Check the given value as "signed" as well, decide if it's "negative" or not
1337  if (REG_H640) {
1338  // Interpret as a "negative" value compared to xcounter_start if it would fit into the real range of 0-xcounter_start,
1339  // otherwise interpret that as a positive offset compared to xcounter_start
1340  if (0x400 - xcounter <= xcounter_start)
1341  xcounter = xcounter_start - (0x400 - xcounter);
1342  else
1343  xcounter += xcounter_start;
1344  } else {
1345  xcounter <<= 1; // multiply by 2, if !H640 (as the pixel is double width for lower resolution)
1346  if (0x800 - xcounter <= xcounter_start)
1347  xcounter = xcounter_start - (0x800 - xcounter);
1348  else
1349  xcounter += xcounter_start;
1350  }
1351  // The ugly: too large goto X values may cause out-of-bound access on eg is_fg buffer. Thus, if the result is larger than
1352  // the width of the SDL texture, it won't be seen anyway, so we "clamp" it for the NEXT raster as an ugly solution, which
1353  // will be overwritten anyway on rendering in the next raster. This way we don't need checking of out-of-bound access (faster
1354  // code) _everywhere_ ...
1355  if (xcounter > TEXTURE_WIDTH)
1356  xcounter = TEXTURE_WIDTH;
1357  // Align current_pixel pointer according the calculated xcounter "horror show" above
1358  current_pixel = pixel_raster_start + xcounter;
1359  // ---- End of the GOTOX re-positioning functionality implementation ----
1360  line_char_index++;
1361  char_fetch_offset = char_value >> 13;
1362  if (SXA_VERTICAL_FLIP(color_data))
1363  enable_bg_paint = 0;
1364  if (SXA_ATTR_BOLD(color_data) && SXA_ATTR_REVERSE(color_data) && !REG_VICIII_ATTRIBS)
1365  used_palette = altpalette; // use the alternate palette from now in the scanline
1366  else
1367  used_palette = palette; // we do this as well, since there can be "double GOTOX" so we want back to "original" palette ...
1368  continue;
1369  }
1370  }
1371  // Background and foreground colors
1372  const Uint8 char_fgcolor = color_data & 0xF;
1373  const Uint16 char_id = REG_EBM ? (char_value & 0x3f) : char_value & 0x1fff; // up to 8192 characters (13-bit)
1374  const Uint8 char_bgcolor = REG_EBM ? vic_registers[0x21 + ((char_value >> 6) & 3)] : REG_SCREEN_COLOR;
1375  const Uint8 glyph_trim = SXA_TRIM_RIGHT_BITS012(char_value) + (SXA_TRIM_RIGHT_BIT3(color_data) ? 8 : 0);
1376  // Default fetch from char mode.
1377  const int sel_char_row = (XEMU_UNLIKELY(SXA_VERTICAL_FLIP(color_data)) ? 7 - char_row : char_row);
1378  // Render character cell row
1379  if (SXA_4BIT_PER_PIXEL(color_data)) { // 16-color character
1380  vic4_render_16color_char_row(
1381  main_ram + (((char_id * 64) + ((sel_char_row + char_fetch_offset) * 8)) & 0x7FFFF),
1382  16 - glyph_trim,
1383  used_palette[char_bgcolor], // bg SDL colour
1384  used_palette + (color_data & 0xF0), // palette(16) pointer
1385  SXA_HORIZONTAL_FLIP(color_data) // hflip?
1386  );
1387  } else if (CHAR_IS256_COLOR(char_id)) { // 256-color character
1388  // fgcolor in case of FCM should mean colour index $FF
1389  // FIXME: check if the passed palette[char_fgcolor] is correct or another index should be used for that $FF colour stuff
1390  vic4_render_fullcolor_char_row(
1391  main_ram + (((char_id * 64) + ((sel_char_row + char_fetch_offset) * 8)) & 0x7FFFF),
1392  8 - glyph_trim,
1393  used_palette[char_bgcolor], // bg SDL colour
1394  used_palette[char_fgcolor], // fg SDL colour
1395  SXA_HORIZONTAL_FLIP(color_data) // hflip?
1396  );
1397  } else if ((REG_MCM && (char_fgcolor & 8)) || (REG_MCM && REG_BMM)) { // Multicolor character
1398  // using static vars: faster in a rapid loop like this, no need to re-adjust stack pointer all the time to allocate space and this way using constant memory address
1399  // also, as an optimization, later, some value can be re-used and not always initialized here, when in reality VIC
1400  // registers in current Xemu cannot change within a scanline anyway (ie, scanline precision based emulation/rendering)
1401  static Uint8 color_source_mcm[4];
1402  Uint8 char_byte;
1403  color_source_mcm[0] = REG_SCREEN_COLOR;
1404  if (REG_BMM) {
1405  // value 00 is common /w or w/o BMM so not initialized here
1406  color_source_mcm[1] = char_value >> 4; // 01
1407  color_source_mcm[2] = char_value & 0xF; // 10
1408  color_source_mcm[3] = color_data & 0xF; // 11
1409  char_byte = *(row_data_base_addr + display_row * (LINESTEP_BYTES * 8) + 8 * line_char_index + sel_char_row);
1410  } else {
1411  // value 00 is common /w or w/o BMM so not initialized here
1412  color_source_mcm[1] = REG_MULTICOLOR_1; // 01
1413  color_source_mcm[2] = REG_MULTICOLOR_2; // 10
1414  color_source_mcm[3] = char_fgcolor & 7; // 11
1415  char_byte = *(row_data_base_addr + (char_id * 8) + sel_char_row);
1416  }
1417  // FIXME: is this really a thing to have FLIP in bitmap mode AS WELL?!
1418  // FIXME: also this is WRONG, MCM data cannot be reversed with this table!!
1419  if (XEMU_UNLIKELY(SXA_HORIZONTAL_FLIP(color_data)))
1420  char_byte = reverse_byte_table[char_byte];
1421  vic4_render_multicolor_char_row(
1422  char_byte,
1423  8 - glyph_trim, // glyph_width
1424  color_source_mcm // 4 element (legacy) MCM colour index table
1425  );
1426  } else { // Single color character
1427  Uint8 char_byte, char_bgcolor_now, char_fgcolor_now;
1428  if (!REG_BMM) {
1429  char_bgcolor_now = char_bgcolor;
1430  char_fgcolor_now = char_fgcolor;
1431  char_byte = *(row_data_base_addr + (char_id * 8) + sel_char_row);
1432  } else {
1433  char_bgcolor_now = char_value & 0xF;
1434  char_fgcolor_now = char_value >> 4;
1435  char_byte = *(row_data_base_addr + display_row * (LINESTEP_BYTES * 8) + 8 * line_char_index + sel_char_row);
1436  }
1437  // FIXME: is this really a thing to have FLIP in bitmap mode AS WELL?!
1438  if (XEMU_UNLIKELY(SXA_HORIZONTAL_FLIP(color_data)))
1439  char_byte = reverse_byte_table[char_byte];
1440  // FIXME: do vic3 attributes work with bitmap mode as well???
1441  vic4_render_mono_char_row(
1442  char_byte,
1443  8 - glyph_trim, // glyph_width
1444  char_bgcolor_now, // bg colour index
1445  char_fgcolor_now, // fg colour index
1446  (REG_VICIII_ATTRIBS && !REG_MCM) ? (color_data >> 4) : 0 // VIC-III hardware attribute info
1447  );
1448  }
1449  line_char_index++;
1450  }
1451  }
1452  if (++char_row > 7) {
1453  char_row = 0;
1454  display_row++;
1455  }
1456  // Fill screen color after chargen phase
1457  while (xcounter++ < border_x_right)
1458  *current_pixel++ = palette[REG_SCREEN_COLOR];
1459 }
1460 
1461 
1463 {
1464  // Work this first. DO NOT OPTIMIZE EARLY.
1465 
1466  used_palette = palette; // may be overriden later by GOTOX token!
1467  xcounter = 0;
1468  current_pixel = pixel_start + ycounter * TEXTURE_WIDTH;
1469  pixel_raster_start = current_pixel;
1470 
1471  SET_PHYSICAL_RASTER(ycounter);
1472  logical_raster = ycounter >> (EFFECTIVE_V400 ? 0 : 1);
1473 
1474  // FIXME: this is probably a bad fix ... Trying to remedy that in V400, no raster interrupts seems to work ... XXX
1475  if (!(ycounter & 1) || EFFECTIVE_V400) // VIC-II raster source: We shall check FNRST ?
1476  check_raster_interrupt(logical_raster);
1477  // "Double-scan hack"
1478  // FIXME: is this really correct? ie even sprites cannot be set to Y pos finer than V200 or ...
1479  // ... having resolution finer than V200 with some "VIC-IV magic"?
1480  if (!EFFECTIVE_V400 && (ycounter & 1)) {
1481  //for (int i = 0; i < TEXTURE_WIDTH; i++, current_pixel++)
1482  // *current_pixel = /* user_scanlines_setting ? 0 : */ *(current_pixel - TEXTURE_WIDTH);
1483  memcpy(current_pixel, current_pixel - TEXTURE_WIDTH, TEXTURE_WIDTH * 4);
1484  current_pixel += TEXTURE_WIDTH;
1485  } else {
1486  // Top and bottom borders
1487  if (ycounter < BORDER_Y_TOP || ycounter >= BORDER_Y_BOTTOM || !REG_DISPLAYENABLE) {
1488  for (int i = 0; i < TEXTURE_WIDTH; i++)
1489  *(current_pixel++) = palette[REG_BORDER_COLOR];
1490  }
1491  if (ycounter >= CHARGEN_Y_START && ycounter < BORDER_Y_BOTTOM) {
1492  // Render chargen area and render side-borders later to cover X-displaced
1493  // character generator if needed. Chargen area maybe covered by top/bottom
1494  // borders also if y-offset applies.
1495  xcounter += border_x_left;
1496  current_pixel += border_x_left;
1497  if (XEMU_LIKELY(!(vic_registers[0x31] & 0x10)))
1498  vic4_render_char_raster();
1499  else
1500  vic4_render_bitplane_raster();
1501 # ifdef SPRITE_SPRITE_COLLISION
1502  memset(is_sprite, 0, sizeof is_sprite);
1503 # endif
1504  }
1505  // Paint screen color if positive y-offset (CHARGEN_Y_START > BORDER_Y_TOP)
1506  // FIXME: in case of changed palette by GOTOX, maybe this must be dependent on bg_paint to use the new palette or the old??
1507  if (ycounter >= BORDER_Y_TOP) {
1508  if (ycounter < CHARGEN_Y_START)
1509  while (xcounter++ < border_x_right)
1510  *current_pixel++ = palette[REG_SCREEN_COLOR];
1511  }
1512  for (Uint32 *p = pixel_raster_start; p < pixel_raster_start + border_x_left; p++)
1513  *p = palette[REG_BORDER_COLOR];
1514  for (Uint32 *p = current_pixel; p < current_pixel + border_x_right; p++)
1515  *p = palette[REG_BORDER_COLOR];
1516  }
1517  // Sprites can be displayed on V200/V400 independently of char generator, so
1518  // this must be outside of the main loop to avoid being affected by double-scan
1519 
1520  if (XEMU_LIKELY(REG_DISPLAYENABLE) && (ycounter >= BORDER_Y_TOP && ycounter < BORDER_Y_BOTTOM)) {
1521  vic4_do_sprites();
1522  }
1523 
1524  ycounter++;
1525  // End of frame?
1526  if (ycounter == max_rasters) {
1527  vic4_reset_display_counters();
1528  static int blink_frame_counter = 0;
1529  blink_frame_counter++;
1530  if (blink_frame_counter == VIC4_BLINK_INTERVAL) {
1531  blink_frame_counter = 0;
1532  blink_phase = !blink_phase;
1533  }
1534  return 1;
1535  }
1536  return 0;
1537 }
1538 
1539 
1540 /* --- SNAPSHOT RELATED --- */
1541 
1542 
1543 #ifdef XEMU_SNAPSHOT_SUPPORT
1544 
1545 #include <string.h>
1546 
1547 #define SNAPSHOT_VIC4_BLOCK_VERSION 2
1548 #define SNAPSHOT_VIC4_BLOCK_SIZE (0x100 + ((NO_OF_PALETTE_REGS) * 3))
1549 
1550 int vic4_snapshot_load_state ( const struct xemu_snapshot_definition_st *def, struct xemu_snapshot_block_st *block )
1551 {
1552  Uint8 buffer[SNAPSHOT_VIC4_BLOCK_SIZE];
1553  int a;
1554  if (block->block_version != SNAPSHOT_VIC4_BLOCK_VERSION || block->sub_counter || block->sub_size != sizeof buffer)
1555  RETURN_XSNAPERR_USER("Bad VIC-4 block syntax");
1556  a = xemusnap_read_file(buffer, sizeof buffer);
1557  if (a) return a;
1558  /* loading state ... */
1559  for (a = 0; a < 0x80; a++)
1560  vic_write_reg(a, buffer[a + 0x80]);
1561  c128_d030_reg = buffer[0x7F];
1562  memcpy(vic_palette_bytes_red, buffer + 0x100 , NO_OF_PALETTE_REGS);
1564  memcpy(vic_palette_bytes_blue, buffer + 0x100 + 2 * NO_OF_PALETTE_REGS, NO_OF_PALETTE_REGS);
1566  vic_iomode = buffer[0];
1567  DEBUG("SNAP: VIC: changing I/O mode to %d(%s)" NL, vic_iomode, iomode_names[vic_iomode]);
1568  interrupt_status = (int)P_AS_BE32(buffer + 1);
1569  return 0;
1570 }
1571 
1572 
1573 int vic4_snapshot_save_state ( const struct xemu_snapshot_definition_st *def )
1574 {
1575  Uint8 buffer[SNAPSHOT_VIC4_BLOCK_SIZE];
1576  int a = xemusnap_write_block_header(def->idstr, SNAPSHOT_VIC4_BLOCK_VERSION);
1577  if (a) return a;
1578  memset(buffer, 0xFF, sizeof buffer);
1579  /* saving state ... */
1580  memcpy(buffer + 0x80, vic_registers, 0x80); // $80 bytes
1581  buffer[0x7F] = c128_d030_reg;
1582  memcpy(buffer + 0x100 , vic_palette_bytes_red, NO_OF_PALETTE_REGS);
1584  memcpy(buffer + 0x100 + 2 * NO_OF_PALETTE_REGS, vic_palette_bytes_blue, NO_OF_PALETTE_REGS);
1585  buffer[0] = vic_iomode;
1586  U32_AS_BE(buffer + 1, interrupt_status);
1587  return xemusnap_write_sub_block(buffer, sizeof buffer);
1588 }
1589 
1590 #endif
SPRITE_MULTICOLOR_2
#define SPRITE_MULTICOLOR_2
Definition: vic4.h:152
PHYSICAL_RASTERS_DEFAULT
#define PHYSICAL_RASTERS_DEFAULT
Definition: vic4.h:44
REG_CHRYSCL
#define REG_CHRYSCL
Definition: vic4.h:99
VIC3_ATTR_REVERSE
#define VIC3_ATTR_REVERSE(c)
Definition: vic4.h:167
NTSC_FRAME_TIME
#define NTSC_FRAME_TIME
Definition: vic4.h:33
SXA_ATTR_REVERSE
#define SXA_ATTR_REVERSE(cw)
Definition: vic4.h:183
vic_read_reg
Uint8 vic_read_reg(int unsigned addr)
Definition: vic4.c:784
SET_COLORRAM_BASE
#define SET_COLORRAM_BASE(x)
Definition: vic4.h:224
REG_CHARPTR_B0
#define REG_CHARPTR_B0
Definition: vic4.h:113
xemu_set_viewport
void xemu_set_viewport(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, unsigned int flags)
Definition: emutools.c:844
CHARGEN_X_START
#define CHARGEN_X_START
Definition: vic4.h:133
vic4.h
configdb.h
XEMU_VIEWPORT_ADJUST_LOGICAL_SIZE
#define XEMU_VIEWPORT_ADJUST_LOGICAL_SIZE
Definition: emutools.h:146
REG_SCRNPTR_B0
#define REG_SCRNPTR_B0
Definition: vic4.h:107
machine_set_speed
void machine_set_speed(int verbose)
Definition: mega65.c:101
REG_MCM
#define REG_MCM
Definition: vic4.h:62
NO_OF_PALETTE_REGS
#define NO_OF_PALETTE_REGS
Definition: vic4_palette.h:23
SXA_VERTICAL_FLIP
#define SXA_VERTICAL_FLIP(cw)
Definition: vic4.h:176
emutools.h
CASE_VIC_ALL
#define CASE_VIC_ALL(n)
Definition: vic4.c:527
vic4_revalidate_all_palette
void vic4_revalidate_all_palette(void)
Definition: vic4_palette.c:51
char_wom
Uint8 char_wom[0x2000]
Definition: memory_mapper.c:67
REG_FNRST
#define REG_FNRST
Definition: vic4.h:92
f011_core.h
display_row_count
#define display_row_count
Definition: vic4.c:66
PAL_FRAME_TIME
#define PAL_FRAME_TIME
Definition: vic4.h:32
REG_CHARXSCALE
#define REG_CHARXSCALE
Definition: vic4.h:105
REG_VIC2_YSCROLL
#define REG_VIC2_YSCROLL
Definition: vic4.h:77
videostd_1mhz_cycles_per_scanline
float videostd_1mhz_cycles_per_scanline
Definition: vic4.c:85
SXA_ATTR_BOLD
#define SXA_ATTR_BOLD(cw)
Definition: vic4.h:182
RASTER_CORRECTION
#define RASTER_CORRECTION
Definition: vic4.h:51
SPRITE_V400
#define SPRITE_V400(n)
Definition: vic4.h:162
SPRITE_VERT_2X
#define SPRITE_VERT_2X(n)
Definition: vic4.h:155
DO_SPRITE_SPRITE_COLLISION
#define DO_SPRITE_SPRITE_COLLISION(pos, cond)
Definition: vic4.c:921
VIC2_BITMAP_ADDR
#define VIC2_BITMAP_ADDR
Definition: vic4.h:138
CASE_VIC_4
#define CASE_VIC_4(n)
Definition: vic4.c:526
SET_BORDER_Y_BOTTOM
#define SET_BORDER_Y_BOTTOM(x)
Definition: vic4.h:218
vic_palettes
Uint32 vic_palettes[NO_OF_PALETTE_REGS]
Definition: vic4_palette.c:32
SET_CHARGEN_X_START
#define SET_CHARGEN_X_START(x)
Definition: vic4.h:219
SPRITE_EXTWIDTH
#define SPRITE_EXTWIDTH(n)
Definition: vic4.h:158
memory_set_vic3_rom_mapping
void memory_set_vic3_rom_mapping(Uint8 value)
Definition: memory_mapper.c:689
xemu_update_screen
void xemu_update_screen(void)
Definition: emutools.c:1184
SCREEN_HEIGHT_VISIBLE_DEFAULT
#define SCREEN_HEIGHT_VISIBLE_DEFAULT
Definition: vic4.h:45
DO_SPRITE_FG_COLLISION
#define DO_SPRITE_FG_COLLISION(pos, cond)
Definition: vic4.c:940
SPRITE_POS_X
#define SPRITE_POS_X(n)
Definition: vic4.h:146
vic4_render_scanline
int vic4_render_scanline(void)
Definition: vic4.c:1462
vic4_open_frame_access
void vic4_open_frame_access(void)
Definition: vic4.c:373
vic_readjust_sdl_viewport
int vic_readjust_sdl_viewport
Definition: vic4.c:89
videostd_name
const char * videostd_name
Definition: vic4.c:83
colour_ram
Uint8 colour_ram[0x8000]
Definition: memory_mapper.c:65
io_mapper.h
addr
int addr
Definition: dma65.c:81
sdl_pix_fmt
SDL_PixelFormat * sdl_pix_fmt
Definition: emutools.c:80
REG_H640
#define REG_H640
Definition: vic4.h:70
check_if_rom_palette
void check_if_rom_palette(int rom_pal)
Definition: vic4_palette.c:192
spritepalette
Uint32 * spritepalette
Definition: vic4_palette.c:34
vic_palette_bytes_green
Uint8 vic_palette_bytes_green[NO_OF_PALETTE_REGS]
Definition: vic4_palette.c:30
REG_HOTREG
#define REG_HOTREG
Definition: vic4.h:102
XEMU_INLINE
#define XEMU_INLINE
Definition: emutools_basicdefs.h:126
m65-memcontent-generator.data
data
Definition: m65-memcontent-generator.py:119
mega65.h
xemu_get_viewport
void xemu_get_viewport(unsigned int *x1, unsigned int *y1, unsigned int *x2, unsigned int *y2)
Definition: emutools.c:877
Uint32
uint32_t Uint32
Definition: fat32.c:49
NTSC_LINE_FREQ
#define NTSC_LINE_FREQ
Definition: vic4.h:30
REG_SPRHGHT
#define REG_SPRHGHT
Definition: vic4.h:97
configdb_st::fullborders
int fullborders
Definition: configdb.h:71
BORDER_Y_TOP
#define BORDER_Y_TOP
Definition: vic4.h:130
REG_SCRNPTR_B1
#define REG_SCRNPTR_B1
Definition: vic4.h:108
REG_SPRITE_ENABLE
#define REG_SPRITE_ENABLE
Definition: vic4.h:64
Uint8
uint8_t Uint8
Definition: fat32.c:51
VIC2_IOMODE
#define VIC2_IOMODE
Definition: vic4.h:23
REG_SPR640
#define REG_SPR640
Definition: vic4.h:96
LINESTEP_BYTES
#define LINESTEP_BYTES
Definition: vic4.h:134
c128_d030_reg
Uint8 c128_d030_reg
Definition: vic4.c:49
SINGLE_SIDE_BORDER
#define SINGLE_SIDE_BORDER
Definition: vic4.h:129
SET_CHARGEN_Y_START
#define SET_CHARGEN_Y_START(x)
Definition: vic4.h:220
main_ram
Uint8 main_ram[512<< 10]
Definition: memory_mapper.c:47
vic4_init_palette
void vic4_init_palette(void)
Definition: vic4_palette.c:62
SCREEN_HEIGHT_VISIBLE_PAL
#define SCREEN_HEIGHT_VISIBLE_PAL
Definition: vic4.h:47
palette
Uint32 * palette
Definition: vic4_palette.c:33
block
Uint32 block
Definition: fat32.c:156
FRAME_H_FRONT
#define FRAME_H_FRONT
Definition: vic4.h:50
REG_VICIII_ATTRIBS
#define REG_VICIII_ATTRIBS
Definition: vic4.h:72
REG_SPRPTR_B1
#define REG_SPRPTR_B1
Definition: vic4.h:117
emutools_files.h
CHARGEN_Y_START
#define CHARGEN_Y_START
Definition: vic4.h:132
SPRITE_BITPLANE_ENABLE
#define SPRITE_BITPLANE_ENABLE(n)
Definition: vic4.h:160
x
int x
Definition: console.c:27
SET_LINESTEP_BYTES
#define SET_LINESTEP_BYTES(x)
Definition: vic4.h:225
BORDER_Y_BOTTOM
#define BORDER_Y_BOTTOM
Definition: vic4.h:131
DEBUGPRINT
#define DEBUGPRINT(...)
Definition: emutools_basicdefs.h:171
SPRITE_16COLOR
#define SPRITE_16COLOR(n)
Definition: vic4.h:157
VIC3_IOMODE
#define VIC3_IOMODE
Definition: vic4.h:24
TEXTURE_WIDTH
#define TEXTURE_WIDTH
Definition: vic4.h:41
vic4_close_frame_access
void vic4_close_frame_access(void)
Definition: vic4.c:217
SPRITE_MULTICOLOR
#define SPRITE_MULTICOLOR(n)
Definition: vic4.h:156
SET_BORDER_Y_TOP
#define SET_BORDER_Y_TOP(x)
Definition: vic4.h:217
VIC4_BLINK_INTERVAL
#define VIC4_BLINK_INTERVAL
Definition: vic4.h:52
SPRITE_EXTHEIGHT
#define SPRITE_EXTHEIGHT(n)
Definition: vic4.h:159
REG_CHRCOUNT
#define REG_CHRCOUNT
Definition: vic4.h:106
vic_palette_bytes_blue
Uint8 vic_palette_bytes_blue[NO_OF_PALETTE_REGS]
Definition: vic4_palette.c:31
vic_reset
void vic_reset(void)
Definition: vic4.c:143
xemu_start_pixel_buffer_access
Uint32 * xemu_start_pixel_buffer_access(int *texture_tail)
Definition: emutools.c:1153
REG_SPRITE_Y_ADJUST
#define REG_SPRITE_Y_ADJUST
Definition: vic4.h:119
SPRITE_HORZ_2X
#define SPRITE_HORZ_2X(n)
Definition: vic4.h:154
palregaccofs
unsigned int palregaccofs
Definition: vic4_palette.c:42
fdc_get_led_state
int fdc_get_led_state(int blink_inc)
Definition: f011_core.c:161
memory_mapper.h
VIC4_IOMODE
#define VIC4_IOMODE
Definition: vic4.h:26
XEMU_LIKELY
#define XEMU_LIKELY(__x__)
Definition: emutools_basicdefs.h:124
videostd_id
Uint8 videostd_id
Definition: vic4.c:82
configdb_st::force_videostd
int force_videostd
Definition: configdb.h:69
last_dd00_bits
Uint8 last_dd00_bits
Definition: mega65.c:85
vic4_disallow_video_std_change
int vic4_disallow_video_std_change
Definition: vic4.c:90
NL
#define NL
Definition: fat32.c:37
altpalette
Uint32 * altpalette
Definition: vic4_palette.c:35
hypervisor.h
configdb
struct configdb_st configdb
Definition: configdb.c:34
SXA_TRIM_RIGHT_BITS012
#define SXA_TRIM_RIGHT_BITS012(sw)
Definition: vic4.h:175
REG_RSEL
#define REG_RSEL
Definition: vic4.h:73
REG_DISPLAYENABLE
#define REG_DISPLAYENABLE
Definition: vic4.h:75
VIC3_ATTR_BLINK
#define VIC3_ATTR_BLINK(c)
Definition: vic4.h:166
xemu_frame_pixel_access_p
Uint32 * xemu_frame_pixel_access_p
Definition: emutools.c:93
SPRITE_COLOR_4BIT
#define SPRITE_COLOR_4BIT(n)
Definition: vic4.h:150
cpu65.h
configdb_st::show_drive_led
int show_drive_led
Definition: configdb.h:73
SPRITE_POINTER_ADDR
#define SPRITE_POINTER_ADDR
Definition: vic4.h:139
PHYSICAL_RASTERS_PAL
#define PHYSICAL_RASTERS_PAL
Definition: vic4.h:49
SXA_HORIZONTAL_FLIP
#define SXA_HORIZONTAL_FLIP(cw)
Definition: vic4.h:177
SCREEN_ADDR
#define SCREEN_ADDR
Definition: vic4.h:136
vic_write_reg
void vic_write_reg(unsigned int addr, Uint8 data)
Definition: vic4.c:539
PHYSICAL_RASTERS_NTSC
#define PHYSICAL_RASTERS_NTSC
Definition: vic4.h:48
COLOUR_RAM_OFFSET
#define COLOUR_RAM_OFFSET
Definition: vic4.h:140
CASE_VIC_3
#define CASE_VIC_3(n)
Definition: vic4.c:525
vic_init
void vic_init(void)
Definition: vic4.c:173
CHAR_IS256_COLOR
#define CHAR_IS256_COLOR(ch)
Definition: vic4.h:170
SPRITE_FIRST_X
#define SPRITE_FIRST_X
Definition: vic4.h:229
SPRITE_IS_BACK
#define SPRITE_IS_BACK(n)
Definition: vic4.h:153
SXA_GOTO_X
#define SXA_GOTO_X(cw)
Definition: vic4.h:179
SPRITE_MULTICOLOR_1
#define SPRITE_MULTICOLOR_1
Definition: vic4.h:151
SET_PHYSICAL_RASTER
#define SET_PHYSICAL_RASTER(x)
Definition: vic4.h:212
REG_BORDER_COLOR
#define REG_BORDER_COLOR
Definition: vic4.h:65
iomode_names
const char * iomode_names[4]
Definition: vic4.c:37
DISPLAY_HEIGHT
#define DISPLAY_HEIGHT
Definition: vic4.c:94
REG_CHARPTR_B1
#define REG_CHARPTR_B1
Definition: vic4.h:114
SPRITE_16BITPOINTER
#define SPRITE_16BITPOINTER
Definition: vic4.h:161
SXA_4BIT_PER_PIXEL
#define SXA_4BIT_PER_PIXEL(cw)
Definition: vic4.h:180
REG_V400
#define REG_V400
Definition: vic4.h:71
REG_VIC2_XSCROLL
#define REG_VIC2_XSCROLL
Definition: vic4.h:76
vic_iomode
int vic_iomode
Definition: vic4.c:44
OSD
#define OSD(...)
Definition: xep128.h:100
REG_CSEL
#define REG_CSEL
Definition: vic4.h:74
REG_BMM
#define REG_BMM
Definition: vic4.h:63
CASE_VIC_2
#define CASE_VIC_2(n)
Definition: vic4.c:524
videostd_frametime
int videostd_frametime
Definition: vic4.c:84
in_hypervisor
int in_hypervisor
Definition: hypervisor.c:40
y
int y
Definition: console.c:27
CASE_VIC_3_4
#define CASE_VIC_3_4(n)
Definition: vic4.c:528
Uint16
uint16_t Uint16
Definition: fat32.c:50
REG_16BITCHARSET
#define REG_16BITCHARSET
Definition: vic4.h:93
REG_EBM
#define REG_EBM
Definition: vic4.h:61
vic_palette_bytes_red
Uint8 vic_palette_bytes_red[NO_OF_PALETTE_REGS]
Definition: vic4_palette.c:29
REG_MULTICOLOR_1
#define REG_MULTICOLOR_1
Definition: vic4.h:67
SXA_TRIM_RIGHT_BIT3
#define SXA_TRIM_RIGHT_BIT3(cw)
Definition: vic4.h:181
vic4_palette.h
DEBUG
#define DEBUG(...)
Definition: emutools_basicdefs.h:167
videostd_changed
int videostd_changed
Definition: vic4.c:86
SPRITE_COLOR
#define SPRITE_COLOR(n)
Definition: vic4.h:149
FATAL
#define FATAL(...)
Definition: xep128.h:117
REG_SCREEN_COLOR
#define REG_SCREEN_COLOR
Definition: vic4.h:66
REG_MULTICOLOR_2
#define REG_MULTICOLOR_2
Definition: vic4.h:68
D7XX
Uint8 D7XX[0x100]
Definition: io_mapper.c:39
VIC3_ATTR_UNDERLINE
#define VIC3_ATTR_UNDERLINE(c)
Definition: vic4.h:169
REG_CHARPTR_B2
#define REG_CHARPTR_B2
Definition: vic4.h:115
REG_SCRNPTR_B2
#define REG_SCRNPTR_B2
Definition: vic4.h:109
vic_registers
Uint8 vic_registers[0x80]
Definition: vic4.c:43
registered_screenshot_request
int registered_screenshot_request
Definition: mega65.c:83
SCREEN_HEIGHT_VISIBLE_NTSC
#define SCREEN_HEIGHT_VISIBLE_NTSC
Definition: vic4.h:46
CHARSET_ADDR
#define CHARSET_ADDR
Definition: vic4.h:137
SINGLE_TOP_BORDER_400
#define SINGLE_TOP_BORDER_400
Definition: vic4.c:103
PAL_LINE_FREQ
#define PAL_LINE_FREQ
Definition: vic4.h:29
XEMU_UNLIKELY
#define XEMU_UNLIKELY(__x__)
Definition: emutools_basicdefs.h:125
REG_SPRPTR_B0
#define REG_SPRPTR_B0
Definition: vic4.h:116
REG_SPRTILEN
#define REG_SPRTILEN
Definition: vic4.h:87
SINGLE_TOP_BORDER_200
#define SINGLE_TOP_BORDER_200
Definition: vic4.c:102
VIC3_ATTR_BOLD
#define VIC3_ATTR_BOLD(c)
Definition: vic4.h:168
SPRITE_POS_Y
#define SPRITE_POS_Y(n)
Definition: vic4.h:143
DIRSEP_CHR
#define DIRSEP_CHR
Definition: emutools_basicdefs.h:142