Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


36bec5141e9750888c3c69bc89e111e661547f88
[palacios.git] / palacios / src / devices / cga.c
1 /* 
2  * This file is part of the Palacios Virtual Machine Monitor developed
3  * by the V3VEE Project with funding from the United States National 
4  * Science Foundation and the Department of Energy.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
10  * Copyright (c) 2009, Robert Deloatch <rtdeloatch@gmail.com>
11  * Copyright (c) 2009, Steven Jaconette <stevenjaconette2007@u.northwestern.edu> 
12  * Copyright (c) 2009, The V3VEE Project <http://www.v3vee.org> 
13  * All rights reserved.
14  *
15  * Author: Robdert Deloatch <rtdeloatch@gmail.com>
16  *         Steven Jaconette <stevenjaconette2007@u.northwestern.edu>
17  *
18  * Initial VGA support added by Erik van der Kouwe <vdkouwe@cs.vu.nl>
19  *
20  * This is free software.  You are permitted to use,
21  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
22  */
23
24 #include <palacios/vmm.h>
25 #include <palacios/vmm_dev_mgr.h>
26 #include <palacios/vmm_emulator.h>
27 #include <palacios/vm_guest_mem.h>
28 #include <palacios/vmm_io.h>
29 #include <palacios/vmm_sprintf.h>
30
31 #include <devices/console.h>
32
33
34 #if V3_CONFIG_DEBUG_CGA >= 2
35 #define PrintVerbose PrintDebug
36 #else
37 #define PrintVerbose(fmt, args...)
38 #endif
39 #if V3_CONFIG_DEBUG_CGA == 0
40 #undef PrintDebug
41 #define PrintDebug(fmt, args...)
42 #endif
43
44
45 #define START_ADDR 0xA0000
46 #define END_ADDR 0xC0000
47
48 #define FRAMEBUF_SIZE (END_ADDR - START_ADDR)
49
50 #define BYTES_PER_COL 2
51
52 struct misc_outp_reg {
53     uint8_t ios         : 1;
54     uint8_t eram        : 1;
55     uint8_t cs          : 1;
56     uint8_t reserved    : 2;
57     uint8_t vsp         : 1;
58     uint8_t hsp         : 1;
59 };
60
61 struct seq_data_reg_clocking_mode {
62     uint8_t d89         : 1;
63     uint8_t reserved1   : 1;
64     uint8_t sl          : 1;
65     uint8_t dc          : 1;
66     uint8_t sh4         : 1;
67     uint8_t so          : 1;
68     uint8_t reserved2   : 2;
69 };
70
71 struct crtc_data_reg_overflow {
72     uint8_t vt8         : 1;
73     uint8_t vde8        : 1;
74     uint8_t vrs8        : 1;
75     uint8_t vbs8        : 1;
76     uint8_t lc8         : 1;
77     uint8_t vt9         : 1;
78     uint8_t vde9        : 1;
79     uint8_t vrs9        : 1;
80 };
81
82 struct crtc_data_reg_max_scan_line {
83     uint8_t msl         : 5;
84     uint8_t vbs9        : 1;
85     uint8_t lc9         : 1;
86     uint8_t dsc         : 1;
87 };
88
89 struct graphc_data_reg_graphics_mode {
90     uint8_t wm          : 2;
91     uint8_t reserved1   : 1;
92     uint8_t rm          : 1;
93     uint8_t or          : 1;
94     uint8_t sr          : 1;
95     uint8_t c256        : 1;
96     uint8_t reserved2   : 1;
97 };
98
99 struct graphc_data_reg_misc {
100     uint8_t gm          : 1;
101     uint8_t oe          : 1;
102     uint8_t mm          : 2;
103     uint8_t reserved    : 4;
104 };
105
106 struct attrc_data_reg_attr_mode_control {
107     uint8_t g           : 1;
108     uint8_t me          : 1;
109     uint8_t elg         : 1;
110     uint8_t eb          : 1;
111     uint8_t reserved    : 1;
112     uint8_t pp          : 1;
113     uint8_t pw          : 1;
114     uint8_t ps          : 1;
115 };
116
117 #define SEQ_REG_COUNT                           5
118 #define SEQ_REGIDX_RESET                        0
119 #define SEQ_REGIDX_CLOCKING_MODE                1
120 #define SEQ_REGIDX_MAP_MASK                     2
121 #define SEQ_REGIDX_CHARACTER_MAP_SELECT         3
122 #define SEQ_REGIDX_MEMORY_MODE                  4
123
124 #define CRTC_REG_COUNT                          25
125 #define CRTC_REGIDX_HORI_TOTAL                  0
126 #define CRTC_REGIDX_HORI_DISPLAY_ENABLE         1
127 #define CRTC_REGIDX_START_HORI_BLANKING         2
128 #define CRTC_REGIDX_END_HORI_BLANKING           3
129 #define CRTC_REGIDX_START_HORI_RETRACE          4
130 #define CRTC_REGIDX_END_HORI_RETRACE            5
131 #define CRTC_REGIDX_VERT_TOTAL                  6
132 #define CRTC_REGIDX_OVERFLOW                    7
133 #define CRTC_REGIDX_PRESET_ROW_SCAN             8
134 #define CRTC_REGIDX_MAX_SCAN_LINE               9
135 #define CRTC_REGIDX_CURSOR_START                10
136 #define CRTC_REGIDX_CURSOR_END                  11
137 #define CRTC_REGIDX_START_ADDR_HIGH             12
138 #define CRTC_REGIDX_START_ADDR_LOW              13
139 #define CRTC_REGIDX_CURSOR_LOC_HIGH             14
140 #define CRTC_REGIDX_CURSOR_LOC_LOW              15
141 #define CRTC_REGIDX_VERT_RETRACE_START          16
142 #define CRTC_REGIDX_VERT_RETRACE_END            17
143 #define CRTC_REGIDX_VERT_DISPLAY_ENABLE_END     18
144 #define CRTC_REGIDX_OFFSET                      19
145 #define CRTC_REGIDX_UNDERLINE_LOCATION          20
146 #define CRTC_REGIDX_START_VERT_BLANKING         21
147 #define CRTC_REGIDX_END_VERT_BLANKING           22
148 #define CRTC_REGIDX_CRT_MODE_CONTROL            23
149 #define CRTC_REGIDX_LINE_COMPATE                24
150
151 #define GRAPHC_REG_COUNT                9
152 #define GRAPHC_REGIDX_SET_RESET         0
153 #define GRAPHC_REGIDX_ENABLE_SET_RESET  1
154 #define GRAPHC_REGIDX_COLOR_COMPARE     2
155 #define GRAPHC_REGIDX_DATA_ROTATE       3
156 #define GRAPHC_REGIDX_READ_MAP_SELECT   4
157 #define GRAPHC_REGIDX_GRAPHICS_MODE     5
158 #define GRAPHC_REGIDX_MISC              6
159 #define GRAPHC_REGIDX_COLOR_DONT_CARE   7
160 #define GRAPHC_REGIDX_BIT_MASK          8
161
162 #define ATTRC_REG_COUNT                         21
163 #define ATTRC_REGIDX_PALETTE_0                  0
164 #define ATTRC_REGIDX_ATTR_MODE_CONTROL          16
165 #define ATTRC_REGIDX_OVERSCAN_COLOR             17
166 #define ATTRC_REGIDX_COLOR_PLANE_ENABLE         18
167 #define ATTRC_REGIDX_HORI_PEL_PANNING           19
168 #define ATTRC_REGIDX_COLOR_SELECT               20
169
170 #define DAC_ENTRY_COUNT                 256
171 #define DAC_COLOR_COUNT                 3
172 #define DAC_REG_COUNT                   (DAC_ENTRY_COUNT * DAC_COLOR_COUNT)
173
174 struct video_internal {
175     uint8_t * framebuf;
176     addr_t framebuf_pa;
177
178     /* registers */
179     struct misc_outp_reg misc_outp_reg;         // io port 3CC (R) / 3C2 (W)
180     uint8_t seq_index_reg;                      // io port 3C4
181     uint8_t seq_data_regs[SEQ_REG_COUNT];       // io port 3C5
182     uint8_t crtc_index_reg;                     // io port 3D4
183     uint8_t crtc_data_regs[CRTC_REG_COUNT];     // io port 3D5
184     uint8_t graphc_index_reg;                   // io port 3CE
185     uint8_t graphc_data_regs[GRAPHC_REG_COUNT]; // io port 3CF
186     uint8_t attrc_index_flipflop;
187     uint8_t attrc_index_reg;                    // io port 3C0
188     uint8_t attrc_data_regs[ATTRC_REG_COUNT];   // io port 3C1 (R) / 3C0 (W)
189     uint8_t dac_indexr_reg;                     // io port 3C8
190     uint8_t dac_indexr_color;
191     uint8_t dac_indexw_reg;                     // io port 3C7
192     uint8_t dac_indexw_color;
193     uint8_t dac_data_regs[DAC_REG_COUNT];       // io port 3C9
194
195     /* auxilary fields derived from register values */
196     addr_t activefb_addr;
197     uint_t activefb_len;
198     uint16_t iorange;
199     uint_t vres;
200     uint_t hres;
201     uint_t vchars;
202     uint_t hchars;
203     int graphmode;
204     
205     /* status */
206     int dirty;
207     int reschanged;
208
209     /* IMPORTANT: These are column offsets _NOT_ byte offsets */
210     uint16_t screen_offset; // relative to the framebuffer
211     uint16_t cursor_offset; // relative to the framebuffer
212     /* ** */
213
214     struct vm_device * dev;
215
216
217     uint8_t passthrough;
218
219
220     struct v3_console_ops * ops;
221     void * private_data;
222
223
224
225 };
226
227 static void refresh_screen(struct video_internal * state) {
228     uint_t screen_size;
229     
230     PrintDebug(VM_NONE, VCORE_NONE, "Screen config: framebuf=0x%x-0x%x, gres=%dx%d, tres=%dx%d, %s mode\n", 
231         (unsigned) state->activefb_addr, 
232         (unsigned) state->activefb_addr + state->activefb_len, 
233         state->hres,
234         state->vres,
235         state->hchars,
236         state->vchars,
237         state->graphmode ? "graphics" : "text");
238     
239     /* tell the frontend to refresh the screen entirely */
240     state->dirty = 0;
241
242     if (state->reschanged) {
243         /* resolution change message will trigger update */
244         state->reschanged = 0;
245         PrintDebug(VM_NONE, VCORE_NONE, "Video: set_text_resolution(%d, %d)\n", 
246             state->hchars, state->vchars);
247         if (state->ops && state->ops->set_text_resolution) {
248             state->ops->set_text_resolution(state->hchars, state->vchars, state->private_data);
249         }
250     } else {
251         /* send update for full buffer */
252         PrintDebug(VM_NONE, VCORE_NONE, "Video: update_screen(0, 0, %d * %d * %d)\n", state->vchars, state->hchars, BYTES_PER_COL);
253         screen_size = state->vchars * state->hchars * BYTES_PER_COL;
254         if (state->ops) {
255             state->ops->update_screen(0, 0, screen_size, state->private_data);
256         }
257     }
258 }
259
260 static void registers_updated(struct video_internal * state) {
261     struct seq_data_reg_clocking_mode *cm;
262     struct graphc_data_reg_misc *misc;
263     struct crtc_data_reg_max_scan_line *msl;
264     struct crtc_data_reg_overflow *ovf;
265     int lines_per_char;
266     uint_t activefb_addr, activefb_len, hchars, vchars, vde, hres, vres;
267     
268     /* framebuffer mapping address */
269     misc = (struct graphc_data_reg_misc *)(state->graphc_data_regs + GRAPHC_REGIDX_MISC);
270
271     if (misc->mm >= 2) {
272         activefb_addr = (misc->mm == 3) ? 0xb8000 : 0xb0000;
273         activefb_len = 0x8000;
274     } else {
275         activefb_addr = 0xa0000;
276         activefb_len = (misc->mm == 1) ? 0x10000 : 0x20000;
277     }
278
279     if ((state->activefb_addr != activefb_addr) || (state->activefb_len != activefb_len)) {
280         state->activefb_addr = activefb_addr;
281         state->activefb_len = activefb_len;
282         state->dirty = 1;
283         PrintVerbose(VM_NONE, VCORE_NONE, "Video: need refresh (activefb=0x%x-0x%x)\n", 
284             activefb_addr, activefb_addr + activefb_len);
285     } 
286     
287     /* mode selection; may be inconclusive, keep old value in that case */
288     if (state->graphmode != misc->gm) {
289         state->graphmode = misc->gm;
290         state->dirty = 1;
291         PrintVerbose(VM_NONE, VCORE_NONE, "Video: need refresh (graphmode=%d)\n", state->graphmode);
292     }
293
294     /* graphics resolution */
295     if (state->misc_outp_reg.hsp) {
296         vres = (state->misc_outp_reg.vsp) ? 480 : 400;
297     } else {
298         if (!state->misc_outp_reg.vsp) {
299             PrintError(VM_NONE, VCORE_NONE, "Video: reserved value in misc_outp_reg (0x%x)\n", 
300                 *(uint8_t *) &state->misc_outp_reg);
301         }
302         vres = 350;
303     }
304     msl = (struct crtc_data_reg_max_scan_line *) (
305         state->crtc_data_regs + CRTC_REGIDX_MAX_SCAN_LINE);
306     if (msl->dsc) vres /= 2;
307     if (state->vres != vres) {
308         state->vres = vres;
309         state->reschanged = 1;
310         PrintVerbose(VM_NONE, VCORE_NONE, "Video: need refresh (vres=%d)\n", vres);
311     }
312     
313     switch (state->misc_outp_reg.cs) {
314         case 0: hres = 640; break;
315         case 1: hres = 720; break;
316         default:
317                 PrintError(VM_NONE, VCORE_NONE, "Video: reserved value in misc_outp_reg (0x%x)\n", 
318                         *(uint8_t *) &state->misc_outp_reg);
319                 hres = 640;
320                 break;
321     }
322     cm = (struct seq_data_reg_clocking_mode *) (
323         state->seq_data_regs + SEQ_REGIDX_CLOCKING_MODE);
324     if (cm->dc) hres /= 2;
325     if (state->hres != hres) {
326         state->hres = hres;
327         state->reschanged = 1;
328         PrintVerbose(VM_NONE, VCORE_NONE, "Video: need refresh (hres=%d)\n", hres);
329     }
330
331     /* text resolution */
332     ovf = (struct crtc_data_reg_overflow *) (state->crtc_data_regs + CRTC_REGIDX_OVERFLOW);
333     
334     hchars = state->crtc_data_regs[CRTC_REGIDX_HORI_DISPLAY_ENABLE] + 1;
335     lines_per_char = msl->msl + 1;
336     vde = state->crtc_data_regs[CRTC_REGIDX_VERT_DISPLAY_ENABLE_END] |
337         (((unsigned) ovf->vde8) << 8) | 
338         (((unsigned) ovf->vde9) << 9);
339     vchars = (vde + 1) / lines_per_char;
340     if (state->hchars != hchars || state->vchars != vchars) {
341         state->hchars = hchars;
342         state->vchars = vchars;
343         state->reschanged = 1;
344         PrintVerbose(VM_NONE, VCORE_NONE, "Video: need refresh (hchars=%d, vchars=%d)\n", hchars, vchars);
345     }
346     
347     /* resolution change implies refresh needed */
348     if (state->reschanged) {
349         state->dirty = 1;
350     }
351
352     /* IO port range selection */
353     state->iorange = state->misc_outp_reg.ios ? 0x3d0 : 0x3b0;
354 }
355
356 static void registers_initialize(struct video_internal * state) {
357
358     /* initialize the registers; defaults taken from vgatables.h in the VGA 
359      * BIOS, mode 3 (which is specified by IBM as the default mode)
360      */
361     static const uint8_t seq_defaults[] = {
362         0x03, 0x00, 0x03, 0x00, 0x02,
363     };
364     static const uint8_t crtc_defaults[] = {
365         0x5f, 0x4f, 0x50, 0x82, /* 0 - 3 */
366         0x55, 0x81, 0xbf, 0x1f, /* 4 - 7 */
367         0x00, 0x4f, 0x0d, 0x0e, /* 8 - 11 */
368         0x00, 0x00, 0x00, 0x00, /* 12 - 15 */
369         0x9c, 0x8e, 0x8f, 0x28, /* 16 - 19 */
370         0x1f, 0x96, 0xb9, 0xa3, /* 20 - 23 */
371         0xff                    /* 24 */
372     };
373     static const uint8_t graphc_defaults[] = {
374         0x00, 0x00, 0x00, 0x00, /* 0 - 3 */
375         0x00, 0x10, 0x0e, 0x0f, /* 4 - 7 */
376         0xff                    /* 8 */
377     };
378     static const uint8_t attrc_defaults[] = {
379         0x00, 0x01, 0x02, 0x03, /* 0 - 3 */
380         0x04, 0x05, 0x14, 0x07, /* 4 - 7 */ 
381         0x38, 0x39, 0x3a, 0x3b, /* 8 - 11 */ 
382         0x3c, 0x3d, 0x3e, 0x3f, /* 12 - 15 */ 
383         0x0c, 0x00, 0x0f, 0x08, /* 16 - 19 */ 
384         0x00,                   /* 20 */
385     };
386  
387     /* general registers */
388     state->misc_outp_reg.ios = 1;
389     state->misc_outp_reg.eram = 1;
390     state->misc_outp_reg.cs = 1;
391     state->misc_outp_reg.hsp = 1;    
392     
393     /* sequencer registers */
394     V3_ASSERT(VM_NONE, VCORE_NONE, sizeof(seq_defaults) == sizeof(state->seq_data_regs));
395     memcpy(state->seq_data_regs, seq_defaults, sizeof(state->seq_data_regs));    
396     
397     /* CRT controller registers */
398     V3_ASSERT(VM_NONE, VCORE_NONE, sizeof(crtc_defaults) == sizeof(state->crtc_data_regs));
399     memcpy(state->crtc_data_regs, crtc_defaults, sizeof(state->crtc_data_regs));    
400     
401     /* graphics controller registers */
402     V3_ASSERT(VM_NONE, VCORE_NONE, sizeof(graphc_defaults) == sizeof(state->graphc_data_regs));
403     memcpy(state->graphc_data_regs, graphc_defaults, sizeof(state->graphc_data_regs));    
404     
405     /* attribute controller registers */
406     V3_ASSERT(VM_NONE, VCORE_NONE, sizeof(attrc_defaults) == sizeof(state->attrc_data_regs));
407     memcpy(state->attrc_data_regs, attrc_defaults, sizeof(state->attrc_data_regs));    
408     
409     /* initialize auxilary fields */
410     registers_updated(state);
411 }
412
413 static void passthrough_in(uint16_t port, void * src, uint_t length) {
414     switch (length) {
415         case 1:
416             *(uint8_t *)src = v3_inb(port);
417             break;
418         case 2:
419             *(uint16_t *)src = v3_inw(port);
420             break;
421         case 4:
422             *(uint32_t *)src = v3_indw(port);
423             break;
424         default:
425             break;
426     }
427 }
428
429
430 static void passthrough_out(uint16_t port, const void * src, uint_t length) {
431     switch (length) {
432         case 1:
433             v3_outb(port, *(uint8_t *)src);
434             break;
435         case 2:
436             v3_outw(port, *(uint16_t *)src);
437             break;
438         case 4:
439             v3_outdw(port, *(uint32_t *)src);
440             break;
441         default:
442             break;
443     }
444 }
445
446 #if V3_CONFIG_DEBUG_CGA >= 2
447 static unsigned long get_value(const void *ptr, int len) {
448   unsigned long value = 0;
449
450   if (len > sizeof(value)) len = sizeof(value);
451   memcpy(&value, ptr, len);
452
453   return value;
454 }
455
456 static char opsize_char(uint_t length) {
457     switch (length) {
458     case 1: return 'b'; 
459     case 2: return 'w'; 
460     case 4: return 'l'; 
461     case 8: return 'q';     
462     default: return '?'; 
463     }
464 }
465 #endif
466
467 static int video_write_mem(struct guest_info * core, addr_t guest_addr, void * dest, uint_t length, void * priv_data) {
468     struct vm_device * dev = (struct vm_device *)priv_data;
469     struct video_internal * state = (struct video_internal *)dev->private_data;
470     uint_t length_adjusted, screen_pos, x, y;
471     addr_t framebuf_offset, framebuf_offset_screen, screen_offset;
472     
473     V3_ASSERT(core->vm_info, core, guest_addr >= START_ADDR);
474     V3_ASSERT(core->vm_info, core, guest_addr < END_ADDR);
475     
476     PrintVerbose(core->vm_info, core, "Video: write(%p, 0x%lx, %d)\n", 
477        (void *)guest_addr, 
478         get_value(state->framebuf + (guest_addr - START_ADDR), length),
479         length);
480
481     /* get data written by passthrough into frame buffer if needed */
482     if (state->passthrough) {
483         memcpy(state->framebuf + (guest_addr - START_ADDR), V3_VAddr((void *)guest_addr), length);
484     }
485     
486     /* refresh the entire screen after the registers have been changed */
487     if (state->dirty) {
488         refresh_screen(state);
489         return length;
490     }
491     
492     /* the remainder is only needed if there is a front-end */
493     if (!state->ops) {
494         return length;
495     }
496
497     /* write may point into a framebuffer not currently active, for example 
498      * preparing a VGA buffer at 0xA0000 while the CGA text mode at 0xB8000 
499      * is still on the display; in this case we have to ignore (part of) the 
500      * write to avoid buffer overflows
501      */
502     length_adjusted = length;
503     if (state->activefb_addr > guest_addr) {
504         uint_t diff = state->activefb_addr - guest_addr;
505         if (diff >= length_adjusted) return length;
506         guest_addr += diff;
507         length_adjusted -= diff;
508     }
509
510     framebuf_offset = guest_addr - state->activefb_addr;
511     if (state->activefb_len <= framebuf_offset) return length;
512     if (length_adjusted > state->activefb_len - framebuf_offset) {
513         length_adjusted = state->activefb_len - framebuf_offset;
514     }
515
516     /* determine position on screen, considering wrapping */
517     framebuf_offset_screen = state->screen_offset * BYTES_PER_COL;
518     if (framebuf_offset > framebuf_offset_screen) {
519         screen_offset = framebuf_offset - framebuf_offset_screen;
520     } else {
521         screen_offset = framebuf_offset + state->activefb_len - framebuf_offset_screen;
522     }
523     
524     /* translate to coordinates and pass to the frontend */
525     screen_pos = screen_offset / BYTES_PER_COL;
526     x = screen_pos % state->hchars;
527     y = screen_pos / state->hchars;
528     if (y >= state->vchars) return length;
529     PrintVerbose(core->vm_info, core, "Video: update_screen(%d, %d, %d)\n", x, y, length_adjusted);
530     state->ops->update_screen(x, y, length_adjusted, state->private_data);
531
532     return length;
533 }
534
535 static void debug_port(struct video_internal * video_state, const char *function, uint16_t port, uint_t length, uint_t maxlength)
536 {
537     uint16_t portrange = port & 0xfff0;
538
539     /* report any unexpected guest behaviour, it may explain failures */
540     if (portrange != 0x3c0 && portrange != video_state->iorange) {
541         PrintError(VM_NONE, VCORE_NONE, "Video %s: got bad port 0x%x\n", function, port);
542     }
543
544     if (!video_state->passthrough && length > maxlength) {
545         PrintError(VM_NONE, VCORE_NONE, "Video %s: got bad length %d\n", function, length);
546     }
547     V3_ASSERT(VM_NONE, VCORE_NONE, length >= 1);
548 }
549
550 static void handle_port_read(struct video_internal * video_state, const char *function, uint16_t port, void *dest, uint_t length, uint_t maxlength) {
551     PrintVerbose(VM_NONE, VCORE_NONE, "Video %s: in%c(0x%x): 0x%lx\n", function, opsize_char(length), port, get_value(dest, length));
552     debug_port(video_state, function, port, length, maxlength);
553
554     if (video_state->passthrough) {
555         passthrough_in(port, dest, length);
556     }
557 }
558
559 static void handle_port_write(struct video_internal * video_state, const char *function, uint16_t port, const void *src, uint_t length, uint_t maxlength) {
560     PrintVerbose(VM_NONE, VCORE_NONE, "Video %s: out%c(0x%x, 0x%lx)\n", function, opsize_char(length), port, get_value(src, length));
561     debug_port(video_state, function, port, length, maxlength);
562
563     if (video_state->passthrough) {
564         passthrough_out(port, src, length);
565     }
566 }
567
568 static int notimpl_port_read(struct video_internal * video_state, const char *function, uint16_t port, void *dest, uint_t length) {
569     memset(dest, 0xff, length);
570     handle_port_read(video_state, function, port, dest, length, 1);
571     if (!video_state->passthrough) {
572         PrintError(VM_NONE, VCORE_NONE, "Video %s: not implemented\n", function);
573     }
574     return length;
575 }
576
577 static int notimpl_port_write(struct video_internal * video_state, const char *function, uint16_t port, const void *src, uint_t length) {
578     handle_port_write(video_state, function, port, src, length, 1);
579     if (!video_state->passthrough) {
580         PrintError(VM_NONE, VCORE_NONE, "Video %s: not implemented\n", function);
581     }
582     return length;
583 }
584
585 /* general registers */
586 static int misc_outp_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
587     struct video_internal * video_state = priv_data;
588
589     *(struct misc_outp_reg *) dest = video_state->misc_outp_reg;
590
591     handle_port_read(video_state, __FUNCTION__, port, dest, length, 1);
592     return length;
593 }
594
595 static int misc_outp_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
596     struct video_internal * video_state = priv_data;
597     handle_port_write(video_state, __FUNCTION__, port, src, length, 1);
598
599     PrintDebug(core->vm_info, core, "Video: misc_outp=0x%x\n", *(uint8_t *) src);
600     video_state->misc_outp_reg = *(struct misc_outp_reg *) src;
601     registers_updated(video_state);
602
603     return length;
604 }
605
606 static int inp_status0_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
607     return notimpl_port_read(priv_data, __FUNCTION__, port, dest, length);
608 }
609
610 static int inp_status1_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
611     struct video_internal * video_state = priv_data;
612
613     /* next write to attrc selects the index rather than data */
614     video_state->attrc_index_flipflop = 0;
615     memset(dest, 0x0, length);
616
617     handle_port_read(priv_data, __FUNCTION__, port, dest, length, 1);
618     return length;
619 }
620
621 static int feat_ctrl_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
622     return notimpl_port_read(priv_data, __FUNCTION__, port, dest, length);
623 }
624
625 static int feat_ctrl_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
626     return notimpl_port_write(priv_data, __FUNCTION__, port, src, length);
627 }
628
629 static int video_enable_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
630     return notimpl_port_read(priv_data, __FUNCTION__, port, dest, length);
631 }
632
633 static int video_enable_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
634     return notimpl_port_write(priv_data, __FUNCTION__, port, src, length);
635 }
636
637 /* sequencer registers */
638 static int seq_data_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
639     struct video_internal * video_state = priv_data;
640     int index = video_state->seq_index_reg;
641
642     if (index < SEQ_REG_COUNT) {
643         *(uint8_t *) dest = video_state->seq_data_regs[index];
644     } else {
645         PrintError(core->vm_info, core, "Video %s: index %d out of range\n", __FUNCTION__, video_state->seq_index_reg);
646         *(uint8_t *) dest = 0;
647     }
648
649     handle_port_read(video_state, __FUNCTION__, port, dest, length, 1);
650     return length;    
651 }
652
653 static int seq_data_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
654     struct video_internal * video_state = priv_data;
655     int index = video_state->seq_index_reg;
656     uint8_t val = *(uint8_t *) src;
657     handle_port_write(video_state, __FUNCTION__, port, src, length, 1);
658
659     if (index < SEQ_REG_COUNT) {
660         PrintDebug(core->vm_info, core, "Video: seq[%d]=0x%x\n", index, val);
661         video_state->seq_data_regs[index] = val;
662         registers_updated(video_state);
663     } else {
664         PrintError(core->vm_info, core, "Video %s: index %d out of range\n", __FUNCTION__, video_state->seq_index_reg);
665     }
666
667     return length;    
668 }
669
670 static int seq_index_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
671     struct video_internal * video_state = priv_data;
672
673     *(uint8_t *) dest = video_state->seq_index_reg;
674
675     handle_port_read(video_state, __FUNCTION__, port, dest, length, 1);
676     return length;
677 }
678
679 static int seq_index_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
680     struct video_internal * video_state = priv_data;
681     handle_port_write(video_state, __FUNCTION__, port, src, length, 2);
682
683     video_state->seq_index_reg = *(uint8_t *) src;
684
685     if (length > 1) {
686         if (seq_data_write(core, port + 1, (uint8_t *) src + 1, length - 1, priv_data) != length - 1) {
687             return -1;
688         }
689     }
690
691     return length;    
692 }
693
694 /* CRT controller registers */
695 static int crtc_data_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
696     struct video_internal * video_state = priv_data;
697
698     *(uint8_t *) dest = video_state->crtc_index_reg;
699
700     handle_port_read(video_state, __FUNCTION__, port, dest, length, 1);
701     return length;
702 }
703
704 static int crtc_data_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
705     struct video_internal * video_state = priv_data;
706     uint8_t val = *(uint8_t *)src;
707     uint_t index = video_state->crtc_index_reg;
708
709     handle_port_write(video_state, __FUNCTION__, port, src, length, 1);
710     if (length != 1) {
711         PrintError(core->vm_info, core, "Invalid write length for port 0x%x\n", port);
712         return -1;
713     }
714
715     video_state->crtc_data_regs[index] = val;
716     registers_updated(video_state);
717
718     switch (index) {
719
720         case CRTC_REGIDX_START_ADDR_HIGH: break; // Dealt by low-order byte write
721         case CRTC_REGIDX_START_ADDR_LOW: {  // Scroll low byte
722             int diff, refresh;
723             uint_t screen_offset;
724
725             screen_offset =
726                 ((uint16_t) video_state->crtc_data_regs[CRTC_REGIDX_START_ADDR_HIGH] << 8) |
727                 ((uint16_t) video_state->crtc_data_regs[CRTC_REGIDX_START_ADDR_LOW]);
728             if ((screen_offset - video_state->screen_offset) % video_state->hchars) {
729                 /* diff is not a multiple of column count, need full refresh */
730                 diff = 0;
731                 refresh = 1;
732             } else {
733                 /* normal scroll (the common case) */
734                 diff = (screen_offset - video_state->screen_offset) / video_state->hchars;
735                 refresh = 0;
736             }
737             PrintVerbose(core->vm_info, core, "Video: screen_offset=%d, video_state->screen_offset=%d, video_state->hchars=%d, diff=%d, refresh=%d\n",
738                 screen_offset, video_state->screen_offset, video_state->hchars, diff, refresh);
739
740             // Update the true offset value
741             video_state->screen_offset = screen_offset;
742
743             if (refresh || video_state->dirty) {
744                 refresh_screen(video_state);
745             } else if (diff && video_state->ops) {
746                 PrintVerbose(core->vm_info, core, "Video: scroll(%d)\n", diff);
747                 if (video_state->ops->scroll(diff, video_state->private_data) == -1) {
748                     PrintError(core->vm_info, core, "Error sending scroll event\n");
749                     return -1;
750                 }
751             }
752             break;
753         }
754         case CRTC_REGIDX_CURSOR_LOC_HIGH: break; // Dealt by low-order byte write
755         case CRTC_REGIDX_CURSOR_LOC_LOW: { // Cursor adjustment low byte
756             uint_t x;
757             uint_t y;
758             
759             video_state->cursor_offset =
760                 ((uint16_t) video_state->crtc_data_regs[CRTC_REGIDX_CURSOR_LOC_HIGH] << 8) |
761                 ((uint16_t) video_state->crtc_data_regs[CRTC_REGIDX_CURSOR_LOC_LOW]);
762             x = video_state->cursor_offset % video_state->hchars;
763             y = (video_state->cursor_offset - video_state->screen_offset) / video_state->hchars;
764             PrintVerbose(core->vm_info, core, "Video: video_state->cursor_offset=%d, x=%d, y=%d\n",
765                 video_state->cursor_offset, x, y);
766
767             if (video_state->dirty) {
768                 refresh_screen(video_state);
769             }
770             
771             PrintVerbose(core->vm_info, core, "Video: set cursor(%d, %d)\n", x, y);
772             if (video_state->ops) {
773                 if (video_state->ops->update_cursor(x, y, video_state->private_data) == -1) {
774                     PrintError(core->vm_info, core, "Error updating cursor\n");
775                     return -1;
776                 }
777             } 
778
779             break;
780         }
781         default:
782             PrintDebug(core->vm_info, core, "Video: crtc[%d]=0x%x\n", index, val);
783             break;
784     }
785
786     if (video_state->passthrough) {
787         passthrough_out(port, src, length);
788     }
789
790     return length;
791 }
792
793 static int crtc_index_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
794     struct video_internal * video_state = priv_data;
795
796     *(uint8_t *) dest = video_state->crtc_index_reg;
797
798     handle_port_read(video_state, __FUNCTION__, port, dest, length, 1);
799     return length;
800 }
801
802 static int crtc_index_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
803     struct video_internal * video_state = priv_data;
804
805     handle_port_write(video_state, __FUNCTION__, port, src, length, 2);
806     if (length > 2) {
807         PrintError(core->vm_info, core, "Invalid write length for crtc index register port: %d (0x%x)\n",
808                    port, port);
809         return -1;
810     }
811
812     video_state->crtc_index_reg = *(uint8_t *)src;
813
814     // Only do the passthrough IO for the first byte
815     // the second byte will be done in the data register handler
816     if (video_state->passthrough) {
817         passthrough_out(port, src, 1);
818     }
819
820     if (length > 1) {
821         if (crtc_data_write(core, port + 1, (uint8_t *) src + 1, length - 1, priv_data) != length - 1) {
822             return -1;
823         }
824     }
825
826     return length;
827 }
828
829 /* graphics controller registers */
830 static int graphc_data_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
831     struct video_internal * video_state = priv_data;
832     int index = video_state->graphc_index_reg;
833
834     if (index < GRAPHC_REG_COUNT) {
835         *(uint8_t *) dest = video_state->graphc_data_regs[index];
836     } else {
837         PrintError(core->vm_info, core, "Video %s: index %d out of range\n", __FUNCTION__, video_state->graphc_index_reg);
838         *(uint8_t *) dest = 0;
839     }
840
841     handle_port_read(video_state, __FUNCTION__, port, dest, length, 1);
842     return length;    
843 }
844
845 static int graphc_data_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
846     struct video_internal * video_state = priv_data;
847     int index = video_state->graphc_index_reg;
848     uint8_t val = *(uint8_t *) src;
849     handle_port_write(video_state, __FUNCTION__, port, src, length, 1);
850
851     if (index < GRAPHC_REG_COUNT) {
852         PrintDebug(core->vm_info, core, "Video: graphc[%d]=0x%x\n", index, val);
853         video_state->graphc_data_regs[index] = val;
854         registers_updated(video_state);
855     } else {
856         PrintError(core->vm_info, core, "Video %s: index %d out of range\n", __FUNCTION__, video_state->graphc_index_reg);
857     }
858
859     return length;    
860 }
861
862 static int graphc_index_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
863     struct video_internal * video_state = priv_data;
864
865     *(uint8_t *) dest = video_state->graphc_index_reg;
866
867     handle_port_read(video_state, __FUNCTION__, port, dest, length, 1);
868     return length;
869 }
870
871 static int graphc_index_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
872     struct video_internal * video_state = priv_data;
873     handle_port_write(video_state, __FUNCTION__, port, src, length, 2);
874
875     video_state->graphc_index_reg = *(uint8_t *) src;
876
877     if (length > 1) {
878         if (graphc_data_write(core, port + 1, (uint8_t *) src + 1, length - 1, priv_data) != length - 1) {
879             return -1;
880         }
881     }
882     
883     return length;    
884 }
885
886 /* attribute controller registers */
887 static int attrc_data_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
888     struct video_internal * video_state = priv_data;
889     int index = video_state->attrc_index_reg;
890
891     if (index < ATTRC_REG_COUNT) {
892         *(uint8_t *) dest = video_state->attrc_data_regs[index];
893     } else {
894         PrintError(core->vm_info, core, "Video %s: index %d out of range\n", __FUNCTION__, video_state->attrc_index_reg);
895         *(uint8_t *) dest = 0;
896     }
897
898     handle_port_read(video_state, __FUNCTION__, port, dest, length, 1);
899     return length;    
900 }
901
902 static int attrc_data_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
903     struct video_internal * video_state = priv_data;
904     int index = video_state->attrc_index_reg;
905     uint8_t val = *(uint8_t *) src;
906     handle_port_write(video_state, __FUNCTION__, port, src, length, 1);
907
908     if (index < ATTRC_REG_COUNT) {
909         PrintDebug(core->vm_info, core, "Video: attrc[%d]=0x%x\n", index, val);
910         video_state->attrc_data_regs[index] = val;
911     } else {
912         PrintError(core->vm_info, core, "Video %s: index %d out of range\n", __FUNCTION__, video_state->attrc_index_reg);
913     }
914
915     return length;    
916 }
917
918 static int attrc_index_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
919     struct video_internal * video_state = priv_data;
920
921     *(uint8_t *) dest = video_state->attrc_index_reg;
922
923     if (length > 1) {
924         if (attrc_data_read(core, port + 1, (uint8_t *) dest + 1, length - 1, priv_data) != length - 1) {
925             return -1;
926         }
927     }
928     
929     handle_port_read(video_state, __FUNCTION__, port, dest, length, 2);
930     return length;
931 }
932
933 static int attrc_index_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
934     struct video_internal * video_state = priv_data;
935     handle_port_write(video_state, __FUNCTION__, port, src, length, 1);
936
937     video_state->attrc_index_reg = *(uint8_t *) src;
938
939     return length;    
940 }
941
942 static int attrc_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
943     struct video_internal * video_state = priv_data;
944
945     /* two registers in one, written in an alternating fashion */
946     if (video_state->attrc_index_flipflop) {
947         video_state->attrc_index_flipflop = 0;
948         return attrc_data_write(core, port, src, length, priv_data);
949     } else {
950         video_state->attrc_index_flipflop = 1;
951         return attrc_index_write(core, port, src, length, priv_data);
952     }
953 }
954
955 /* video DAC palette registers */
956 static int dac_indexw_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
957     struct video_internal * video_state = priv_data;
958
959     *(uint8_t *) dest = video_state->dac_indexw_reg;
960
961     handle_port_read(video_state, __FUNCTION__, port, dest, length, 1);
962     return length;
963 }
964
965 static int dac_indexw_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
966     struct video_internal * video_state = priv_data;
967     handle_port_write(video_state, __FUNCTION__, port, src, length, 1);
968
969     video_state->dac_indexw_reg = *(uint8_t *) src;
970     video_state->dac_indexw_color = 0;
971
972     return length;  
973 }
974
975 static int dac_indexr_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
976     struct video_internal * video_state = priv_data;
977     handle_port_write(video_state, __FUNCTION__, port, src, length, 1);
978
979     video_state->dac_indexr_reg = *(uint8_t *) src;
980     video_state->dac_indexr_color = 0;
981
982     return length;  
983 }
984
985 static int dac_data_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
986     struct video_internal * video_state = priv_data;
987     unsigned index;
988
989     /* update palette */
990     index = (unsigned) video_state->dac_indexr_reg * DAC_COLOR_COUNT + 
991         video_state->dac_indexr_color;
992     V3_ASSERT(core->vm_info, core, index < DAC_REG_COUNT);
993     *(uint8_t *) dest = video_state->dac_data_regs[index];
994     
995     /* move on to next entry/color */
996     if (++video_state->dac_indexr_color > DAC_COLOR_COUNT) {
997         video_state->dac_indexr_reg++;
998         video_state->dac_indexr_color -= DAC_COLOR_COUNT;
999     }
1000
1001     handle_port_read(video_state, __FUNCTION__, port, dest, length, 1);
1002     return length;
1003 }
1004
1005 static int dac_data_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
1006     struct video_internal * video_state = priv_data;
1007     unsigned index;
1008     handle_port_write(video_state, __FUNCTION__, port, src, length, 1);
1009
1010     /* update palette */
1011     index = (unsigned) video_state->dac_indexw_reg * DAC_COLOR_COUNT + 
1012         video_state->dac_indexw_color;
1013     V3_ASSERT(core->vm_info, core, index < DAC_REG_COUNT);
1014     video_state->dac_data_regs[index] = *(uint8_t *) src;
1015     
1016     /* move on to next entry/color */
1017     if (++video_state->dac_indexw_color > DAC_COLOR_COUNT) {
1018         video_state->dac_indexw_reg++;
1019         video_state->dac_indexw_color -= DAC_COLOR_COUNT;
1020     }
1021
1022     return length;
1023 }
1024
1025 static int dac_pelmask_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
1026     return notimpl_port_read(priv_data, __FUNCTION__, port, dest, length);
1027 }
1028
1029 static int dac_pelmask_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
1030     return notimpl_port_write(priv_data, __FUNCTION__, port, src, length);
1031 }
1032
1033
1034 static int v3_cons_get_fb_graph(struct video_internal * state, uint8_t * dst, uint_t offset, uint_t length) {
1035     char c, text[80];
1036     uint_t textlength, textoffset, textsize;
1037
1038     /* this call is not intended for graphics mode, tell the user this */
1039
1040     /* center informative text on 10th line */
1041     snprintf(text, sizeof(text), "* * * GRAPHICS MODE %dx%d * * *",
1042         state->hres, state->vres);
1043     textlength = strlen(text);
1044     textoffset = (state->hchars * 9 + (state->hchars - textlength) / 2) * BYTES_PER_COL;
1045     textsize = textlength * BYTES_PER_COL;
1046
1047     /* fill the buffer */
1048     while (length-- > 0) {
1049         if (offset % BYTES_PER_COL) {
1050             c = 0; /* attribute byte */
1051         } else if (offset < textoffset || offset - textoffset >= textsize) {
1052             c = ' '; /* unused character byte */
1053         } else {
1054             c = text[(offset - textoffset) / BYTES_PER_COL];
1055         }
1056
1057         *(dst++) = c;
1058         offset++;
1059     }
1060
1061     return 0;
1062 }
1063
1064 static uint_t min_uint(uint_t x, uint_t y) {
1065     return (x < y) ? x : y;
1066 }
1067
1068 int v3_cons_get_fb_text(struct video_internal * state, uint8_t * dst, uint_t offset, uint_t length) {
1069     uint8_t *framebuf;
1070     uint_t framebuf_offset, len1, len2;
1071     uint_t screen_byte_offset = state->screen_offset * BYTES_PER_COL;
1072
1073     PrintVerbose(VM_NONE, VCORE_NONE, "Video: getfb o=%d l=%d so=%d aa=0x%x al=0x%x hc=%d vc=%d\n",
1074         offset, length, state->screen_offset, 
1075         (unsigned) state->activefb_addr, (unsigned) state->activefb_len,
1076         state->hchars, state->vchars);
1077     V3_ASSERT(VM_NONE, VCORE_NONE, state->activefb_addr >= START_ADDR);
1078     V3_ASSERT(VM_NONE, VCORE_NONE, state->activefb_addr + state->activefb_len <= END_ADDR);
1079
1080     /* Copy memory with wrapping (should be configurable, but where else to get the data?) */
1081     framebuf = state->framebuf + (state->activefb_addr - START_ADDR);
1082     framebuf_offset = (screen_byte_offset + offset) % state->activefb_len;
1083     len1 = min_uint(length, state->activefb_len - framebuf_offset);
1084     len2 = length - len1;
1085     if (len1 > 0) memcpy(dst, framebuf + framebuf_offset, len1);
1086     if (len2 > 0) memcpy(dst + len1, framebuf, len2);
1087
1088     return 0;
1089 }
1090
1091 int v3_cons_get_fb(struct vm_device * frontend_dev, uint8_t * dst, uint_t offset, uint_t length) {
1092     struct video_internal * state = (struct video_internal *)frontend_dev->private_data;
1093
1094     /* Deal with call depending on mode */
1095     if (state->graphmode) {
1096         return v3_cons_get_fb_graph(state, dst, offset, length);
1097     } else {
1098         return v3_cons_get_fb_text(state, dst, offset, length);
1099     }
1100 }
1101
1102 static int cga_free(struct video_internal * video_state) {
1103
1104     if (video_state->framebuf_pa) {
1105         PrintDebug(VM_NONE, VCORE_NONE, "Freeing framebuffer PA %p\n", (void *)(video_state->framebuf_pa));
1106         V3_FreePages((void *)(video_state->framebuf_pa), (FRAMEBUF_SIZE / 4096));
1107     }
1108
1109     v3_unhook_mem(video_state->dev->vm, V3_MEM_CORE_ANY, START_ADDR);
1110
1111     V3_Free(video_state);
1112
1113     return 0;
1114 }
1115
1116
1117 #ifdef V3_CONFIG_CHECKPOINT
1118 static int cga_save(struct v3_chkpt_ctx * ctx, void * private_data) {
1119     struct video_internal * cga = (struct video_internal *)private_data;
1120
1121     if (v3_chkpt_save(ctx, "FRAMEBUFFER", FRAMEBUF_SIZE, cga->framebuf)) { 
1122       goto savefailout;
1123     }
1124
1125     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->misc_outp_reg, savefailout);
1126     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->seq_index_reg, savefailout);                
1127     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->seq_data_regs, savefailout);        
1128     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->crtc_index_reg, savefailout);               
1129     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->crtc_data_regs, savefailout);
1130     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->graphc_index_reg, savefailout);             
1131     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->graphc_data_regs, savefailout);
1132     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->attrc_index_flipflop, savefailout);
1133     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->attrc_index_reg, savefailout);      
1134     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->attrc_data_regs, savefailout);      
1135     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->dac_indexr_reg, savefailout);       
1136     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->dac_indexr_color, savefailout);
1137     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->dac_indexw_reg, savefailout);               
1138     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->dac_indexw_color, savefailout);
1139     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->dac_data_regs, savefailout);
1140
1141     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->activefb_addr, savefailout);
1142     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->activefb_len, savefailout);
1143     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->iorange, savefailout);
1144     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->vres, savefailout);
1145     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->hres, savefailout);
1146     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->vchars, savefailout);
1147     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->hchars, savefailout);
1148     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->graphmode, savefailout);
1149
1150     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->dirty, savefailout);
1151     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->reschanged, savefailout);
1152
1153     V3_CHKPT_SAVE_AUTOTAG(ctx, cga->passthrough, savefailout);
1154
1155     V3_CHKPT_SAVE(ctx, "SCREEN_OFFSET", cga->screen_offset, savefailout);
1156     V3_CHKPT_SAVE(ctx, "CURSOR_OFFSET", cga->cursor_offset, savefailout);
1157
1158     return 0;
1159
1160  savefailout:
1161     PrintError(VM_NONE, VCORE_NONE, "Failed to save CGA\n");
1162     return -1;
1163
1164 }
1165
1166 static int cga_load(struct v3_chkpt_ctx * ctx, void * private_data) {
1167     struct video_internal * cga = (struct video_internal *)private_data;
1168
1169     if (v3_chkpt_load(ctx, "FRAMEBUFFER", FRAMEBUF_SIZE, cga->framebuf)) { 
1170       goto loadfailout;
1171     }
1172
1173
1174     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->misc_outp_reg, loadfailout);
1175     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->seq_index_reg, loadfailout);                
1176     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->seq_data_regs, loadfailout);        
1177     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->crtc_index_reg, loadfailout);               
1178     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->crtc_data_regs, loadfailout);
1179     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->graphc_index_reg, loadfailout);             
1180     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->graphc_data_regs, loadfailout);
1181     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->attrc_index_flipflop, loadfailout);
1182     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->attrc_index_reg, loadfailout);      
1183     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->attrc_data_regs, loadfailout);      
1184     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->dac_indexr_reg, loadfailout);       
1185     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->dac_indexr_color, loadfailout);
1186     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->dac_indexw_reg, loadfailout);               
1187     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->dac_indexw_color, loadfailout);
1188     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->dac_data_regs, loadfailout);
1189
1190     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->activefb_addr, loadfailout);
1191     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->activefb_len, loadfailout);
1192     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->iorange, loadfailout);
1193     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->vres, loadfailout);
1194     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->hres, loadfailout);
1195     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->vchars, loadfailout);
1196     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->hchars, loadfailout);
1197     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->graphmode, loadfailout);
1198
1199     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->dirty, loadfailout);
1200     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->reschanged, loadfailout);
1201
1202     V3_CHKPT_LOAD_AUTOTAG(ctx, cga->passthrough, loadfailout);
1203
1204     V3_CHKPT_LOAD(ctx, "SCREEN_OFFSET", cga->screen_offset, loadfailout);
1205     V3_CHKPT_LOAD(ctx, "CURSOR_OFFSET", cga->cursor_offset, loadfailout);
1206
1207
1208     return 0;
1209
1210  loadfailout:
1211     PrintError(VM_NONE, VCORE_NONE, "Failed to load cga\n");
1212     return -1;
1213
1214 }
1215
1216 #endif
1217
1218
1219 static struct v3_device_ops dev_ops = {
1220     .free = (int (*)(void *))cga_free,
1221 #ifdef V3_CONFIG_CHECKPOINT
1222     .save = cga_save, 
1223     .load = cga_load
1224 #endif
1225
1226 };
1227
1228 static int cga_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
1229     struct video_internal * video_state = NULL;
1230     char * dev_id = v3_cfg_val(cfg, "ID");
1231     char * passthrough_str = v3_cfg_val(cfg, "passthrough");
1232     int ret = 0;
1233     
1234     PrintDebug(vm, VCORE_NONE, "video: init_device\n");
1235
1236     video_state = (struct video_internal *)V3_Malloc(sizeof(struct video_internal));
1237
1238     if (!video_state) {
1239         PrintError(vm, VCORE_NONE, "Cannot allocate space for CGA state\n");
1240         return -1;
1241     }
1242
1243     memset(video_state, 0, sizeof(struct video_internal));
1244
1245     struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, video_state);
1246
1247     if (dev == NULL) {
1248         PrintError(vm, VCORE_NONE, "Could not attach device %s\n", dev_id);
1249         V3_Free(video_state);
1250         return -1;
1251     }
1252   
1253     video_state->dev = dev;
1254
1255     video_state->framebuf_pa = (addr_t)V3_AllocPages(FRAMEBUF_SIZE / 4096);
1256
1257     if (!video_state->framebuf_pa) { 
1258         PrintError(vm, VCORE_NONE, "Cannot allocate frame buffer\n");
1259         V3_Free(video_state);
1260         return -1;
1261     }
1262
1263     video_state->framebuf = V3_VAddr((void *)(video_state->framebuf_pa));
1264
1265     memset(video_state->framebuf, 0, FRAMEBUF_SIZE);
1266
1267     PrintDebug(vm, VCORE_NONE, "PA of array: %p\n", (void *)(video_state->framebuf_pa));
1268
1269     if ((passthrough_str != NULL) &&
1270         (strcasecmp(passthrough_str, "enable") == 0)) {;
1271         video_state->passthrough = 1;
1272     }
1273
1274
1275     if (video_state->passthrough) {
1276         PrintDebug(vm, VCORE_NONE, "Enabling CGA Passthrough\n");
1277         if (v3_hook_write_mem(vm, V3_MEM_CORE_ANY, START_ADDR, END_ADDR, 
1278                               START_ADDR, &video_write_mem, dev) == -1) {
1279             PrintError(vm, VCORE_NONE, "\n\nVideo Hook failed.\n\n");
1280             return -1;
1281         }
1282     } else {
1283         if (v3_hook_write_mem(vm, V3_MEM_CORE_ANY, START_ADDR, END_ADDR, 
1284                               video_state->framebuf_pa, &video_write_mem, dev) == -1) {
1285             PrintError(vm, VCORE_NONE, "\n\nVideo Hook failed.\n\n");
1286             return -1;
1287         }
1288     }
1289
1290     /* registers according to http://www.mcamafia.de/pdf/ibm_vgaxga_trm2.pdf */
1291
1292     /* general registers */
1293     ret |= v3_dev_hook_io(dev, 0x3cc, &misc_outp_read, NULL);
1294     ret |= v3_dev_hook_io(dev, 0x3c2, &inp_status0_read, &misc_outp_write);
1295     ret |= v3_dev_hook_io(dev, 0x3ba, &inp_status1_read, &feat_ctrl_write);
1296     ret |= v3_dev_hook_io(dev, 0x3da, &inp_status1_read, &feat_ctrl_write);
1297     ret |= v3_dev_hook_io(dev, 0x3ca, &feat_ctrl_read, NULL);
1298     ret |= v3_dev_hook_io(dev, 0x3c3, &video_enable_read, &video_enable_write);
1299
1300     /* sequencer registers */
1301     ret |= v3_dev_hook_io(dev, 0x3c4, &seq_index_read, &seq_index_write);
1302     ret |= v3_dev_hook_io(dev, 0x3c5, &seq_data_read, &seq_data_write);
1303
1304     /* CRT controller registers, both CGA and VGA ranges */
1305     ret |= v3_dev_hook_io(dev, 0x3b4, &crtc_index_read, &crtc_index_write);
1306     ret |= v3_dev_hook_io(dev, 0x3b5, &crtc_data_read, &crtc_data_write);
1307     ret |= v3_dev_hook_io(dev, 0x3d4, &crtc_index_read, &crtc_index_write);
1308     ret |= v3_dev_hook_io(dev, 0x3d5, &crtc_data_read, &crtc_data_write);
1309
1310     /* graphics controller registers */
1311     ret |= v3_dev_hook_io(dev, 0x3ce, &graphc_index_read, &graphc_index_write);
1312     ret |= v3_dev_hook_io(dev, 0x3cf, &graphc_data_read, &graphc_data_write);
1313
1314     /* attribute controller registers */
1315     ret |= v3_dev_hook_io(dev, 0x3c0, &attrc_index_read, &attrc_write);
1316     ret |= v3_dev_hook_io(dev, 0x3c1, &attrc_data_read, NULL);
1317
1318     /* video DAC palette registers */
1319     ret |= v3_dev_hook_io(dev, 0x3c8, &dac_indexw_read, &dac_indexw_write);
1320     ret |= v3_dev_hook_io(dev, 0x3c7, NULL, &dac_indexr_write);
1321     ret |= v3_dev_hook_io(dev, 0x3c9, &dac_data_read, &dac_data_write);
1322     ret |= v3_dev_hook_io(dev, 0x3c6, &dac_pelmask_read, &dac_pelmask_write);
1323
1324     if (ret != 0) {
1325         PrintError(vm, VCORE_NONE, "Error allocating VGA IO ports\n");
1326         v3_remove_device(dev);
1327         return -1;
1328     }
1329
1330     /* initialize the state as it is at boot time */
1331     registers_initialize(video_state);
1332
1333     return 0;
1334 }
1335
1336 device_register("CGA_VIDEO", cga_init);
1337
1338
1339 int v3_console_register_cga(struct vm_device * cga_dev, struct v3_console_ops * ops, void * private_data) {
1340     struct video_internal * video_state = (struct video_internal *)cga_dev->private_data;
1341     
1342     video_state->ops = ops;
1343     video_state->private_data = private_data;
1344
1345     return 0;
1346 }