1 /*
2 * vdc.c - MOS 8563 (VDC) emulation.
3 *
4 * Written by
5 * Markus Brenner <markus@brenner.de>
6 * Ettore Perazzoli <ettore@comm2000.it>
7 * Andreas Boose <viceteam@t-online.de>
8 *
9 * This file is part of VICE, the Versatile Commodore Emulator.
10 * See README for copyright notice.
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
25 * 02111-1307 USA.
26 *
27 */
28
29 #include "vice.h"
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include "alarm.h"
36 #include "lib.h"
37 #include "log.h"
38 #include "machine.h"
39 #include "maincpu.h"
40 #include "raster.h"
41 #include "raster-line.h"
42 #include "raster-modes.h"
43 #include "resources.h"
44 #include "screenshot.h"
45 #include "snapshot.h"
46 #include "types.h"
47 #include "vdc-cmdline-options.h"
48 #include "vdc-color.h"
49 #include "vdc-draw.h"
50 #include "vdc-resources.h"
51 #include "vdc-snapshot.h"
52 #include "vdc.h"
53 #include "vdctypes.h"
54 #include "video.h"
55 #include "viewport.h"
56
57 vdc_t vdc;
58
59 static void vdc_raster_draw_alarm_handler(CLOCK offset, void *data);
60
61 /* return pixel aspect ratio for current video mode */
62 /* FIXME: calculate proper values.
63 look at http://www.codebase64.org/doku.php?id=base:pixel_aspect_ratio&s[]=aspect
64 for an example calculation
65 */
vdc_get_pixel_aspect(void)66 static float vdc_get_pixel_aspect(void)
67 {
68 /*
69 int video;
70 resources_get_int("MachineVideoStandard", &video);
71 switch (video) {
72 case MACHINE_SYNC_PAL:
73 case MACHINE_SYNC_PALN:
74 return 0.936f;
75 default:
76 return 0.75f;
77 }
78 */
79 return 1.0f; /* assume 1:1 for CGA */
80 }
81
82 /* return type of monitor used for current video mode */
vdc_get_crt_type(void)83 static int vdc_get_crt_type(void)
84 {
85 return 2; /* RGB */
86 }
87
vdc_set_geometry(void)88 static void vdc_set_geometry(void)
89 {
90 raster_t *raster;
91 unsigned int screen_width, screen_height;
92 unsigned int first_displayed_line, last_displayed_line;
93 unsigned int screen_xpix, screen_ypix;
94 unsigned int border_height, border_width;
95 unsigned int vdc_25row_start_line, vdc_25row_stop_line;
96 unsigned int displayed_width, displayed_height;
97 unsigned int vdc_80col_start_pixel, vdc_80col_stop_pixel;
98 unsigned int charwidth;
99
100 raster = &vdc.raster;
101
102 screen_width = VDC_SCREEN_WIDTH;
103 screen_height = VDC_SCREEN_HEIGHT;
104
105 first_displayed_line = vdc.first_displayed_line;
106 last_displayed_line = vdc.last_displayed_line;
107
108 screen_xpix = vdc.screen_xpix;
109 screen_ypix = vdc.screen_ypix;
110
111 border_width = vdc.border_width;
112 border_height = vdc.border_height;
113
114 vdc_25row_start_line = border_height;
115 vdc_25row_stop_line = vdc_25row_start_line + screen_ypix;
116
117 if(vdc.regs[25] & 0x10) { /* double pixel a.k.a 40column mode */
118 charwidth = 2 * (vdc.regs[22] >> 4);
119 } else { /* 80 column mode */
120 charwidth = 1 + (vdc.regs[22] >> 4);
121 }
122 vdc_80col_start_pixel = border_width;
123 vdc_80col_stop_pixel = vdc_80col_start_pixel + charwidth * vdc.screen_text_cols;
124
125 displayed_width = VDC_SCREEN_WIDTH;
126 displayed_height = last_displayed_line - first_displayed_line + 1;
127
128 /*
129 printf("SH: %03i SW: %03i\n", screen_height, screen_width);
130 printf("YP: %03i XP: %03i\n", screen_ypix, screen_xpix);
131 printf("DH: %03i DW: %03i\n", displayed_height, displayed_width);
132 printf("BH: %03i BW: %03i\n", border_height, border_width);
133 printf("SA: %03i SO: %03i\n", vdc_25row_start_line, vdc_25row_stop_line);
134 printf("LD: %03i FD: %03i\n", last_displayed_line, first_displayed_line);
135 */
136
137 raster->display_ystart = vdc_25row_start_line;
138 raster->display_ystop = vdc_25row_stop_line;
139 raster->display_xstart = vdc_80col_start_pixel;
140 raster->display_xstop = vdc_80col_stop_pixel;
141
142 raster_set_geometry(raster,
143 displayed_width, displayed_height, /* canvas - physically displayed width/height, ie size of visible window */
144 screen_width, screen_height, /* width/height of virtual screen */
145 screen_xpix, screen_ypix, /* size of the foreground area (pixels) */
146 VDC_SCREEN_MAX_TEXTCOLS, vdc.screen_textlines, /* size of the foreground area (characters) */
147 border_width, vdc_25row_start_line, /* gfx_pos_x/y - position of visible screen in virtual coords */
148 0, /* gfx_area_moves */
149 first_displayed_line, /* 1st line of virtual screen physically visible */
150 last_displayed_line, /* last line physically visible */
151 0, 0); /* extra off screen border left / right */
152
153 raster->geometry->pixel_aspect_ratio = vdc_get_pixel_aspect();
154 raster->geometry->char_pixel_width = charwidth;
155 raster->viewport->crt_type = vdc_get_crt_type();
156 }
157
vdc_invalidate_cache(raster_t * raster,unsigned int screen_height)158 static void vdc_invalidate_cache(raster_t *raster, unsigned int screen_height)
159 {
160 raster_new_cache(raster, screen_height);
161 }
162
init_raster(void)163 static int init_raster(void)
164 {
165 raster_t *raster;
166
167 raster = &vdc.raster;
168
169 raster->sprite_status = NULL;
170 raster_line_changes_init(raster);
171
172 if (raster_init(raster, VDC_NUM_VMODES) < 0) {
173 return -1;
174 }
175
176 raster_modes_set_idle_mode(raster->modes, VDC_IDLE_MODE);
177 resources_touch("VDCVideoCache");
178
179 vdc_set_geometry();
180
181 if (vdc_color_update_palette(vdc.raster.canvas) < 0) {
182 log_error(vdc.log, "Cannot load palette.");
183 return -1;
184 }
185
186 raster_set_title(raster, machine_name);
187
188 if (raster_realize(raster) < 0) {
189 return -1;
190 }
191
192 raster->border_color = 0;
193
194 /* FIXME: this seems to be the only way to disable cache on VDC.
195 The GUI (at least on win32) doesn't let you do it */
196 /* raster->cache_enabled = 0; */ /* Force disable cache for testing non-cache mode */
197
198 return 0;
199 }
200
201 /* Initialize the VDC emulation. */
vdc_init(void)202 raster_t *vdc_init(void)
203 {
204 vdc.initialized = 0;
205
206 vdc.log = log_open("VDC");
207
208 vdc.raster_draw_alarm = alarm_new(maincpu_alarm_context, "VdcRasterDraw",
209 vdc_raster_draw_alarm_handler, NULL);
210
211 vdc_powerup();
212
213 if (init_raster() < 0) {
214 return NULL;
215 }
216
217 vdc.force_resize = 0;
218 vdc.force_repaint = 0;
219
220 vdc_draw_init();
221
222 vdc.initialized = 1;
223
224 /*vdc_set_geometry();*/
225 resources_touch("VDCDoubleSize");
226
227 return &vdc.raster;
228 }
229
vdc_get_canvas(void)230 struct video_canvas_s *vdc_get_canvas(void)
231 {
232 return vdc.raster.canvas;
233 }
234
235
vdc_set_next_alarm(CLOCK offset)236 static void vdc_set_next_alarm(CLOCK offset)
237 {
238 unsigned int next_alarm;
239 static unsigned int next_line_accu = 0;
240
241 next_line_accu += vdc.xsync_increment;
242 next_alarm = next_line_accu >> 16;
243 next_line_accu -= (next_alarm << 16);
244
245 /* Set the next draw event. */
246 alarm_set(vdc.raster_draw_alarm, maincpu_clk + (CLOCK)next_alarm - offset);
247 }
248
vdc_update_geometry(void)249 static void vdc_update_geometry(void)
250 { /* This sets things based on registers: 2, 6, 9
251 it sets vdc.screenheight
252 last_displayed_line
253 screen_textlines
254 screen_ypix
255 screen_text_cols
256 hsync_shift
257 border_width */
258
259 int charwidth, hsync;
260
261 /* Leave this fixed so the window isn't getting constantly resized */
262 vdc.screen_height = VDC_SCREEN_HEIGHT;
263
264 vdc.last_displayed_line = MIN(VDC_LAST_DISPLAYED_LINE, vdc.screen_height - 1);
265
266 /* TODO get rid of this if/when it we don't need it anymore.. */
267 /* printf("BH:%1i 0:%02X 1:%02X 2:%02X 3:%02X 4:%02X 5:%02X 6:%02X 7:%02X 9:%02X 22:%02X 24:%02X 25:%02X 26:%02X\n",
268 vdc.border_height, vdc.regs[0], vdc.regs[1], vdc.regs[2], vdc.regs[3], vdc.regs[4], (vdc.regs[5] & 0x1f), vdc.regs[6], vdc.regs[7], vdc.regs[9] & 0x1f, vdc.regs[22], vdc.regs[24], vdc.regs[25], vdc.regs[26] );
269 */
270
271 vdc.screen_textlines = vdc.regs[6];
272
273 vdc.screen_ypix = vdc.regs[6] * ((vdc.regs[9] & 0x1f) + 1);
274
275 if (vdc.regs[1] >= 6 && vdc.regs[1] <= VDC_SCREEN_MAX_TEXTCOLS) {
276 vdc.screen_text_cols = vdc.regs[1];
277 } else if (vdc.regs[1] < 6) {
278 vdc.screen_text_cols = 6;
279 } else {
280 vdc.screen_text_cols = VDC_SCREEN_MAX_TEXTCOLS;
281 }
282
283 /* FIXME: this seems to have semi-randomly selected constants... */
284 if(vdc.regs[25] & 0x10) { /* double pixel a.k.a 40column mode */
285 charwidth = 2 * (vdc.regs[22] >> 4);
286 hsync = 62 * 16 /* 992 */
287 - vdc.regs[2] * charwidth; /* default (55) - 880 = 112 */
288 } else { /* 80 column mode */
289 charwidth = 1 + (vdc.regs[22] >> 4);
290 hsync = 116 * 8 /* 928 */
291 - vdc.regs[2] * charwidth; /* default (102) - 816 = 112 */
292 }
293 /* FIXME: It's potentially valid for the active display area to start off the left (or right) of the display,
294 because the VDC can put the horizontal sync anywhere. But we can't really emulate that (yet) without things crashing. */
295 if (hsync < 0) {
296 hsync = 0;
297 }
298 vdc.hsync_shift = hsync;
299
300 /* clamp the display within the right edge of the screen */
301 if ((vdc.screen_text_cols * charwidth) > VDC_SCREEN_WIDTH ) {
302 /* bounds check so we don't wind up with a "negative unsigned" hsync_shift (i.e. a massive value) which then causes a segfault in vdc-draw.. */
303 vdc.hsync_shift = 0;
304 } else if (vdc.hsync_shift + (vdc.screen_text_cols * charwidth) > VDC_SCREEN_WIDTH ) {
305 vdc.hsync_shift = VDC_SCREEN_WIDTH - (vdc.screen_text_cols * charwidth);
306 }
307 vdc.border_width = vdc.hsync_shift;
308
309 vdc.update_geometry = 0;
310 }
311
312
313 /* Reset the VDC chip */
vdc_reset(void)314 void vdc_reset(void)
315 {
316 if (vdc.initialized) {
317 raster_reset(&vdc.raster);
318 }
319
320 vdc.frame_counter = 0;
321 vdc.screen_text_cols = VDC_SCREEN_MAX_TEXTCOLS;
322 vdc.xsmooth = 7;
323 vdc.regs[0] = 126;
324 vdc.regs[1] = 102;
325 vdc.xchars_total = vdc.regs[0] + 1;
326 vdc_calculate_xsync();
327 vdc.regs[4] = 39;
328 vdc.regs[5] = 0;
329 vdc.regs[6] = 25;
330 vdc.regs[9] = vdc.raster_ycounter_max = 7;
331 vdc.attribute_offset = 0;
332 vdc.border_height = 59;
333 vdc.bytes_per_char = 16;
334 vdc_update_geometry();
335 vdc_set_next_alarm((CLOCK)0);
336 vdc.light_pen.x = vdc.light_pen.y = vdc.light_pen.triggered = 0;
337 }
338
339 /* This _should_ put the VDC in the same state as powerup */
vdc_powerup(void)340 void vdc_powerup(void)
341 {
342 /* Setup the VDC's ram with a 0xff00ff00.. pattern */
343 unsigned int i;
344 uint8_t v = 0xff;
345 for (i = 0; i < sizeof(vdc.ram); i++) {
346 vdc.ram[i] = v;
347 v ^= 0xff;
348 }
349 memset(vdc.regs, 0, sizeof(vdc.regs));
350 vdc.mem_counter = 0;
351 vdc.mem_counter_inc = 0;
352
353 vdc.screen_xpix = VDC_SCREEN_XPIX;
354 vdc.first_displayed_line = VDC_FIRST_DISPLAYED_LINE;
355 vdc.last_displayed_line = VDC_LAST_DISPLAYED_LINE;
356
357 vdc_reset();
358 }
359
360
361
362 /* ---------------------------------------------------------------------*/
363
364 /* Trigger the light pen. */
vdc_trigger_light_pen(CLOCK mclk)365 void vdc_trigger_light_pen(CLOCK mclk)
366 {
367 vdc.light_pen.triggered = 1;
368 vdc.regs[16] = vdc.light_pen.y;
369 vdc.regs[17] = vdc.light_pen.x;
370 }
371
372 /* Calculate lightpen pulse time based on x/y */
vdc_lightpen_timing(int x,int y)373 CLOCK vdc_lightpen_timing(int x, int y)
374 {
375 CLOCK pulse_time;
376
377 double vdc_cycles_per_line, host_cycles_per_second;
378 host_cycles_per_second = (double)machine_get_cycles_per_second();
379 vdc_cycles_per_line = (double)(vdc.xchars_total) * 8.0
380 * host_cycles_per_second / VDC_DOT_CLOCK;
381
382 /* FIXME - this doesn't work properly.. */
383 pulse_time = maincpu_clk;
384 pulse_time += (CLOCK)((x / 8) + (y * vdc_cycles_per_line));
385
386 /* Figure out what values should go into the registers when triggered */
387 vdc.light_pen.y = (y - (int)vdc.first_displayed_line - 1) / ((int)(vdc.regs[9] & 0x1f) + 1);
388 if (vdc.light_pen.y < 0) {
389 vdc.light_pen.y += vdc.regs[4] + 1;
390 }
391 vdc.light_pen.x = (x - vdc.border_width) / ((vdc.regs[22] >> 4) + 1) + 22;
392 return pulse_time;
393 }
394
395 /* ---------------------------------------------------------------------*/
396
397 /* Set the memory pointers according to the values in the registers. */
vdc_update_memory_ptrs(unsigned int cycle)398 void vdc_update_memory_ptrs(unsigned int cycle)
399 {
400 /* FIXME: use it or lose it */
401 }
402
vdc_increment_memory_pointer(void)403 static void vdc_increment_memory_pointer(void)
404 {
405 vdc.mem_counter_inc = vdc.screen_text_cols;
406 if (vdc.raster.ycounter >= vdc.raster_ycounter_max) {
407 vdc.mem_counter += vdc.mem_counter_inc + vdc.regs[27];
408 }
409
410 vdc.raster.ycounter = (vdc.raster.ycounter + 1)
411 % (vdc.raster_ycounter_max + 1);
412
413 vdc.bitmap_counter += vdc.mem_counter_inc + vdc.regs[27];
414 }
415
vdc_increment_memory_pointer_interlace_bitmap(void)416 static void vdc_increment_memory_pointer_interlace_bitmap(void)
417 { /* This is identical to above (and should remain so), we just don't increment the bitmap pointer */
418 vdc.mem_counter_inc = vdc.screen_text_cols;
419 if (vdc.raster.ycounter >= vdc.raster_ycounter_max) {
420 vdc.mem_counter += vdc.mem_counter_inc + vdc.regs[27];
421 }
422 vdc.raster.ycounter = (vdc.raster.ycounter + 1)
423 % (vdc.raster_ycounter_max + 1);
424 }
425
vdc_set_video_mode(void)426 static void vdc_set_video_mode(void)
427 {
428 vdc.raster.video_mode = (vdc.regs[25] & 0x80)
429 ? VDC_BITMAP_MODE : VDC_TEXT_MODE;
430
431 if (vdc.raster.ycounter > (unsigned int)(vdc.regs[9] & 0x1f)) {
432 vdc.raster.video_mode = VDC_IDLE_MODE;
433 }
434 }
435
436
437 /* Redraw the current raster line. */
vdc_raster_draw_alarm_handler(CLOCK offset,void * data)438 static void vdc_raster_draw_alarm_handler(CLOCK offset, void *data)
439 {
440 int in_idle_state, calculated_border_height, i;
441 static unsigned int old_screen_adr, old_attribute_adr, screen_ystart, need_increment_memory_pointer;
442
443 /* Update the memory pointers just before we draw the next line (vs after last line),
444 in case relevant registers changed since last call. */
445 if (need_increment_memory_pointer) {
446 vdc_increment_memory_pointer();
447 /* If interlace we need to skip a line, so we increment again */
448 if ((vdc.regs[8] & 0x03) == 3) {
449 if (vdc.regs[25] & 0x80) { /* in interlace bitmap mode we want to increment the attribute memory pointer again, but not the bitmap pointer */
450 vdc_increment_memory_pointer_interlace_bitmap();
451 } else { /* text mode */
452 vdc_increment_memory_pointer();
453 }
454 }
455 need_increment_memory_pointer = 0;
456 }
457
458 /* VDC locks in the screen/attr start addresses after the last raster line of foreground */
459 if (vdc.raster.current_line == vdc.border_height + vdc.screen_ypix + 1) {
460 vdc.screen_adr = ((vdc.regs[12] << 8) | vdc.regs[13])
461 & vdc.vdc_address_mask;
462 vdc.attribute_adr = ((vdc.regs[20] << 8) | vdc.regs[21])
463 & vdc.vdc_address_mask;
464 if (old_screen_adr != vdc.screen_adr || old_attribute_adr != vdc.attribute_adr) {
465 /* the cache can't cleanly handle these changing */
466 vdc.force_repaint = 1;
467 old_screen_adr = vdc.screen_adr;
468 old_attribute_adr = vdc.attribute_adr;
469 }
470 }
471
472 if (vdc.raster.current_line == 0) { /* We are on the first raster line, so go reset and/or handle everything for a new frame */
473 /* The top border position is based on the position of the vertical
474 sync pulse [7] in relation to the total height of the screen [4]
475 and the width of the sync pulse [3] */
476 calculated_border_height = (vdc.regs[4] + 1 - vdc.regs[7]) /* # of rows from sync pulse */
477 * ((vdc.regs[9] & 0x1f) + 1) /* height of each row (R9) */
478 - (vdc.regs[3] >> 4) /* vertical sync pulse width */
479 + (vdc.regs[5] & 0x1f); /* vertical total adjust */
480
481 if (calculated_border_height >= 0 && calculated_border_height <= VDC_SCREEN_HEIGHT) {
482 vdc.border_height = calculated_border_height;
483 } else {
484 vdc.border_height = 0;
485 }
486 vdc.screen_ypix = vdc.regs[6] * ((vdc.regs[9] & 0x1f) + 1);
487 /* screen_ystart is the raster line the foreground data actually starts on, which may be above or below the border */
488 screen_ystart = vdc.border_height + (((vdc.regs[9] & 0x1f) - (vdc.regs[24] & 0x1f)) & 0x1f); /* - R24 is vertical smooth scroll, which interacts with the screen & R9 like this based on experimentation. */
489 vdc.border_height = vdc.border_height + (vdc.regs[9] & 0x1f);
490
491 /* If interlace we need to adjust the borders and screen height because they are twice as big otherwise */
492 if ((vdc.regs[8] & 0x03) == 3) {
493 vdc.border_height >>= 1;
494 vdc.screen_ypix >>= 1;
495 screen_ystart >>= 1;
496 }
497
498 /* fix to catch the end of the display for the bitmap/character memory pointers */
499 if ((vdc.border_height + vdc.screen_ypix + 1) > vdc.last_displayed_line) {
500 vdc.screen_ypix = vdc.last_displayed_line - vdc.border_height - 1;
501 }
502 vdc.raster.display_ystart = vdc.border_height;
503 vdc.raster.display_ystop = vdc.border_height + vdc.screen_ypix;
504 vdc.row_counter = 0;
505 vdc.row_counter_y = vdc.raster_ycounter_max;
506 vdc.raster.video_mode = VDC_IDLE_MODE;
507 vdc.frame_counter++; /* Note that as far as the frame counter is concerned, we are now on a new frame */
508 if (vdc.regs[24] & 0x20) {
509 vdc.attribute_blink = vdc.frame_counter & 16;
510 } else {
511 vdc.attribute_blink = vdc.frame_counter & 8;
512 }
513
514 /* Normally we would reset all the memory pointers etc. on a new frame.
515 BUT, in one particular circumstance we don't - interlace bitmap mode on an even frame -
516 because interlace bitmap is interleaved, we want it to keep reading into the 2nd bank on the even frame
517 (it starts on an odd frame) */
518 if (((vdc.regs[8] & 0x03) == 3) /* interlace */
519 && (vdc.regs[25] & 0x80) /* bitmap mode */
520 && !(vdc.frame_counter & 1)) { /* even frame */
521 /* FIXME: hideous hacks to fix VDCModeMania
522 Need to figure out WTF the VDC is doing with its memory pointers that we need this much hackery.. */
523 switch (vdc.regs[4]) {
524 case 0x84: /* Option 1 - NTSC Interlace */
525 for (i = 0; i < 20; i++) { /* decrement the memory pointer a few times based on [WTF?] */
526 vdc.mem_counter -= vdc.mem_counter_inc + vdc.regs[27];
527 }
528 for (i = 0; i < 40; i++) { /* decrement the bitmap memory pointer a few times based on how many columns are in a row */
529 vdc.bitmap_counter -= (vdc.mem_counter_inc + vdc.regs[27]);
530 }
531 break;
532 case 0x68: /* Option 2 - PAL Interlace */
533 for (i = 0; i < 47; i++) { /* increment the memory pointer a few times based on [WTF?] */
534 vdc_increment_memory_pointer_interlace_bitmap(); /* increment everything except the bitmap pointer */
535 }
536 for (i = 0; i < 23; i++) { /* increment the bitmap memory pointer a few times based on how many columns are in a row */
537 vdc.bitmap_counter += (vdc.mem_counter_inc + vdc.regs[27]);
538 }
539 break;
540 case 0x6a: /* Option 5 - "VDC-IMONO" 720x700 Mono Interlace */
541 for (i = 0; i < 83; i++) { /* increment the bitmap memory pointer a few times based on how many columns are in a row */
542 vdc.bitmap_counter += (vdc.mem_counter_inc + vdc.regs[27]);
543 }
544 break;
545 case 0x5c: /* Option 6 - "VDC-IM800" 800x600 Mono Interlace */
546 for (i = 0; i < 34; i++) { /* increment the bitmap memory pointer a few times based on how many columns are in a row */
547 vdc.bitmap_counter += (vdc.mem_counter_inc + vdc.regs[27]);
548 }
549 break;
550 case 0x4c: /* Platoterm - 640x532 Mono Interlace */
551 for (i = 0; i < 20; i++) { /* decrement the bitmap memory pointer a few times based on how many columns are in a row */
552 vdc.bitmap_counter -= (vdc.mem_counter_inc + vdc.regs[27]);
553 }
554 break;
555 }
556 } else { /* Reset all the internal VDC memory pointers and counters to 0 */
557 vdc.mem_counter = 0;
558 need_increment_memory_pointer = 0;
559 vdc.bitmap_counter = 0;
560 vdc.raster.ycounter = 0;
561 }
562
563 /* If interlace mode we need to skip to the 2nd line on an odd frame so that we render the odd field vs the even */
564 if (((vdc.regs[8] & 0x03) == 3) /* interlace and */
565 && (vdc.frame_counter & 1)) { /* odd frame */
566 if (!(vdc.regs[25] & 0x80)) { /* text mode */
567 vdc_increment_memory_pointer();
568 } else { /* bitmap mode */
569 /* FIXME: this hack fixes VDCModeMania, but I'm not completely sure why, or if it's correct in general.. */
570 /* Add half the number of raster lines in a char * number of characters in a row, to the bitmap pointer only
571 effectively skips the bitmap pointer over one full (interlace) attribute cell row worth of bitmap data
572 but why, because we don't increment the attribute pointer?? */
573 vdc.bitmap_counter += (((vdc.regs[9] & 0x1f) + 1) >> 1) * (vdc.mem_counter_inc + vdc.regs[27]);
574 }
575 }
576
577 if (vdc.update_geometry) {
578 vdc_update_geometry();
579 vdc.force_resize = 1;
580 vdc.force_repaint = 1;
581 /* Screen height has changed, so do not invalidate cache with
582 the new value. It will be recreated by resize anyway. */
583 vdc.force_cache_flush = 0;
584 } else {
585 if (vdc.force_cache_flush) {
586 vdc_invalidate_cache(&vdc.raster, vdc.screen_height);
587 vdc.force_cache_flush = 0;
588 }
589 }
590
591 if (vdc.force_resize) {
592 if (vdc.initialized) {
593 vdc_set_geometry();
594 raster_mode_change();
595 }
596 vdc.force_resize = 0;
597 }
598
599 if (vdc.force_repaint) {
600 vdc.force_repaint = 0;
601 raster_force_repaint(&vdc.raster);
602 }
603 }
604
605 /* If in_idle_state then we are not drawing anything on the current raster line */
606 in_idle_state = (vdc.raster.current_line < vdc.border_height)
607 || vdc.raster.current_line < screen_ystart
608 || (vdc.raster.current_line >= (vdc.border_height + vdc.screen_ypix));
609
610 if (!in_idle_state) {
611 vdc_set_video_mode();
612 } else {
613 vdc.raster.video_mode = VDC_IDLE_MODE;
614 }
615
616 /* actually draw the current raster line */
617 raster_line_emulate(&vdc.raster);
618
619 /* see if we still should be drawing things - if we haven't drawn more than regs[6] rows since the top border */
620 if (!in_idle_state) {
621 vdc.row_counter_y--;
622 if (vdc.row_counter_y < 0) {
623 vdc.row_counter_y = vdc.raster_ycounter_max;
624 /* update the row counter if we are starting a new line */
625 vdc.row_counter++;
626 /* check if we are at the end of the display */
627 if (vdc.row_counter == vdc.regs[6]) {
628 /* vdc.last_displayed_line = vdc.raster.current_line; */
629 /* FIXME - this is really a hack to lock in the screen/attr addresses at the next raster alarm handler */
630 vdc.screen_ypix = vdc.raster.current_line - vdc.border_height;
631 }
632 }
633 }
634
635 /* update the memory pointers if we are past screen_ystart, which may be above or below the top border */
636 need_increment_memory_pointer = (vdc.raster.current_line > screen_ystart);
637
638 vdc_set_next_alarm(offset);
639 }
640
641
vdc_calculate_xsync(void)642 void vdc_calculate_xsync(void)
643 {
644 double vdc_cycles_per_line, host_cycles_per_second;
645
646 host_cycles_per_second = (double)machine_get_cycles_per_second();
647
648 vdc_cycles_per_line = (double)(vdc.xchars_total) * 8.0
649 * host_cycles_per_second / VDC_DOT_CLOCK;
650
651 vdc.xsync_increment = (unsigned int)(vdc_cycles_per_line * 65536);
652 }
653
vdc_set_canvas_refresh(int enable)654 void vdc_set_canvas_refresh(int enable)
655 {
656 raster_set_canvas_refresh(&vdc.raster, enable);
657 }
658
vdc_write_snapshot_module(snapshot_t * s)659 int vdc_write_snapshot_module(snapshot_t *s)
660 {
661 return vdc_snapshot_write_module(s);
662 }
663
vdc_read_snapshot_module(snapshot_t * s)664 int vdc_read_snapshot_module(snapshot_t *s)
665 {
666 return vdc_snapshot_read_module(s);
667 }
668
vdc_screenshot(screenshot_t * screenshot)669 void vdc_screenshot(screenshot_t *screenshot)
670 {
671 raster_screenshot(&vdc.raster, screenshot);
672 screenshot->chipid = "VDC";
673 screenshot->video_regs = vdc.regs;
674 screenshot->screen_ptr = vdc.ram + vdc.screen_adr;
675 screenshot->chargen_ptr = vdc.ram + vdc.chargen_adr;
676 screenshot->bitmap_ptr = NULL; /* todo */
677 screenshot->bitmap_low_ptr = NULL;
678 screenshot->bitmap_high_ptr = NULL;
679 screenshot->color_ram_ptr = vdc.ram + vdc.attribute_adr;
680 }
681
vdc_async_refresh(struct canvas_refresh_s * refresh)682 void vdc_async_refresh(struct canvas_refresh_s *refresh)
683 {
684 raster_async_refresh(&vdc.raster, refresh);
685 }
686
vdc_shutdown(void)687 void vdc_shutdown(void)
688 {
689 raster_shutdown(&vdc.raster);
690 }
691