blob: 896407d8650964540cc1716f8eaf29c00596e198 [file] [log] [blame]
bellard420557e2004-09-30 22:13:50 +00001/*
bellard6f7e9ae2005-03-13 09:43:36 +00002 * QEMU TCX Frame buffer
ths5fafdf22007-09-16 21:08:06 +00003 *
bellard6f7e9ae2005-03-13 09:43:36 +00004 * Copyright (c) 2003-2005 Fabrice Bellard
ths5fafdf22007-09-16 21:08:06 +00005 *
bellard420557e2004-09-30 22:13:50 +00006 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
Blue Swirlf40070c2009-07-12 19:21:36 +000024
Paolo Bonzini077805f2012-09-25 10:04:17 +020025#include "qemu-common.h"
Paolo Bonzini28ecbae2012-11-28 12:06:30 +010026#include "ui/console.h"
27#include "ui/pixel_ops.h"
Paolo Bonzini83c9f4c2013-02-04 15:40:22 +010028#include "hw/sysbus.h"
29#include "hw/qdev-addr.h"
bellard420557e2004-09-30 22:13:50 +000030
bellard420557e2004-09-30 22:13:50 +000031#define MAXX 1024
32#define MAXY 768
bellard6f7e9ae2005-03-13 09:43:36 +000033#define TCX_DAC_NREGS 16
blueswir18508b892007-05-06 17:39:55 +000034#define TCX_THC_NREGS_8 0x081c
35#define TCX_THC_NREGS_24 0x1000
36#define TCX_TEC_NREGS 0x1000
bellard420557e2004-09-30 22:13:50 +000037
bellard420557e2004-09-30 22:13:50 +000038typedef struct TCXState {
Blue Swirlf40070c2009-07-12 19:21:36 +000039 SysBusDevice busdev;
Avi Kivitya8170e52012-10-23 12:30:10 +020040 hwaddr addr;
bellard420557e2004-09-30 22:13:50 +000041 DisplayState *ds;
bellard8d5f07f2004-10-04 21:23:09 +000042 uint8_t *vram;
blueswir1eee0b832007-04-21 19:45:49 +000043 uint32_t *vram24, *cplane;
Avi Kivityd08151b2011-10-05 18:26:24 +020044 MemoryRegion vram_mem;
45 MemoryRegion vram_8bit;
46 MemoryRegion vram_24bit;
47 MemoryRegion vram_cplane;
48 MemoryRegion dac;
49 MemoryRegion tec;
50 MemoryRegion thc24;
51 MemoryRegion thc8;
52 ram_addr_t vram24_offset, cplane_offset;
Gerd Hoffmannee6847d2009-07-15 13:43:31 +020053 uint32_t vram_size;
bellard21206a12006-09-09 11:31:34 +000054 uint32_t palette[256];
Blue Swirl427a66c2011-08-07 19:13:24 +000055 uint8_t r[256], g[256], b[256];
56 uint16_t width, height, depth;
bellard6f7e9ae2005-03-13 09:43:36 +000057 uint8_t dac_index, dac_state;
bellard420557e2004-09-30 22:13:50 +000058} TCXState;
59
Luiz Capitulinod7098132012-05-21 16:41:37 -030060static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch,
61 Error **errp);
62static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch,
63 Error **errp);
Blue Swirld3ffcaf2009-07-16 13:45:57 +000064
65static void tcx_set_dirty(TCXState *s)
66{
Blue Swirlfd4aa972011-10-16 16:04:59 +000067 memory_region_set_dirty(&s->vram_mem, 0, MAXX * MAXY);
Blue Swirld3ffcaf2009-07-16 13:45:57 +000068}
69
70static void tcx24_set_dirty(TCXState *s)
71{
Blue Swirlfd4aa972011-10-16 16:04:59 +000072 memory_region_set_dirty(&s->vram_mem, s->vram24_offset, MAXX * MAXY * 4);
73 memory_region_set_dirty(&s->vram_mem, s->cplane_offset, MAXX * MAXY * 4);
Blue Swirld3ffcaf2009-07-16 13:45:57 +000074}
pbrook95219892006-04-09 01:06:34 +000075
bellard21206a12006-09-09 11:31:34 +000076static void update_palette_entries(TCXState *s, int start, int end)
77{
78 int i;
79 for(i = start; i < end; i++) {
aliguori0e1f5a02008-11-24 19:29:13 +000080 switch(ds_get_bits_per_pixel(s->ds)) {
bellard21206a12006-09-09 11:31:34 +000081 default:
82 case 8:
83 s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]);
84 break;
85 case 15:
aliguori8927bcf2009-01-15 22:07:16 +000086 s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]);
bellard21206a12006-09-09 11:31:34 +000087 break;
88 case 16:
aliguori8927bcf2009-01-15 22:07:16 +000089 s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]);
bellard21206a12006-09-09 11:31:34 +000090 break;
91 case 32:
aliguori7b5d76d2009-03-13 15:02:13 +000092 if (is_surface_bgr(s->ds->surface))
93 s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]);
94 else
95 s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
bellard21206a12006-09-09 11:31:34 +000096 break;
97 }
98 }
Blue Swirld3ffcaf2009-07-16 13:45:57 +000099 if (s->depth == 24) {
100 tcx24_set_dirty(s);
101 } else {
102 tcx_set_dirty(s);
103 }
bellard21206a12006-09-09 11:31:34 +0000104}
105
ths5fafdf22007-09-16 21:08:06 +0000106static void tcx_draw_line32(TCXState *s1, uint8_t *d,
blueswir1f930d072007-10-06 11:28:21 +0000107 const uint8_t *s, int width)
bellard420557e2004-09-30 22:13:50 +0000108{
bellarde80cfcf2004-12-19 23:18:01 +0000109 int x;
110 uint8_t val;
ths8bdc2152006-12-21 17:24:45 +0000111 uint32_t *p = (uint32_t *)d;
bellarde80cfcf2004-12-19 23:18:01 +0000112
113 for(x = 0; x < width; x++) {
blueswir1f930d072007-10-06 11:28:21 +0000114 val = *s++;
ths8bdc2152006-12-21 17:24:45 +0000115 *p++ = s1->palette[val];
bellarde80cfcf2004-12-19 23:18:01 +0000116 }
bellard420557e2004-09-30 22:13:50 +0000117}
118
ths5fafdf22007-09-16 21:08:06 +0000119static void tcx_draw_line16(TCXState *s1, uint8_t *d,
blueswir1f930d072007-10-06 11:28:21 +0000120 const uint8_t *s, int width)
bellarde80cfcf2004-12-19 23:18:01 +0000121{
122 int x;
123 uint8_t val;
ths8bdc2152006-12-21 17:24:45 +0000124 uint16_t *p = (uint16_t *)d;
bellard8d5f07f2004-10-04 21:23:09 +0000125
bellarde80cfcf2004-12-19 23:18:01 +0000126 for(x = 0; x < width; x++) {
blueswir1f930d072007-10-06 11:28:21 +0000127 val = *s++;
ths8bdc2152006-12-21 17:24:45 +0000128 *p++ = s1->palette[val];
bellarde80cfcf2004-12-19 23:18:01 +0000129 }
130}
131
ths5fafdf22007-09-16 21:08:06 +0000132static void tcx_draw_line8(TCXState *s1, uint8_t *d,
blueswir1f930d072007-10-06 11:28:21 +0000133 const uint8_t *s, int width)
bellarde80cfcf2004-12-19 23:18:01 +0000134{
135 int x;
136 uint8_t val;
137
138 for(x = 0; x < width; x++) {
blueswir1f930d072007-10-06 11:28:21 +0000139 val = *s++;
bellard21206a12006-09-09 11:31:34 +0000140 *d++ = s1->palette[val];
bellarde80cfcf2004-12-19 23:18:01 +0000141 }
142}
143
blueswir1688ea2e2008-07-24 11:26:38 +0000144/*
145 XXX Could be much more optimal:
146 * detect if line/page/whole screen is in 24 bit mode
147 * if destination is also BGR, use memcpy
148 */
blueswir1eee0b832007-04-21 19:45:49 +0000149static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
150 const uint8_t *s, int width,
151 const uint32_t *cplane,
152 const uint32_t *s24)
153{
aliguori7b5d76d2009-03-13 15:02:13 +0000154 int x, bgr, r, g, b;
blueswir1688ea2e2008-07-24 11:26:38 +0000155 uint8_t val, *p8;
blueswir1eee0b832007-04-21 19:45:49 +0000156 uint32_t *p = (uint32_t *)d;
157 uint32_t dval;
158
aliguori7b5d76d2009-03-13 15:02:13 +0000159 bgr = is_surface_bgr(s1->ds->surface);
blueswir1eee0b832007-04-21 19:45:49 +0000160 for(x = 0; x < width; x++, s++, s24++) {
blueswir1688ea2e2008-07-24 11:26:38 +0000161 if ((be32_to_cpu(*cplane++) & 0xff000000) == 0x03000000) {
162 // 24-bit direct, BGR order
163 p8 = (uint8_t *)s24;
164 p8++;
165 b = *p8++;
166 g = *p8++;
Blue Swirlf7e683b2010-01-13 18:58:51 +0000167 r = *p8;
aliguori7b5d76d2009-03-13 15:02:13 +0000168 if (bgr)
169 dval = rgb_to_pixel32bgr(r, g, b);
170 else
171 dval = rgb_to_pixel32(r, g, b);
blueswir1eee0b832007-04-21 19:45:49 +0000172 } else {
173 val = *s;
174 dval = s1->palette[val];
175 }
176 *p++ = dval;
177 }
178}
179
Avi Kivityd08151b2011-10-05 18:26:24 +0200180static inline int check_dirty(TCXState *s, ram_addr_t page, ram_addr_t page24,
Anthony Liguoric227f092009-10-01 16:12:16 -0500181 ram_addr_t cpage)
blueswir1eee0b832007-04-21 19:45:49 +0000182{
183 int ret;
blueswir1eee0b832007-04-21 19:45:49 +0000184
Blue Swirlcd7a45c2012-01-22 16:38:21 +0000185 ret = memory_region_get_dirty(&s->vram_mem, page, TARGET_PAGE_SIZE,
186 DIRTY_MEMORY_VGA);
187 ret |= memory_region_get_dirty(&s->vram_mem, page24, TARGET_PAGE_SIZE * 4,
188 DIRTY_MEMORY_VGA);
189 ret |= memory_region_get_dirty(&s->vram_mem, cpage, TARGET_PAGE_SIZE * 4,
190 DIRTY_MEMORY_VGA);
blueswir1eee0b832007-04-21 19:45:49 +0000191 return ret;
192}
193
Anthony Liguoric227f092009-10-01 16:12:16 -0500194static inline void reset_dirty(TCXState *ts, ram_addr_t page_min,
195 ram_addr_t page_max, ram_addr_t page24,
196 ram_addr_t cpage)
blueswir1eee0b832007-04-21 19:45:49 +0000197{
Avi Kivityd08151b2011-10-05 18:26:24 +0200198 memory_region_reset_dirty(&ts->vram_mem,
199 page_min, page_max + TARGET_PAGE_SIZE,
200 DIRTY_MEMORY_VGA);
201 memory_region_reset_dirty(&ts->vram_mem,
202 page24 + page_min * 4,
203 page24 + page_max * 4 + TARGET_PAGE_SIZE,
204 DIRTY_MEMORY_VGA);
205 memory_region_reset_dirty(&ts->vram_mem,
206 cpage + page_min * 4,
207 cpage + page_max * 4 + TARGET_PAGE_SIZE,
208 DIRTY_MEMORY_VGA);
blueswir1eee0b832007-04-21 19:45:49 +0000209}
210
bellarde80cfcf2004-12-19 23:18:01 +0000211/* Fixed line length 1024 allows us to do nice tricks not possible on
212 VGA... */
pbrook95219892006-04-09 01:06:34 +0000213static void tcx_update_display(void *opaque)
bellarde80cfcf2004-12-19 23:18:01 +0000214{
215 TCXState *ts = opaque;
Anthony Liguoric227f092009-10-01 16:12:16 -0500216 ram_addr_t page, page_min, page_max;
bellard550be122006-08-02 22:19:33 +0000217 int y, y_start, dd, ds;
bellarde80cfcf2004-12-19 23:18:01 +0000218 uint8_t *d, *s;
blueswir1b3ceef22007-06-25 19:56:13 +0000219 void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width);
bellarde80cfcf2004-12-19 23:18:01 +0000220
aliguori0e1f5a02008-11-24 19:29:13 +0000221 if (ds_get_bits_per_pixel(ts->ds) == 0)
blueswir1f930d072007-10-06 11:28:21 +0000222 return;
Avi Kivityd08151b2011-10-05 18:26:24 +0200223 page = 0;
bellarde80cfcf2004-12-19 23:18:01 +0000224 y_start = -1;
Blue Swirlc0c440f2009-04-27 18:10:37 +0000225 page_min = -1;
bellard550be122006-08-02 22:19:33 +0000226 page_max = 0;
aliguori0e1f5a02008-11-24 19:29:13 +0000227 d = ds_get_data(ts->ds);
bellard6f7e9ae2005-03-13 09:43:36 +0000228 s = ts->vram;
aliguori0e1f5a02008-11-24 19:29:13 +0000229 dd = ds_get_linesize(ts->ds);
bellarde80cfcf2004-12-19 23:18:01 +0000230 ds = 1024;
231
aliguori0e1f5a02008-11-24 19:29:13 +0000232 switch (ds_get_bits_per_pixel(ts->ds)) {
bellarde80cfcf2004-12-19 23:18:01 +0000233 case 32:
blueswir1f930d072007-10-06 11:28:21 +0000234 f = tcx_draw_line32;
235 break;
bellard21206a12006-09-09 11:31:34 +0000236 case 15:
237 case 16:
blueswir1f930d072007-10-06 11:28:21 +0000238 f = tcx_draw_line16;
239 break;
bellarde80cfcf2004-12-19 23:18:01 +0000240 default:
241 case 8:
blueswir1f930d072007-10-06 11:28:21 +0000242 f = tcx_draw_line8;
243 break;
bellarde80cfcf2004-12-19 23:18:01 +0000244 case 0:
blueswir1f930d072007-10-06 11:28:21 +0000245 return;
bellarde80cfcf2004-12-19 23:18:01 +0000246 }
ths3b46e622007-09-17 08:09:54 +0000247
bellard6f7e9ae2005-03-13 09:43:36 +0000248 for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE) {
Blue Swirlcd7a45c2012-01-22 16:38:21 +0000249 if (memory_region_get_dirty(&ts->vram_mem, page, TARGET_PAGE_SIZE,
250 DIRTY_MEMORY_VGA)) {
blueswir1f930d072007-10-06 11:28:21 +0000251 if (y_start < 0)
bellarde80cfcf2004-12-19 23:18:01 +0000252 y_start = y;
253 if (page < page_min)
254 page_min = page;
255 if (page > page_max)
256 page_max = page;
blueswir1f930d072007-10-06 11:28:21 +0000257 f(ts, d, s, ts->width);
258 d += dd;
259 s += ds;
260 f(ts, d, s, ts->width);
261 d += dd;
262 s += ds;
263 f(ts, d, s, ts->width);
264 d += dd;
265 s += ds;
266 f(ts, d, s, ts->width);
267 d += dd;
268 s += ds;
269 } else {
bellarde80cfcf2004-12-19 23:18:01 +0000270 if (y_start >= 0) {
271 /* flush to display */
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +0200272 dpy_gfx_update(ts->ds, 0, y_start,
273 ts->width, y - y_start);
bellarde80cfcf2004-12-19 23:18:01 +0000274 y_start = -1;
275 }
blueswir1f930d072007-10-06 11:28:21 +0000276 d += dd * 4;
277 s += ds * 4;
278 }
bellarde80cfcf2004-12-19 23:18:01 +0000279 }
280 if (y_start >= 0) {
blueswir1f930d072007-10-06 11:28:21 +0000281 /* flush to display */
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +0200282 dpy_gfx_update(ts->ds, 0, y_start,
283 ts->width, y - y_start);
bellarde80cfcf2004-12-19 23:18:01 +0000284 }
285 /* reset modified pages */
Blue Swirlc0c440f2009-04-27 18:10:37 +0000286 if (page_max >= page_min) {
Avi Kivityd08151b2011-10-05 18:26:24 +0200287 memory_region_reset_dirty(&ts->vram_mem,
288 page_min, page_max + TARGET_PAGE_SIZE,
289 DIRTY_MEMORY_VGA);
bellarde80cfcf2004-12-19 23:18:01 +0000290 }
291}
292
blueswir1eee0b832007-04-21 19:45:49 +0000293static void tcx24_update_display(void *opaque)
294{
295 TCXState *ts = opaque;
Anthony Liguoric227f092009-10-01 16:12:16 -0500296 ram_addr_t page, page_min, page_max, cpage, page24;
blueswir1eee0b832007-04-21 19:45:49 +0000297 int y, y_start, dd, ds;
298 uint8_t *d, *s;
299 uint32_t *cptr, *s24;
300
aliguori0e1f5a02008-11-24 19:29:13 +0000301 if (ds_get_bits_per_pixel(ts->ds) != 32)
blueswir1eee0b832007-04-21 19:45:49 +0000302 return;
Avi Kivityd08151b2011-10-05 18:26:24 +0200303 page = 0;
blueswir1eee0b832007-04-21 19:45:49 +0000304 page24 = ts->vram24_offset;
305 cpage = ts->cplane_offset;
306 y_start = -1;
Blue Swirlc0c440f2009-04-27 18:10:37 +0000307 page_min = -1;
blueswir1eee0b832007-04-21 19:45:49 +0000308 page_max = 0;
aliguori0e1f5a02008-11-24 19:29:13 +0000309 d = ds_get_data(ts->ds);
blueswir1eee0b832007-04-21 19:45:49 +0000310 s = ts->vram;
311 s24 = ts->vram24;
312 cptr = ts->cplane;
aliguori0e1f5a02008-11-24 19:29:13 +0000313 dd = ds_get_linesize(ts->ds);
blueswir1eee0b832007-04-21 19:45:49 +0000314 ds = 1024;
315
316 for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE,
317 page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) {
Avi Kivityd08151b2011-10-05 18:26:24 +0200318 if (check_dirty(ts, page, page24, cpage)) {
blueswir1eee0b832007-04-21 19:45:49 +0000319 if (y_start < 0)
320 y_start = y;
321 if (page < page_min)
322 page_min = page;
323 if (page > page_max)
324 page_max = page;
325 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
326 d += dd;
327 s += ds;
328 cptr += ds;
329 s24 += ds;
330 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
331 d += dd;
332 s += ds;
333 cptr += ds;
334 s24 += ds;
335 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
336 d += dd;
337 s += ds;
338 cptr += ds;
339 s24 += ds;
340 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
341 d += dd;
342 s += ds;
343 cptr += ds;
344 s24 += ds;
345 } else {
346 if (y_start >= 0) {
347 /* flush to display */
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +0200348 dpy_gfx_update(ts->ds, 0, y_start,
349 ts->width, y - y_start);
blueswir1eee0b832007-04-21 19:45:49 +0000350 y_start = -1;
351 }
352 d += dd * 4;
353 s += ds * 4;
354 cptr += ds * 4;
355 s24 += ds * 4;
356 }
357 }
358 if (y_start >= 0) {
359 /* flush to display */
Gerd Hoffmanna93a4a22012-09-28 15:02:08 +0200360 dpy_gfx_update(ts->ds, 0, y_start,
361 ts->width, y - y_start);
blueswir1eee0b832007-04-21 19:45:49 +0000362 }
363 /* reset modified pages */
Blue Swirlc0c440f2009-04-27 18:10:37 +0000364 if (page_max >= page_min) {
blueswir1eee0b832007-04-21 19:45:49 +0000365 reset_dirty(ts, page_min, page_max, page24, cpage);
366 }
367}
368
pbrook95219892006-04-09 01:06:34 +0000369static void tcx_invalidate_display(void *opaque)
bellard420557e2004-09-30 22:13:50 +0000370{
371 TCXState *s = opaque;
bellard420557e2004-09-30 22:13:50 +0000372
Blue Swirld3ffcaf2009-07-16 13:45:57 +0000373 tcx_set_dirty(s);
374 qemu_console_resize(s->ds, s->width, s->height);
bellarde80cfcf2004-12-19 23:18:01 +0000375}
376
blueswir1eee0b832007-04-21 19:45:49 +0000377static void tcx24_invalidate_display(void *opaque)
378{
379 TCXState *s = opaque;
blueswir1eee0b832007-04-21 19:45:49 +0000380
Blue Swirld3ffcaf2009-07-16 13:45:57 +0000381 tcx_set_dirty(s);
382 tcx24_set_dirty(s);
383 qemu_console_resize(s->ds, s->width, s->height);
blueswir1eee0b832007-04-21 19:45:49 +0000384}
385
Juan Quintelae59fb372009-09-29 22:48:21 +0200386static int vmstate_tcx_post_load(void *opaque, int version_id)
bellarde80cfcf2004-12-19 23:18:01 +0000387{
388 TCXState *s = opaque;
ths3b46e622007-09-17 08:09:54 +0000389
bellard21206a12006-09-09 11:31:34 +0000390 update_palette_entries(s, 0, 256);
Blue Swirld3ffcaf2009-07-16 13:45:57 +0000391 if (s->depth == 24) {
392 tcx24_set_dirty(s);
393 } else {
394 tcx_set_dirty(s);
395 }
blueswir15425a212007-04-13 19:24:07 +0000396
bellard420557e2004-09-30 22:13:50 +0000397 return 0;
398}
399
Blue Swirlc0c41a42009-08-28 20:43:01 +0000400static const VMStateDescription vmstate_tcx = {
401 .name ="tcx",
402 .version_id = 4,
403 .minimum_version_id = 4,
404 .minimum_version_id_old = 4,
Juan Quintela752ff2f2009-09-10 03:04:30 +0200405 .post_load = vmstate_tcx_post_load,
Blue Swirlc0c41a42009-08-28 20:43:01 +0000406 .fields = (VMStateField []) {
407 VMSTATE_UINT16(height, TCXState),
408 VMSTATE_UINT16(width, TCXState),
409 VMSTATE_UINT16(depth, TCXState),
410 VMSTATE_BUFFER(r, TCXState),
411 VMSTATE_BUFFER(g, TCXState),
412 VMSTATE_BUFFER(b, TCXState),
413 VMSTATE_UINT8(dac_index, TCXState),
414 VMSTATE_UINT8(dac_state, TCXState),
415 VMSTATE_END_OF_LIST()
416 }
417};
418
Michael S. Tsirkin7f23f812009-09-16 13:40:27 +0300419static void tcx_reset(DeviceState *d)
bellard420557e2004-09-30 22:13:50 +0000420{
Michael S. Tsirkin7f23f812009-09-16 13:40:27 +0300421 TCXState *s = container_of(d, TCXState, busdev.qdev);
bellard420557e2004-09-30 22:13:50 +0000422
bellarde80cfcf2004-12-19 23:18:01 +0000423 /* Initialize palette */
424 memset(s->r, 0, 256);
425 memset(s->g, 0, 256);
426 memset(s->b, 0, 256);
427 s->r[255] = s->g[255] = s->b[255] = 255;
bellard21206a12006-09-09 11:31:34 +0000428 update_palette_entries(s, 0, 256);
bellarde80cfcf2004-12-19 23:18:01 +0000429 memset(s->vram, 0, MAXX*MAXY);
Avi Kivityd08151b2011-10-05 18:26:24 +0200430 memory_region_reset_dirty(&s->vram_mem, 0, MAXX * MAXY * (1 + 4 + 4),
431 DIRTY_MEMORY_VGA);
bellard6f7e9ae2005-03-13 09:43:36 +0000432 s->dac_index = 0;
433 s->dac_state = 0;
bellard420557e2004-09-30 22:13:50 +0000434}
435
Avi Kivitya8170e52012-10-23 12:30:10 +0200436static uint64_t tcx_dac_readl(void *opaque, hwaddr addr,
Avi Kivityd08151b2011-10-05 18:26:24 +0200437 unsigned size)
bellard6f7e9ae2005-03-13 09:43:36 +0000438{
439 return 0;
440}
441
Avi Kivitya8170e52012-10-23 12:30:10 +0200442static void tcx_dac_writel(void *opaque, hwaddr addr, uint64_t val,
Avi Kivityd08151b2011-10-05 18:26:24 +0200443 unsigned size)
bellard6f7e9ae2005-03-13 09:43:36 +0000444{
445 TCXState *s = opaque;
bellard6f7e9ae2005-03-13 09:43:36 +0000446
blueswir1e64d7d52008-12-02 17:47:02 +0000447 switch (addr) {
bellard6f7e9ae2005-03-13 09:43:36 +0000448 case 0:
blueswir1f930d072007-10-06 11:28:21 +0000449 s->dac_index = val >> 24;
450 s->dac_state = 0;
451 break;
blueswir1e64d7d52008-12-02 17:47:02 +0000452 case 4:
blueswir1f930d072007-10-06 11:28:21 +0000453 switch (s->dac_state) {
454 case 0:
455 s->r[s->dac_index] = val >> 24;
bellard21206a12006-09-09 11:31:34 +0000456 update_palette_entries(s, s->dac_index, s->dac_index + 1);
blueswir1f930d072007-10-06 11:28:21 +0000457 s->dac_state++;
458 break;
459 case 1:
460 s->g[s->dac_index] = val >> 24;
bellard21206a12006-09-09 11:31:34 +0000461 update_palette_entries(s, s->dac_index, s->dac_index + 1);
blueswir1f930d072007-10-06 11:28:21 +0000462 s->dac_state++;
463 break;
464 case 2:
465 s->b[s->dac_index] = val >> 24;
bellard21206a12006-09-09 11:31:34 +0000466 update_palette_entries(s, s->dac_index, s->dac_index + 1);
blueswir15c8cdbf2007-04-17 19:42:21 +0000467 s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement
blueswir1f930d072007-10-06 11:28:21 +0000468 default:
469 s->dac_state = 0;
470 break;
471 }
472 break;
bellard6f7e9ae2005-03-13 09:43:36 +0000473 default:
blueswir1f930d072007-10-06 11:28:21 +0000474 break;
bellard6f7e9ae2005-03-13 09:43:36 +0000475 }
bellard6f7e9ae2005-03-13 09:43:36 +0000476}
477
Avi Kivityd08151b2011-10-05 18:26:24 +0200478static const MemoryRegionOps tcx_dac_ops = {
479 .read = tcx_dac_readl,
480 .write = tcx_dac_writel,
481 .endianness = DEVICE_NATIVE_ENDIAN,
482 .valid = {
483 .min_access_size = 4,
484 .max_access_size = 4,
485 },
bellard6f7e9ae2005-03-13 09:43:36 +0000486};
487
Avi Kivitya8170e52012-10-23 12:30:10 +0200488static uint64_t dummy_readl(void *opaque, hwaddr addr,
Avi Kivityd08151b2011-10-05 18:26:24 +0200489 unsigned size)
blueswir18508b892007-05-06 17:39:55 +0000490{
491 return 0;
492}
493
Avi Kivitya8170e52012-10-23 12:30:10 +0200494static void dummy_writel(void *opaque, hwaddr addr,
Avi Kivityd08151b2011-10-05 18:26:24 +0200495 uint64_t val, unsigned size)
blueswir18508b892007-05-06 17:39:55 +0000496{
497}
498
Avi Kivityd08151b2011-10-05 18:26:24 +0200499static const MemoryRegionOps dummy_ops = {
500 .read = dummy_readl,
501 .write = dummy_writel,
502 .endianness = DEVICE_NATIVE_ENDIAN,
503 .valid = {
504 .min_access_size = 4,
505 .max_access_size = 4,
506 },
blueswir18508b892007-05-06 17:39:55 +0000507};
508
Gerd Hoffmann81a322d2009-08-14 10:36:05 +0200509static int tcx_init1(SysBusDevice *dev)
Blue Swirlf40070c2009-07-12 19:21:36 +0000510{
511 TCXState *s = FROM_SYSBUS(TCXState, dev);
Avi Kivityd08151b2011-10-05 18:26:24 +0200512 ram_addr_t vram_offset = 0;
Gerd Hoffmannee6847d2009-07-15 13:43:31 +0200513 int size;
pbrookdc828ca2009-04-09 22:21:07 +0000514 uint8_t *vram_base;
515
Avi Kivityc5705a72011-12-20 15:59:12 +0200516 memory_region_init_ram(&s->vram_mem, "tcx.vram",
Avi Kivityd08151b2011-10-05 18:26:24 +0200517 s->vram_size * (1 + 4 + 4));
Avi Kivityc5705a72011-12-20 15:59:12 +0200518 vmstate_register_ram_global(&s->vram_mem);
Avi Kivityd08151b2011-10-05 18:26:24 +0200519 vram_base = memory_region_get_ram_ptr(&s->vram_mem);
bellarde80cfcf2004-12-19 23:18:01 +0000520
Blue Swirlf40070c2009-07-12 19:21:36 +0000521 /* 8-bit plane */
blueswir1eee0b832007-04-21 19:45:49 +0000522 s->vram = vram_base;
Gerd Hoffmannee6847d2009-07-15 13:43:31 +0200523 size = s->vram_size;
Avi Kivityd08151b2011-10-05 18:26:24 +0200524 memory_region_init_alias(&s->vram_8bit, "tcx.vram.8bit",
525 &s->vram_mem, vram_offset, size);
Avi Kivity750ecd42011-11-27 11:38:10 +0200526 sysbus_init_mmio(dev, &s->vram_8bit);
blueswir1eee0b832007-04-21 19:45:49 +0000527 vram_offset += size;
528 vram_base += size;
529
Blue Swirlf40070c2009-07-12 19:21:36 +0000530 /* DAC */
Avi Kivityd08151b2011-10-05 18:26:24 +0200531 memory_region_init_io(&s->dac, &tcx_dac_ops, s, "tcx.dac", TCX_DAC_NREGS);
Avi Kivity750ecd42011-11-27 11:38:10 +0200532 sysbus_init_mmio(dev, &s->dac);
bellarde80cfcf2004-12-19 23:18:01 +0000533
Blue Swirlf40070c2009-07-12 19:21:36 +0000534 /* TEC (dummy) */
Avi Kivityd08151b2011-10-05 18:26:24 +0200535 memory_region_init_io(&s->tec, &dummy_ops, s, "tcx.tec", TCX_TEC_NREGS);
Avi Kivity750ecd42011-11-27 11:38:10 +0200536 sysbus_init_mmio(dev, &s->tec);
Blue Swirlf40070c2009-07-12 19:21:36 +0000537 /* THC: NetBSD writes here even with 8-bit display: dummy */
Avi Kivityd08151b2011-10-05 18:26:24 +0200538 memory_region_init_io(&s->thc24, &dummy_ops, s, "tcx.thc24",
539 TCX_THC_NREGS_24);
Avi Kivity750ecd42011-11-27 11:38:10 +0200540 sysbus_init_mmio(dev, &s->thc24);
Blue Swirlf40070c2009-07-12 19:21:36 +0000541
542 if (s->depth == 24) {
543 /* 24-bit plane */
Gerd Hoffmannee6847d2009-07-15 13:43:31 +0200544 size = s->vram_size * 4;
blueswir1eee0b832007-04-21 19:45:49 +0000545 s->vram24 = (uint32_t *)vram_base;
546 s->vram24_offset = vram_offset;
Avi Kivityd08151b2011-10-05 18:26:24 +0200547 memory_region_init_alias(&s->vram_24bit, "tcx.vram.24bit",
548 &s->vram_mem, vram_offset, size);
Avi Kivity750ecd42011-11-27 11:38:10 +0200549 sysbus_init_mmio(dev, &s->vram_24bit);
blueswir1eee0b832007-04-21 19:45:49 +0000550 vram_offset += size;
551 vram_base += size;
552
Blue Swirlf40070c2009-07-12 19:21:36 +0000553 /* Control plane */
Gerd Hoffmannee6847d2009-07-15 13:43:31 +0200554 size = s->vram_size * 4;
blueswir1eee0b832007-04-21 19:45:49 +0000555 s->cplane = (uint32_t *)vram_base;
556 s->cplane_offset = vram_offset;
Avi Kivityd08151b2011-10-05 18:26:24 +0200557 memory_region_init_alias(&s->vram_cplane, "tcx.vram.cplane",
558 &s->vram_mem, vram_offset, size);
Avi Kivity750ecd42011-11-27 11:38:10 +0200559 sysbus_init_mmio(dev, &s->vram_cplane);
Blue Swirlf40070c2009-07-12 19:21:36 +0000560
aliguori3023f3322009-01-16 19:04:14 +0000561 s->ds = graphic_console_init(tcx24_update_display,
562 tcx24_invalidate_display,
563 tcx24_screen_dump, NULL, s);
blueswir1eee0b832007-04-21 19:45:49 +0000564 } else {
Blue Swirlf40070c2009-07-12 19:21:36 +0000565 /* THC 8 bit (dummy) */
Avi Kivityd08151b2011-10-05 18:26:24 +0200566 memory_region_init_io(&s->thc8, &dummy_ops, s, "tcx.thc8",
567 TCX_THC_NREGS_8);
Avi Kivity750ecd42011-11-27 11:38:10 +0200568 sysbus_init_mmio(dev, &s->thc8);
Blue Swirlf40070c2009-07-12 19:21:36 +0000569
aliguori3023f3322009-01-16 19:04:14 +0000570 s->ds = graphic_console_init(tcx_update_display,
571 tcx_invalidate_display,
572 tcx_screen_dump, NULL, s);
blueswir1eee0b832007-04-21 19:45:49 +0000573 }
574
Blue Swirlf40070c2009-07-12 19:21:36 +0000575 qemu_console_resize(s->ds, s->width, s->height);
Gerd Hoffmann81a322d2009-08-14 10:36:05 +0200576 return 0;
bellard420557e2004-09-30 22:13:50 +0000577}
578
Luiz Capitulinod7098132012-05-21 16:41:37 -0300579static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch,
580 Error **errp)
bellard8d5f07f2004-10-04 21:23:09 +0000581{
bellarde80cfcf2004-12-19 23:18:01 +0000582 TCXState *s = opaque;
bellard8d5f07f2004-10-04 21:23:09 +0000583 FILE *f;
bellarde80cfcf2004-12-19 23:18:01 +0000584 uint8_t *d, *d1, v;
Luiz Capitulino0ab6b632012-05-24 11:33:25 -0300585 int ret, y, x;
bellard8d5f07f2004-10-04 21:23:09 +0000586
587 f = fopen(filename, "wb");
Luiz Capitulino0ab6b632012-05-24 11:33:25 -0300588 if (!f) {
589 error_setg(errp, "failed to open file '%s': %s", filename,
590 strerror(errno));
bellarde80cfcf2004-12-19 23:18:01 +0000591 return;
Luiz Capitulino0ab6b632012-05-24 11:33:25 -0300592 }
593 ret = fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
594 if (ret < 0) {
595 goto write_err;
596 }
bellard6f7e9ae2005-03-13 09:43:36 +0000597 d1 = s->vram;
598 for(y = 0; y < s->height; y++) {
bellard8d5f07f2004-10-04 21:23:09 +0000599 d = d1;
bellard6f7e9ae2005-03-13 09:43:36 +0000600 for(x = 0; x < s->width; x++) {
bellard8d5f07f2004-10-04 21:23:09 +0000601 v = *d;
Luiz Capitulino0ab6b632012-05-24 11:33:25 -0300602 ret = fputc(s->r[v], f);
603 if (ret == EOF) {
604 goto write_err;
605 }
606 ret = fputc(s->g[v], f);
607 if (ret == EOF) {
608 goto write_err;
609 }
610 ret = fputc(s->b[v], f);
611 if (ret == EOF) {
612 goto write_err;
613 }
bellard8d5f07f2004-10-04 21:23:09 +0000614 d++;
615 }
bellarde80cfcf2004-12-19 23:18:01 +0000616 d1 += MAXX;
bellard8d5f07f2004-10-04 21:23:09 +0000617 }
Luiz Capitulino0ab6b632012-05-24 11:33:25 -0300618
619out:
bellard8d5f07f2004-10-04 21:23:09 +0000620 fclose(f);
621 return;
Luiz Capitulino0ab6b632012-05-24 11:33:25 -0300622
623write_err:
624 error_setg(errp, "failed to write to file '%s': %s", filename,
625 strerror(errno));
626 unlink(filename);
627 goto out;
bellard8d5f07f2004-10-04 21:23:09 +0000628}
629
Luiz Capitulinod7098132012-05-21 16:41:37 -0300630static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch,
631 Error **errp)
blueswir1eee0b832007-04-21 19:45:49 +0000632{
633 TCXState *s = opaque;
634 FILE *f;
635 uint8_t *d, *d1, v;
636 uint32_t *s24, *cptr, dval;
Luiz Capitulino537f2d22012-05-24 11:30:40 -0300637 int ret, y, x;
bellard8d5f07f2004-10-04 21:23:09 +0000638
blueswir1eee0b832007-04-21 19:45:49 +0000639 f = fopen(filename, "wb");
Luiz Capitulino537f2d22012-05-24 11:30:40 -0300640 if (!f) {
641 error_setg(errp, "failed to open file '%s': %s", filename,
642 strerror(errno));
blueswir1eee0b832007-04-21 19:45:49 +0000643 return;
Luiz Capitulino537f2d22012-05-24 11:30:40 -0300644 }
645 ret = fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
646 if (ret < 0) {
647 goto write_err;
648 }
blueswir1eee0b832007-04-21 19:45:49 +0000649 d1 = s->vram;
650 s24 = s->vram24;
651 cptr = s->cplane;
652 for(y = 0; y < s->height; y++) {
653 d = d1;
654 for(x = 0; x < s->width; x++, d++, s24++) {
655 if ((*cptr++ & 0xff000000) == 0x03000000) { // 24-bit direct
656 dval = *s24 & 0x00ffffff;
Luiz Capitulino537f2d22012-05-24 11:30:40 -0300657 ret = fputc((dval >> 16) & 0xff, f);
658 if (ret == EOF) {
659 goto write_err;
660 }
661 ret = fputc((dval >> 8) & 0xff, f);
662 if (ret == EOF) {
663 goto write_err;
664 }
665 ret = fputc(dval & 0xff, f);
666 if (ret == EOF) {
667 goto write_err;
668 }
blueswir1eee0b832007-04-21 19:45:49 +0000669 } else {
670 v = *d;
Luiz Capitulino537f2d22012-05-24 11:30:40 -0300671 ret = fputc(s->r[v], f);
672 if (ret == EOF) {
673 goto write_err;
674 }
675 ret = fputc(s->g[v], f);
676 if (ret == EOF) {
677 goto write_err;
678 }
679 ret = fputc(s->b[v], f);
680 if (ret == EOF) {
681 goto write_err;
682 }
blueswir1eee0b832007-04-21 19:45:49 +0000683 }
684 }
685 d1 += MAXX;
686 }
Luiz Capitulino537f2d22012-05-24 11:30:40 -0300687
688out:
blueswir1eee0b832007-04-21 19:45:49 +0000689 fclose(f);
690 return;
Luiz Capitulino537f2d22012-05-24 11:30:40 -0300691
692write_err:
693 error_setg(errp, "failed to write to file '%s': %s", filename,
694 strerror(errno));
695 unlink(filename);
696 goto out;
blueswir1eee0b832007-04-21 19:45:49 +0000697}
Blue Swirlf40070c2009-07-12 19:21:36 +0000698
Anthony Liguori999e12b2012-01-24 13:12:29 -0600699static Property tcx_properties[] = {
700 DEFINE_PROP_TADDR("addr", TCXState, addr, -1),
701 DEFINE_PROP_HEX32("vram_size", TCXState, vram_size, -1),
702 DEFINE_PROP_UINT16("width", TCXState, width, -1),
703 DEFINE_PROP_UINT16("height", TCXState, height, -1),
704 DEFINE_PROP_UINT16("depth", TCXState, depth, -1),
705 DEFINE_PROP_END_OF_LIST(),
706};
707
708static void tcx_class_init(ObjectClass *klass, void *data)
709{
Anthony Liguori39bffca2011-12-07 21:34:16 -0600710 DeviceClass *dc = DEVICE_CLASS(klass);
Anthony Liguori999e12b2012-01-24 13:12:29 -0600711 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
712
713 k->init = tcx_init1;
Anthony Liguori39bffca2011-12-07 21:34:16 -0600714 dc->reset = tcx_reset;
715 dc->vmsd = &vmstate_tcx;
716 dc->props = tcx_properties;
Anthony Liguori999e12b2012-01-24 13:12:29 -0600717}
718
Andreas Färber8c43a6f2013-01-10 16:19:07 +0100719static const TypeInfo tcx_info = {
Anthony Liguori39bffca2011-12-07 21:34:16 -0600720 .name = "SUNW,tcx",
721 .parent = TYPE_SYS_BUS_DEVICE,
722 .instance_size = sizeof(TCXState),
723 .class_init = tcx_class_init,
Gerd Hoffmannee6847d2009-07-15 13:43:31 +0200724};
725
Andreas Färber83f7d432012-02-09 15:20:55 +0100726static void tcx_register_types(void)
Blue Swirlf40070c2009-07-12 19:21:36 +0000727{
Anthony Liguori39bffca2011-12-07 21:34:16 -0600728 type_register_static(&tcx_info);
Blue Swirlf40070c2009-07-12 19:21:36 +0000729}
730
Andreas Färber83f7d432012-02-09 15:20:55 +0100731type_init(tcx_register_types)