2 * QEMU Motorola 680x0 Macintosh Video Card Emulation
3 * Copyright (c) 2012-2018 Laurent Vivier
5 * some parts from QEMU G364 framebuffer Emulator.
6 * Copyright (c) 2007-2011 Herve Poussineau
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.
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"
24 #define VIDEO_BASE 0x00001000
25 #define DAFB_BASE 0x00800000
27 #define MACFB_PAGE_SIZE 4096
28 #define MACFB_VRAM_SIZE (4 * MiB)
30 #define DAFB_RESET 0x200
31 #define DAFB_LUT 0x213
34 typedef void macfb_draw_line_func(MacfbState
*s
, uint8_t *d
, uint32_t addr
,
37 static inline uint8_t macfb_read_byte(MacfbState
*s
, uint32_t addr
)
39 return s
->vram
[addr
& s
->vram_bit_mask
];
43 static void macfb_draw_line1(MacfbState
*s
, uint8_t *d
, uint32_t addr
,
49 for (x
= 0; x
< width
; x
++) {
51 int idx
= (macfb_read_byte(s
, addr
) >> (7 - bit
)) & 1;
52 r
= g
= b
= ((1 - idx
) << 7);
55 *(uint32_t *)d
= rgb_to_pixel32(r
, g
, b
);
61 static void macfb_draw_line2(MacfbState
*s
, uint8_t *d
, uint32_t addr
,
67 for (x
= 0; x
< width
; x
++) {
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];
75 *(uint32_t *)d
= rgb_to_pixel32(r
, g
, b
);
81 static void macfb_draw_line4(MacfbState
*s
, uint8_t *d
, uint32_t addr
,
87 for (x
= 0; x
< width
; x
++) {
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];
95 *(uint32_t *)d
= rgb_to_pixel32(r
, g
, b
);
101 static void macfb_draw_line8(MacfbState
*s
, uint8_t *d
, uint32_t addr
,
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];
113 *(uint32_t *)d
= rgb_to_pixel32(r
, g
, b
);
119 static void macfb_draw_line16(MacfbState
*s
, uint8_t *d
, uint32_t addr
,
125 for (x
= 0; x
< width
; x
++) {
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;
133 *(uint32_t *)d
= rgb_to_pixel32(r
, g
, b
);
139 static void macfb_draw_line24(MacfbState
*s
, uint8_t *d
, uint32_t addr
,
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);
151 *(uint32_t *)d
= rgb_to_pixel32(r
, g
, b
);
167 static macfb_draw_line_func
* const
168 macfb_draw_line_table
[MACFB_DRAW_LINE_NB
] = {
177 static int macfb_check_dirty(MacfbState
*s
, DirtyBitmapSnapshot
*snap
,
178 ram_addr_t addr
, int len
)
180 return memory_region_snapshot_get_dirty(&s
->mem_vram
, snap
, addr
, len
);
183 static void macfb_draw_graphic(MacfbState
*s
)
185 DisplaySurface
*surface
= qemu_console_surface(s
->con
);
186 DirtyBitmapSnapshot
*snap
= NULL
;
190 int macfb_stride
= (s
->depth
* s
->width
+ 7) / 8;
191 macfb_draw_line_func
*macfb_draw_line
;
195 v
= MACFB_DRAW_LINE1
;
198 v
= MACFB_DRAW_LINE2
;
201 v
= MACFB_DRAW_LINE4
;
204 v
= MACFB_DRAW_LINE8
;
207 v
= MACFB_DRAW_LINE16
;
210 v
= MACFB_DRAW_LINE24
;
214 macfb_draw_line
= macfb_draw_line_table
[v
];
215 assert(macfb_draw_line
!= NULL
);
217 snap
= memory_region_snapshot_and_clear_dirty(&s
->mem_vram
, 0x0,
218 memory_region_size(&s
->mem_vram
),
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
;
227 data_display
= surface_data(surface
) + y
* surface_stride(surface
);
228 macfb_draw_line(s
, data_display
, page
, s
->width
);
235 dpy_gfx_update(s
->con
, 0, ymin
, s
->width
, y
- ymin
);
242 dpy_gfx_update(s
->con
, 0, ymin
, s
->width
, y
- ymin
);
248 static void macfb_invalidate_display(void *opaque
)
250 MacfbState
*s
= opaque
;
252 memory_region_set_dirty(&s
->mem_vram
, 0, MACFB_VRAM_SIZE
);
255 static void macfb_update_display(void *opaque
)
257 MacfbState
*s
= opaque
;
258 DisplaySurface
*surface
= qemu_console_surface(s
->con
);
260 qemu_flush_coalesced_mmio_buffer();
262 if (s
->width
== 0 || s
->height
== 0) {
266 if (s
->width
!= surface_width(surface
) ||
267 s
->height
!= surface_height(surface
)) {
268 qemu_console_resize(s
->con
, s
->width
, s
->height
);
271 macfb_draw_graphic(s
);
274 static void macfb_reset(MacfbState
*s
)
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
;
284 memset(s
->vram
, 0, MACFB_VRAM_SIZE
);
285 macfb_invalidate_display(s
);
288 static uint64_t macfb_ctrl_read(void *opaque
,
295 static void macfb_ctrl_write(void *opaque
,
300 MacfbState
*s
= opaque
;
303 s
->palette_current
= 0;
306 s
->color_palette
[s
->palette_current
++] = val
;
307 if (s
->palette_current
% 3) {
308 macfb_invalidate_display(s
);
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,
322 static int macfb_post_load(void *opaque
, int version_id
)
324 macfb_invalidate_display(opaque
);
328 static const VMStateDescription vmstate_macfb
= {
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()
341 static const GraphicHwOps macfb_ops
= {
342 .invalidate
= macfb_invalidate_display
,
343 .gfx_update
= macfb_update_display
,
346 static void macfb_common_realize(DeviceState
*dev
, MacfbState
*s
, Error
**errp
)
348 DisplaySurface
*surface
;
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
);
356 s
->con
= graphic_console_init(dev
, 0, &macfb_ops
, s
);
357 surface
= qemu_console_surface(s
->con
);
359 if (surface_bits_per_pixel(surface
) != 32) {
360 error_setg(errp
, "unknown host depth %d",
361 surface_bits_per_pixel(surface
));
365 memory_region_init_io(&s
->mem_ctrl
, OBJECT(dev
), &macfb_ctrl_ops
, s
,
366 "macfb-ctrl", 0x1000);
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
);
376 static void macfb_sysbus_realize(DeviceState
*dev
, Error
**errp
)
378 MacfbSysBusState
*s
= MACFB(dev
);
379 MacfbState
*ms
= &s
->macfb
;
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
);
386 const uint8_t macfb_rom
[] = {
390 static void macfb_nubus_realize(DeviceState
*dev
, Error
**errp
)
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
;
397 ndc
->parent_realize(dev
, errp
);
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
);
403 nubus_register_rom(nd
, macfb_rom
, sizeof(macfb_rom
), 1, 9, 0xf);
406 static void macfb_sysbus_reset(DeviceState
*d
)
408 MacfbSysBusState
*s
= MACFB(d
);
409 macfb_reset(&s
->macfb
);
412 static void macfb_nubus_reset(DeviceState
*d
)
414 MacfbNubusState
*s
= NUBUS_MACFB(d
);
415 macfb_reset(&s
->macfb
);
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(),
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(),
432 static void macfb_sysbus_class_init(ObjectClass
*klass
, void *data
)
434 DeviceClass
*dc
= DEVICE_CLASS(klass
);
436 dc
->realize
= macfb_sysbus_realize
;
437 dc
->desc
= "SysBus Macintosh framebuffer";
438 dc
->reset
= macfb_sysbus_reset
;
439 dc
->vmsd
= &vmstate_macfb
;
440 device_class_set_props(dc
, macfb_sysbus_properties
);
443 static void macfb_nubus_class_init(ObjectClass
*klass
, void *data
)
445 DeviceClass
*dc
= DEVICE_CLASS(klass
);
446 MacfbNubusDeviceClass
*ndc
= MACFB_NUBUS_DEVICE_CLASS(klass
);
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 device_class_set_props(dc
, macfb_nubus_properties
);
456 static TypeInfo macfb_sysbus_info
= {
458 .parent
= TYPE_SYS_BUS_DEVICE
,
459 .instance_size
= sizeof(MacfbSysBusState
),
460 .class_init
= macfb_sysbus_class_init
,
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
),
471 static void macfb_register_types(void)
473 type_register_static(&macfb_sysbus_info
);
474 type_register_static(&macfb_nubus_info
);
477 type_init(macfb_register_types
)