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.


79978189269f99972398ca6a3c07b7553ae527ad
[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("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("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("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("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("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("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("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("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("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("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(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(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(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(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(guest_addr >= START_ADDR);
474     V3_ASSERT(guest_addr < END_ADDR);
475     
476     PrintVerbose("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("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("Video %s: got bad port 0x%x\n", function, port);
542     }
543
544     if (!video_state->passthrough && length > maxlength) {
545         PrintError("Video %s: got bad length %d\n", function, length);
546     }
547     V3_ASSERT(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("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("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("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("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("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
616     return notimpl_port_read(priv_data, __FUNCTION__, port, dest, length);
617 }
618
619 static int feat_ctrl_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
620     return notimpl_port_read(priv_data, __FUNCTION__, port, dest, length);
621 }
622
623 static int feat_ctrl_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
624     return notimpl_port_write(priv_data, __FUNCTION__, port, src, length);
625 }
626
627 static int video_enable_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
628     return notimpl_port_read(priv_data, __FUNCTION__, port, dest, length);
629 }
630
631 static int video_enable_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
632     return notimpl_port_write(priv_data, __FUNCTION__, port, src, length);
633 }
634
635 /* sequencer registers */
636 static int seq_data_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
637     struct video_internal * video_state = priv_data;
638     int index = video_state->seq_index_reg;
639
640     if (index < SEQ_REG_COUNT) {
641         *(uint8_t *) dest = video_state->seq_data_regs[index];
642     } else {
643         PrintError("Video %s: index %d out of range\n", __FUNCTION__, video_state->seq_index_reg);
644         *(uint8_t *) dest = 0;
645     }
646
647     handle_port_read(video_state, __FUNCTION__, port, dest, length, 1);
648     return length;    
649 }
650
651 static int seq_data_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
652     struct video_internal * video_state = priv_data;
653     int index = video_state->seq_index_reg;
654     uint8_t val = *(uint8_t *) src;
655     handle_port_write(video_state, __FUNCTION__, port, src, length, 1);
656
657     if (index < SEQ_REG_COUNT) {
658         PrintDebug("Video: seq[%d]=0x%x\n", index, val);
659         video_state->seq_data_regs[index] = val;
660         registers_updated(video_state);
661     } else {
662         PrintError("Video %s: index %d out of range\n", __FUNCTION__, video_state->seq_index_reg);
663     }
664
665     return length;    
666 }
667
668 static int seq_index_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
669     struct video_internal * video_state = priv_data;
670
671     *(uint8_t *) dest = video_state->seq_index_reg;
672
673     handle_port_read(video_state, __FUNCTION__, port, dest, length, 1);
674     return length;
675 }
676
677 static int seq_index_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
678     struct video_internal * video_state = priv_data;
679     handle_port_write(video_state, __FUNCTION__, port, src, length, 2);
680
681     video_state->seq_index_reg = *(uint8_t *) src;
682
683     if (length > 1) {
684         if (seq_data_write(core, port + 1, (uint8_t *) src + 1, length - 1, priv_data) != length - 1) {
685             return -1;
686         }
687     }
688
689     return length;    
690 }
691
692 /* CRT controller registers */
693 static int crtc_data_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
694     struct video_internal * video_state = priv_data;
695
696     *(uint8_t *) dest = video_state->crtc_index_reg;
697
698     handle_port_read(video_state, __FUNCTION__, port, dest, length, 1);
699     return length;
700 }
701
702 static int crtc_data_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
703     struct video_internal * video_state = priv_data;
704     uint8_t val = *(uint8_t *)src;
705     uint_t index = video_state->crtc_index_reg;
706
707     handle_port_write(video_state, __FUNCTION__, port, src, length, 1);
708     if (length != 1) {
709         PrintError("Invalid write length for port 0x%x\n", port);
710         return -1;
711     }
712
713     video_state->crtc_data_regs[index] = val;
714     registers_updated(video_state);
715
716     switch (index) {
717
718         case CRTC_REGIDX_START_ADDR_HIGH: break; // Dealt by low-order byte write
719         case CRTC_REGIDX_START_ADDR_LOW: {  // Scroll low byte
720             int diff, refresh;
721             uint_t screen_offset;
722
723             screen_offset =
724                 ((uint16_t) video_state->crtc_data_regs[CRTC_REGIDX_START_ADDR_HIGH] << 8) |
725                 ((uint16_t) video_state->crtc_data_regs[CRTC_REGIDX_START_ADDR_LOW]);
726             if ((screen_offset - video_state->screen_offset) % video_state->hchars) {
727                 /* diff is not a multiple of column count, need full refresh */
728                 diff = 0;
729                 refresh = 1;
730             } else {
731                 /* normal scroll (the common case) */
732                 diff = (screen_offset - video_state->screen_offset) / video_state->hchars;
733                 refresh = 0;
734             }
735             PrintVerbose("Video: screen_offset=%d, video_state->screen_offset=%d, video_state->hchars=%d, diff=%d, refresh=%d\n",
736                 screen_offset, video_state->screen_offset, video_state->hchars, diff, refresh);
737
738             // Update the true offset value
739             video_state->screen_offset = screen_offset;
740
741             if (refresh || video_state->dirty) {
742                 refresh_screen(video_state);
743             } else if (diff && video_state->ops) {
744                 PrintVerbose("Video: scroll(%d)\n", diff);
745                 if (video_state->ops->scroll(diff, video_state->private_data) == -1) {
746                     PrintError("Error sending scroll event\n");
747                     return -1;
748                 }
749             }
750             break;
751         }
752         case CRTC_REGIDX_CURSOR_LOC_HIGH: break; // Dealt by low-order byte write
753         case CRTC_REGIDX_CURSOR_LOC_LOW: { // Cursor adjustment low byte
754             uint_t x;
755             uint_t y;
756             
757             video_state->cursor_offset =
758                 ((uint16_t) video_state->crtc_data_regs[CRTC_REGIDX_CURSOR_LOC_HIGH] << 8) |
759                 ((uint16_t) video_state->crtc_data_regs[CRTC_REGIDX_CURSOR_LOC_LOW]);
760             x = video_state->cursor_offset % video_state->hchars;
761             y = (video_state->cursor_offset - video_state->screen_offset) / video_state->hchars;
762             PrintVerbose("Video: video_state->cursor_offset=%d, x=%d, y=%d\n",
763                 video_state->cursor_offset, x, y);
764
765             if (video_state->dirty) {
766                 refresh_screen(video_state);
767             }
768             
769             PrintVerbose("Video: set cursor(%d, %d)\n", x, y);
770             if (video_state->ops) {
771                 if (video_state->ops->update_cursor(x, y, video_state->private_data) == -1) {
772                     PrintError("Error updating cursor\n");
773                     return -1;
774                 }
775             } 
776
777             break;
778         }
779         default:
780             PrintDebug("Video: crtc[%d]=0x%x\n", index, val);
781             break;
782     }
783
784     if (video_state->passthrough) {
785         passthrough_out(port, src, length);
786     }
787
788     return length;
789 }
790
791 static int crtc_index_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
792     struct video_internal * video_state = priv_data;
793
794     *(uint8_t *) dest = video_state->crtc_index_reg;
795
796     handle_port_read(video_state, __FUNCTION__, port, dest, length, 1);
797     return length;
798 }
799
800 static int crtc_index_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
801     struct video_internal * video_state = priv_data;
802
803     handle_port_write(video_state, __FUNCTION__, port, src, length, 2);
804     if (length > 2) {
805         PrintError("Invalid write length for crtc index register port: %d (0x%x)\n",
806                    port, port);
807         return -1;
808     }
809
810     video_state->crtc_index_reg = *(uint8_t *)src;
811
812     // Only do the passthrough IO for the first byte
813     // the second byte will be done in the data register handler
814     if (video_state->passthrough) {
815         passthrough_out(port, src, 1);
816     }
817
818     if (length > 1) {
819         if (crtc_data_write(core, port + 1, (uint8_t *) src + 1, length - 1, priv_data) != length - 1) {
820             return -1;
821         }
822     }
823
824     return length;
825 }
826
827 /* graphics controller registers */
828 static int graphc_data_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
829     struct video_internal * video_state = priv_data;
830     int index = video_state->graphc_index_reg;
831
832     if (index < GRAPHC_REG_COUNT) {
833         *(uint8_t *) dest = video_state->graphc_data_regs[index];
834     } else {
835         PrintError("Video %s: index %d out of range\n", __FUNCTION__, video_state->graphc_index_reg);
836         *(uint8_t *) dest = 0;
837     }
838
839     handle_port_read(video_state, __FUNCTION__, port, dest, length, 1);
840     return length;    
841 }
842
843 static int graphc_data_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
844     struct video_internal * video_state = priv_data;
845     int index = video_state->graphc_index_reg;
846     uint8_t val = *(uint8_t *) src;
847     handle_port_write(video_state, __FUNCTION__, port, src, length, 1);
848
849     if (index < GRAPHC_REG_COUNT) {
850         PrintDebug("Video: graphc[%d]=0x%x\n", index, val);
851         video_state->graphc_data_regs[index] = val;
852         registers_updated(video_state);
853     } else {
854         PrintError("Video %s: index %d out of range\n", __FUNCTION__, video_state->graphc_index_reg);
855     }
856
857     return length;    
858 }
859
860 static int graphc_index_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
861     struct video_internal * video_state = priv_data;
862
863     *(uint8_t *) dest = video_state->graphc_index_reg;
864
865     handle_port_read(video_state, __FUNCTION__, port, dest, length, 1);
866     return length;
867 }
868
869 static int graphc_index_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
870     struct video_internal * video_state = priv_data;
871     handle_port_write(video_state, __FUNCTION__, port, src, length, 2);
872
873     video_state->graphc_index_reg = *(uint8_t *) src;
874
875     if (length > 1) {
876         if (graphc_data_write(core, port + 1, (uint8_t *) src + 1, length - 1, priv_data) != length - 1) {
877             return -1;
878         }
879     }
880     
881     return length;    
882 }
883
884 /* attribute controller registers */
885 static int attrc_data_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
886     struct video_internal * video_state = priv_data;
887     int index = video_state->attrc_index_reg;
888
889     if (index < ATTRC_REG_COUNT) {
890         *(uint8_t *) dest = video_state->attrc_data_regs[index];
891     } else {
892         PrintError("Video %s: index %d out of range\n", __FUNCTION__, video_state->attrc_index_reg);
893         *(uint8_t *) dest = 0;
894     }
895
896     handle_port_read(video_state, __FUNCTION__, port, dest, length, 1);
897     return length;    
898 }
899
900 static int attrc_data_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
901     struct video_internal * video_state = priv_data;
902     int index = video_state->attrc_index_reg;
903     uint8_t val = *(uint8_t *) src;
904     handle_port_write(video_state, __FUNCTION__, port, src, length, 1);
905
906     if (index < ATTRC_REG_COUNT) {
907         PrintDebug("Video: attrc[%d]=0x%x\n", index, val);
908         video_state->attrc_data_regs[index] = val;
909     } else {
910         PrintError("Video %s: index %d out of range\n", __FUNCTION__, video_state->attrc_index_reg);
911     }
912
913     return length;    
914 }
915
916 static int attrc_index_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
917     struct video_internal * video_state = priv_data;
918
919     *(uint8_t *) dest = video_state->attrc_index_reg;
920
921     if (length > 1) {
922         if (attrc_data_read(core, port + 1, (uint8_t *) dest + 1, length - 1, priv_data) != length - 1) {
923             return -1;
924         }
925     }
926     
927     handle_port_read(video_state, __FUNCTION__, port, dest, length, 2);
928     return length;
929 }
930
931 static int attrc_index_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
932     struct video_internal * video_state = priv_data;
933     handle_port_write(video_state, __FUNCTION__, port, src, length, 1);
934
935     video_state->attrc_index_reg = *(uint8_t *) src;
936
937     return length;    
938 }
939
940 static int attrc_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
941     struct video_internal * video_state = priv_data;
942
943     /* two registers in one, written in an alternating fashion */
944     if (video_state->attrc_index_flipflop) {
945         video_state->attrc_index_flipflop = 0;
946         return attrc_data_write(core, port, src, length, priv_data);
947     } else {
948         video_state->attrc_index_flipflop = 1;
949         return attrc_index_write(core, port, src, length, priv_data);
950     }
951 }
952
953 /* video DAC palette registers */
954 static int dac_indexw_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
955     struct video_internal * video_state = priv_data;
956
957     *(uint8_t *) dest = video_state->dac_indexw_reg;
958
959     handle_port_read(video_state, __FUNCTION__, port, dest, length, 1);
960     return length;
961 }
962
963 static int dac_indexw_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
964     struct video_internal * video_state = priv_data;
965     handle_port_write(video_state, __FUNCTION__, port, src, length, 1);
966
967     video_state->dac_indexw_reg = *(uint8_t *) src;
968     video_state->dac_indexw_color = 0;
969
970     return length;  
971 }
972
973 static int dac_indexr_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
974     struct video_internal * video_state = priv_data;
975     handle_port_write(video_state, __FUNCTION__, port, src, length, 1);
976
977     video_state->dac_indexr_reg = *(uint8_t *) src;
978     video_state->dac_indexr_color = 0;
979
980     return length;  
981 }
982
983 static int dac_data_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
984     struct video_internal * video_state = priv_data;
985     unsigned index;
986
987     /* update palette */
988     index = (unsigned) video_state->dac_indexr_reg * DAC_COLOR_COUNT + 
989         video_state->dac_indexr_color;
990     V3_ASSERT(index < DAC_REG_COUNT);
991     *(uint8_t *) dest = video_state->dac_data_regs[index];
992     
993     /* move on to next entry/color */
994     if (++video_state->dac_indexr_color > DAC_COLOR_COUNT) {
995         video_state->dac_indexr_reg++;
996         video_state->dac_indexr_color -= DAC_COLOR_COUNT;
997     }
998
999     handle_port_read(video_state, __FUNCTION__, port, dest, length, 1);
1000     return length;
1001 }
1002
1003 static int dac_data_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
1004     struct video_internal * video_state = priv_data;
1005     unsigned index;
1006     handle_port_write(video_state, __FUNCTION__, port, src, length, 1);
1007
1008     /* update palette */
1009     index = (unsigned) video_state->dac_indexw_reg * DAC_COLOR_COUNT + 
1010         video_state->dac_indexw_color;
1011     V3_ASSERT(index < DAC_REG_COUNT);
1012     video_state->dac_data_regs[index] = *(uint8_t *) src;
1013     
1014     /* move on to next entry/color */
1015     if (++video_state->dac_indexw_color > DAC_COLOR_COUNT) {
1016         video_state->dac_indexw_reg++;
1017         video_state->dac_indexw_color -= DAC_COLOR_COUNT;
1018     }
1019
1020     return length;
1021 }
1022
1023 static int dac_pelmask_read(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
1024     return notimpl_port_read(priv_data, __FUNCTION__, port, dest, length);
1025 }
1026
1027 static int dac_pelmask_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
1028     return notimpl_port_write(priv_data, __FUNCTION__, port, src, length);
1029 }
1030
1031
1032 static int v3_cons_get_fb_graph(struct video_internal * state, uint8_t * dst, uint_t offset, uint_t length) {
1033     char c, text[80];
1034     uint_t textlength, textoffset, textsize;
1035
1036     /* this call is not intended for graphics mode, tell the user this */
1037
1038     /* center informative text on 10th line */
1039     snprintf(text, sizeof(text), "* * * GRAPHICS MODE %dx%d * * *",
1040         state->hres, state->vres);
1041     textlength = strlen(text);
1042     textoffset = (state->hchars * 9 + (state->hchars - textlength) / 2) * BYTES_PER_COL;
1043     textsize = textlength * BYTES_PER_COL;
1044
1045     /* fill the buffer */
1046     while (length-- > 0) {
1047         if (offset % BYTES_PER_COL) {
1048             c = 0; /* attribute byte */
1049         } else if (offset < textoffset || offset - textoffset >= textsize) {
1050             c = ' '; /* unused character byte */
1051         } else {
1052             c = text[(offset - textoffset) / BYTES_PER_COL];
1053         }
1054
1055         *(dst++) = c;
1056         offset++;
1057     }
1058
1059     return 0;
1060 }
1061
1062 static uint_t min_uint(uint_t x, uint_t y) {
1063     return (x < y) ? x : y;
1064 }
1065
1066 int v3_cons_get_fb_text(struct video_internal * state, uint8_t * dst, uint_t offset, uint_t length) {
1067     uint8_t *framebuf;
1068     uint_t framebuf_offset, len1, len2;
1069     uint_t screen_byte_offset = state->screen_offset * BYTES_PER_COL;
1070
1071     PrintVerbose("Video: getfb o=%d l=%d so=%d aa=0x%x al=0x%x hc=%d vc=%d\n",
1072         offset, length, state->screen_offset, 
1073         (unsigned) state->activefb_addr, (unsigned) state->activefb_len,
1074         state->hchars, state->vchars);
1075     V3_ASSERT(state->activefb_addr >= START_ADDR);
1076     V3_ASSERT(state->activefb_addr + state->activefb_len <= END_ADDR);
1077
1078     /* Copy memory with wrapping (should be configurable, but where else to get the data?) */
1079     framebuf = state->framebuf + (state->activefb_addr - START_ADDR);
1080     framebuf_offset = (screen_byte_offset + offset) % state->activefb_len;
1081     len1 = min_uint(length, state->activefb_len - framebuf_offset);
1082     len2 = length - len1;
1083     if (len1 > 0) memcpy(dst, framebuf + framebuf_offset, len1);
1084     if (len2 > 0) memcpy(dst + len1, framebuf, len2);
1085
1086     return 0;
1087 }
1088
1089 int v3_cons_get_fb(struct vm_device * frontend_dev, uint8_t * dst, uint_t offset, uint_t length) {
1090     struct video_internal * state = (struct video_internal *)frontend_dev->private_data;
1091
1092     /* Deal with call depending on mode */
1093     if (state->graphmode) {
1094         return v3_cons_get_fb_graph(state, dst, offset, length);
1095     } else {
1096         return v3_cons_get_fb_text(state, dst, offset, length);
1097     }
1098 }
1099
1100 static int free_cga(struct video_internal * video_state) {
1101
1102     if (video_state->framebuf_pa) {
1103         PrintError("Freeing framebuffer PA %p\n", (void *)(video_state->framebuf_pa));
1104         V3_FreePages((void *)(video_state->framebuf_pa), (FRAMEBUF_SIZE / 4096));
1105     }
1106
1107     v3_unhook_mem(video_state->dev->vm, V3_MEM_CORE_ANY, START_ADDR);
1108
1109     V3_Free(video_state);
1110
1111     return 0;
1112 }
1113
1114
1115 static struct v3_device_ops dev_ops = {
1116     .free = (int (*)(void *))free_cga,
1117 };
1118
1119 static int cga_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
1120     struct video_internal * video_state = NULL;
1121     char * dev_id = v3_cfg_val(cfg, "ID");
1122     char * passthrough_str = v3_cfg_val(cfg, "passthrough");
1123     int ret = 0;
1124     
1125     PrintDebug("video: init_device\n");
1126
1127     video_state = (struct video_internal *)V3_Malloc(sizeof(struct video_internal));
1128     memset(video_state, 0, sizeof(struct video_internal));
1129
1130     struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, video_state);
1131
1132     if (dev == NULL) {
1133         PrintError("Could not attach device %s\n", dev_id);
1134         V3_Free(video_state);
1135         return -1;
1136     }
1137   
1138     video_state->dev = dev;
1139
1140     video_state->framebuf_pa = (addr_t)V3_AllocPages(FRAMEBUF_SIZE / 4096);
1141     video_state->framebuf = V3_VAddr((void *)(video_state->framebuf_pa));
1142     memset(video_state->framebuf, 0, FRAMEBUF_SIZE);
1143
1144     PrintDebug("PA of array: %p\n", (void *)(video_state->framebuf_pa));
1145
1146     if ((passthrough_str != NULL) &&
1147         (strcasecmp(passthrough_str, "enable") == 0)) {;
1148         video_state->passthrough = 1;
1149     }
1150
1151
1152     if (video_state->passthrough) {
1153         PrintDebug("Enabling CGA Passthrough\n");
1154         if (v3_hook_write_mem(vm, V3_MEM_CORE_ANY, START_ADDR, END_ADDR, 
1155                               START_ADDR, &video_write_mem, dev) == -1) {
1156             PrintError("\n\nVideo Hook failed.\n\n");
1157             return -1;
1158         }
1159     } else {
1160         if (v3_hook_write_mem(vm, V3_MEM_CORE_ANY, START_ADDR, END_ADDR, 
1161                               video_state->framebuf_pa, &video_write_mem, dev) == -1) {
1162             PrintError("\n\nVideo Hook failed.\n\n");
1163             return -1;
1164         }
1165     }
1166
1167     /* registers according to http://www.mcamafia.de/pdf/ibm_vgaxga_trm2.pdf */
1168
1169     /* general registers */
1170     ret |= v3_dev_hook_io(dev, 0x3cc, &misc_outp_read, NULL);
1171     ret |= v3_dev_hook_io(dev, 0x3c2, &inp_status0_read, &misc_outp_write);
1172     ret |= v3_dev_hook_io(dev, 0x3ba, &inp_status1_read, &feat_ctrl_write);
1173     ret |= v3_dev_hook_io(dev, 0x3da, &inp_status1_read, &feat_ctrl_write);
1174     ret |= v3_dev_hook_io(dev, 0x3ca, &feat_ctrl_read, NULL);
1175     ret |= v3_dev_hook_io(dev, 0x3c3, &video_enable_read, &video_enable_write);
1176
1177     /* sequencer registers */
1178     ret |= v3_dev_hook_io(dev, 0x3c4, &seq_index_read, &seq_index_write);
1179     ret |= v3_dev_hook_io(dev, 0x3c5, &seq_data_read, &seq_data_write);
1180
1181     /* CRT controller registers, both CGA and VGA ranges */
1182     ret |= v3_dev_hook_io(dev, 0x3b4, &crtc_index_read, &crtc_index_write);
1183     ret |= v3_dev_hook_io(dev, 0x3b5, &crtc_data_read, &crtc_data_write);
1184     ret |= v3_dev_hook_io(dev, 0x3d4, &crtc_index_read, &crtc_index_write);
1185     ret |= v3_dev_hook_io(dev, 0x3d5, &crtc_data_read, &crtc_data_write);
1186
1187     /* graphics controller registers */
1188     ret |= v3_dev_hook_io(dev, 0x3ce, &graphc_index_read, &graphc_index_write);
1189     ret |= v3_dev_hook_io(dev, 0x3cf, &graphc_data_read, &graphc_data_write);
1190
1191     /* attribute controller registers */
1192     ret |= v3_dev_hook_io(dev, 0x3c0, &attrc_index_read, &attrc_write);
1193     ret |= v3_dev_hook_io(dev, 0x3c1, &attrc_data_read, NULL);
1194
1195     /* video DAC palette registers */
1196     ret |= v3_dev_hook_io(dev, 0x3c8, &dac_indexw_read, &dac_indexw_write);
1197     ret |= v3_dev_hook_io(dev, 0x3c7, NULL, &dac_indexr_write);
1198     ret |= v3_dev_hook_io(dev, 0x3c9, &dac_data_read, &dac_data_write);
1199     ret |= v3_dev_hook_io(dev, 0x3c6, &dac_pelmask_read, &dac_pelmask_write);
1200
1201     if (ret != 0) {
1202         PrintError("Error allocating VGA IO ports\n");
1203         v3_remove_device(dev);
1204         return -1;
1205     }
1206
1207     /* initialize the state as it is at boot time */
1208     registers_initialize(video_state);
1209
1210     return 0;
1211 }
1212
1213 device_register("CGA_VIDEO", cga_init);
1214
1215
1216 int v3_console_register_cga(struct vm_device * cga_dev, struct v3_console_ops * ops, void * private_data) {
1217     struct video_internal * video_state = (struct video_internal *)cga_dev->private_data;
1218     
1219     video_state->ops = ops;
1220     video_state->private_data = private_data;
1221
1222     return 0;
1223 }