]>
Commit | Line | Data |
---|---|---|
8ac919a0 LV |
1 | /* |
2 | * QEMU Motorola 680x0 Macintosh Video Card Emulation | |
3 | * Copyright (c) 2012-2018 Laurent Vivier | |
4 | * | |
5 | * some parts from QEMU G364 framebuffer Emulator. | |
6 | * Copyright (c) 2007-2011 Herve Poussineau | |
7 | * | |
8 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
9 | * See the COPYING file in the top-level directory. | |
10 | * | |
11 | */ | |
12 | ||
13 | #include "qemu/osdep.h" | |
14 | #include "qemu/units.h" | |
15 | #include "hw/sysbus.h" | |
16 | #include "ui/console.h" | |
17 | #include "ui/pixel_ops.h" | |
18 | #include "hw/nubus/nubus.h" | |
19 | #include "hw/display/macfb.h" | |
20 | #include "qapi/error.h" | |
21 | #include "hw/qdev-properties.h" | |
22 | #include "migration/vmstate.h" | |
23 | ||
24 | #define VIDEO_BASE 0x00001000 | |
25 | #define DAFB_BASE 0x00800000 | |
26 | ||
27 | #define MACFB_PAGE_SIZE 4096 | |
28 | #define MACFB_VRAM_SIZE (4 * MiB) | |
29 | ||
30 | #define DAFB_RESET 0x200 | |
31 | #define DAFB_LUT 0x213 | |
32 | ||
33 | ||
34 | typedef void macfb_draw_line_func(MacfbState *s, uint8_t *d, uint32_t addr, | |
35 | int width); | |
36 | ||
37 | static inline uint8_t macfb_read_byte(MacfbState *s, uint32_t addr) | |
38 | { | |
39 | return s->vram[addr & s->vram_bit_mask]; | |
40 | } | |
41 | ||
42 | /* 1-bit color */ | |
43 | static void macfb_draw_line1(MacfbState *s, uint8_t *d, uint32_t addr, | |
44 | int width) | |
45 | { | |
46 | uint8_t r, g, b; | |
47 | int x; | |
48 | ||
49 | for (x = 0; x < width; x++) { | |
50 | int bit = x & 7; | |
51 | int idx = (macfb_read_byte(s, addr) >> (7 - bit)) & 1; | |
52 | r = g = b = ((1 - idx) << 7); | |
53 | addr += (bit == 7); | |
54 | ||
55 | *(uint32_t *)d = rgb_to_pixel32(r, g, b); | |
56 | d += 4; | |
57 | } | |
58 | } | |
59 | ||
60 | /* 2-bit color */ | |
61 | static void macfb_draw_line2(MacfbState *s, uint8_t *d, uint32_t addr, | |
62 | int width) | |
63 | { | |
64 | uint8_t r, g, b; | |
65 | int x; | |
66 | ||
67 | for (x = 0; x < width; x++) { | |
68 | int bit = (x & 3); | |
69 | int idx = (macfb_read_byte(s, addr) >> ((3 - bit) << 1)) & 3; | |
70 | r = s->color_palette[idx * 3]; | |
71 | g = s->color_palette[idx * 3 + 1]; | |
72 | b = s->color_palette[idx * 3 + 2]; | |
73 | addr += (bit == 3); | |
74 | ||
75 | *(uint32_t *)d = rgb_to_pixel32(r, g, b); | |
76 | d += 4; | |
77 | } | |
78 | } | |
79 | ||
80 | /* 4-bit color */ | |
81 | static void macfb_draw_line4(MacfbState *s, uint8_t *d, uint32_t addr, | |
82 | int width) | |
83 | { | |
84 | uint8_t r, g, b; | |
85 | int x; | |
86 | ||
87 | for (x = 0; x < width; x++) { | |
88 | int bit = x & 1; | |
89 | int idx = (macfb_read_byte(s, addr) >> ((1 - bit) << 2)) & 15; | |
90 | r = s->color_palette[idx * 3]; | |
91 | g = s->color_palette[idx * 3 + 1]; | |
92 | b = s->color_palette[idx * 3 + 2]; | |
93 | addr += (bit == 1); | |
94 | ||
95 | *(uint32_t *)d = rgb_to_pixel32(r, g, b); | |
96 | d += 4; | |
97 | } | |
98 | } | |
99 | ||
100 | /* 8-bit color */ | |
101 | static void macfb_draw_line8(MacfbState *s, uint8_t *d, uint32_t addr, | |
102 | int width) | |
103 | { | |
104 | uint8_t r, g, b; | |
105 | int x; | |
106 | ||
107 | for (x = 0; x < width; x++) { | |
108 | r = s->color_palette[macfb_read_byte(s, addr) * 3]; | |
109 | g = s->color_palette[macfb_read_byte(s, addr) * 3 + 1]; | |
110 | b = s->color_palette[macfb_read_byte(s, addr) * 3 + 2]; | |
111 | addr++; | |
112 | ||
113 | *(uint32_t *)d = rgb_to_pixel32(r, g, b); | |
114 | d += 4; | |
115 | } | |
116 | } | |
117 | ||
118 | /* 16-bit color */ | |
119 | static void macfb_draw_line16(MacfbState *s, uint8_t *d, uint32_t addr, | |
120 | int width) | |
121 | { | |
122 | uint8_t r, g, b; | |
123 | int x; | |
124 | ||
125 | for (x = 0; x < width; x++) { | |
126 | uint16_t pixel; | |
127 | pixel = (macfb_read_byte(s, addr) << 8) | macfb_read_byte(s, addr + 1); | |
128 | r = ((pixel >> 10) & 0x1f) << 3; | |
129 | g = ((pixel >> 5) & 0x1f) << 3; | |
130 | b = (pixel & 0x1f) << 3; | |
131 | addr += 2; | |
132 | ||
133 | *(uint32_t *)d = rgb_to_pixel32(r, g, b); | |
134 | d += 4; | |
135 | } | |
136 | } | |
137 | ||
138 | /* 24-bit color */ | |
139 | static void macfb_draw_line24(MacfbState *s, uint8_t *d, uint32_t addr, | |
140 | int width) | |
141 | { | |
142 | uint8_t r, g, b; | |
143 | int x; | |
144 | ||
145 | for (x = 0; x < width; x++) { | |
146 | r = macfb_read_byte(s, addr); | |
147 | g = macfb_read_byte(s, addr + 1); | |
148 | b = macfb_read_byte(s, addr + 2); | |
149 | addr += 3; | |
150 | ||
151 | *(uint32_t *)d = rgb_to_pixel32(r, g, b); | |
152 | d += 4; | |
153 | } | |
154 | } | |
155 | ||
156 | ||
157 | enum { | |
158 | MACFB_DRAW_LINE1, | |
159 | MACFB_DRAW_LINE2, | |
160 | MACFB_DRAW_LINE4, | |
161 | MACFB_DRAW_LINE8, | |
162 | MACFB_DRAW_LINE16, | |
163 | MACFB_DRAW_LINE24, | |
164 | MACFB_DRAW_LINE_NB, | |
165 | }; | |
166 | ||
167 | static macfb_draw_line_func * const | |
168 | macfb_draw_line_table[MACFB_DRAW_LINE_NB] = { | |
169 | macfb_draw_line1, | |
170 | macfb_draw_line2, | |
171 | macfb_draw_line4, | |
172 | macfb_draw_line8, | |
173 | macfb_draw_line16, | |
174 | macfb_draw_line24, | |
175 | }; | |
176 | ||
177 | static int macfb_check_dirty(MacfbState *s, DirtyBitmapSnapshot *snap, | |
178 | ram_addr_t addr, int len) | |
179 | { | |
180 | return memory_region_snapshot_get_dirty(&s->mem_vram, snap, addr, len); | |
181 | } | |
182 | ||
183 | static void macfb_draw_graphic(MacfbState *s) | |
184 | { | |
185 | DisplaySurface *surface = qemu_console_surface(s->con); | |
186 | DirtyBitmapSnapshot *snap = NULL; | |
187 | ram_addr_t page; | |
188 | uint32_t v = 0; | |
189 | int y, ymin; | |
190 | int macfb_stride = (s->depth * s->width + 7) / 8; | |
191 | macfb_draw_line_func *macfb_draw_line; | |
192 | ||
193 | switch (s->depth) { | |
194 | case 1: | |
195 | v = MACFB_DRAW_LINE1; | |
196 | break; | |
197 | case 2: | |
198 | v = MACFB_DRAW_LINE2; | |
199 | break; | |
200 | case 4: | |
201 | v = MACFB_DRAW_LINE4; | |
202 | break; | |
203 | case 8: | |
204 | v = MACFB_DRAW_LINE8; | |
205 | break; | |
206 | case 16: | |
207 | v = MACFB_DRAW_LINE16; | |
208 | break; | |
209 | case 24: | |
210 | v = MACFB_DRAW_LINE24; | |
211 | break; | |
212 | } | |
213 | ||
214 | macfb_draw_line = macfb_draw_line_table[v]; | |
215 | assert(macfb_draw_line != NULL); | |
216 | ||
217 | snap = memory_region_snapshot_and_clear_dirty(&s->mem_vram, 0x0, | |
218 | memory_region_size(&s->mem_vram), | |
219 | DIRTY_MEMORY_VGA); | |
220 | ||
221 | ymin = -1; | |
222 | page = 0; | |
223 | for (y = 0; y < s->height; y++, page += macfb_stride) { | |
224 | if (macfb_check_dirty(s, snap, page, macfb_stride)) { | |
225 | uint8_t *data_display; | |
226 | ||
227 | data_display = surface_data(surface) + y * surface_stride(surface); | |
228 | macfb_draw_line(s, data_display, page, s->width); | |
229 | ||
230 | if (ymin < 0) { | |
231 | ymin = y; | |
232 | } | |
233 | } else { | |
234 | if (ymin >= 0) { | |
235 | dpy_gfx_update(s->con, 0, ymin, s->width, y - ymin); | |
236 | ymin = -1; | |
237 | } | |
238 | } | |
239 | } | |
240 | ||
241 | if (ymin >= 0) { | |
242 | dpy_gfx_update(s->con, 0, ymin, s->width, y - ymin); | |
243 | } | |
244 | ||
245 | g_free(snap); | |
246 | } | |
247 | ||
248 | static void macfb_invalidate_display(void *opaque) | |
249 | { | |
250 | MacfbState *s = opaque; | |
251 | ||
252 | memory_region_set_dirty(&s->mem_vram, 0, MACFB_VRAM_SIZE); | |
253 | } | |
254 | ||
255 | static void macfb_update_display(void *opaque) | |
256 | { | |
257 | MacfbState *s = opaque; | |
258 | DisplaySurface *surface = qemu_console_surface(s->con); | |
259 | ||
260 | qemu_flush_coalesced_mmio_buffer(); | |
261 | ||
262 | if (s->width == 0 || s->height == 0) { | |
263 | return; | |
264 | } | |
265 | ||
266 | if (s->width != surface_width(surface) || | |
267 | s->height != surface_height(surface)) { | |
268 | qemu_console_resize(s->con, s->width, s->height); | |
269 | } | |
270 | ||
271 | macfb_draw_graphic(s); | |
272 | } | |
273 | ||
274 | static void macfb_reset(MacfbState *s) | |
275 | { | |
276 | int i; | |
277 | ||
278 | s->palette_current = 0; | |
279 | for (i = 0; i < 256; i++) { | |
280 | s->color_palette[i * 3] = 255 - i; | |
281 | s->color_palette[i * 3 + 1] = 255 - i; | |
282 | s->color_palette[i * 3 + 2] = 255 - i; | |
283 | } | |
284 | memset(s->vram, 0, MACFB_VRAM_SIZE); | |
285 | macfb_invalidate_display(s); | |
286 | } | |
287 | ||
288 | static uint64_t macfb_ctrl_read(void *opaque, | |
289 | hwaddr addr, | |
290 | unsigned int size) | |
291 | { | |
292 | return 0; | |
293 | } | |
294 | ||
295 | static void macfb_ctrl_write(void *opaque, | |
296 | hwaddr addr, | |
297 | uint64_t val, | |
298 | unsigned int size) | |
299 | { | |
300 | MacfbState *s = opaque; | |
301 | switch (addr) { | |
302 | case DAFB_RESET: | |
303 | s->palette_current = 0; | |
304 | break; | |
305 | case DAFB_LUT: | |
306 | s->color_palette[s->palette_current++] = val; | |
307 | if (s->palette_current % 3) { | |
308 | macfb_invalidate_display(s); | |
309 | } | |
310 | break; | |
311 | } | |
312 | } | |
313 | ||
314 | static const MemoryRegionOps macfb_ctrl_ops = { | |
315 | .read = macfb_ctrl_read, | |
316 | .write = macfb_ctrl_write, | |
317 | .endianness = DEVICE_BIG_ENDIAN, | |
318 | .impl.min_access_size = 1, | |
319 | .impl.max_access_size = 4, | |
320 | }; | |
321 | ||
322 | static int macfb_post_load(void *opaque, int version_id) | |
323 | { | |
324 | macfb_invalidate_display(opaque); | |
325 | return 0; | |
326 | } | |
327 | ||
328 | static const VMStateDescription vmstate_macfb = { | |
329 | .name = "macfb", | |
330 | .version_id = 1, | |
331 | .minimum_version_id = 1, | |
332 | .minimum_version_id_old = 1, | |
333 | .post_load = macfb_post_load, | |
334 | .fields = (VMStateField[]) { | |
335 | VMSTATE_UINT8_ARRAY(color_palette, MacfbState, 256 * 3), | |
336 | VMSTATE_UINT32(palette_current, MacfbState), | |
337 | VMSTATE_END_OF_LIST() | |
338 | } | |
339 | }; | |
340 | ||
341 | static const GraphicHwOps macfb_ops = { | |
342 | .invalidate = macfb_invalidate_display, | |
343 | .gfx_update = macfb_update_display, | |
344 | }; | |
345 | ||
346 | static void macfb_common_realize(DeviceState *dev, MacfbState *s, Error **errp) | |
347 | { | |
348 | DisplaySurface *surface; | |
349 | ||
350 | if (s->depth != 1 && s->depth != 2 && s->depth != 4 && s->depth != 8 && | |
351 | s->depth != 16 && s->depth != 24) { | |
352 | error_setg(errp, "unknown guest depth %d", s->depth); | |
353 | return; | |
354 | } | |
355 | ||
356 | s->con = graphic_console_init(dev, 0, &macfb_ops, s); | |
357 | surface = qemu_console_surface(s->con); | |
358 | ||
359 | if (surface_bits_per_pixel(surface) != 32) { | |
360 | error_setg(errp, "unknown host depth %d", | |
361 | surface_bits_per_pixel(surface)); | |
362 | return; | |
363 | } | |
364 | ||
365 | memory_region_init_io(&s->mem_ctrl, NULL, &macfb_ctrl_ops, s, "macfb-ctrl", | |
366 | 0x1000); | |
367 | ||
368 | memory_region_init_ram_nomigrate(&s->mem_vram, OBJECT(s), "macfb-vram", | |
369 | MACFB_VRAM_SIZE, errp); | |
370 | s->vram = memory_region_get_ram_ptr(&s->mem_vram); | |
371 | s->vram_bit_mask = MACFB_VRAM_SIZE - 1; | |
372 | vmstate_register_ram(&s->mem_vram, dev); | |
373 | memory_region_set_coalescing(&s->mem_vram); | |
374 | } | |
375 | ||
376 | static void macfb_sysbus_realize(DeviceState *dev, Error **errp) | |
377 | { | |
378 | MacfbSysBusState *s = MACFB(dev); | |
379 | MacfbState *ms = &s->macfb; | |
380 | ||
381 | macfb_common_realize(dev, ms, errp); | |
382 | sysbus_init_mmio(SYS_BUS_DEVICE(s), &ms->mem_ctrl); | |
383 | sysbus_init_mmio(SYS_BUS_DEVICE(s), &ms->mem_vram); | |
384 | } | |
385 | ||
386 | const uint8_t macfb_rom[] = { | |
387 | 255, 0, 0, 0, | |
388 | }; | |
389 | ||
390 | static void macfb_nubus_realize(DeviceState *dev, Error **errp) | |
391 | { | |
392 | NubusDevice *nd = NUBUS_DEVICE(dev); | |
393 | MacfbNubusState *s = NUBUS_MACFB(dev); | |
394 | MacfbNubusDeviceClass *ndc = MACFB_NUBUS_GET_CLASS(dev); | |
395 | MacfbState *ms = &s->macfb; | |
396 | ||
397 | ndc->parent_realize(dev, errp); | |
398 | ||
399 | macfb_common_realize(dev, ms, errp); | |
400 | memory_region_add_subregion(&nd->slot_mem, DAFB_BASE, &ms->mem_ctrl); | |
401 | memory_region_add_subregion(&nd->slot_mem, VIDEO_BASE, &ms->mem_vram); | |
402 | ||
403 | nubus_register_rom(nd, macfb_rom, sizeof(macfb_rom), 1, 9, 0xf); | |
404 | } | |
405 | ||
406 | static void macfb_sysbus_reset(DeviceState *d) | |
407 | { | |
408 | MacfbSysBusState *s = MACFB(d); | |
409 | macfb_reset(&s->macfb); | |
410 | } | |
411 | ||
412 | static void macfb_nubus_reset(DeviceState *d) | |
413 | { | |
414 | MacfbNubusState *s = NUBUS_MACFB(d); | |
415 | macfb_reset(&s->macfb); | |
416 | } | |
417 | ||
418 | static Property macfb_sysbus_properties[] = { | |
419 | DEFINE_PROP_UINT32("width", MacfbSysBusState, macfb.width, 640), | |
420 | DEFINE_PROP_UINT32("height", MacfbSysBusState, macfb.height, 480), | |
421 | DEFINE_PROP_UINT8("depth", MacfbSysBusState, macfb.depth, 8), | |
422 | DEFINE_PROP_END_OF_LIST(), | |
423 | }; | |
424 | ||
425 | static Property macfb_nubus_properties[] = { | |
426 | DEFINE_PROP_UINT32("width", MacfbNubusState, macfb.width, 640), | |
427 | DEFINE_PROP_UINT32("height", MacfbNubusState, macfb.height, 480), | |
428 | DEFINE_PROP_UINT8("depth", MacfbNubusState, macfb.depth, 8), | |
429 | DEFINE_PROP_END_OF_LIST(), | |
430 | }; | |
431 | ||
432 | static void macfb_sysbus_class_init(ObjectClass *klass, void *data) | |
433 | { | |
434 | DeviceClass *dc = DEVICE_CLASS(klass); | |
435 | ||
436 | dc->realize = macfb_sysbus_realize; | |
437 | dc->desc = "SysBus Macintosh framebuffer"; | |
438 | dc->reset = macfb_sysbus_reset; | |
439 | dc->vmsd = &vmstate_macfb; | |
440 | dc->props = macfb_sysbus_properties; | |
441 | } | |
442 | ||
443 | static void macfb_nubus_class_init(ObjectClass *klass, void *data) | |
444 | { | |
445 | DeviceClass *dc = DEVICE_CLASS(klass); | |
446 | MacfbNubusDeviceClass *ndc = MACFB_NUBUS_DEVICE_CLASS(klass); | |
447 | ||
448 | device_class_set_parent_realize(dc, macfb_nubus_realize, | |
449 | &ndc->parent_realize); | |
450 | dc->desc = "Nubus Macintosh framebuffer"; | |
451 | dc->reset = macfb_nubus_reset; | |
452 | dc->vmsd = &vmstate_macfb; | |
453 | dc->props = macfb_nubus_properties; | |
454 | } | |
455 | ||
456 | static TypeInfo macfb_sysbus_info = { | |
457 | .name = TYPE_MACFB, | |
458 | .parent = TYPE_SYS_BUS_DEVICE, | |
459 | .instance_size = sizeof(MacfbSysBusState), | |
460 | .class_init = macfb_sysbus_class_init, | |
461 | }; | |
462 | ||
463 | static TypeInfo macfb_nubus_info = { | |
464 | .name = TYPE_NUBUS_MACFB, | |
465 | .parent = TYPE_NUBUS_DEVICE, | |
466 | .instance_size = sizeof(MacfbNubusState), | |
467 | .class_init = macfb_nubus_class_init, | |
468 | .class_size = sizeof(MacfbNubusDeviceClass), | |
469 | }; | |
470 | ||
471 | static void macfb_register_types(void) | |
472 | { | |
473 | type_register_static(&macfb_sysbus_info); | |
474 | type_register_static(&macfb_nubus_info); | |
475 | } | |
476 | ||
477 | type_init(macfb_register_types) |