qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

tcx.c (25630B)


      1 /*
      2  * QEMU TCX Frame buffer
      3  *
      4  * Copyright (c) 2003-2005 Fabrice Bellard
      5  *
      6  * 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  */
     24 
     25 #include "qemu/osdep.h"
     26 #include "qemu/datadir.h"
     27 #include "qapi/error.h"
     28 #include "ui/console.h"
     29 #include "ui/pixel_ops.h"
     30 #include "hw/loader.h"
     31 #include "hw/qdev-properties.h"
     32 #include "hw/sysbus.h"
     33 #include "migration/vmstate.h"
     34 #include "qemu/error-report.h"
     35 #include "qemu/module.h"
     36 #include "qom/object.h"
     37 
     38 #define TCX_ROM_FILE "QEMU,tcx.bin"
     39 #define FCODE_MAX_ROM_SIZE 0x10000
     40 
     41 #define MAXX 1024
     42 #define MAXY 768
     43 #define TCX_DAC_NREGS    16
     44 #define TCX_THC_NREGS    0x1000
     45 #define TCX_DHC_NREGS    0x4000
     46 #define TCX_TEC_NREGS    0x1000
     47 #define TCX_ALT_NREGS    0x8000
     48 #define TCX_STIP_NREGS   0x800000
     49 #define TCX_BLIT_NREGS   0x800000
     50 #define TCX_RSTIP_NREGS  0x800000
     51 #define TCX_RBLIT_NREGS  0x800000
     52 
     53 #define TCX_THC_MISC     0x818
     54 #define TCX_THC_CURSXY   0x8fc
     55 #define TCX_THC_CURSMASK 0x900
     56 #define TCX_THC_CURSBITS 0x980
     57 
     58 #define TYPE_TCX "sun-tcx"
     59 OBJECT_DECLARE_SIMPLE_TYPE(TCXState, TCX)
     60 
     61 struct TCXState {
     62     SysBusDevice parent_obj;
     63 
     64     QemuConsole *con;
     65     qemu_irq irq;
     66     uint8_t *vram;
     67     uint32_t *vram24, *cplane;
     68     hwaddr prom_addr;
     69     MemoryRegion rom;
     70     MemoryRegion vram_mem;
     71     MemoryRegion vram_8bit;
     72     MemoryRegion vram_24bit;
     73     MemoryRegion stip;
     74     MemoryRegion blit;
     75     MemoryRegion vram_cplane;
     76     MemoryRegion rstip;
     77     MemoryRegion rblit;
     78     MemoryRegion tec;
     79     MemoryRegion dac;
     80     MemoryRegion thc;
     81     MemoryRegion dhc;
     82     MemoryRegion alt;
     83     MemoryRegion thc24;
     84 
     85     ram_addr_t vram24_offset, cplane_offset;
     86     uint32_t tmpblit;
     87     uint32_t vram_size;
     88     uint32_t palette[260];
     89     uint8_t r[260], g[260], b[260];
     90     uint16_t width, height, depth;
     91     uint8_t dac_index, dac_state;
     92     uint32_t thcmisc;
     93     uint32_t cursmask[32];
     94     uint32_t cursbits[32];
     95     uint16_t cursx;
     96     uint16_t cursy;
     97 };
     98 
     99 static void tcx_set_dirty(TCXState *s, ram_addr_t addr, int len)
    100 {
    101     memory_region_set_dirty(&s->vram_mem, addr, len);
    102 
    103     if (s->depth == 24) {
    104         memory_region_set_dirty(&s->vram_mem, s->vram24_offset + addr * 4,
    105                                 len * 4);
    106         memory_region_set_dirty(&s->vram_mem, s->cplane_offset + addr * 4,
    107                                 len * 4);
    108     }
    109 }
    110 
    111 static int tcx_check_dirty(TCXState *s, DirtyBitmapSnapshot *snap,
    112                            ram_addr_t addr, int len)
    113 {
    114     int ret;
    115 
    116     ret = memory_region_snapshot_get_dirty(&s->vram_mem, snap, addr, len);
    117 
    118     if (s->depth == 24) {
    119         ret |= memory_region_snapshot_get_dirty(&s->vram_mem, snap,
    120                                        s->vram24_offset + addr * 4, len * 4);
    121         ret |= memory_region_snapshot_get_dirty(&s->vram_mem, snap,
    122                                        s->cplane_offset + addr * 4, len * 4);
    123     }
    124 
    125     return ret;
    126 }
    127 
    128 static void update_palette_entries(TCXState *s, int start, int end)
    129 {
    130     int i;
    131 
    132     for (i = start; i < end; i++) {
    133         s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
    134     }
    135     tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
    136 }
    137 
    138 static void tcx_draw_line32(TCXState *s1, uint8_t *d,
    139                             const uint8_t *s, int width)
    140 {
    141     int x;
    142     uint8_t val;
    143     uint32_t *p = (uint32_t *)d;
    144 
    145     for (x = 0; x < width; x++) {
    146         val = *s++;
    147         *p++ = s1->palette[val];
    148     }
    149 }
    150 
    151 static void tcx_draw_cursor32(TCXState *s1, uint8_t *d,
    152                               int y, int width)
    153 {
    154     int x, len;
    155     uint32_t mask, bits;
    156     uint32_t *p = (uint32_t *)d;
    157 
    158     y = y - s1->cursy;
    159     mask = s1->cursmask[y];
    160     bits = s1->cursbits[y];
    161     len = MIN(width - s1->cursx, 32);
    162     p = &p[s1->cursx];
    163     for (x = 0; x < len; x++) {
    164         if (mask & 0x80000000) {
    165             if (bits & 0x80000000) {
    166                 *p = s1->palette[259];
    167             } else {
    168                 *p = s1->palette[258];
    169             }
    170         }
    171         p++;
    172         mask <<= 1;
    173         bits <<= 1;
    174     }
    175 }
    176 
    177 /*
    178  * XXX Could be much more optimal:
    179  * detect if line/page/whole screen is in 24 bit mode
    180  */
    181 static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
    182                                      const uint8_t *s, int width,
    183                                      const uint32_t *cplane,
    184                                      const uint32_t *s24)
    185 {
    186     int x, r, g, b;
    187     uint8_t val, *p8;
    188     uint32_t *p = (uint32_t *)d;
    189     uint32_t dval;
    190     for(x = 0; x < width; x++, s++, s24++) {
    191         if (be32_to_cpu(*cplane) & 0x03000000) {
    192             /* 24-bit direct, BGR order */
    193             p8 = (uint8_t *)s24;
    194             p8++;
    195             b = *p8++;
    196             g = *p8++;
    197             r = *p8;
    198             dval = rgb_to_pixel32(r, g, b);
    199         } else {
    200             /* 8-bit pseudocolor */
    201             val = *s;
    202             dval = s1->palette[val];
    203         }
    204         *p++ = dval;
    205         cplane++;
    206     }
    207 }
    208 
    209 /* Fixed line length 1024 allows us to do nice tricks not possible on
    210    VGA... */
    211 
    212 static void tcx_update_display(void *opaque)
    213 {
    214     TCXState *ts = opaque;
    215     DisplaySurface *surface = qemu_console_surface(ts->con);
    216     ram_addr_t page;
    217     DirtyBitmapSnapshot *snap = NULL;
    218     int y, y_start, dd, ds;
    219     uint8_t *d, *s;
    220 
    221     assert(surface_bits_per_pixel(surface) == 32);
    222 
    223     page = 0;
    224     y_start = -1;
    225     d = surface_data(surface);
    226     s = ts->vram;
    227     dd = surface_stride(surface);
    228     ds = 1024;
    229 
    230     snap = memory_region_snapshot_and_clear_dirty(&ts->vram_mem, 0x0,
    231                                              memory_region_size(&ts->vram_mem),
    232                                              DIRTY_MEMORY_VGA);
    233 
    234     for (y = 0; y < ts->height; y++, page += ds) {
    235         if (tcx_check_dirty(ts, snap, page, ds)) {
    236             if (y_start < 0)
    237                 y_start = y;
    238 
    239             tcx_draw_line32(ts, d, s, ts->width);
    240             if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) {
    241                 tcx_draw_cursor32(ts, d, y, ts->width);
    242             }
    243         } else {
    244             if (y_start >= 0) {
    245                 /* flush to display */
    246                 dpy_gfx_update(ts->con, 0, y_start,
    247                                ts->width, y - y_start);
    248                 y_start = -1;
    249             }
    250         }
    251         s += ds;
    252         d += dd;
    253     }
    254     if (y_start >= 0) {
    255         /* flush to display */
    256         dpy_gfx_update(ts->con, 0, y_start,
    257                        ts->width, y - y_start);
    258     }
    259     g_free(snap);
    260 }
    261 
    262 static void tcx24_update_display(void *opaque)
    263 {
    264     TCXState *ts = opaque;
    265     DisplaySurface *surface = qemu_console_surface(ts->con);
    266     ram_addr_t page;
    267     DirtyBitmapSnapshot *snap = NULL;
    268     int y, y_start, dd, ds;
    269     uint8_t *d, *s;
    270     uint32_t *cptr, *s24;
    271 
    272     assert(surface_bits_per_pixel(surface) == 32);
    273 
    274     page = 0;
    275     y_start = -1;
    276     d = surface_data(surface);
    277     s = ts->vram;
    278     s24 = ts->vram24;
    279     cptr = ts->cplane;
    280     dd = surface_stride(surface);
    281     ds = 1024;
    282 
    283     snap = memory_region_snapshot_and_clear_dirty(&ts->vram_mem, 0x0,
    284                                              memory_region_size(&ts->vram_mem),
    285                                              DIRTY_MEMORY_VGA);
    286 
    287     for (y = 0; y < ts->height; y++, page += ds) {
    288         if (tcx_check_dirty(ts, snap, page, ds)) {
    289             if (y_start < 0)
    290                 y_start = y;
    291 
    292             tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
    293             if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) {
    294                 tcx_draw_cursor32(ts, d, y, ts->width);
    295             }
    296         } else {
    297             if (y_start >= 0) {
    298                 /* flush to display */
    299                 dpy_gfx_update(ts->con, 0, y_start,
    300                                ts->width, y - y_start);
    301                 y_start = -1;
    302             }
    303         }
    304         d += dd;
    305         s += ds;
    306         cptr += ds;
    307         s24 += ds;
    308     }
    309     if (y_start >= 0) {
    310         /* flush to display */
    311         dpy_gfx_update(ts->con, 0, y_start,
    312                        ts->width, y - y_start);
    313     }
    314     g_free(snap);
    315 }
    316 
    317 static void tcx_invalidate_display(void *opaque)
    318 {
    319     TCXState *s = opaque;
    320 
    321     tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
    322     qemu_console_resize(s->con, s->width, s->height);
    323 }
    324 
    325 static void tcx24_invalidate_display(void *opaque)
    326 {
    327     TCXState *s = opaque;
    328 
    329     tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
    330     qemu_console_resize(s->con, s->width, s->height);
    331 }
    332 
    333 static int vmstate_tcx_post_load(void *opaque, int version_id)
    334 {
    335     TCXState *s = opaque;
    336 
    337     update_palette_entries(s, 0, 256);
    338     tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
    339     return 0;
    340 }
    341 
    342 static const VMStateDescription vmstate_tcx = {
    343     .name ="tcx",
    344     .version_id = 4,
    345     .minimum_version_id = 4,
    346     .post_load = vmstate_tcx_post_load,
    347     .fields = (VMStateField[]) {
    348         VMSTATE_UINT16(height, TCXState),
    349         VMSTATE_UINT16(width, TCXState),
    350         VMSTATE_UINT16(depth, TCXState),
    351         VMSTATE_BUFFER(r, TCXState),
    352         VMSTATE_BUFFER(g, TCXState),
    353         VMSTATE_BUFFER(b, TCXState),
    354         VMSTATE_UINT8(dac_index, TCXState),
    355         VMSTATE_UINT8(dac_state, TCXState),
    356         VMSTATE_END_OF_LIST()
    357     }
    358 };
    359 
    360 static void tcx_reset(DeviceState *d)
    361 {
    362     TCXState *s = TCX(d);
    363 
    364     /* Initialize palette */
    365     memset(s->r, 0, 260);
    366     memset(s->g, 0, 260);
    367     memset(s->b, 0, 260);
    368     s->r[255] = s->g[255] = s->b[255] = 255;
    369     s->r[256] = s->g[256] = s->b[256] = 255;
    370     s->r[258] = s->g[258] = s->b[258] = 255;
    371     update_palette_entries(s, 0, 260);
    372     memset(s->vram, 0, MAXX*MAXY);
    373     memory_region_reset_dirty(&s->vram_mem, 0, MAXX * MAXY * (1 + 4 + 4),
    374                               DIRTY_MEMORY_VGA);
    375     s->dac_index = 0;
    376     s->dac_state = 0;
    377     s->cursx = 0xf000; /* Put cursor off screen */
    378     s->cursy = 0xf000;
    379 }
    380 
    381 static uint64_t tcx_dac_readl(void *opaque, hwaddr addr,
    382                               unsigned size)
    383 {
    384     TCXState *s = opaque;
    385     uint32_t val = 0;
    386 
    387     switch (s->dac_state) {
    388     case 0:
    389         val = s->r[s->dac_index] << 24;
    390         s->dac_state++;
    391         break;
    392     case 1:
    393         val = s->g[s->dac_index] << 24;
    394         s->dac_state++;
    395         break;
    396     case 2:
    397         val = s->b[s->dac_index] << 24;
    398         s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */
    399         /* fall through */
    400     default:
    401         s->dac_state = 0;
    402         break;
    403     }
    404 
    405     return val;
    406 }
    407 
    408 static void tcx_dac_writel(void *opaque, hwaddr addr, uint64_t val,
    409                            unsigned size)
    410 {
    411     TCXState *s = opaque;
    412     unsigned index;
    413 
    414     switch (addr) {
    415     case 0: /* Address */
    416         s->dac_index = val >> 24;
    417         s->dac_state = 0;
    418         break;
    419     case 4:  /* Pixel colours */
    420     case 12: /* Overlay (cursor) colours */
    421         if (addr & 8) {
    422             index = (s->dac_index & 3) + 256;
    423         } else {
    424             index = s->dac_index;
    425         }
    426         switch (s->dac_state) {
    427         case 0:
    428             s->r[index] = val >> 24;
    429             update_palette_entries(s, index, index + 1);
    430             s->dac_state++;
    431             break;
    432         case 1:
    433             s->g[index] = val >> 24;
    434             update_palette_entries(s, index, index + 1);
    435             s->dac_state++;
    436             break;
    437         case 2:
    438             s->b[index] = val >> 24;
    439             update_palette_entries(s, index, index + 1);
    440             s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */
    441             /* fall through */
    442         default:
    443             s->dac_state = 0;
    444             break;
    445         }
    446         break;
    447     default: /* Control registers */
    448         break;
    449     }
    450 }
    451 
    452 static const MemoryRegionOps tcx_dac_ops = {
    453     .read = tcx_dac_readl,
    454     .write = tcx_dac_writel,
    455     .endianness = DEVICE_NATIVE_ENDIAN,
    456     .valid = {
    457         .min_access_size = 4,
    458         .max_access_size = 4,
    459     },
    460 };
    461 
    462 static uint64_t tcx_stip_readl(void *opaque, hwaddr addr,
    463                                unsigned size)
    464 {
    465     return 0;
    466 }
    467 
    468 static void tcx_stip_writel(void *opaque, hwaddr addr,
    469                             uint64_t val, unsigned size)
    470 {
    471     TCXState *s = opaque;
    472     int i;
    473     uint32_t col;
    474 
    475     if (!(addr & 4)) {
    476         s->tmpblit = val;
    477     } else {
    478         addr = (addr >> 3) & 0xfffff;
    479         col = cpu_to_be32(s->tmpblit);
    480         if (s->depth == 24) {
    481             for (i = 0; i < 32; i++)  {
    482                 if (val & 0x80000000) {
    483                     s->vram[addr + i] = s->tmpblit;
    484                     s->vram24[addr + i] = col;
    485                 }
    486                 val <<= 1;
    487             }
    488         } else {
    489             for (i = 0; i < 32; i++)  {
    490                 if (val & 0x80000000) {
    491                     s->vram[addr + i] = s->tmpblit;
    492                 }
    493                 val <<= 1;
    494             }
    495         }
    496         tcx_set_dirty(s, addr, 32);
    497     }
    498 }
    499 
    500 static void tcx_rstip_writel(void *opaque, hwaddr addr,
    501                              uint64_t val, unsigned size)
    502 {
    503     TCXState *s = opaque;
    504     int i;
    505     uint32_t col;
    506 
    507     if (!(addr & 4)) {
    508         s->tmpblit = val;
    509     } else {
    510         addr = (addr >> 3) & 0xfffff;
    511         col = cpu_to_be32(s->tmpblit);
    512         if (s->depth == 24) {
    513             for (i = 0; i < 32; i++) {
    514                 if (val & 0x80000000) {
    515                     s->vram[addr + i] = s->tmpblit;
    516                     s->vram24[addr + i] = col;
    517                     s->cplane[addr + i] = col;
    518                 }
    519                 val <<= 1;
    520             }
    521         } else {
    522             for (i = 0; i < 32; i++)  {
    523                 if (val & 0x80000000) {
    524                     s->vram[addr + i] = s->tmpblit;
    525                 }
    526                 val <<= 1;
    527             }
    528         }
    529         tcx_set_dirty(s, addr, 32);
    530     }
    531 }
    532 
    533 static const MemoryRegionOps tcx_stip_ops = {
    534     .read = tcx_stip_readl,
    535     .write = tcx_stip_writel,
    536     .endianness = DEVICE_NATIVE_ENDIAN,
    537     .impl = {
    538         .min_access_size = 4,
    539         .max_access_size = 4,
    540     },
    541     .valid = {
    542         .min_access_size = 4,
    543         .max_access_size = 8,
    544     },
    545 };
    546 
    547 static const MemoryRegionOps tcx_rstip_ops = {
    548     .read = tcx_stip_readl,
    549     .write = tcx_rstip_writel,
    550     .endianness = DEVICE_NATIVE_ENDIAN,
    551     .impl = {
    552         .min_access_size = 4,
    553         .max_access_size = 4,
    554     },
    555     .valid = {
    556         .min_access_size = 4,
    557         .max_access_size = 8,
    558     },
    559 };
    560 
    561 static uint64_t tcx_blit_readl(void *opaque, hwaddr addr,
    562                                unsigned size)
    563 {
    564     return 0;
    565 }
    566 
    567 static void tcx_blit_writel(void *opaque, hwaddr addr,
    568                             uint64_t val, unsigned size)
    569 {
    570     TCXState *s = opaque;
    571     uint32_t adsr, len;
    572     int i;
    573 
    574     if (!(addr & 4)) {
    575         s->tmpblit = val;
    576     } else {
    577         addr = (addr >> 3) & 0xfffff;
    578         adsr = val & 0xffffff;
    579         len = ((val >> 24) & 0x1f) + 1;
    580         if (adsr == 0xffffff) {
    581             memset(&s->vram[addr], s->tmpblit, len);
    582             if (s->depth == 24) {
    583                 val = s->tmpblit & 0xffffff;
    584                 val = cpu_to_be32(val);
    585                 for (i = 0; i < len; i++) {
    586                     s->vram24[addr + i] = val;
    587                 }
    588             }
    589         } else {
    590             memcpy(&s->vram[addr], &s->vram[adsr], len);
    591             if (s->depth == 24) {
    592                 memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4);
    593             }
    594         }
    595         tcx_set_dirty(s, addr, len);
    596     }
    597 }
    598 
    599 static void tcx_rblit_writel(void *opaque, hwaddr addr,
    600                          uint64_t val, unsigned size)
    601 {
    602     TCXState *s = opaque;
    603     uint32_t adsr, len;
    604     int i;
    605 
    606     if (!(addr & 4)) {
    607         s->tmpblit = val;
    608     } else {
    609         addr = (addr >> 3) & 0xfffff;
    610         adsr = val & 0xffffff;
    611         len = ((val >> 24) & 0x1f) + 1;
    612         if (adsr == 0xffffff) {
    613             memset(&s->vram[addr], s->tmpblit, len);
    614             if (s->depth == 24) {
    615                 val = s->tmpblit & 0xffffff;
    616                 val = cpu_to_be32(val);
    617                 for (i = 0; i < len; i++) {
    618                     s->vram24[addr + i] = val;
    619                     s->cplane[addr + i] = val;
    620                 }
    621             }
    622         } else {
    623             memcpy(&s->vram[addr], &s->vram[adsr], len);
    624             if (s->depth == 24) {
    625                 memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4);
    626                 memcpy(&s->cplane[addr], &s->cplane[adsr], len * 4);
    627             }
    628         }
    629         tcx_set_dirty(s, addr, len);
    630     }
    631 }
    632 
    633 static const MemoryRegionOps tcx_blit_ops = {
    634     .read = tcx_blit_readl,
    635     .write = tcx_blit_writel,
    636     .endianness = DEVICE_NATIVE_ENDIAN,
    637     .impl = {
    638         .min_access_size = 4,
    639         .max_access_size = 4,
    640     },
    641     .valid = {
    642         .min_access_size = 4,
    643         .max_access_size = 8,
    644     },
    645 };
    646 
    647 static const MemoryRegionOps tcx_rblit_ops = {
    648     .read = tcx_blit_readl,
    649     .write = tcx_rblit_writel,
    650     .endianness = DEVICE_NATIVE_ENDIAN,
    651     .impl = {
    652         .min_access_size = 4,
    653         .max_access_size = 4,
    654     },
    655     .valid = {
    656         .min_access_size = 4,
    657         .max_access_size = 8,
    658     },
    659 };
    660 
    661 static void tcx_invalidate_cursor_position(TCXState *s)
    662 {
    663     int ymin, ymax, start, end;
    664 
    665     /* invalidate only near the cursor */
    666     ymin = s->cursy;
    667     if (ymin >= s->height) {
    668         return;
    669     }
    670     ymax = MIN(s->height, ymin + 32);
    671     start = ymin * 1024;
    672     end   = ymax * 1024;
    673 
    674     tcx_set_dirty(s, start, end - start);
    675 }
    676 
    677 static uint64_t tcx_thc_readl(void *opaque, hwaddr addr,
    678                             unsigned size)
    679 {
    680     TCXState *s = opaque;
    681     uint64_t val;
    682 
    683     if (addr == TCX_THC_MISC) {
    684         val = s->thcmisc | 0x02000000;
    685     } else {
    686         val = 0;
    687     }
    688     return val;
    689 }
    690 
    691 static void tcx_thc_writel(void *opaque, hwaddr addr,
    692                          uint64_t val, unsigned size)
    693 {
    694     TCXState *s = opaque;
    695 
    696     if (addr == TCX_THC_CURSXY) {
    697         tcx_invalidate_cursor_position(s);
    698         s->cursx = val >> 16;
    699         s->cursy = val;
    700         tcx_invalidate_cursor_position(s);
    701     } else if (addr >= TCX_THC_CURSMASK && addr < TCX_THC_CURSMASK + 128) {
    702         s->cursmask[(addr - TCX_THC_CURSMASK) >> 2] = val;
    703         tcx_invalidate_cursor_position(s);
    704     } else if (addr >= TCX_THC_CURSBITS && addr < TCX_THC_CURSBITS + 128) {
    705         s->cursbits[(addr - TCX_THC_CURSBITS) >> 2] = val;
    706         tcx_invalidate_cursor_position(s);
    707     } else if (addr == TCX_THC_MISC) {
    708         s->thcmisc = val;
    709     }
    710 
    711 }
    712 
    713 static const MemoryRegionOps tcx_thc_ops = {
    714     .read = tcx_thc_readl,
    715     .write = tcx_thc_writel,
    716     .endianness = DEVICE_NATIVE_ENDIAN,
    717     .valid = {
    718         .min_access_size = 4,
    719         .max_access_size = 4,
    720     },
    721 };
    722 
    723 static uint64_t tcx_dummy_readl(void *opaque, hwaddr addr,
    724                             unsigned size)
    725 {
    726     return 0;
    727 }
    728 
    729 static void tcx_dummy_writel(void *opaque, hwaddr addr,
    730                          uint64_t val, unsigned size)
    731 {
    732     return;
    733 }
    734 
    735 static const MemoryRegionOps tcx_dummy_ops = {
    736     .read = tcx_dummy_readl,
    737     .write = tcx_dummy_writel,
    738     .endianness = DEVICE_NATIVE_ENDIAN,
    739     .valid = {
    740         .min_access_size = 4,
    741         .max_access_size = 4,
    742     },
    743 };
    744 
    745 static const GraphicHwOps tcx_ops = {
    746     .invalidate = tcx_invalidate_display,
    747     .gfx_update = tcx_update_display,
    748 };
    749 
    750 static const GraphicHwOps tcx24_ops = {
    751     .invalidate = tcx24_invalidate_display,
    752     .gfx_update = tcx24_update_display,
    753 };
    754 
    755 static void tcx_initfn(Object *obj)
    756 {
    757     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    758     TCXState *s = TCX(obj);
    759 
    760     memory_region_init_rom_nomigrate(&s->rom, obj, "tcx.prom",
    761                                      FCODE_MAX_ROM_SIZE, &error_fatal);
    762     sysbus_init_mmio(sbd, &s->rom);
    763 
    764     /* 2/STIP : Stippler */
    765     memory_region_init_io(&s->stip, obj, &tcx_stip_ops, s, "tcx.stip",
    766                           TCX_STIP_NREGS);
    767     sysbus_init_mmio(sbd, &s->stip);
    768 
    769     /* 3/BLIT : Blitter */
    770     memory_region_init_io(&s->blit, obj, &tcx_blit_ops, s, "tcx.blit",
    771                           TCX_BLIT_NREGS);
    772     sysbus_init_mmio(sbd, &s->blit);
    773 
    774     /* 5/RSTIP : Raw Stippler */
    775     memory_region_init_io(&s->rstip, obj, &tcx_rstip_ops, s, "tcx.rstip",
    776                           TCX_RSTIP_NREGS);
    777     sysbus_init_mmio(sbd, &s->rstip);
    778 
    779     /* 6/RBLIT : Raw Blitter */
    780     memory_region_init_io(&s->rblit, obj, &tcx_rblit_ops, s, "tcx.rblit",
    781                           TCX_RBLIT_NREGS);
    782     sysbus_init_mmio(sbd, &s->rblit);
    783 
    784     /* 7/TEC : ??? */
    785     memory_region_init_io(&s->tec, obj, &tcx_dummy_ops, s, "tcx.tec",
    786                           TCX_TEC_NREGS);
    787     sysbus_init_mmio(sbd, &s->tec);
    788 
    789     /* 8/CMAP : DAC */
    790     memory_region_init_io(&s->dac, obj, &tcx_dac_ops, s, "tcx.dac",
    791                           TCX_DAC_NREGS);
    792     sysbus_init_mmio(sbd, &s->dac);
    793 
    794     /* 9/THC : Cursor */
    795     memory_region_init_io(&s->thc, obj, &tcx_thc_ops, s, "tcx.thc",
    796                           TCX_THC_NREGS);
    797     sysbus_init_mmio(sbd, &s->thc);
    798 
    799     /* 11/DHC : ??? */
    800     memory_region_init_io(&s->dhc, obj, &tcx_dummy_ops, s, "tcx.dhc",
    801                           TCX_DHC_NREGS);
    802     sysbus_init_mmio(sbd, &s->dhc);
    803 
    804     /* 12/ALT : ??? */
    805     memory_region_init_io(&s->alt, obj, &tcx_dummy_ops, s, "tcx.alt",
    806                           TCX_ALT_NREGS);
    807     sysbus_init_mmio(sbd, &s->alt);
    808 }
    809 
    810 static void tcx_realizefn(DeviceState *dev, Error **errp)
    811 {
    812     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
    813     TCXState *s = TCX(dev);
    814     ram_addr_t vram_offset = 0;
    815     int size, ret;
    816     uint8_t *vram_base;
    817     char *fcode_filename;
    818 
    819     memory_region_init_ram_nomigrate(&s->vram_mem, OBJECT(s), "tcx.vram",
    820                            s->vram_size * (1 + 4 + 4), &error_fatal);
    821     vmstate_register_ram_global(&s->vram_mem);
    822     memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA);
    823     vram_base = memory_region_get_ram_ptr(&s->vram_mem);
    824 
    825     /* 10/ROM : FCode ROM */
    826     vmstate_register_ram_global(&s->rom);
    827     fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, TCX_ROM_FILE);
    828     if (fcode_filename) {
    829         ret = load_image_mr(fcode_filename, &s->rom);
    830         g_free(fcode_filename);
    831         if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) {
    832             warn_report("tcx: could not load prom '%s'", TCX_ROM_FILE);
    833         }
    834     }
    835 
    836     /* 0/DFB8 : 8-bit plane */
    837     s->vram = vram_base;
    838     size = s->vram_size;
    839     memory_region_init_alias(&s->vram_8bit, OBJECT(s), "tcx.vram.8bit",
    840                              &s->vram_mem, vram_offset, size);
    841     sysbus_init_mmio(sbd, &s->vram_8bit);
    842     vram_offset += size;
    843     vram_base += size;
    844 
    845     /* 1/DFB24 : 24bit plane */
    846     size = s->vram_size * 4;
    847     s->vram24 = (uint32_t *)vram_base;
    848     s->vram24_offset = vram_offset;
    849     memory_region_init_alias(&s->vram_24bit, OBJECT(s), "tcx.vram.24bit",
    850                              &s->vram_mem, vram_offset, size);
    851     sysbus_init_mmio(sbd, &s->vram_24bit);
    852     vram_offset += size;
    853     vram_base += size;
    854 
    855     /* 4/RDFB32 : Raw Framebuffer */
    856     size = s->vram_size * 4;
    857     s->cplane = (uint32_t *)vram_base;
    858     s->cplane_offset = vram_offset;
    859     memory_region_init_alias(&s->vram_cplane, OBJECT(s), "tcx.vram.cplane",
    860                              &s->vram_mem, vram_offset, size);
    861     sysbus_init_mmio(sbd, &s->vram_cplane);
    862 
    863     /* 9/THC24bits : NetBSD writes here even with 8-bit display: dummy */
    864     if (s->depth == 8) {
    865         memory_region_init_io(&s->thc24, OBJECT(s), &tcx_dummy_ops, s,
    866                               "tcx.thc24", TCX_THC_NREGS);
    867         sysbus_init_mmio(sbd, &s->thc24);
    868     }
    869 
    870     sysbus_init_irq(sbd, &s->irq);
    871 
    872     if (s->depth == 8) {
    873         s->con = graphic_console_init(dev, 0, &tcx_ops, s);
    874     } else {
    875         s->con = graphic_console_init(dev, 0, &tcx24_ops, s);
    876     }
    877     s->thcmisc = 0;
    878 
    879     qemu_console_resize(s->con, s->width, s->height);
    880 }
    881 
    882 static Property tcx_properties[] = {
    883     DEFINE_PROP_UINT32("vram_size", TCXState, vram_size, -1),
    884     DEFINE_PROP_UINT16("width",    TCXState, width,     -1),
    885     DEFINE_PROP_UINT16("height",   TCXState, height,    -1),
    886     DEFINE_PROP_UINT16("depth",    TCXState, depth,     -1),
    887     DEFINE_PROP_END_OF_LIST(),
    888 };
    889 
    890 static void tcx_class_init(ObjectClass *klass, void *data)
    891 {
    892     DeviceClass *dc = DEVICE_CLASS(klass);
    893 
    894     dc->realize = tcx_realizefn;
    895     dc->reset = tcx_reset;
    896     dc->vmsd = &vmstate_tcx;
    897     device_class_set_props(dc, tcx_properties);
    898 }
    899 
    900 static const TypeInfo tcx_info = {
    901     .name          = TYPE_TCX,
    902     .parent        = TYPE_SYS_BUS_DEVICE,
    903     .instance_size = sizeof(TCXState),
    904     .instance_init = tcx_initfn,
    905     .class_init    = tcx_class_init,
    906 };
    907 
    908 static void tcx_register_types(void)
    909 {
    910     type_register_static(&tcx_info);
    911 }
    912 
    913 type_init(tcx_register_types)