1420557e8Sbellard /* 26f7e9aecSbellard * QEMU TCX Frame buffer 3420557e8Sbellard * 46f7e9aecSbellard * Copyright (c) 2003-2005 Fabrice Bellard 5420557e8Sbellard * 6420557e8Sbellard * Permission is hereby granted, free of charge, to any person obtaining a copy 7420557e8Sbellard * of this software and associated documentation files (the "Software"), to deal 8420557e8Sbellard * in the Software without restriction, including without limitation the rights 9420557e8Sbellard * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10420557e8Sbellard * copies of the Software, and to permit persons to whom the Software is 11420557e8Sbellard * furnished to do so, subject to the following conditions: 12420557e8Sbellard * 13420557e8Sbellard * The above copyright notice and this permission notice shall be included in 14420557e8Sbellard * all copies or substantial portions of the Software. 15420557e8Sbellard * 16420557e8Sbellard * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17420557e8Sbellard * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18420557e8Sbellard * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19420557e8Sbellard * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20420557e8Sbellard * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21420557e8Sbellard * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22420557e8Sbellard * THE SOFTWARE. 23420557e8Sbellard */ 24f40070c3SBlue Swirl 2547df5154SPeter Maydell #include "qemu/osdep.h" 262c65db5eSPaolo Bonzini #include "qemu/datadir.h" 27da34e65cSMarkus Armbruster #include "qapi/error.h" 2828ecbaeeSPaolo Bonzini #include "ui/console.h" 2928ecbaeeSPaolo Bonzini #include "ui/pixel_ops.h" 30da87dd7bSMark Cave-Ayland #include "hw/loader.h" 31a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 3283c9f4caSPaolo Bonzini #include "hw/sysbus.h" 33d6454270SMarkus Armbruster #include "migration/vmstate.h" 34d49b6836SMarkus Armbruster #include "qemu/error-report.h" 350b8fa32fSMarkus Armbruster #include "qemu/module.h" 36db1015e9SEduardo Habkost #include "qom/object.h" 37420557e8Sbellard 38da87dd7bSMark Cave-Ayland #define TCX_ROM_FILE "QEMU,tcx.bin" 39da87dd7bSMark Cave-Ayland #define FCODE_MAX_ROM_SIZE 0x10000 40da87dd7bSMark Cave-Ayland 41420557e8Sbellard #define MAXX 1024 42420557e8Sbellard #define MAXY 768 436f7e9aecSbellard #define TCX_DAC_NREGS 16 4455d7bfe2SMark Cave-Ayland #define TCX_THC_NREGS 0x1000 4555d7bfe2SMark Cave-Ayland #define TCX_DHC_NREGS 0x4000 468508b89eSblueswir1 #define TCX_TEC_NREGS 0x1000 4755d7bfe2SMark Cave-Ayland #define TCX_ALT_NREGS 0x8000 4855d7bfe2SMark Cave-Ayland #define TCX_STIP_NREGS 0x800000 4955d7bfe2SMark Cave-Ayland #define TCX_BLIT_NREGS 0x800000 5055d7bfe2SMark Cave-Ayland #define TCX_RSTIP_NREGS 0x800000 5155d7bfe2SMark Cave-Ayland #define TCX_RBLIT_NREGS 0x800000 5255d7bfe2SMark Cave-Ayland 5355d7bfe2SMark Cave-Ayland #define TCX_THC_MISC 0x818 5455d7bfe2SMark Cave-Ayland #define TCX_THC_CURSXY 0x8fc 5555d7bfe2SMark Cave-Ayland #define TCX_THC_CURSMASK 0x900 5655d7bfe2SMark Cave-Ayland #define TCX_THC_CURSBITS 0x980 57420557e8Sbellard 58e178113fSMarkus Armbruster #define TYPE_TCX "sun-tcx" 598063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(TCXState, TCX) 6001774ddbSAndreas Färber 61db1015e9SEduardo Habkost struct TCXState { 6201774ddbSAndreas Färber SysBusDevice parent_obj; 6301774ddbSAndreas Färber 64c78f7137SGerd Hoffmann QemuConsole *con; 6555d7bfe2SMark Cave-Ayland qemu_irq irq; 668d5f07faSbellard uint8_t *vram; 67eee0b836Sblueswir1 uint32_t *vram24, *cplane; 68da87dd7bSMark Cave-Ayland hwaddr prom_addr; 69da87dd7bSMark Cave-Ayland MemoryRegion rom; 70d08151bfSAvi Kivity MemoryRegion vram_mem; 71d08151bfSAvi Kivity MemoryRegion vram_8bit; 72d08151bfSAvi Kivity MemoryRegion vram_24bit; 7355d7bfe2SMark Cave-Ayland MemoryRegion stip; 7455d7bfe2SMark Cave-Ayland MemoryRegion blit; 75d08151bfSAvi Kivity MemoryRegion vram_cplane; 7655d7bfe2SMark Cave-Ayland MemoryRegion rstip; 7755d7bfe2SMark Cave-Ayland MemoryRegion rblit; 78d08151bfSAvi Kivity MemoryRegion tec; 7955d7bfe2SMark Cave-Ayland MemoryRegion dac; 8055d7bfe2SMark Cave-Ayland MemoryRegion thc; 8155d7bfe2SMark Cave-Ayland MemoryRegion dhc; 8255d7bfe2SMark Cave-Ayland MemoryRegion alt; 83d08151bfSAvi Kivity MemoryRegion thc24; 8455d7bfe2SMark Cave-Ayland 85d08151bfSAvi Kivity ram_addr_t vram24_offset, cplane_offset; 8655d7bfe2SMark Cave-Ayland uint32_t tmpblit; 87ee6847d1SGerd Hoffmann uint32_t vram_size; 8855d7bfe2SMark Cave-Ayland uint32_t palette[260]; 8955d7bfe2SMark Cave-Ayland uint8_t r[260], g[260], b[260]; 90427a66c3SBlue Swirl uint16_t width, height, depth; 916f7e9aecSbellard uint8_t dac_index, dac_state; 9255d7bfe2SMark Cave-Ayland uint32_t thcmisc; 9355d7bfe2SMark Cave-Ayland uint32_t cursmask[32]; 9455d7bfe2SMark Cave-Ayland uint32_t cursbits[32]; 9555d7bfe2SMark Cave-Ayland uint16_t cursx; 9655d7bfe2SMark Cave-Ayland uint16_t cursy; 97db1015e9SEduardo Habkost }; 98420557e8Sbellard 999800b3c2SMark Cave-Ayland static void tcx_set_dirty(TCXState *s, ram_addr_t addr, int len) 100d3ffcafeSBlue Swirl { 1019800b3c2SMark Cave-Ayland memory_region_set_dirty(&s->vram_mem, addr, len); 1024b865c28SMark Cave-Ayland 1034b865c28SMark Cave-Ayland if (s->depth == 24) { 1044b865c28SMark Cave-Ayland memory_region_set_dirty(&s->vram_mem, s->vram24_offset + addr * 4, 1054b865c28SMark Cave-Ayland len * 4); 1064b865c28SMark Cave-Ayland memory_region_set_dirty(&s->vram_mem, s->cplane_offset + addr * 4, 1074b865c28SMark Cave-Ayland len * 4); 1084b865c28SMark Cave-Ayland } 109d3ffcafeSBlue Swirl } 110d3ffcafeSBlue Swirl 1112dd285b5SMark Cave-Ayland static int tcx_check_dirty(TCXState *s, DirtyBitmapSnapshot *snap, 1122dd285b5SMark Cave-Ayland ram_addr_t addr, int len) 113d3ffcafeSBlue Swirl { 11455d7bfe2SMark Cave-Ayland int ret; 11555d7bfe2SMark Cave-Ayland 1162dd285b5SMark Cave-Ayland ret = memory_region_snapshot_get_dirty(&s->vram_mem, snap, addr, len); 117427ee02bSMark Cave-Ayland 118427ee02bSMark Cave-Ayland if (s->depth == 24) { 1192dd285b5SMark Cave-Ayland ret |= memory_region_snapshot_get_dirty(&s->vram_mem, snap, 1202dd285b5SMark Cave-Ayland s->vram24_offset + addr * 4, len * 4); 1212dd285b5SMark Cave-Ayland ret |= memory_region_snapshot_get_dirty(&s->vram_mem, snap, 1222dd285b5SMark Cave-Ayland s->cplane_offset + addr * 4, len * 4); 123427ee02bSMark Cave-Ayland } 124427ee02bSMark Cave-Ayland 12555d7bfe2SMark Cave-Ayland return ret; 12655d7bfe2SMark Cave-Ayland } 12755d7bfe2SMark Cave-Ayland 12821206a10Sbellard static void update_palette_entries(TCXState *s, int start, int end) 12921206a10Sbellard { 13021206a10Sbellard int i; 131c78f7137SGerd Hoffmann 13221206a10Sbellard for (i = start; i < end; i++) { 13321206a10Sbellard s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]); 134c78f7137SGerd Hoffmann } 1359800b3c2SMark Cave-Ayland tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem)); 136d3ffcafeSBlue Swirl } 13721206a10Sbellard 138e80cfcfcSbellard static void tcx_draw_line32(TCXState *s1, uint8_t *d, 139e80cfcfcSbellard const uint8_t *s, int width) 140420557e8Sbellard { 141e80cfcfcSbellard int x; 142e80cfcfcSbellard uint8_t val; 1438bdc2159Sths uint32_t *p = (uint32_t *)d; 144e80cfcfcSbellard 145e80cfcfcSbellard for (x = 0; x < width; x++) { 146e80cfcfcSbellard val = *s++; 1478bdc2159Sths *p++ = s1->palette[val]; 148e80cfcfcSbellard } 149420557e8Sbellard } 150420557e8Sbellard 15155d7bfe2SMark Cave-Ayland static void tcx_draw_cursor32(TCXState *s1, uint8_t *d, 15255d7bfe2SMark Cave-Ayland int y, int width) 15355d7bfe2SMark Cave-Ayland { 15455d7bfe2SMark Cave-Ayland int x, len; 15555d7bfe2SMark Cave-Ayland uint32_t mask, bits; 15655d7bfe2SMark Cave-Ayland uint32_t *p = (uint32_t *)d; 15755d7bfe2SMark Cave-Ayland 15855d7bfe2SMark Cave-Ayland y = y - s1->cursy; 15955d7bfe2SMark Cave-Ayland mask = s1->cursmask[y]; 16055d7bfe2SMark Cave-Ayland bits = s1->cursbits[y]; 16155d7bfe2SMark Cave-Ayland len = MIN(width - s1->cursx, 32); 16255d7bfe2SMark Cave-Ayland p = &p[s1->cursx]; 16355d7bfe2SMark Cave-Ayland for (x = 0; x < len; x++) { 16455d7bfe2SMark Cave-Ayland if (mask & 0x80000000) { 16555d7bfe2SMark Cave-Ayland if (bits & 0x80000000) { 16655d7bfe2SMark Cave-Ayland *p = s1->palette[259]; 16755d7bfe2SMark Cave-Ayland } else { 16855d7bfe2SMark Cave-Ayland *p = s1->palette[258]; 16955d7bfe2SMark Cave-Ayland } 17055d7bfe2SMark Cave-Ayland } 17155d7bfe2SMark Cave-Ayland p++; 17255d7bfe2SMark Cave-Ayland mask <<= 1; 17355d7bfe2SMark Cave-Ayland bits <<= 1; 17455d7bfe2SMark Cave-Ayland } 17555d7bfe2SMark Cave-Ayland } 17655d7bfe2SMark Cave-Ayland 177688ea2ebSblueswir1 /* 1787713fff4SPeter Maydell * XXX Could be much more optimal: 179688ea2ebSblueswir1 * detect if line/page/whole screen is in 24 bit mode 180688ea2ebSblueswir1 */ 181eee0b836Sblueswir1 static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d, 182eee0b836Sblueswir1 const uint8_t *s, int width, 183eee0b836Sblueswir1 const uint32_t *cplane, 184eee0b836Sblueswir1 const uint32_t *s24) 185eee0b836Sblueswir1 { 1867713fff4SPeter Maydell int x, r, g, b; 187688ea2ebSblueswir1 uint8_t val, *p8; 188eee0b836Sblueswir1 uint32_t *p = (uint32_t *)d; 189eee0b836Sblueswir1 uint32_t dval; 190eee0b836Sblueswir1 for(x = 0; x < width; x++, s++, s24++) { 19155d7bfe2SMark Cave-Ayland if (be32_to_cpu(*cplane) & 0x03000000) { 19255d7bfe2SMark Cave-Ayland /* 24-bit direct, BGR order */ 193688ea2ebSblueswir1 p8 = (uint8_t *)s24; 194688ea2ebSblueswir1 p8++; 195688ea2ebSblueswir1 b = *p8++; 196688ea2ebSblueswir1 g = *p8++; 197f7e683b8SBlue Swirl r = *p8; 198688ea2ebSblueswir1 dval = rgb_to_pixel32(r, g, b); 199eee0b836Sblueswir1 } else { 20055d7bfe2SMark Cave-Ayland /* 8-bit pseudocolor */ 201eee0b836Sblueswir1 val = *s; 202eee0b836Sblueswir1 dval = s1->palette[val]; 203eee0b836Sblueswir1 } 204eee0b836Sblueswir1 *p++ = dval; 20555d7bfe2SMark Cave-Ayland cplane++; 206eee0b836Sblueswir1 } 207eee0b836Sblueswir1 } 208eee0b836Sblueswir1 209e80cfcfcSbellard /* Fixed line length 1024 allows us to do nice tricks not possible on 210e80cfcfcSbellard VGA... */ 21155d7bfe2SMark Cave-Ayland 21295219897Spbrook static void tcx_update_display(void *opaque) 213e80cfcfcSbellard { 214e80cfcfcSbellard TCXState *ts = opaque; 215c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(ts->con); 2162dd285b5SMark Cave-Ayland ram_addr_t page; 2172dd285b5SMark Cave-Ayland DirtyBitmapSnapshot *snap = NULL; 218550be127Sbellard int y, y_start, dd, ds; 219e80cfcfcSbellard uint8_t *d, *s; 220e80cfcfcSbellard 2217713fff4SPeter Maydell assert(surface_bits_per_pixel(surface) == 32); 222c78f7137SGerd Hoffmann 223d08151bfSAvi Kivity page = 0; 224e80cfcfcSbellard y_start = -1; 225c78f7137SGerd Hoffmann d = surface_data(surface); 2266f7e9aecSbellard s = ts->vram; 227c78f7137SGerd Hoffmann dd = surface_stride(surface); 228e80cfcfcSbellard ds = 1024; 229e80cfcfcSbellard 2302dd285b5SMark Cave-Ayland snap = memory_region_snapshot_and_clear_dirty(&ts->vram_mem, 0x0, 2312dd285b5SMark Cave-Ayland memory_region_size(&ts->vram_mem), 2322dd285b5SMark Cave-Ayland DIRTY_MEMORY_VGA); 2332dd285b5SMark Cave-Ayland 2340a97c6c4SMark Cave-Ayland for (y = 0; y < ts->height; y++, page += ds) { 2352dd285b5SMark Cave-Ayland if (tcx_check_dirty(ts, snap, page, ds)) { 236e80cfcfcSbellard if (y_start < 0) 237e80cfcfcSbellard y_start = y; 23855d7bfe2SMark Cave-Ayland 239ee72bed0SMark Cave-Ayland tcx_draw_line32(ts, d, s, ts->width); 24055d7bfe2SMark Cave-Ayland if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) { 241ee72bed0SMark Cave-Ayland tcx_draw_cursor32(ts, d, y, ts->width); 24255d7bfe2SMark Cave-Ayland } 243e80cfcfcSbellard } else { 244e80cfcfcSbellard if (y_start >= 0) { 245e80cfcfcSbellard /* flush to display */ 246c78f7137SGerd Hoffmann dpy_gfx_update(ts->con, 0, y_start, 2476f7e9aecSbellard ts->width, y - y_start); 248e80cfcfcSbellard y_start = -1; 249e80cfcfcSbellard } 250e80cfcfcSbellard } 2510a97c6c4SMark Cave-Ayland s += ds; 2520a97c6c4SMark Cave-Ayland d += dd; 253e80cfcfcSbellard } 254e80cfcfcSbellard if (y_start >= 0) { 255e80cfcfcSbellard /* flush to display */ 256c78f7137SGerd Hoffmann dpy_gfx_update(ts->con, 0, y_start, 2576f7e9aecSbellard ts->width, y - y_start); 258e80cfcfcSbellard } 2592dd285b5SMark Cave-Ayland g_free(snap); 260e80cfcfcSbellard } 261e80cfcfcSbellard 262eee0b836Sblueswir1 static void tcx24_update_display(void *opaque) 263eee0b836Sblueswir1 { 264eee0b836Sblueswir1 TCXState *ts = opaque; 265c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(ts->con); 2662dd285b5SMark Cave-Ayland ram_addr_t page; 2672dd285b5SMark Cave-Ayland DirtyBitmapSnapshot *snap = NULL; 268eee0b836Sblueswir1 int y, y_start, dd, ds; 269eee0b836Sblueswir1 uint8_t *d, *s; 270eee0b836Sblueswir1 uint32_t *cptr, *s24; 271eee0b836Sblueswir1 2727713fff4SPeter Maydell assert(surface_bits_per_pixel(surface) == 32); 273c78f7137SGerd Hoffmann 274d08151bfSAvi Kivity page = 0; 275eee0b836Sblueswir1 y_start = -1; 276c78f7137SGerd Hoffmann d = surface_data(surface); 277eee0b836Sblueswir1 s = ts->vram; 278eee0b836Sblueswir1 s24 = ts->vram24; 279eee0b836Sblueswir1 cptr = ts->cplane; 280c78f7137SGerd Hoffmann dd = surface_stride(surface); 281eee0b836Sblueswir1 ds = 1024; 282eee0b836Sblueswir1 2832dd285b5SMark Cave-Ayland snap = memory_region_snapshot_and_clear_dirty(&ts->vram_mem, 0x0, 2842dd285b5SMark Cave-Ayland memory_region_size(&ts->vram_mem), 2852dd285b5SMark Cave-Ayland DIRTY_MEMORY_VGA); 2862dd285b5SMark Cave-Ayland 287d18e1012SMark Cave-Ayland for (y = 0; y < ts->height; y++, page += ds) { 2882dd285b5SMark Cave-Ayland if (tcx_check_dirty(ts, snap, page, ds)) { 289eee0b836Sblueswir1 if (y_start < 0) 290eee0b836Sblueswir1 y_start = y; 2912dd285b5SMark Cave-Ayland 292eee0b836Sblueswir1 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); 29355d7bfe2SMark Cave-Ayland if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) { 29455d7bfe2SMark Cave-Ayland tcx_draw_cursor32(ts, d, y, ts->width); 29555d7bfe2SMark Cave-Ayland } 296eee0b836Sblueswir1 } else { 297eee0b836Sblueswir1 if (y_start >= 0) { 298eee0b836Sblueswir1 /* flush to display */ 299c78f7137SGerd Hoffmann dpy_gfx_update(ts->con, 0, y_start, 300eee0b836Sblueswir1 ts->width, y - y_start); 301eee0b836Sblueswir1 y_start = -1; 302eee0b836Sblueswir1 } 303eee0b836Sblueswir1 } 304d18e1012SMark Cave-Ayland d += dd; 305d18e1012SMark Cave-Ayland s += ds; 306d18e1012SMark Cave-Ayland cptr += ds; 307d18e1012SMark Cave-Ayland s24 += ds; 308eee0b836Sblueswir1 } 309eee0b836Sblueswir1 if (y_start >= 0) { 310eee0b836Sblueswir1 /* flush to display */ 311c78f7137SGerd Hoffmann dpy_gfx_update(ts->con, 0, y_start, 312eee0b836Sblueswir1 ts->width, y - y_start); 313eee0b836Sblueswir1 } 3142dd285b5SMark Cave-Ayland g_free(snap); 315eee0b836Sblueswir1 } 316eee0b836Sblueswir1 31795219897Spbrook static void tcx_invalidate_display(void *opaque) 318420557e8Sbellard { 319420557e8Sbellard TCXState *s = opaque; 320420557e8Sbellard 3219800b3c2SMark Cave-Ayland tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem)); 322c78f7137SGerd Hoffmann qemu_console_resize(s->con, s->width, s->height); 323e80cfcfcSbellard } 324e80cfcfcSbellard 325eee0b836Sblueswir1 static void tcx24_invalidate_display(void *opaque) 326eee0b836Sblueswir1 { 327eee0b836Sblueswir1 TCXState *s = opaque; 328eee0b836Sblueswir1 3299800b3c2SMark Cave-Ayland tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem)); 330c78f7137SGerd Hoffmann qemu_console_resize(s->con, s->width, s->height); 331eee0b836Sblueswir1 } 332eee0b836Sblueswir1 333e59fb374SJuan Quintela static int vmstate_tcx_post_load(void *opaque, int version_id) 334e80cfcfcSbellard { 335e80cfcfcSbellard TCXState *s = opaque; 336e80cfcfcSbellard 33721206a10Sbellard update_palette_entries(s, 0, 256); 3389800b3c2SMark Cave-Ayland tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem)); 339420557e8Sbellard return 0; 340420557e8Sbellard } 341420557e8Sbellard 342c0c41a4bSBlue Swirl static const VMStateDescription vmstate_tcx = { 343c0c41a4bSBlue Swirl .name ="tcx", 344c0c41a4bSBlue Swirl .version_id = 4, 345c0c41a4bSBlue Swirl .minimum_version_id = 4, 346752ff2faSJuan Quintela .post_load = vmstate_tcx_post_load, 347f0613160SRichard Henderson .fields = (const VMStateField[]) { 348c0c41a4bSBlue Swirl VMSTATE_UINT16(height, TCXState), 349c0c41a4bSBlue Swirl VMSTATE_UINT16(width, TCXState), 350c0c41a4bSBlue Swirl VMSTATE_UINT16(depth, TCXState), 351c0c41a4bSBlue Swirl VMSTATE_BUFFER(r, TCXState), 352c0c41a4bSBlue Swirl VMSTATE_BUFFER(g, TCXState), 353c0c41a4bSBlue Swirl VMSTATE_BUFFER(b, TCXState), 354c0c41a4bSBlue Swirl VMSTATE_UINT8(dac_index, TCXState), 355c0c41a4bSBlue Swirl VMSTATE_UINT8(dac_state, TCXState), 356c0c41a4bSBlue Swirl VMSTATE_END_OF_LIST() 357c0c41a4bSBlue Swirl } 358c0c41a4bSBlue Swirl }; 359c0c41a4bSBlue Swirl 3607f23f812SMichael S. Tsirkin static void tcx_reset(DeviceState *d) 361420557e8Sbellard { 36201774ddbSAndreas Färber TCXState *s = TCX(d); 363420557e8Sbellard 364e80cfcfcSbellard /* Initialize palette */ 36555d7bfe2SMark Cave-Ayland memset(s->r, 0, 260); 36655d7bfe2SMark Cave-Ayland memset(s->g, 0, 260); 36755d7bfe2SMark Cave-Ayland memset(s->b, 0, 260); 368e80cfcfcSbellard s->r[255] = s->g[255] = s->b[255] = 255; 36955d7bfe2SMark Cave-Ayland s->r[256] = s->g[256] = s->b[256] = 255; 37055d7bfe2SMark Cave-Ayland s->r[258] = s->g[258] = s->b[258] = 255; 37155d7bfe2SMark Cave-Ayland update_palette_entries(s, 0, 260); 372e80cfcfcSbellard memset(s->vram, 0, MAXX*MAXY); 373d08151bfSAvi Kivity memory_region_reset_dirty(&s->vram_mem, 0, MAXX * MAXY * (1 + 4 + 4), 374d08151bfSAvi Kivity DIRTY_MEMORY_VGA); 3756f7e9aecSbellard s->dac_index = 0; 3766f7e9aecSbellard s->dac_state = 0; 37755d7bfe2SMark Cave-Ayland s->cursx = 0xf000; /* Put cursor off screen */ 37855d7bfe2SMark Cave-Ayland s->cursy = 0xf000; 379420557e8Sbellard } 380420557e8Sbellard 381a8170e5eSAvi Kivity static uint64_t tcx_dac_readl(void *opaque, hwaddr addr, 382d08151bfSAvi Kivity unsigned size) 3836f7e9aecSbellard { 38455d7bfe2SMark Cave-Ayland TCXState *s = opaque; 38555d7bfe2SMark Cave-Ayland uint32_t val = 0; 38655d7bfe2SMark Cave-Ayland 38755d7bfe2SMark Cave-Ayland switch (s->dac_state) { 38855d7bfe2SMark Cave-Ayland case 0: 38955d7bfe2SMark Cave-Ayland val = s->r[s->dac_index] << 24; 39055d7bfe2SMark Cave-Ayland s->dac_state++; 39155d7bfe2SMark Cave-Ayland break; 39255d7bfe2SMark Cave-Ayland case 1: 39355d7bfe2SMark Cave-Ayland val = s->g[s->dac_index] << 24; 39455d7bfe2SMark Cave-Ayland s->dac_state++; 39555d7bfe2SMark Cave-Ayland break; 39655d7bfe2SMark Cave-Ayland case 2: 39755d7bfe2SMark Cave-Ayland val = s->b[s->dac_index] << 24; 39855d7bfe2SMark Cave-Ayland s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */ 399ada44065SPhilippe Mathieu-Daudé /* fall through */ 40055d7bfe2SMark Cave-Ayland default: 40155d7bfe2SMark Cave-Ayland s->dac_state = 0; 40255d7bfe2SMark Cave-Ayland break; 40355d7bfe2SMark Cave-Ayland } 40455d7bfe2SMark Cave-Ayland 40555d7bfe2SMark Cave-Ayland return val; 4066f7e9aecSbellard } 4076f7e9aecSbellard 408a8170e5eSAvi Kivity static void tcx_dac_writel(void *opaque, hwaddr addr, uint64_t val, 409d08151bfSAvi Kivity unsigned size) 4106f7e9aecSbellard { 4116f7e9aecSbellard TCXState *s = opaque; 41255d7bfe2SMark Cave-Ayland unsigned index; 4136f7e9aecSbellard 414e64d7d59Sblueswir1 switch (addr) { 41555d7bfe2SMark Cave-Ayland case 0: /* Address */ 4166f7e9aecSbellard s->dac_index = val >> 24; 4176f7e9aecSbellard s->dac_state = 0; 4186f7e9aecSbellard break; 41955d7bfe2SMark Cave-Ayland case 4: /* Pixel colours */ 42055d7bfe2SMark Cave-Ayland case 12: /* Overlay (cursor) colours */ 42155d7bfe2SMark Cave-Ayland if (addr & 8) { 42255d7bfe2SMark Cave-Ayland index = (s->dac_index & 3) + 256; 42355d7bfe2SMark Cave-Ayland } else { 42455d7bfe2SMark Cave-Ayland index = s->dac_index; 42555d7bfe2SMark Cave-Ayland } 4266f7e9aecSbellard switch (s->dac_state) { 4276f7e9aecSbellard case 0: 42855d7bfe2SMark Cave-Ayland s->r[index] = val >> 24; 42955d7bfe2SMark Cave-Ayland update_palette_entries(s, index, index + 1); 4306f7e9aecSbellard s->dac_state++; 4316f7e9aecSbellard break; 4326f7e9aecSbellard case 1: 43355d7bfe2SMark Cave-Ayland s->g[index] = val >> 24; 43455d7bfe2SMark Cave-Ayland update_palette_entries(s, index, index + 1); 4356f7e9aecSbellard s->dac_state++; 4366f7e9aecSbellard break; 4376f7e9aecSbellard case 2: 43855d7bfe2SMark Cave-Ayland s->b[index] = val >> 24; 43955d7bfe2SMark Cave-Ayland update_palette_entries(s, index, index + 1); 44055d7bfe2SMark Cave-Ayland s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */ 441ada44065SPhilippe Mathieu-Daudé /* fall through */ 4426f7e9aecSbellard default: 4436f7e9aecSbellard s->dac_state = 0; 4446f7e9aecSbellard break; 4456f7e9aecSbellard } 4466f7e9aecSbellard break; 44755d7bfe2SMark Cave-Ayland default: /* Control registers */ 4486f7e9aecSbellard break; 4496f7e9aecSbellard } 4506f7e9aecSbellard } 4516f7e9aecSbellard 452d08151bfSAvi Kivity static const MemoryRegionOps tcx_dac_ops = { 453d08151bfSAvi Kivity .read = tcx_dac_readl, 454d08151bfSAvi Kivity .write = tcx_dac_writel, 455d08151bfSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 456d08151bfSAvi Kivity .valid = { 457d08151bfSAvi Kivity .min_access_size = 4, 458d08151bfSAvi Kivity .max_access_size = 4, 459d08151bfSAvi Kivity }, 4606f7e9aecSbellard }; 4616f7e9aecSbellard 46255d7bfe2SMark Cave-Ayland static uint64_t tcx_stip_readl(void *opaque, hwaddr addr, 463d08151bfSAvi Kivity unsigned size) 4648508b89eSblueswir1 { 4658508b89eSblueswir1 return 0; 4668508b89eSblueswir1 } 4678508b89eSblueswir1 46855d7bfe2SMark Cave-Ayland static void tcx_stip_writel(void *opaque, hwaddr addr, 469d08151bfSAvi Kivity uint64_t val, unsigned size) 4708508b89eSblueswir1 { 47155d7bfe2SMark Cave-Ayland TCXState *s = opaque; 47255d7bfe2SMark Cave-Ayland int i; 47355d7bfe2SMark Cave-Ayland uint32_t col; 47455d7bfe2SMark Cave-Ayland 47555d7bfe2SMark Cave-Ayland if (!(addr & 4)) { 47655d7bfe2SMark Cave-Ayland s->tmpblit = val; 47755d7bfe2SMark Cave-Ayland } else { 47855d7bfe2SMark Cave-Ayland addr = (addr >> 3) & 0xfffff; 47955d7bfe2SMark Cave-Ayland col = cpu_to_be32(s->tmpblit); 48055d7bfe2SMark Cave-Ayland if (s->depth == 24) { 48155d7bfe2SMark Cave-Ayland for (i = 0; i < 32; i++) { 48255d7bfe2SMark Cave-Ayland if (val & 0x80000000) { 48355d7bfe2SMark Cave-Ayland s->vram[addr + i] = s->tmpblit; 48455d7bfe2SMark Cave-Ayland s->vram24[addr + i] = col; 48555d7bfe2SMark Cave-Ayland } 48655d7bfe2SMark Cave-Ayland val <<= 1; 48755d7bfe2SMark Cave-Ayland } 48855d7bfe2SMark Cave-Ayland } else { 48955d7bfe2SMark Cave-Ayland for (i = 0; i < 32; i++) { 49055d7bfe2SMark Cave-Ayland if (val & 0x80000000) { 49155d7bfe2SMark Cave-Ayland s->vram[addr + i] = s->tmpblit; 49255d7bfe2SMark Cave-Ayland } 49355d7bfe2SMark Cave-Ayland val <<= 1; 49455d7bfe2SMark Cave-Ayland } 49555d7bfe2SMark Cave-Ayland } 49697394580SMark Cave-Ayland tcx_set_dirty(s, addr, 32); 49755d7bfe2SMark Cave-Ayland } 4988508b89eSblueswir1 } 4998508b89eSblueswir1 50055d7bfe2SMark Cave-Ayland static void tcx_rstip_writel(void *opaque, hwaddr addr, 50155d7bfe2SMark Cave-Ayland uint64_t val, unsigned size) 50255d7bfe2SMark Cave-Ayland { 50355d7bfe2SMark Cave-Ayland TCXState *s = opaque; 50455d7bfe2SMark Cave-Ayland int i; 50555d7bfe2SMark Cave-Ayland uint32_t col; 50655d7bfe2SMark Cave-Ayland 50755d7bfe2SMark Cave-Ayland if (!(addr & 4)) { 50855d7bfe2SMark Cave-Ayland s->tmpblit = val; 50955d7bfe2SMark Cave-Ayland } else { 51055d7bfe2SMark Cave-Ayland addr = (addr >> 3) & 0xfffff; 51155d7bfe2SMark Cave-Ayland col = cpu_to_be32(s->tmpblit); 51255d7bfe2SMark Cave-Ayland if (s->depth == 24) { 51355d7bfe2SMark Cave-Ayland for (i = 0; i < 32; i++) { 51455d7bfe2SMark Cave-Ayland if (val & 0x80000000) { 51555d7bfe2SMark Cave-Ayland s->vram[addr + i] = s->tmpblit; 51655d7bfe2SMark Cave-Ayland s->vram24[addr + i] = col; 51755d7bfe2SMark Cave-Ayland s->cplane[addr + i] = col; 51855d7bfe2SMark Cave-Ayland } 51955d7bfe2SMark Cave-Ayland val <<= 1; 52055d7bfe2SMark Cave-Ayland } 52155d7bfe2SMark Cave-Ayland } else { 52255d7bfe2SMark Cave-Ayland for (i = 0; i < 32; i++) { 52355d7bfe2SMark Cave-Ayland if (val & 0x80000000) { 52455d7bfe2SMark Cave-Ayland s->vram[addr + i] = s->tmpblit; 52555d7bfe2SMark Cave-Ayland } 52655d7bfe2SMark Cave-Ayland val <<= 1; 52755d7bfe2SMark Cave-Ayland } 52855d7bfe2SMark Cave-Ayland } 52997394580SMark Cave-Ayland tcx_set_dirty(s, addr, 32); 53055d7bfe2SMark Cave-Ayland } 53155d7bfe2SMark Cave-Ayland } 53255d7bfe2SMark Cave-Ayland 53355d7bfe2SMark Cave-Ayland static const MemoryRegionOps tcx_stip_ops = { 53455d7bfe2SMark Cave-Ayland .read = tcx_stip_readl, 53555d7bfe2SMark Cave-Ayland .write = tcx_stip_writel, 53655d7bfe2SMark Cave-Ayland .endianness = DEVICE_NATIVE_ENDIAN, 537ae5643ecSPhilippe Mathieu-Daudé .impl = { 53855d7bfe2SMark Cave-Ayland .min_access_size = 4, 53955d7bfe2SMark Cave-Ayland .max_access_size = 4, 54055d7bfe2SMark Cave-Ayland }, 541ae5643ecSPhilippe Mathieu-Daudé .valid = { 542ae5643ecSPhilippe Mathieu-Daudé .min_access_size = 4, 543ae5643ecSPhilippe Mathieu-Daudé .max_access_size = 8, 544ae5643ecSPhilippe Mathieu-Daudé }, 54555d7bfe2SMark Cave-Ayland }; 54655d7bfe2SMark Cave-Ayland 54755d7bfe2SMark Cave-Ayland static const MemoryRegionOps tcx_rstip_ops = { 54855d7bfe2SMark Cave-Ayland .read = tcx_stip_readl, 54955d7bfe2SMark Cave-Ayland .write = tcx_rstip_writel, 55055d7bfe2SMark Cave-Ayland .endianness = DEVICE_NATIVE_ENDIAN, 551ae5643ecSPhilippe Mathieu-Daudé .impl = { 55255d7bfe2SMark Cave-Ayland .min_access_size = 4, 55355d7bfe2SMark Cave-Ayland .max_access_size = 4, 55455d7bfe2SMark Cave-Ayland }, 555ae5643ecSPhilippe Mathieu-Daudé .valid = { 556ae5643ecSPhilippe Mathieu-Daudé .min_access_size = 4, 557ae5643ecSPhilippe Mathieu-Daudé .max_access_size = 8, 558ae5643ecSPhilippe Mathieu-Daudé }, 55955d7bfe2SMark Cave-Ayland }; 56055d7bfe2SMark Cave-Ayland 56155d7bfe2SMark Cave-Ayland static uint64_t tcx_blit_readl(void *opaque, hwaddr addr, 56255d7bfe2SMark Cave-Ayland unsigned size) 56355d7bfe2SMark Cave-Ayland { 56455d7bfe2SMark Cave-Ayland return 0; 56555d7bfe2SMark Cave-Ayland } 56655d7bfe2SMark Cave-Ayland 56755d7bfe2SMark Cave-Ayland static void tcx_blit_writel(void *opaque, hwaddr addr, 56855d7bfe2SMark Cave-Ayland uint64_t val, unsigned size) 56955d7bfe2SMark Cave-Ayland { 57055d7bfe2SMark Cave-Ayland TCXState *s = opaque; 57155d7bfe2SMark Cave-Ayland uint32_t adsr, len; 57255d7bfe2SMark Cave-Ayland int i; 57355d7bfe2SMark Cave-Ayland 57455d7bfe2SMark Cave-Ayland if (!(addr & 4)) { 57555d7bfe2SMark Cave-Ayland s->tmpblit = val; 57655d7bfe2SMark Cave-Ayland } else { 57755d7bfe2SMark Cave-Ayland addr = (addr >> 3) & 0xfffff; 57855d7bfe2SMark Cave-Ayland adsr = val & 0xffffff; 57955d7bfe2SMark Cave-Ayland len = ((val >> 24) & 0x1f) + 1; 58055d7bfe2SMark Cave-Ayland if (adsr == 0xffffff) { 58155d7bfe2SMark Cave-Ayland memset(&s->vram[addr], s->tmpblit, len); 58255d7bfe2SMark Cave-Ayland if (s->depth == 24) { 58355d7bfe2SMark Cave-Ayland val = s->tmpblit & 0xffffff; 58455d7bfe2SMark Cave-Ayland val = cpu_to_be32(val); 58555d7bfe2SMark Cave-Ayland for (i = 0; i < len; i++) { 58655d7bfe2SMark Cave-Ayland s->vram24[addr + i] = val; 58755d7bfe2SMark Cave-Ayland } 58855d7bfe2SMark Cave-Ayland } 58955d7bfe2SMark Cave-Ayland } else { 59055d7bfe2SMark Cave-Ayland memcpy(&s->vram[addr], &s->vram[adsr], len); 59155d7bfe2SMark Cave-Ayland if (s->depth == 24) { 59255d7bfe2SMark Cave-Ayland memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4); 59355d7bfe2SMark Cave-Ayland } 59455d7bfe2SMark Cave-Ayland } 59597394580SMark Cave-Ayland tcx_set_dirty(s, addr, len); 59655d7bfe2SMark Cave-Ayland } 59755d7bfe2SMark Cave-Ayland } 59855d7bfe2SMark Cave-Ayland 59955d7bfe2SMark Cave-Ayland static void tcx_rblit_writel(void *opaque, hwaddr addr, 60055d7bfe2SMark Cave-Ayland uint64_t val, unsigned size) 60155d7bfe2SMark Cave-Ayland { 60255d7bfe2SMark Cave-Ayland TCXState *s = opaque; 60355d7bfe2SMark Cave-Ayland uint32_t adsr, len; 60455d7bfe2SMark Cave-Ayland int i; 60555d7bfe2SMark Cave-Ayland 60655d7bfe2SMark Cave-Ayland if (!(addr & 4)) { 60755d7bfe2SMark Cave-Ayland s->tmpblit = val; 60855d7bfe2SMark Cave-Ayland } else { 60955d7bfe2SMark Cave-Ayland addr = (addr >> 3) & 0xfffff; 61055d7bfe2SMark Cave-Ayland adsr = val & 0xffffff; 61155d7bfe2SMark Cave-Ayland len = ((val >> 24) & 0x1f) + 1; 61255d7bfe2SMark Cave-Ayland if (adsr == 0xffffff) { 61355d7bfe2SMark Cave-Ayland memset(&s->vram[addr], s->tmpblit, len); 61455d7bfe2SMark Cave-Ayland if (s->depth == 24) { 61555d7bfe2SMark Cave-Ayland val = s->tmpblit & 0xffffff; 61655d7bfe2SMark Cave-Ayland val = cpu_to_be32(val); 61755d7bfe2SMark Cave-Ayland for (i = 0; i < len; i++) { 61855d7bfe2SMark Cave-Ayland s->vram24[addr + i] = val; 61955d7bfe2SMark Cave-Ayland s->cplane[addr + i] = val; 62055d7bfe2SMark Cave-Ayland } 62155d7bfe2SMark Cave-Ayland } 62255d7bfe2SMark Cave-Ayland } else { 62355d7bfe2SMark Cave-Ayland memcpy(&s->vram[addr], &s->vram[adsr], len); 62455d7bfe2SMark Cave-Ayland if (s->depth == 24) { 62555d7bfe2SMark Cave-Ayland memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4); 62655d7bfe2SMark Cave-Ayland memcpy(&s->cplane[addr], &s->cplane[adsr], len * 4); 62755d7bfe2SMark Cave-Ayland } 62855d7bfe2SMark Cave-Ayland } 62997394580SMark Cave-Ayland tcx_set_dirty(s, addr, len); 63055d7bfe2SMark Cave-Ayland } 63155d7bfe2SMark Cave-Ayland } 63255d7bfe2SMark Cave-Ayland 63355d7bfe2SMark Cave-Ayland static const MemoryRegionOps tcx_blit_ops = { 63455d7bfe2SMark Cave-Ayland .read = tcx_blit_readl, 63555d7bfe2SMark Cave-Ayland .write = tcx_blit_writel, 63655d7bfe2SMark Cave-Ayland .endianness = DEVICE_NATIVE_ENDIAN, 63748e5c7f3SMark Cave-Ayland .impl = { 63855d7bfe2SMark Cave-Ayland .min_access_size = 4, 63955d7bfe2SMark Cave-Ayland .max_access_size = 4, 64055d7bfe2SMark Cave-Ayland }, 64148e5c7f3SMark Cave-Ayland .valid = { 64248e5c7f3SMark Cave-Ayland .min_access_size = 4, 64348e5c7f3SMark Cave-Ayland .max_access_size = 8, 64448e5c7f3SMark Cave-Ayland }, 64555d7bfe2SMark Cave-Ayland }; 64655d7bfe2SMark Cave-Ayland 64755d7bfe2SMark Cave-Ayland static const MemoryRegionOps tcx_rblit_ops = { 64855d7bfe2SMark Cave-Ayland .read = tcx_blit_readl, 64955d7bfe2SMark Cave-Ayland .write = tcx_rblit_writel, 65055d7bfe2SMark Cave-Ayland .endianness = DEVICE_NATIVE_ENDIAN, 651ae5643ecSPhilippe Mathieu-Daudé .impl = { 65255d7bfe2SMark Cave-Ayland .min_access_size = 4, 65355d7bfe2SMark Cave-Ayland .max_access_size = 4, 65455d7bfe2SMark Cave-Ayland }, 655ae5643ecSPhilippe Mathieu-Daudé .valid = { 656ae5643ecSPhilippe Mathieu-Daudé .min_access_size = 4, 657ae5643ecSPhilippe Mathieu-Daudé .max_access_size = 8, 658ae5643ecSPhilippe Mathieu-Daudé }, 65955d7bfe2SMark Cave-Ayland }; 66055d7bfe2SMark Cave-Ayland 66155d7bfe2SMark Cave-Ayland static void tcx_invalidate_cursor_position(TCXState *s) 66255d7bfe2SMark Cave-Ayland { 66355d7bfe2SMark Cave-Ayland int ymin, ymax, start, end; 66455d7bfe2SMark Cave-Ayland 66555d7bfe2SMark Cave-Ayland /* invalidate only near the cursor */ 66655d7bfe2SMark Cave-Ayland ymin = s->cursy; 66755d7bfe2SMark Cave-Ayland if (ymin >= s->height) { 66855d7bfe2SMark Cave-Ayland return; 66955d7bfe2SMark Cave-Ayland } 67055d7bfe2SMark Cave-Ayland ymax = MIN(s->height, ymin + 32); 67155d7bfe2SMark Cave-Ayland start = ymin * 1024; 67255d7bfe2SMark Cave-Ayland end = ymax * 1024; 67355d7bfe2SMark Cave-Ayland 67497394580SMark Cave-Ayland tcx_set_dirty(s, start, end - start); 67555d7bfe2SMark Cave-Ayland } 67655d7bfe2SMark Cave-Ayland 67755d7bfe2SMark Cave-Ayland static uint64_t tcx_thc_readl(void *opaque, hwaddr addr, 67855d7bfe2SMark Cave-Ayland unsigned size) 67955d7bfe2SMark Cave-Ayland { 68055d7bfe2SMark Cave-Ayland TCXState *s = opaque; 68155d7bfe2SMark Cave-Ayland uint64_t val; 68255d7bfe2SMark Cave-Ayland 68355d7bfe2SMark Cave-Ayland if (addr == TCX_THC_MISC) { 68455d7bfe2SMark Cave-Ayland val = s->thcmisc | 0x02000000; 68555d7bfe2SMark Cave-Ayland } else { 68655d7bfe2SMark Cave-Ayland val = 0; 68755d7bfe2SMark Cave-Ayland } 68855d7bfe2SMark Cave-Ayland return val; 68955d7bfe2SMark Cave-Ayland } 69055d7bfe2SMark Cave-Ayland 69155d7bfe2SMark Cave-Ayland static void tcx_thc_writel(void *opaque, hwaddr addr, 69255d7bfe2SMark Cave-Ayland uint64_t val, unsigned size) 69355d7bfe2SMark Cave-Ayland { 69455d7bfe2SMark Cave-Ayland TCXState *s = opaque; 69555d7bfe2SMark Cave-Ayland 69655d7bfe2SMark Cave-Ayland if (addr == TCX_THC_CURSXY) { 69755d7bfe2SMark Cave-Ayland tcx_invalidate_cursor_position(s); 69855d7bfe2SMark Cave-Ayland s->cursx = val >> 16; 69955d7bfe2SMark Cave-Ayland s->cursy = val; 70055d7bfe2SMark Cave-Ayland tcx_invalidate_cursor_position(s); 70155d7bfe2SMark Cave-Ayland } else if (addr >= TCX_THC_CURSMASK && addr < TCX_THC_CURSMASK + 128) { 70255d7bfe2SMark Cave-Ayland s->cursmask[(addr - TCX_THC_CURSMASK) >> 2] = val; 70355d7bfe2SMark Cave-Ayland tcx_invalidate_cursor_position(s); 70455d7bfe2SMark Cave-Ayland } else if (addr >= TCX_THC_CURSBITS && addr < TCX_THC_CURSBITS + 128) { 70555d7bfe2SMark Cave-Ayland s->cursbits[(addr - TCX_THC_CURSBITS) >> 2] = val; 70655d7bfe2SMark Cave-Ayland tcx_invalidate_cursor_position(s); 70755d7bfe2SMark Cave-Ayland } else if (addr == TCX_THC_MISC) { 70855d7bfe2SMark Cave-Ayland s->thcmisc = val; 70955d7bfe2SMark Cave-Ayland } 71055d7bfe2SMark Cave-Ayland 71155d7bfe2SMark Cave-Ayland } 71255d7bfe2SMark Cave-Ayland 71355d7bfe2SMark Cave-Ayland static const MemoryRegionOps tcx_thc_ops = { 71455d7bfe2SMark Cave-Ayland .read = tcx_thc_readl, 71555d7bfe2SMark Cave-Ayland .write = tcx_thc_writel, 71655d7bfe2SMark Cave-Ayland .endianness = DEVICE_NATIVE_ENDIAN, 71755d7bfe2SMark Cave-Ayland .valid = { 71855d7bfe2SMark Cave-Ayland .min_access_size = 4, 71955d7bfe2SMark Cave-Ayland .max_access_size = 4, 72055d7bfe2SMark Cave-Ayland }, 72155d7bfe2SMark Cave-Ayland }; 72255d7bfe2SMark Cave-Ayland 72355d7bfe2SMark Cave-Ayland static uint64_t tcx_dummy_readl(void *opaque, hwaddr addr, 72455d7bfe2SMark Cave-Ayland unsigned size) 72555d7bfe2SMark Cave-Ayland { 72655d7bfe2SMark Cave-Ayland return 0; 72755d7bfe2SMark Cave-Ayland } 72855d7bfe2SMark Cave-Ayland 72955d7bfe2SMark Cave-Ayland static void tcx_dummy_writel(void *opaque, hwaddr addr, 73055d7bfe2SMark Cave-Ayland uint64_t val, unsigned size) 73155d7bfe2SMark Cave-Ayland { 73255d7bfe2SMark Cave-Ayland } 73355d7bfe2SMark Cave-Ayland 73455d7bfe2SMark Cave-Ayland static const MemoryRegionOps tcx_dummy_ops = { 73555d7bfe2SMark Cave-Ayland .read = tcx_dummy_readl, 73655d7bfe2SMark Cave-Ayland .write = tcx_dummy_writel, 737d08151bfSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 738d08151bfSAvi Kivity .valid = { 739d08151bfSAvi Kivity .min_access_size = 4, 740d08151bfSAvi Kivity .max_access_size = 4, 741d08151bfSAvi Kivity }, 7428508b89eSblueswir1 }; 7438508b89eSblueswir1 744380cd056SGerd Hoffmann static const GraphicHwOps tcx_ops = { 745380cd056SGerd Hoffmann .invalidate = tcx_invalidate_display, 746380cd056SGerd Hoffmann .gfx_update = tcx_update_display, 747380cd056SGerd Hoffmann }; 748380cd056SGerd Hoffmann 749380cd056SGerd Hoffmann static const GraphicHwOps tcx24_ops = { 750380cd056SGerd Hoffmann .invalidate = tcx24_invalidate_display, 751380cd056SGerd Hoffmann .gfx_update = tcx24_update_display, 752380cd056SGerd Hoffmann }; 753380cd056SGerd Hoffmann 75401b91ac2SMark Cave-Ayland static void tcx_initfn(Object *obj) 75501b91ac2SMark Cave-Ayland { 75601b91ac2SMark Cave-Ayland SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 75701b91ac2SMark Cave-Ayland TCXState *s = TCX(obj); 75801b91ac2SMark Cave-Ayland 75952013bceSPhilippe Mathieu-Daudé memory_region_init_rom_nomigrate(&s->rom, obj, "tcx.prom", 76052013bceSPhilippe Mathieu-Daudé FCODE_MAX_ROM_SIZE, &error_fatal); 76101b91ac2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->rom); 76201b91ac2SMark Cave-Ayland 76355d7bfe2SMark Cave-Ayland /* 2/STIP : Stippler */ 764b21de199SThomas Huth memory_region_init_io(&s->stip, obj, &tcx_stip_ops, s, "tcx.stip", 76555d7bfe2SMark Cave-Ayland TCX_STIP_NREGS); 76655d7bfe2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->stip); 76755d7bfe2SMark Cave-Ayland 76855d7bfe2SMark Cave-Ayland /* 3/BLIT : Blitter */ 769b21de199SThomas Huth memory_region_init_io(&s->blit, obj, &tcx_blit_ops, s, "tcx.blit", 77055d7bfe2SMark Cave-Ayland TCX_BLIT_NREGS); 77155d7bfe2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->blit); 77255d7bfe2SMark Cave-Ayland 77355d7bfe2SMark Cave-Ayland /* 5/RSTIP : Raw Stippler */ 774b21de199SThomas Huth memory_region_init_io(&s->rstip, obj, &tcx_rstip_ops, s, "tcx.rstip", 77555d7bfe2SMark Cave-Ayland TCX_RSTIP_NREGS); 77655d7bfe2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->rstip); 77755d7bfe2SMark Cave-Ayland 77855d7bfe2SMark Cave-Ayland /* 6/RBLIT : Raw Blitter */ 779b21de199SThomas Huth memory_region_init_io(&s->rblit, obj, &tcx_rblit_ops, s, "tcx.rblit", 78055d7bfe2SMark Cave-Ayland TCX_RBLIT_NREGS); 78155d7bfe2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->rblit); 78255d7bfe2SMark Cave-Ayland 78355d7bfe2SMark Cave-Ayland /* 7/TEC : ??? */ 784b21de199SThomas Huth memory_region_init_io(&s->tec, obj, &tcx_dummy_ops, s, "tcx.tec", 785b21de199SThomas Huth TCX_TEC_NREGS); 78655d7bfe2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->tec); 78755d7bfe2SMark Cave-Ayland 78855d7bfe2SMark Cave-Ayland /* 8/CMAP : DAC */ 789b21de199SThomas Huth memory_region_init_io(&s->dac, obj, &tcx_dac_ops, s, "tcx.dac", 790b21de199SThomas Huth TCX_DAC_NREGS); 79101b91ac2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->dac); 79201b91ac2SMark Cave-Ayland 79355d7bfe2SMark Cave-Ayland /* 9/THC : Cursor */ 794b21de199SThomas Huth memory_region_init_io(&s->thc, obj, &tcx_thc_ops, s, "tcx.thc", 79555d7bfe2SMark Cave-Ayland TCX_THC_NREGS); 79655d7bfe2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->thc); 79701b91ac2SMark Cave-Ayland 79855d7bfe2SMark Cave-Ayland /* 11/DHC : ??? */ 799b21de199SThomas Huth memory_region_init_io(&s->dhc, obj, &tcx_dummy_ops, s, "tcx.dhc", 80055d7bfe2SMark Cave-Ayland TCX_DHC_NREGS); 80155d7bfe2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->dhc); 80255d7bfe2SMark Cave-Ayland 80355d7bfe2SMark Cave-Ayland /* 12/ALT : ??? */ 804b21de199SThomas Huth memory_region_init_io(&s->alt, obj, &tcx_dummy_ops, s, "tcx.alt", 80555d7bfe2SMark Cave-Ayland TCX_ALT_NREGS); 80655d7bfe2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->alt); 80701b91ac2SMark Cave-Ayland } 80801b91ac2SMark Cave-Ayland 809d4ad9decSMark Cave-Ayland static void tcx_realizefn(DeviceState *dev, Error **errp) 810f40070c3SBlue Swirl { 811d4ad9decSMark Cave-Ayland SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 81201774ddbSAndreas Färber TCXState *s = TCX(dev); 813d08151bfSAvi Kivity ram_addr_t vram_offset = 0; 814da87dd7bSMark Cave-Ayland int size, ret; 815dc828ca1Spbrook uint8_t *vram_base; 816da87dd7bSMark Cave-Ayland char *fcode_filename; 817dc828ca1Spbrook 8181cfe48c1SPeter Maydell memory_region_init_ram_nomigrate(&s->vram_mem, OBJECT(s), "tcx.vram", 819f8ed85acSMarkus Armbruster s->vram_size * (1 + 4 + 4), &error_fatal); 820c5705a77SAvi Kivity vmstate_register_ram_global(&s->vram_mem); 82174259ae5SPaolo Bonzini memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA); 822d08151bfSAvi Kivity vram_base = memory_region_get_ram_ptr(&s->vram_mem); 823e80cfcfcSbellard 82455d7bfe2SMark Cave-Ayland /* 10/ROM : FCode ROM */ 825da87dd7bSMark Cave-Ayland vmstate_register_ram_global(&s->rom); 826da87dd7bSMark Cave-Ayland fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, TCX_ROM_FILE); 827da87dd7bSMark Cave-Ayland if (fcode_filename) { 82874976386SMark Cave-Ayland ret = load_image_mr(fcode_filename, &s->rom); 8298684e85cSShannon Zhao g_free(fcode_filename); 830da87dd7bSMark Cave-Ayland if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) { 8310765691eSMarkus Armbruster warn_report("tcx: could not load prom '%s'", TCX_ROM_FILE); 832da87dd7bSMark Cave-Ayland } 833da87dd7bSMark Cave-Ayland } 834da87dd7bSMark Cave-Ayland 83555d7bfe2SMark Cave-Ayland /* 0/DFB8 : 8-bit plane */ 836eee0b836Sblueswir1 s->vram = vram_base; 837ee6847d1SGerd Hoffmann size = s->vram_size; 8383eadad55SPaolo Bonzini memory_region_init_alias(&s->vram_8bit, OBJECT(s), "tcx.vram.8bit", 839d08151bfSAvi Kivity &s->vram_mem, vram_offset, size); 840d4ad9decSMark Cave-Ayland sysbus_init_mmio(sbd, &s->vram_8bit); 841eee0b836Sblueswir1 vram_offset += size; 842eee0b836Sblueswir1 vram_base += size; 843eee0b836Sblueswir1 84455d7bfe2SMark Cave-Ayland /* 1/DFB24 : 24bit plane */ 845ee6847d1SGerd Hoffmann size = s->vram_size * 4; 846eee0b836Sblueswir1 s->vram24 = (uint32_t *)vram_base; 847eee0b836Sblueswir1 s->vram24_offset = vram_offset; 8483eadad55SPaolo Bonzini memory_region_init_alias(&s->vram_24bit, OBJECT(s), "tcx.vram.24bit", 849d08151bfSAvi Kivity &s->vram_mem, vram_offset, size); 850d4ad9decSMark Cave-Ayland sysbus_init_mmio(sbd, &s->vram_24bit); 851eee0b836Sblueswir1 vram_offset += size; 852eee0b836Sblueswir1 vram_base += size; 853eee0b836Sblueswir1 85455d7bfe2SMark Cave-Ayland /* 4/RDFB32 : Raw Framebuffer */ 855ee6847d1SGerd Hoffmann size = s->vram_size * 4; 856eee0b836Sblueswir1 s->cplane = (uint32_t *)vram_base; 857eee0b836Sblueswir1 s->cplane_offset = vram_offset; 8583eadad55SPaolo Bonzini memory_region_init_alias(&s->vram_cplane, OBJECT(s), "tcx.vram.cplane", 859d08151bfSAvi Kivity &s->vram_mem, vram_offset, size); 860d4ad9decSMark Cave-Ayland sysbus_init_mmio(sbd, &s->vram_cplane); 861f40070c3SBlue Swirl 86255d7bfe2SMark Cave-Ayland /* 9/THC24bits : NetBSD writes here even with 8-bit display: dummy */ 86355d7bfe2SMark Cave-Ayland if (s->depth == 8) { 86455d7bfe2SMark Cave-Ayland memory_region_init_io(&s->thc24, OBJECT(s), &tcx_dummy_ops, s, 86555d7bfe2SMark Cave-Ayland "tcx.thc24", TCX_THC_NREGS); 86655d7bfe2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->thc24); 867eee0b836Sblueswir1 } 868eee0b836Sblueswir1 86955d7bfe2SMark Cave-Ayland sysbus_init_irq(sbd, &s->irq); 87055d7bfe2SMark Cave-Ayland 87155d7bfe2SMark Cave-Ayland if (s->depth == 8) { 8728e5c952bSPhilippe Mathieu-Daudé s->con = graphic_console_init(dev, 0, &tcx_ops, s); 87355d7bfe2SMark Cave-Ayland } else { 8748e5c952bSPhilippe Mathieu-Daudé s->con = graphic_console_init(dev, 0, &tcx24_ops, s); 87555d7bfe2SMark Cave-Ayland } 87655d7bfe2SMark Cave-Ayland s->thcmisc = 0; 87755d7bfe2SMark Cave-Ayland 878c78f7137SGerd Hoffmann qemu_console_resize(s->con, s->width, s->height); 879420557e8Sbellard } 880420557e8Sbellard 881d432edd5SRichard Henderson static const Property tcx_properties[] = { 882c7bcc85dSPaolo Bonzini DEFINE_PROP_UINT32("vram_size", TCXState, vram_size, -1), 88353dad499SGerd Hoffmann DEFINE_PROP_UINT16("width", TCXState, width, -1), 88453dad499SGerd Hoffmann DEFINE_PROP_UINT16("height", TCXState, height, -1), 88553dad499SGerd Hoffmann DEFINE_PROP_UINT16("depth", TCXState, depth, -1), 886999e12bbSAnthony Liguori }; 887999e12bbSAnthony Liguori 888*12d1a768SPhilippe Mathieu-Daudé static void tcx_class_init(ObjectClass *klass, const void *data) 889999e12bbSAnthony Liguori { 89039bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 891999e12bbSAnthony Liguori 892d4ad9decSMark Cave-Ayland dc->realize = tcx_realizefn; 893e3d08143SPeter Maydell device_class_set_legacy_reset(dc, tcx_reset); 89439bffca2SAnthony Liguori dc->vmsd = &vmstate_tcx; 8954f67d30bSMarc-André Lureau device_class_set_props(dc, tcx_properties); 896ee6847d1SGerd Hoffmann } 897999e12bbSAnthony Liguori 8988c43a6f0SAndreas Färber static const TypeInfo tcx_info = { 89901774ddbSAndreas Färber .name = TYPE_TCX, 90039bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 90139bffca2SAnthony Liguori .instance_size = sizeof(TCXState), 90201b91ac2SMark Cave-Ayland .instance_init = tcx_initfn, 903999e12bbSAnthony Liguori .class_init = tcx_class_init, 904ee6847d1SGerd Hoffmann }; 905ee6847d1SGerd Hoffmann 90683f7d43aSAndreas Färber static void tcx_register_types(void) 907f40070c3SBlue Swirl { 90839bffca2SAnthony Liguori type_register_static(&tcx_info); 909f40070c3SBlue Swirl } 910f40070c3SBlue Swirl 91183f7d43aSAndreas Färber type_init(tcx_register_types) 912