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" 26da34e65cSMarkus Armbruster #include "qapi/error.h" 27077805faSPaolo Bonzini #include "qemu-common.h" 2828ecbaeeSPaolo Bonzini #include "ui/console.h" 2928ecbaeeSPaolo Bonzini #include "ui/pixel_ops.h" 30da87dd7bSMark Cave-Ayland #include "hw/loader.h" 3183c9f4caSPaolo Bonzini #include "hw/sysbus.h" 32d49b6836SMarkus Armbruster #include "qemu/error-report.h" 33420557e8Sbellard 34da87dd7bSMark Cave-Ayland #define TCX_ROM_FILE "QEMU,tcx.bin" 35da87dd7bSMark Cave-Ayland #define FCODE_MAX_ROM_SIZE 0x10000 36da87dd7bSMark Cave-Ayland 37420557e8Sbellard #define MAXX 1024 38420557e8Sbellard #define MAXY 768 396f7e9aecSbellard #define TCX_DAC_NREGS 16 4055d7bfe2SMark Cave-Ayland #define TCX_THC_NREGS 0x1000 4155d7bfe2SMark Cave-Ayland #define TCX_DHC_NREGS 0x4000 428508b89eSblueswir1 #define TCX_TEC_NREGS 0x1000 4355d7bfe2SMark Cave-Ayland #define TCX_ALT_NREGS 0x8000 4455d7bfe2SMark Cave-Ayland #define TCX_STIP_NREGS 0x800000 4555d7bfe2SMark Cave-Ayland #define TCX_BLIT_NREGS 0x800000 4655d7bfe2SMark Cave-Ayland #define TCX_RSTIP_NREGS 0x800000 4755d7bfe2SMark Cave-Ayland #define TCX_RBLIT_NREGS 0x800000 4855d7bfe2SMark Cave-Ayland 4955d7bfe2SMark Cave-Ayland #define TCX_THC_MISC 0x818 5055d7bfe2SMark Cave-Ayland #define TCX_THC_CURSXY 0x8fc 5155d7bfe2SMark Cave-Ayland #define TCX_THC_CURSMASK 0x900 5255d7bfe2SMark Cave-Ayland #define TCX_THC_CURSBITS 0x980 53420557e8Sbellard 5401774ddbSAndreas Färber #define TYPE_TCX "SUNW,tcx" 5501774ddbSAndreas Färber #define TCX(obj) OBJECT_CHECK(TCXState, (obj), TYPE_TCX) 5601774ddbSAndreas Färber 57420557e8Sbellard typedef struct TCXState { 5801774ddbSAndreas Färber SysBusDevice parent_obj; 5901774ddbSAndreas Färber 60c78f7137SGerd Hoffmann QemuConsole *con; 6155d7bfe2SMark Cave-Ayland qemu_irq irq; 628d5f07faSbellard uint8_t *vram; 63eee0b836Sblueswir1 uint32_t *vram24, *cplane; 64da87dd7bSMark Cave-Ayland hwaddr prom_addr; 65da87dd7bSMark Cave-Ayland MemoryRegion rom; 66d08151bfSAvi Kivity MemoryRegion vram_mem; 67d08151bfSAvi Kivity MemoryRegion vram_8bit; 68d08151bfSAvi Kivity MemoryRegion vram_24bit; 6955d7bfe2SMark Cave-Ayland MemoryRegion stip; 7055d7bfe2SMark Cave-Ayland MemoryRegion blit; 71d08151bfSAvi Kivity MemoryRegion vram_cplane; 7255d7bfe2SMark Cave-Ayland MemoryRegion rstip; 7355d7bfe2SMark Cave-Ayland MemoryRegion rblit; 74d08151bfSAvi Kivity MemoryRegion tec; 7555d7bfe2SMark Cave-Ayland MemoryRegion dac; 7655d7bfe2SMark Cave-Ayland MemoryRegion thc; 7755d7bfe2SMark Cave-Ayland MemoryRegion dhc; 7855d7bfe2SMark Cave-Ayland MemoryRegion alt; 79d08151bfSAvi Kivity MemoryRegion thc24; 8055d7bfe2SMark Cave-Ayland 81d08151bfSAvi Kivity ram_addr_t vram24_offset, cplane_offset; 8255d7bfe2SMark Cave-Ayland uint32_t tmpblit; 83ee6847d1SGerd Hoffmann uint32_t vram_size; 8455d7bfe2SMark Cave-Ayland uint32_t palette[260]; 8555d7bfe2SMark Cave-Ayland uint8_t r[260], g[260], b[260]; 86427a66c3SBlue Swirl uint16_t width, height, depth; 876f7e9aecSbellard uint8_t dac_index, dac_state; 8855d7bfe2SMark Cave-Ayland uint32_t thcmisc; 8955d7bfe2SMark Cave-Ayland uint32_t cursmask[32]; 9055d7bfe2SMark Cave-Ayland uint32_t cursbits[32]; 9155d7bfe2SMark Cave-Ayland uint16_t cursx; 9255d7bfe2SMark Cave-Ayland uint16_t cursy; 93420557e8Sbellard } TCXState; 94420557e8Sbellard 959800b3c2SMark Cave-Ayland static void tcx_set_dirty(TCXState *s, ram_addr_t addr, int len) 96d3ffcafeSBlue Swirl { 979800b3c2SMark Cave-Ayland memory_region_set_dirty(&s->vram_mem, addr, len); 984b865c28SMark Cave-Ayland 994b865c28SMark Cave-Ayland if (s->depth == 24) { 1004b865c28SMark Cave-Ayland memory_region_set_dirty(&s->vram_mem, s->vram24_offset + addr * 4, 1014b865c28SMark Cave-Ayland len * 4); 1024b865c28SMark Cave-Ayland memory_region_set_dirty(&s->vram_mem, s->cplane_offset + addr * 4, 1034b865c28SMark Cave-Ayland len * 4); 1044b865c28SMark Cave-Ayland } 105d3ffcafeSBlue Swirl } 106d3ffcafeSBlue Swirl 107427ee02bSMark Cave-Ayland static int tcx_check_dirty(TCXState *s, ram_addr_t addr, int len) 108d3ffcafeSBlue Swirl { 10955d7bfe2SMark Cave-Ayland int ret; 11055d7bfe2SMark Cave-Ayland 111427ee02bSMark Cave-Ayland ret = memory_region_get_dirty(&s->vram_mem, addr, len, DIRTY_MEMORY_VGA); 112427ee02bSMark Cave-Ayland 113427ee02bSMark Cave-Ayland if (s->depth == 24) { 114427ee02bSMark Cave-Ayland ret |= memory_region_get_dirty(&s->vram_mem, 115427ee02bSMark Cave-Ayland s->vram24_offset + addr * 4, len * 4, 11655d7bfe2SMark Cave-Ayland DIRTY_MEMORY_VGA); 117427ee02bSMark Cave-Ayland ret |= memory_region_get_dirty(&s->vram_mem, 118427ee02bSMark Cave-Ayland s->cplane_offset + addr * 4, len * 4, 11955d7bfe2SMark Cave-Ayland DIRTY_MEMORY_VGA); 120427ee02bSMark Cave-Ayland } 121427ee02bSMark Cave-Ayland 12255d7bfe2SMark Cave-Ayland return ret; 12355d7bfe2SMark Cave-Ayland } 12455d7bfe2SMark Cave-Ayland 12536180430SMark Cave-Ayland static void tcx_reset_dirty(TCXState *s, ram_addr_t addr, int len) 12655d7bfe2SMark Cave-Ayland { 12736180430SMark Cave-Ayland memory_region_reset_dirty(&s->vram_mem, addr, len, DIRTY_MEMORY_VGA); 12836180430SMark Cave-Ayland 12936180430SMark Cave-Ayland if (s->depth == 24) { 13036180430SMark Cave-Ayland memory_region_reset_dirty(&s->vram_mem, s->vram24_offset + addr * 4, 13136180430SMark Cave-Ayland len * 4, DIRTY_MEMORY_VGA); 13236180430SMark Cave-Ayland memory_region_reset_dirty(&s->vram_mem, s->cplane_offset + addr * 4, 13336180430SMark Cave-Ayland len * 4, DIRTY_MEMORY_VGA); 13436180430SMark Cave-Ayland } 135d3ffcafeSBlue Swirl } 13695219897Spbrook 13721206a10Sbellard static void update_palette_entries(TCXState *s, int start, int end) 13821206a10Sbellard { 139c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s->con); 14021206a10Sbellard int i; 141c78f7137SGerd Hoffmann 14221206a10Sbellard for (i = start; i < end; i++) { 143c78f7137SGerd Hoffmann if (is_surface_bgr(surface)) { 1447b5d76daSaliguori s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]); 145c78f7137SGerd Hoffmann } else { 14621206a10Sbellard s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]); 147c78f7137SGerd Hoffmann } 14821206a10Sbellard break; 14921206a10Sbellard } 1509800b3c2SMark Cave-Ayland tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem)); 151d3ffcafeSBlue Swirl } 15221206a10Sbellard 153e80cfcfcSbellard static void tcx_draw_line32(TCXState *s1, uint8_t *d, 154e80cfcfcSbellard const uint8_t *s, int width) 155420557e8Sbellard { 156e80cfcfcSbellard int x; 157e80cfcfcSbellard uint8_t val; 1588bdc2159Sths uint32_t *p = (uint32_t *)d; 159e80cfcfcSbellard 160e80cfcfcSbellard for (x = 0; x < width; x++) { 161e80cfcfcSbellard val = *s++; 1628bdc2159Sths *p++ = s1->palette[val]; 163e80cfcfcSbellard } 164420557e8Sbellard } 165420557e8Sbellard 16655d7bfe2SMark Cave-Ayland static void tcx_draw_cursor32(TCXState *s1, uint8_t *d, 16755d7bfe2SMark Cave-Ayland int y, int width) 16855d7bfe2SMark Cave-Ayland { 16955d7bfe2SMark Cave-Ayland int x, len; 17055d7bfe2SMark Cave-Ayland uint32_t mask, bits; 17155d7bfe2SMark Cave-Ayland uint32_t *p = (uint32_t *)d; 17255d7bfe2SMark Cave-Ayland 17355d7bfe2SMark Cave-Ayland y = y - s1->cursy; 17455d7bfe2SMark Cave-Ayland mask = s1->cursmask[y]; 17555d7bfe2SMark Cave-Ayland bits = s1->cursbits[y]; 17655d7bfe2SMark Cave-Ayland len = MIN(width - s1->cursx, 32); 17755d7bfe2SMark Cave-Ayland p = &p[s1->cursx]; 17855d7bfe2SMark Cave-Ayland for (x = 0; x < len; x++) { 17955d7bfe2SMark Cave-Ayland if (mask & 0x80000000) { 18055d7bfe2SMark Cave-Ayland if (bits & 0x80000000) { 18155d7bfe2SMark Cave-Ayland *p = s1->palette[259]; 18255d7bfe2SMark Cave-Ayland } else { 18355d7bfe2SMark Cave-Ayland *p = s1->palette[258]; 18455d7bfe2SMark Cave-Ayland } 18555d7bfe2SMark Cave-Ayland } 18655d7bfe2SMark Cave-Ayland p++; 18755d7bfe2SMark Cave-Ayland mask <<= 1; 18855d7bfe2SMark Cave-Ayland bits <<= 1; 18955d7bfe2SMark Cave-Ayland } 19055d7bfe2SMark Cave-Ayland } 19155d7bfe2SMark Cave-Ayland 192688ea2ebSblueswir1 /* 193688ea2ebSblueswir1 XXX Could be much more optimal: 194688ea2ebSblueswir1 * detect if line/page/whole screen is in 24 bit mode 195688ea2ebSblueswir1 * if destination is also BGR, use memcpy 196688ea2ebSblueswir1 */ 197eee0b836Sblueswir1 static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d, 198eee0b836Sblueswir1 const uint8_t *s, int width, 199eee0b836Sblueswir1 const uint32_t *cplane, 200eee0b836Sblueswir1 const uint32_t *s24) 201eee0b836Sblueswir1 { 202c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s1->con); 2037b5d76daSaliguori int x, bgr, r, g, b; 204688ea2ebSblueswir1 uint8_t val, *p8; 205eee0b836Sblueswir1 uint32_t *p = (uint32_t *)d; 206eee0b836Sblueswir1 uint32_t dval; 207c78f7137SGerd Hoffmann bgr = is_surface_bgr(surface); 208eee0b836Sblueswir1 for(x = 0; x < width; x++, s++, s24++) { 20955d7bfe2SMark Cave-Ayland if (be32_to_cpu(*cplane) & 0x03000000) { 21055d7bfe2SMark Cave-Ayland /* 24-bit direct, BGR order */ 211688ea2ebSblueswir1 p8 = (uint8_t *)s24; 212688ea2ebSblueswir1 p8++; 213688ea2ebSblueswir1 b = *p8++; 214688ea2ebSblueswir1 g = *p8++; 215f7e683b8SBlue Swirl r = *p8; 2167b5d76daSaliguori if (bgr) 2177b5d76daSaliguori dval = rgb_to_pixel32bgr(r, g, b); 2187b5d76daSaliguori else 219688ea2ebSblueswir1 dval = rgb_to_pixel32(r, g, b); 220eee0b836Sblueswir1 } else { 22155d7bfe2SMark Cave-Ayland /* 8-bit pseudocolor */ 222eee0b836Sblueswir1 val = *s; 223eee0b836Sblueswir1 dval = s1->palette[val]; 224eee0b836Sblueswir1 } 225eee0b836Sblueswir1 *p++ = dval; 22655d7bfe2SMark Cave-Ayland cplane++; 227eee0b836Sblueswir1 } 228eee0b836Sblueswir1 } 229eee0b836Sblueswir1 230e80cfcfcSbellard /* Fixed line length 1024 allows us to do nice tricks not possible on 231e80cfcfcSbellard VGA... */ 23255d7bfe2SMark Cave-Ayland 23395219897Spbrook static void tcx_update_display(void *opaque) 234e80cfcfcSbellard { 235e80cfcfcSbellard TCXState *ts = opaque; 236c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(ts->con); 237c227f099SAnthony Liguori ram_addr_t page, page_min, page_max; 238550be127Sbellard int y, y_start, dd, ds; 239e80cfcfcSbellard uint8_t *d, *s; 240e80cfcfcSbellard 241ee72bed0SMark Cave-Ayland if (surface_bits_per_pixel(surface) != 32) { 242e80cfcfcSbellard return; 243c78f7137SGerd Hoffmann } 244c78f7137SGerd Hoffmann 245d08151bfSAvi Kivity page = 0; 246e80cfcfcSbellard y_start = -1; 247c0c440f3SBlue Swirl page_min = -1; 248550be127Sbellard page_max = 0; 249c78f7137SGerd Hoffmann d = surface_data(surface); 2506f7e9aecSbellard s = ts->vram; 251c78f7137SGerd Hoffmann dd = surface_stride(surface); 252e80cfcfcSbellard ds = 1024; 253e80cfcfcSbellard 2545299c0f2SPaolo Bonzini memory_region_sync_dirty_bitmap(&ts->vram_mem); 2550a97c6c4SMark Cave-Ayland for (y = 0; y < ts->height; y++, page += ds) { 2560a97c6c4SMark Cave-Ayland if (tcx_check_dirty(ts, page, ds)) { 257e80cfcfcSbellard if (y_start < 0) 258e80cfcfcSbellard y_start = y; 259e80cfcfcSbellard if (page < page_min) 260e80cfcfcSbellard page_min = page; 261e80cfcfcSbellard if (page > page_max) 262e80cfcfcSbellard page_max = page; 26355d7bfe2SMark Cave-Ayland 264ee72bed0SMark Cave-Ayland tcx_draw_line32(ts, d, s, ts->width); 26555d7bfe2SMark Cave-Ayland if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) { 266ee72bed0SMark Cave-Ayland tcx_draw_cursor32(ts, d, y, ts->width); 26755d7bfe2SMark Cave-Ayland } 268e80cfcfcSbellard } else { 269e80cfcfcSbellard if (y_start >= 0) { 270e80cfcfcSbellard /* flush to display */ 271c78f7137SGerd Hoffmann dpy_gfx_update(ts->con, 0, y_start, 2726f7e9aecSbellard ts->width, y - y_start); 273e80cfcfcSbellard y_start = -1; 274e80cfcfcSbellard } 275e80cfcfcSbellard } 2760a97c6c4SMark Cave-Ayland s += ds; 2770a97c6c4SMark Cave-Ayland d += dd; 278e80cfcfcSbellard } 279e80cfcfcSbellard if (y_start >= 0) { 280e80cfcfcSbellard /* flush to display */ 281c78f7137SGerd Hoffmann dpy_gfx_update(ts->con, 0, y_start, 2826f7e9aecSbellard ts->width, y - y_start); 283e80cfcfcSbellard } 284e80cfcfcSbellard /* reset modified pages */ 285c0c440f3SBlue Swirl if (page_max >= page_min) { 28636180430SMark Cave-Ayland tcx_reset_dirty(ts, page_min, page_max - page_min); 287e80cfcfcSbellard } 288e80cfcfcSbellard } 289e80cfcfcSbellard 290eee0b836Sblueswir1 static void tcx24_update_display(void *opaque) 291eee0b836Sblueswir1 { 292eee0b836Sblueswir1 TCXState *ts = opaque; 293c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(ts->con); 29466dcabeaSMark Cave-Ayland ram_addr_t page, page_min, page_max; 295eee0b836Sblueswir1 int y, y_start, dd, ds; 296eee0b836Sblueswir1 uint8_t *d, *s; 297eee0b836Sblueswir1 uint32_t *cptr, *s24; 298eee0b836Sblueswir1 299c78f7137SGerd Hoffmann if (surface_bits_per_pixel(surface) != 32) { 300eee0b836Sblueswir1 return; 301c78f7137SGerd Hoffmann } 302c78f7137SGerd Hoffmann 303d08151bfSAvi Kivity page = 0; 304eee0b836Sblueswir1 y_start = -1; 305c0c440f3SBlue Swirl page_min = -1; 306eee0b836Sblueswir1 page_max = 0; 307c78f7137SGerd Hoffmann d = surface_data(surface); 308eee0b836Sblueswir1 s = ts->vram; 309eee0b836Sblueswir1 s24 = ts->vram24; 310eee0b836Sblueswir1 cptr = ts->cplane; 311c78f7137SGerd Hoffmann dd = surface_stride(surface); 312eee0b836Sblueswir1 ds = 1024; 313eee0b836Sblueswir1 3145299c0f2SPaolo Bonzini memory_region_sync_dirty_bitmap(&ts->vram_mem); 315d18e1012SMark Cave-Ayland for (y = 0; y < ts->height; y++, page += ds) { 316d18e1012SMark Cave-Ayland if (tcx_check_dirty(ts, page, ds)) { 317eee0b836Sblueswir1 if (y_start < 0) 318eee0b836Sblueswir1 y_start = y; 319eee0b836Sblueswir1 if (page < page_min) 320eee0b836Sblueswir1 page_min = page; 321eee0b836Sblueswir1 if (page > page_max) 322eee0b836Sblueswir1 page_max = page; 323eee0b836Sblueswir1 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); 32455d7bfe2SMark Cave-Ayland if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) { 32555d7bfe2SMark Cave-Ayland tcx_draw_cursor32(ts, d, y, ts->width); 32655d7bfe2SMark Cave-Ayland } 327eee0b836Sblueswir1 } else { 328eee0b836Sblueswir1 if (y_start >= 0) { 329eee0b836Sblueswir1 /* flush to display */ 330c78f7137SGerd Hoffmann dpy_gfx_update(ts->con, 0, y_start, 331eee0b836Sblueswir1 ts->width, y - y_start); 332eee0b836Sblueswir1 y_start = -1; 333eee0b836Sblueswir1 } 334eee0b836Sblueswir1 } 335d18e1012SMark Cave-Ayland d += dd; 336d18e1012SMark Cave-Ayland s += ds; 337d18e1012SMark Cave-Ayland cptr += ds; 338d18e1012SMark Cave-Ayland s24 += ds; 339eee0b836Sblueswir1 } 340eee0b836Sblueswir1 if (y_start >= 0) { 341eee0b836Sblueswir1 /* flush to display */ 342c78f7137SGerd Hoffmann dpy_gfx_update(ts->con, 0, y_start, 343eee0b836Sblueswir1 ts->width, y - y_start); 344eee0b836Sblueswir1 } 345eee0b836Sblueswir1 /* reset modified pages */ 346c0c440f3SBlue Swirl if (page_max >= page_min) { 34736180430SMark Cave-Ayland tcx_reset_dirty(ts, page_min, page_max - page_min); 348eee0b836Sblueswir1 } 349eee0b836Sblueswir1 } 350eee0b836Sblueswir1 35195219897Spbrook static void tcx_invalidate_display(void *opaque) 352420557e8Sbellard { 353420557e8Sbellard TCXState *s = opaque; 354420557e8Sbellard 3559800b3c2SMark Cave-Ayland tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem)); 356c78f7137SGerd Hoffmann qemu_console_resize(s->con, s->width, s->height); 357e80cfcfcSbellard } 358e80cfcfcSbellard 359eee0b836Sblueswir1 static void tcx24_invalidate_display(void *opaque) 360eee0b836Sblueswir1 { 361eee0b836Sblueswir1 TCXState *s = opaque; 362eee0b836Sblueswir1 3639800b3c2SMark Cave-Ayland tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem)); 364c78f7137SGerd Hoffmann qemu_console_resize(s->con, s->width, s->height); 365eee0b836Sblueswir1 } 366eee0b836Sblueswir1 367e59fb374SJuan Quintela static int vmstate_tcx_post_load(void *opaque, int version_id) 368e80cfcfcSbellard { 369e80cfcfcSbellard TCXState *s = opaque; 370e80cfcfcSbellard 37121206a10Sbellard update_palette_entries(s, 0, 256); 3729800b3c2SMark Cave-Ayland tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem)); 373420557e8Sbellard return 0; 374420557e8Sbellard } 375420557e8Sbellard 376c0c41a4bSBlue Swirl static const VMStateDescription vmstate_tcx = { 377c0c41a4bSBlue Swirl .name ="tcx", 378c0c41a4bSBlue Swirl .version_id = 4, 379c0c41a4bSBlue Swirl .minimum_version_id = 4, 380752ff2faSJuan Quintela .post_load = vmstate_tcx_post_load, 381c0c41a4bSBlue Swirl .fields = (VMStateField[]) { 382c0c41a4bSBlue Swirl VMSTATE_UINT16(height, TCXState), 383c0c41a4bSBlue Swirl VMSTATE_UINT16(width, TCXState), 384c0c41a4bSBlue Swirl VMSTATE_UINT16(depth, TCXState), 385c0c41a4bSBlue Swirl VMSTATE_BUFFER(r, TCXState), 386c0c41a4bSBlue Swirl VMSTATE_BUFFER(g, TCXState), 387c0c41a4bSBlue Swirl VMSTATE_BUFFER(b, TCXState), 388c0c41a4bSBlue Swirl VMSTATE_UINT8(dac_index, TCXState), 389c0c41a4bSBlue Swirl VMSTATE_UINT8(dac_state, TCXState), 390c0c41a4bSBlue Swirl VMSTATE_END_OF_LIST() 391c0c41a4bSBlue Swirl } 392c0c41a4bSBlue Swirl }; 393c0c41a4bSBlue Swirl 3947f23f812SMichael S. Tsirkin static void tcx_reset(DeviceState *d) 395420557e8Sbellard { 39601774ddbSAndreas Färber TCXState *s = TCX(d); 397420557e8Sbellard 398e80cfcfcSbellard /* Initialize palette */ 39955d7bfe2SMark Cave-Ayland memset(s->r, 0, 260); 40055d7bfe2SMark Cave-Ayland memset(s->g, 0, 260); 40155d7bfe2SMark Cave-Ayland memset(s->b, 0, 260); 402e80cfcfcSbellard s->r[255] = s->g[255] = s->b[255] = 255; 40355d7bfe2SMark Cave-Ayland s->r[256] = s->g[256] = s->b[256] = 255; 40455d7bfe2SMark Cave-Ayland s->r[258] = s->g[258] = s->b[258] = 255; 40555d7bfe2SMark Cave-Ayland update_palette_entries(s, 0, 260); 406e80cfcfcSbellard memset(s->vram, 0, MAXX*MAXY); 407d08151bfSAvi Kivity memory_region_reset_dirty(&s->vram_mem, 0, MAXX * MAXY * (1 + 4 + 4), 408d08151bfSAvi Kivity DIRTY_MEMORY_VGA); 4096f7e9aecSbellard s->dac_index = 0; 4106f7e9aecSbellard s->dac_state = 0; 41155d7bfe2SMark Cave-Ayland s->cursx = 0xf000; /* Put cursor off screen */ 41255d7bfe2SMark Cave-Ayland s->cursy = 0xf000; 413420557e8Sbellard } 414420557e8Sbellard 415a8170e5eSAvi Kivity static uint64_t tcx_dac_readl(void *opaque, hwaddr addr, 416d08151bfSAvi Kivity unsigned size) 4176f7e9aecSbellard { 41855d7bfe2SMark Cave-Ayland TCXState *s = opaque; 41955d7bfe2SMark Cave-Ayland uint32_t val = 0; 42055d7bfe2SMark Cave-Ayland 42155d7bfe2SMark Cave-Ayland switch (s->dac_state) { 42255d7bfe2SMark Cave-Ayland case 0: 42355d7bfe2SMark Cave-Ayland val = s->r[s->dac_index] << 24; 42455d7bfe2SMark Cave-Ayland s->dac_state++; 42555d7bfe2SMark Cave-Ayland break; 42655d7bfe2SMark Cave-Ayland case 1: 42755d7bfe2SMark Cave-Ayland val = s->g[s->dac_index] << 24; 42855d7bfe2SMark Cave-Ayland s->dac_state++; 42955d7bfe2SMark Cave-Ayland break; 43055d7bfe2SMark Cave-Ayland case 2: 43155d7bfe2SMark Cave-Ayland val = s->b[s->dac_index] << 24; 43255d7bfe2SMark Cave-Ayland s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */ 43355d7bfe2SMark Cave-Ayland default: 43455d7bfe2SMark Cave-Ayland s->dac_state = 0; 43555d7bfe2SMark Cave-Ayland break; 43655d7bfe2SMark Cave-Ayland } 43755d7bfe2SMark Cave-Ayland 43855d7bfe2SMark Cave-Ayland return val; 4396f7e9aecSbellard } 4406f7e9aecSbellard 441a8170e5eSAvi Kivity static void tcx_dac_writel(void *opaque, hwaddr addr, uint64_t val, 442d08151bfSAvi Kivity unsigned size) 4436f7e9aecSbellard { 4446f7e9aecSbellard TCXState *s = opaque; 44555d7bfe2SMark Cave-Ayland unsigned index; 4466f7e9aecSbellard 447e64d7d59Sblueswir1 switch (addr) { 44855d7bfe2SMark Cave-Ayland case 0: /* Address */ 4496f7e9aecSbellard s->dac_index = val >> 24; 4506f7e9aecSbellard s->dac_state = 0; 4516f7e9aecSbellard break; 45255d7bfe2SMark Cave-Ayland case 4: /* Pixel colours */ 45355d7bfe2SMark Cave-Ayland case 12: /* Overlay (cursor) colours */ 45455d7bfe2SMark Cave-Ayland if (addr & 8) { 45555d7bfe2SMark Cave-Ayland index = (s->dac_index & 3) + 256; 45655d7bfe2SMark Cave-Ayland } else { 45755d7bfe2SMark Cave-Ayland index = s->dac_index; 45855d7bfe2SMark Cave-Ayland } 4596f7e9aecSbellard switch (s->dac_state) { 4606f7e9aecSbellard case 0: 46155d7bfe2SMark Cave-Ayland s->r[index] = val >> 24; 46255d7bfe2SMark Cave-Ayland update_palette_entries(s, index, index + 1); 4636f7e9aecSbellard s->dac_state++; 4646f7e9aecSbellard break; 4656f7e9aecSbellard case 1: 46655d7bfe2SMark Cave-Ayland s->g[index] = val >> 24; 46755d7bfe2SMark Cave-Ayland update_palette_entries(s, index, index + 1); 4686f7e9aecSbellard s->dac_state++; 4696f7e9aecSbellard break; 4706f7e9aecSbellard case 2: 47155d7bfe2SMark Cave-Ayland s->b[index] = val >> 24; 47255d7bfe2SMark Cave-Ayland update_palette_entries(s, index, index + 1); 47355d7bfe2SMark Cave-Ayland s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */ 4746f7e9aecSbellard default: 4756f7e9aecSbellard s->dac_state = 0; 4766f7e9aecSbellard break; 4776f7e9aecSbellard } 4786f7e9aecSbellard break; 47955d7bfe2SMark Cave-Ayland default: /* Control registers */ 4806f7e9aecSbellard break; 4816f7e9aecSbellard } 4826f7e9aecSbellard } 4836f7e9aecSbellard 484d08151bfSAvi Kivity static const MemoryRegionOps tcx_dac_ops = { 485d08151bfSAvi Kivity .read = tcx_dac_readl, 486d08151bfSAvi Kivity .write = tcx_dac_writel, 487d08151bfSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 488d08151bfSAvi Kivity .valid = { 489d08151bfSAvi Kivity .min_access_size = 4, 490d08151bfSAvi Kivity .max_access_size = 4, 491d08151bfSAvi Kivity }, 4926f7e9aecSbellard }; 4936f7e9aecSbellard 49455d7bfe2SMark Cave-Ayland static uint64_t tcx_stip_readl(void *opaque, hwaddr addr, 495d08151bfSAvi Kivity unsigned size) 4968508b89eSblueswir1 { 4978508b89eSblueswir1 return 0; 4988508b89eSblueswir1 } 4998508b89eSblueswir1 50055d7bfe2SMark Cave-Ayland static void tcx_stip_writel(void *opaque, hwaddr addr, 501d08151bfSAvi Kivity uint64_t val, unsigned size) 5028508b89eSblueswir1 { 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 } 51855d7bfe2SMark Cave-Ayland val <<= 1; 51955d7bfe2SMark Cave-Ayland } 52055d7bfe2SMark Cave-Ayland } else { 52155d7bfe2SMark Cave-Ayland for (i = 0; i < 32; i++) { 52255d7bfe2SMark Cave-Ayland if (val & 0x80000000) { 52355d7bfe2SMark Cave-Ayland s->vram[addr + i] = s->tmpblit; 52455d7bfe2SMark Cave-Ayland } 52555d7bfe2SMark Cave-Ayland val <<= 1; 52655d7bfe2SMark Cave-Ayland } 52755d7bfe2SMark Cave-Ayland } 528*97394580SMark Cave-Ayland tcx_set_dirty(s, addr, 32); 52955d7bfe2SMark Cave-Ayland } 5308508b89eSblueswir1 } 5318508b89eSblueswir1 53255d7bfe2SMark Cave-Ayland static void tcx_rstip_writel(void *opaque, hwaddr addr, 53355d7bfe2SMark Cave-Ayland uint64_t val, unsigned size) 53455d7bfe2SMark Cave-Ayland { 53555d7bfe2SMark Cave-Ayland TCXState *s = opaque; 53655d7bfe2SMark Cave-Ayland int i; 53755d7bfe2SMark Cave-Ayland uint32_t col; 53855d7bfe2SMark Cave-Ayland 53955d7bfe2SMark Cave-Ayland if (!(addr & 4)) { 54055d7bfe2SMark Cave-Ayland s->tmpblit = val; 54155d7bfe2SMark Cave-Ayland } else { 54255d7bfe2SMark Cave-Ayland addr = (addr >> 3) & 0xfffff; 54355d7bfe2SMark Cave-Ayland col = cpu_to_be32(s->tmpblit); 54455d7bfe2SMark Cave-Ayland if (s->depth == 24) { 54555d7bfe2SMark Cave-Ayland for (i = 0; i < 32; i++) { 54655d7bfe2SMark Cave-Ayland if (val & 0x80000000) { 54755d7bfe2SMark Cave-Ayland s->vram[addr + i] = s->tmpblit; 54855d7bfe2SMark Cave-Ayland s->vram24[addr + i] = col; 54955d7bfe2SMark Cave-Ayland s->cplane[addr + i] = col; 55055d7bfe2SMark Cave-Ayland } 55155d7bfe2SMark Cave-Ayland val <<= 1; 55255d7bfe2SMark Cave-Ayland } 55355d7bfe2SMark Cave-Ayland } else { 55455d7bfe2SMark Cave-Ayland for (i = 0; i < 32; i++) { 55555d7bfe2SMark Cave-Ayland if (val & 0x80000000) { 55655d7bfe2SMark Cave-Ayland s->vram[addr + i] = s->tmpblit; 55755d7bfe2SMark Cave-Ayland } 55855d7bfe2SMark Cave-Ayland val <<= 1; 55955d7bfe2SMark Cave-Ayland } 56055d7bfe2SMark Cave-Ayland } 561*97394580SMark Cave-Ayland tcx_set_dirty(s, addr, 32); 56255d7bfe2SMark Cave-Ayland } 56355d7bfe2SMark Cave-Ayland } 56455d7bfe2SMark Cave-Ayland 56555d7bfe2SMark Cave-Ayland static const MemoryRegionOps tcx_stip_ops = { 56655d7bfe2SMark Cave-Ayland .read = tcx_stip_readl, 56755d7bfe2SMark Cave-Ayland .write = tcx_stip_writel, 56855d7bfe2SMark Cave-Ayland .endianness = DEVICE_NATIVE_ENDIAN, 56955d7bfe2SMark Cave-Ayland .valid = { 57055d7bfe2SMark Cave-Ayland .min_access_size = 4, 57155d7bfe2SMark Cave-Ayland .max_access_size = 4, 57255d7bfe2SMark Cave-Ayland }, 57355d7bfe2SMark Cave-Ayland }; 57455d7bfe2SMark Cave-Ayland 57555d7bfe2SMark Cave-Ayland static const MemoryRegionOps tcx_rstip_ops = { 57655d7bfe2SMark Cave-Ayland .read = tcx_stip_readl, 57755d7bfe2SMark Cave-Ayland .write = tcx_rstip_writel, 57855d7bfe2SMark Cave-Ayland .endianness = DEVICE_NATIVE_ENDIAN, 57955d7bfe2SMark Cave-Ayland .valid = { 58055d7bfe2SMark Cave-Ayland .min_access_size = 4, 58155d7bfe2SMark Cave-Ayland .max_access_size = 4, 58255d7bfe2SMark Cave-Ayland }, 58355d7bfe2SMark Cave-Ayland }; 58455d7bfe2SMark Cave-Ayland 58555d7bfe2SMark Cave-Ayland static uint64_t tcx_blit_readl(void *opaque, hwaddr addr, 58655d7bfe2SMark Cave-Ayland unsigned size) 58755d7bfe2SMark Cave-Ayland { 58855d7bfe2SMark Cave-Ayland return 0; 58955d7bfe2SMark Cave-Ayland } 59055d7bfe2SMark Cave-Ayland 59155d7bfe2SMark Cave-Ayland static void tcx_blit_writel(void *opaque, hwaddr addr, 59255d7bfe2SMark Cave-Ayland uint64_t val, unsigned size) 59355d7bfe2SMark Cave-Ayland { 59455d7bfe2SMark Cave-Ayland TCXState *s = opaque; 59555d7bfe2SMark Cave-Ayland uint32_t adsr, len; 59655d7bfe2SMark Cave-Ayland int i; 59755d7bfe2SMark Cave-Ayland 59855d7bfe2SMark Cave-Ayland if (!(addr & 4)) { 59955d7bfe2SMark Cave-Ayland s->tmpblit = val; 60055d7bfe2SMark Cave-Ayland } else { 60155d7bfe2SMark Cave-Ayland addr = (addr >> 3) & 0xfffff; 60255d7bfe2SMark Cave-Ayland adsr = val & 0xffffff; 60355d7bfe2SMark Cave-Ayland len = ((val >> 24) & 0x1f) + 1; 60455d7bfe2SMark Cave-Ayland if (adsr == 0xffffff) { 60555d7bfe2SMark Cave-Ayland memset(&s->vram[addr], s->tmpblit, len); 60655d7bfe2SMark Cave-Ayland if (s->depth == 24) { 60755d7bfe2SMark Cave-Ayland val = s->tmpblit & 0xffffff; 60855d7bfe2SMark Cave-Ayland val = cpu_to_be32(val); 60955d7bfe2SMark Cave-Ayland for (i = 0; i < len; i++) { 61055d7bfe2SMark Cave-Ayland s->vram24[addr + i] = val; 61155d7bfe2SMark Cave-Ayland } 61255d7bfe2SMark Cave-Ayland } 61355d7bfe2SMark Cave-Ayland } else { 61455d7bfe2SMark Cave-Ayland memcpy(&s->vram[addr], &s->vram[adsr], len); 61555d7bfe2SMark Cave-Ayland if (s->depth == 24) { 61655d7bfe2SMark Cave-Ayland memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4); 61755d7bfe2SMark Cave-Ayland } 61855d7bfe2SMark Cave-Ayland } 619*97394580SMark Cave-Ayland tcx_set_dirty(s, addr, len); 62055d7bfe2SMark Cave-Ayland } 62155d7bfe2SMark Cave-Ayland } 62255d7bfe2SMark Cave-Ayland 62355d7bfe2SMark Cave-Ayland static void tcx_rblit_writel(void *opaque, hwaddr addr, 62455d7bfe2SMark Cave-Ayland uint64_t val, unsigned size) 62555d7bfe2SMark Cave-Ayland { 62655d7bfe2SMark Cave-Ayland TCXState *s = opaque; 62755d7bfe2SMark Cave-Ayland uint32_t adsr, len; 62855d7bfe2SMark Cave-Ayland int i; 62955d7bfe2SMark Cave-Ayland 63055d7bfe2SMark Cave-Ayland if (!(addr & 4)) { 63155d7bfe2SMark Cave-Ayland s->tmpblit = val; 63255d7bfe2SMark Cave-Ayland } else { 63355d7bfe2SMark Cave-Ayland addr = (addr >> 3) & 0xfffff; 63455d7bfe2SMark Cave-Ayland adsr = val & 0xffffff; 63555d7bfe2SMark Cave-Ayland len = ((val >> 24) & 0x1f) + 1; 63655d7bfe2SMark Cave-Ayland if (adsr == 0xffffff) { 63755d7bfe2SMark Cave-Ayland memset(&s->vram[addr], s->tmpblit, len); 63855d7bfe2SMark Cave-Ayland if (s->depth == 24) { 63955d7bfe2SMark Cave-Ayland val = s->tmpblit & 0xffffff; 64055d7bfe2SMark Cave-Ayland val = cpu_to_be32(val); 64155d7bfe2SMark Cave-Ayland for (i = 0; i < len; i++) { 64255d7bfe2SMark Cave-Ayland s->vram24[addr + i] = val; 64355d7bfe2SMark Cave-Ayland s->cplane[addr + i] = val; 64455d7bfe2SMark Cave-Ayland } 64555d7bfe2SMark Cave-Ayland } 64655d7bfe2SMark Cave-Ayland } else { 64755d7bfe2SMark Cave-Ayland memcpy(&s->vram[addr], &s->vram[adsr], len); 64855d7bfe2SMark Cave-Ayland if (s->depth == 24) { 64955d7bfe2SMark Cave-Ayland memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4); 65055d7bfe2SMark Cave-Ayland memcpy(&s->cplane[addr], &s->cplane[adsr], len * 4); 65155d7bfe2SMark Cave-Ayland } 65255d7bfe2SMark Cave-Ayland } 653*97394580SMark Cave-Ayland tcx_set_dirty(s, addr, len); 65455d7bfe2SMark Cave-Ayland } 65555d7bfe2SMark Cave-Ayland } 65655d7bfe2SMark Cave-Ayland 65755d7bfe2SMark Cave-Ayland static const MemoryRegionOps tcx_blit_ops = { 65855d7bfe2SMark Cave-Ayland .read = tcx_blit_readl, 65955d7bfe2SMark Cave-Ayland .write = tcx_blit_writel, 66055d7bfe2SMark Cave-Ayland .endianness = DEVICE_NATIVE_ENDIAN, 66155d7bfe2SMark Cave-Ayland .valid = { 66255d7bfe2SMark Cave-Ayland .min_access_size = 4, 66355d7bfe2SMark Cave-Ayland .max_access_size = 4, 66455d7bfe2SMark Cave-Ayland }, 66555d7bfe2SMark Cave-Ayland }; 66655d7bfe2SMark Cave-Ayland 66755d7bfe2SMark Cave-Ayland static const MemoryRegionOps tcx_rblit_ops = { 66855d7bfe2SMark Cave-Ayland .read = tcx_blit_readl, 66955d7bfe2SMark Cave-Ayland .write = tcx_rblit_writel, 67055d7bfe2SMark Cave-Ayland .endianness = DEVICE_NATIVE_ENDIAN, 67155d7bfe2SMark Cave-Ayland .valid = { 67255d7bfe2SMark Cave-Ayland .min_access_size = 4, 67355d7bfe2SMark Cave-Ayland .max_access_size = 4, 67455d7bfe2SMark Cave-Ayland }, 67555d7bfe2SMark Cave-Ayland }; 67655d7bfe2SMark Cave-Ayland 67755d7bfe2SMark Cave-Ayland static void tcx_invalidate_cursor_position(TCXState *s) 67855d7bfe2SMark Cave-Ayland { 67955d7bfe2SMark Cave-Ayland int ymin, ymax, start, end; 68055d7bfe2SMark Cave-Ayland 68155d7bfe2SMark Cave-Ayland /* invalidate only near the cursor */ 68255d7bfe2SMark Cave-Ayland ymin = s->cursy; 68355d7bfe2SMark Cave-Ayland if (ymin >= s->height) { 68455d7bfe2SMark Cave-Ayland return; 68555d7bfe2SMark Cave-Ayland } 68655d7bfe2SMark Cave-Ayland ymax = MIN(s->height, ymin + 32); 68755d7bfe2SMark Cave-Ayland start = ymin * 1024; 68855d7bfe2SMark Cave-Ayland end = ymax * 1024; 68955d7bfe2SMark Cave-Ayland 690*97394580SMark Cave-Ayland tcx_set_dirty(s, start, end - start); 69155d7bfe2SMark Cave-Ayland } 69255d7bfe2SMark Cave-Ayland 69355d7bfe2SMark Cave-Ayland static uint64_t tcx_thc_readl(void *opaque, hwaddr addr, 69455d7bfe2SMark Cave-Ayland unsigned size) 69555d7bfe2SMark Cave-Ayland { 69655d7bfe2SMark Cave-Ayland TCXState *s = opaque; 69755d7bfe2SMark Cave-Ayland uint64_t val; 69855d7bfe2SMark Cave-Ayland 69955d7bfe2SMark Cave-Ayland if (addr == TCX_THC_MISC) { 70055d7bfe2SMark Cave-Ayland val = s->thcmisc | 0x02000000; 70155d7bfe2SMark Cave-Ayland } else { 70255d7bfe2SMark Cave-Ayland val = 0; 70355d7bfe2SMark Cave-Ayland } 70455d7bfe2SMark Cave-Ayland return val; 70555d7bfe2SMark Cave-Ayland } 70655d7bfe2SMark Cave-Ayland 70755d7bfe2SMark Cave-Ayland static void tcx_thc_writel(void *opaque, hwaddr addr, 70855d7bfe2SMark Cave-Ayland uint64_t val, unsigned size) 70955d7bfe2SMark Cave-Ayland { 71055d7bfe2SMark Cave-Ayland TCXState *s = opaque; 71155d7bfe2SMark Cave-Ayland 71255d7bfe2SMark Cave-Ayland if (addr == TCX_THC_CURSXY) { 71355d7bfe2SMark Cave-Ayland tcx_invalidate_cursor_position(s); 71455d7bfe2SMark Cave-Ayland s->cursx = val >> 16; 71555d7bfe2SMark Cave-Ayland s->cursy = val; 71655d7bfe2SMark Cave-Ayland tcx_invalidate_cursor_position(s); 71755d7bfe2SMark Cave-Ayland } else if (addr >= TCX_THC_CURSMASK && addr < TCX_THC_CURSMASK + 128) { 71855d7bfe2SMark Cave-Ayland s->cursmask[(addr - TCX_THC_CURSMASK) >> 2] = val; 71955d7bfe2SMark Cave-Ayland tcx_invalidate_cursor_position(s); 72055d7bfe2SMark Cave-Ayland } else if (addr >= TCX_THC_CURSBITS && addr < TCX_THC_CURSBITS + 128) { 72155d7bfe2SMark Cave-Ayland s->cursbits[(addr - TCX_THC_CURSBITS) >> 2] = val; 72255d7bfe2SMark Cave-Ayland tcx_invalidate_cursor_position(s); 72355d7bfe2SMark Cave-Ayland } else if (addr == TCX_THC_MISC) { 72455d7bfe2SMark Cave-Ayland s->thcmisc = val; 72555d7bfe2SMark Cave-Ayland } 72655d7bfe2SMark Cave-Ayland 72755d7bfe2SMark Cave-Ayland } 72855d7bfe2SMark Cave-Ayland 72955d7bfe2SMark Cave-Ayland static const MemoryRegionOps tcx_thc_ops = { 73055d7bfe2SMark Cave-Ayland .read = tcx_thc_readl, 73155d7bfe2SMark Cave-Ayland .write = tcx_thc_writel, 73255d7bfe2SMark Cave-Ayland .endianness = DEVICE_NATIVE_ENDIAN, 73355d7bfe2SMark Cave-Ayland .valid = { 73455d7bfe2SMark Cave-Ayland .min_access_size = 4, 73555d7bfe2SMark Cave-Ayland .max_access_size = 4, 73655d7bfe2SMark Cave-Ayland }, 73755d7bfe2SMark Cave-Ayland }; 73855d7bfe2SMark Cave-Ayland 73955d7bfe2SMark Cave-Ayland static uint64_t tcx_dummy_readl(void *opaque, hwaddr addr, 74055d7bfe2SMark Cave-Ayland unsigned size) 74155d7bfe2SMark Cave-Ayland { 74255d7bfe2SMark Cave-Ayland return 0; 74355d7bfe2SMark Cave-Ayland } 74455d7bfe2SMark Cave-Ayland 74555d7bfe2SMark Cave-Ayland static void tcx_dummy_writel(void *opaque, hwaddr addr, 74655d7bfe2SMark Cave-Ayland uint64_t val, unsigned size) 74755d7bfe2SMark Cave-Ayland { 74855d7bfe2SMark Cave-Ayland return; 74955d7bfe2SMark Cave-Ayland } 75055d7bfe2SMark Cave-Ayland 75155d7bfe2SMark Cave-Ayland static const MemoryRegionOps tcx_dummy_ops = { 75255d7bfe2SMark Cave-Ayland .read = tcx_dummy_readl, 75355d7bfe2SMark Cave-Ayland .write = tcx_dummy_writel, 754d08151bfSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 755d08151bfSAvi Kivity .valid = { 756d08151bfSAvi Kivity .min_access_size = 4, 757d08151bfSAvi Kivity .max_access_size = 4, 758d08151bfSAvi Kivity }, 7598508b89eSblueswir1 }; 7608508b89eSblueswir1 761380cd056SGerd Hoffmann static const GraphicHwOps tcx_ops = { 762380cd056SGerd Hoffmann .invalidate = tcx_invalidate_display, 763380cd056SGerd Hoffmann .gfx_update = tcx_update_display, 764380cd056SGerd Hoffmann }; 765380cd056SGerd Hoffmann 766380cd056SGerd Hoffmann static const GraphicHwOps tcx24_ops = { 767380cd056SGerd Hoffmann .invalidate = tcx24_invalidate_display, 768380cd056SGerd Hoffmann .gfx_update = tcx24_update_display, 769380cd056SGerd Hoffmann }; 770380cd056SGerd Hoffmann 77101b91ac2SMark Cave-Ayland static void tcx_initfn(Object *obj) 77201b91ac2SMark Cave-Ayland { 77301b91ac2SMark Cave-Ayland SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 77401b91ac2SMark Cave-Ayland TCXState *s = TCX(obj); 77501b91ac2SMark Cave-Ayland 776b21de199SThomas Huth memory_region_init_ram(&s->rom, obj, "tcx.prom", FCODE_MAX_ROM_SIZE, 777f8ed85acSMarkus Armbruster &error_fatal); 77801b91ac2SMark Cave-Ayland memory_region_set_readonly(&s->rom, true); 77901b91ac2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->rom); 78001b91ac2SMark Cave-Ayland 78155d7bfe2SMark Cave-Ayland /* 2/STIP : Stippler */ 782b21de199SThomas Huth memory_region_init_io(&s->stip, obj, &tcx_stip_ops, s, "tcx.stip", 78355d7bfe2SMark Cave-Ayland TCX_STIP_NREGS); 78455d7bfe2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->stip); 78555d7bfe2SMark Cave-Ayland 78655d7bfe2SMark Cave-Ayland /* 3/BLIT : Blitter */ 787b21de199SThomas Huth memory_region_init_io(&s->blit, obj, &tcx_blit_ops, s, "tcx.blit", 78855d7bfe2SMark Cave-Ayland TCX_BLIT_NREGS); 78955d7bfe2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->blit); 79055d7bfe2SMark Cave-Ayland 79155d7bfe2SMark Cave-Ayland /* 5/RSTIP : Raw Stippler */ 792b21de199SThomas Huth memory_region_init_io(&s->rstip, obj, &tcx_rstip_ops, s, "tcx.rstip", 79355d7bfe2SMark Cave-Ayland TCX_RSTIP_NREGS); 79455d7bfe2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->rstip); 79555d7bfe2SMark Cave-Ayland 79655d7bfe2SMark Cave-Ayland /* 6/RBLIT : Raw Blitter */ 797b21de199SThomas Huth memory_region_init_io(&s->rblit, obj, &tcx_rblit_ops, s, "tcx.rblit", 79855d7bfe2SMark Cave-Ayland TCX_RBLIT_NREGS); 79955d7bfe2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->rblit); 80055d7bfe2SMark Cave-Ayland 80155d7bfe2SMark Cave-Ayland /* 7/TEC : ??? */ 802b21de199SThomas Huth memory_region_init_io(&s->tec, obj, &tcx_dummy_ops, s, "tcx.tec", 803b21de199SThomas Huth TCX_TEC_NREGS); 80455d7bfe2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->tec); 80555d7bfe2SMark Cave-Ayland 80655d7bfe2SMark Cave-Ayland /* 8/CMAP : DAC */ 807b21de199SThomas Huth memory_region_init_io(&s->dac, obj, &tcx_dac_ops, s, "tcx.dac", 808b21de199SThomas Huth TCX_DAC_NREGS); 80901b91ac2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->dac); 81001b91ac2SMark Cave-Ayland 81155d7bfe2SMark Cave-Ayland /* 9/THC : Cursor */ 812b21de199SThomas Huth memory_region_init_io(&s->thc, obj, &tcx_thc_ops, s, "tcx.thc", 81355d7bfe2SMark Cave-Ayland TCX_THC_NREGS); 81455d7bfe2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->thc); 81501b91ac2SMark Cave-Ayland 81655d7bfe2SMark Cave-Ayland /* 11/DHC : ??? */ 817b21de199SThomas Huth memory_region_init_io(&s->dhc, obj, &tcx_dummy_ops, s, "tcx.dhc", 81855d7bfe2SMark Cave-Ayland TCX_DHC_NREGS); 81955d7bfe2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->dhc); 82055d7bfe2SMark Cave-Ayland 82155d7bfe2SMark Cave-Ayland /* 12/ALT : ??? */ 822b21de199SThomas Huth memory_region_init_io(&s->alt, obj, &tcx_dummy_ops, s, "tcx.alt", 82355d7bfe2SMark Cave-Ayland TCX_ALT_NREGS); 82455d7bfe2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->alt); 82501b91ac2SMark Cave-Ayland } 82601b91ac2SMark Cave-Ayland 827d4ad9decSMark Cave-Ayland static void tcx_realizefn(DeviceState *dev, Error **errp) 828f40070c3SBlue Swirl { 829d4ad9decSMark Cave-Ayland SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 83001774ddbSAndreas Färber TCXState *s = TCX(dev); 831d08151bfSAvi Kivity ram_addr_t vram_offset = 0; 832da87dd7bSMark Cave-Ayland int size, ret; 833dc828ca1Spbrook uint8_t *vram_base; 834da87dd7bSMark Cave-Ayland char *fcode_filename; 835dc828ca1Spbrook 8363eadad55SPaolo Bonzini memory_region_init_ram(&s->vram_mem, OBJECT(s), "tcx.vram", 837f8ed85acSMarkus Armbruster s->vram_size * (1 + 4 + 4), &error_fatal); 838c5705a77SAvi Kivity vmstate_register_ram_global(&s->vram_mem); 83974259ae5SPaolo Bonzini memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA); 840d08151bfSAvi Kivity vram_base = memory_region_get_ram_ptr(&s->vram_mem); 841e80cfcfcSbellard 84255d7bfe2SMark Cave-Ayland /* 10/ROM : FCode ROM */ 843da87dd7bSMark Cave-Ayland vmstate_register_ram_global(&s->rom); 844da87dd7bSMark Cave-Ayland fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, TCX_ROM_FILE); 845da87dd7bSMark Cave-Ayland if (fcode_filename) { 846da87dd7bSMark Cave-Ayland ret = load_image_targphys(fcode_filename, s->prom_addr, 847da87dd7bSMark Cave-Ayland FCODE_MAX_ROM_SIZE); 8488684e85cSShannon Zhao g_free(fcode_filename); 849da87dd7bSMark Cave-Ayland if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) { 850d4ad9decSMark Cave-Ayland error_report("tcx: could not load prom '%s'", TCX_ROM_FILE); 851da87dd7bSMark Cave-Ayland } 852da87dd7bSMark Cave-Ayland } 853da87dd7bSMark Cave-Ayland 85455d7bfe2SMark Cave-Ayland /* 0/DFB8 : 8-bit plane */ 855eee0b836Sblueswir1 s->vram = vram_base; 856ee6847d1SGerd Hoffmann size = s->vram_size; 8573eadad55SPaolo Bonzini memory_region_init_alias(&s->vram_8bit, OBJECT(s), "tcx.vram.8bit", 858d08151bfSAvi Kivity &s->vram_mem, vram_offset, size); 859d4ad9decSMark Cave-Ayland sysbus_init_mmio(sbd, &s->vram_8bit); 860eee0b836Sblueswir1 vram_offset += size; 861eee0b836Sblueswir1 vram_base += size; 862eee0b836Sblueswir1 86355d7bfe2SMark Cave-Ayland /* 1/DFB24 : 24bit plane */ 864ee6847d1SGerd Hoffmann size = s->vram_size * 4; 865eee0b836Sblueswir1 s->vram24 = (uint32_t *)vram_base; 866eee0b836Sblueswir1 s->vram24_offset = vram_offset; 8673eadad55SPaolo Bonzini memory_region_init_alias(&s->vram_24bit, OBJECT(s), "tcx.vram.24bit", 868d08151bfSAvi Kivity &s->vram_mem, vram_offset, size); 869d4ad9decSMark Cave-Ayland sysbus_init_mmio(sbd, &s->vram_24bit); 870eee0b836Sblueswir1 vram_offset += size; 871eee0b836Sblueswir1 vram_base += size; 872eee0b836Sblueswir1 87355d7bfe2SMark Cave-Ayland /* 4/RDFB32 : Raw Framebuffer */ 874ee6847d1SGerd Hoffmann size = s->vram_size * 4; 875eee0b836Sblueswir1 s->cplane = (uint32_t *)vram_base; 876eee0b836Sblueswir1 s->cplane_offset = vram_offset; 8773eadad55SPaolo Bonzini memory_region_init_alias(&s->vram_cplane, OBJECT(s), "tcx.vram.cplane", 878d08151bfSAvi Kivity &s->vram_mem, vram_offset, size); 879d4ad9decSMark Cave-Ayland sysbus_init_mmio(sbd, &s->vram_cplane); 880f40070c3SBlue Swirl 88155d7bfe2SMark Cave-Ayland /* 9/THC24bits : NetBSD writes here even with 8-bit display: dummy */ 88255d7bfe2SMark Cave-Ayland if (s->depth == 8) { 88355d7bfe2SMark Cave-Ayland memory_region_init_io(&s->thc24, OBJECT(s), &tcx_dummy_ops, s, 88455d7bfe2SMark Cave-Ayland "tcx.thc24", TCX_THC_NREGS); 88555d7bfe2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->thc24); 886eee0b836Sblueswir1 } 887eee0b836Sblueswir1 88855d7bfe2SMark Cave-Ayland sysbus_init_irq(sbd, &s->irq); 88955d7bfe2SMark Cave-Ayland 89055d7bfe2SMark Cave-Ayland if (s->depth == 8) { 89155d7bfe2SMark Cave-Ayland s->con = graphic_console_init(DEVICE(dev), 0, &tcx_ops, s); 89255d7bfe2SMark Cave-Ayland } else { 89355d7bfe2SMark Cave-Ayland s->con = graphic_console_init(DEVICE(dev), 0, &tcx24_ops, s); 89455d7bfe2SMark Cave-Ayland } 89555d7bfe2SMark Cave-Ayland s->thcmisc = 0; 89655d7bfe2SMark Cave-Ayland 897c78f7137SGerd Hoffmann qemu_console_resize(s->con, s->width, s->height); 898420557e8Sbellard } 899420557e8Sbellard 900999e12bbSAnthony Liguori static Property tcx_properties[] = { 901c7bcc85dSPaolo Bonzini DEFINE_PROP_UINT32("vram_size", TCXState, vram_size, -1), 90253dad499SGerd Hoffmann DEFINE_PROP_UINT16("width", TCXState, width, -1), 90353dad499SGerd Hoffmann DEFINE_PROP_UINT16("height", TCXState, height, -1), 90453dad499SGerd Hoffmann DEFINE_PROP_UINT16("depth", TCXState, depth, -1), 905c7bcc85dSPaolo Bonzini DEFINE_PROP_UINT64("prom_addr", TCXState, prom_addr, -1), 90653dad499SGerd Hoffmann DEFINE_PROP_END_OF_LIST(), 907999e12bbSAnthony Liguori }; 908999e12bbSAnthony Liguori 909999e12bbSAnthony Liguori static void tcx_class_init(ObjectClass *klass, void *data) 910999e12bbSAnthony Liguori { 91139bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 912999e12bbSAnthony Liguori 913d4ad9decSMark Cave-Ayland dc->realize = tcx_realizefn; 91439bffca2SAnthony Liguori dc->reset = tcx_reset; 91539bffca2SAnthony Liguori dc->vmsd = &vmstate_tcx; 91639bffca2SAnthony Liguori dc->props = tcx_properties; 917ee6847d1SGerd Hoffmann } 918999e12bbSAnthony Liguori 9198c43a6f0SAndreas Färber static const TypeInfo tcx_info = { 92001774ddbSAndreas Färber .name = TYPE_TCX, 92139bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 92239bffca2SAnthony Liguori .instance_size = sizeof(TCXState), 92301b91ac2SMark Cave-Ayland .instance_init = tcx_initfn, 924999e12bbSAnthony Liguori .class_init = tcx_class_init, 925ee6847d1SGerd Hoffmann }; 926ee6847d1SGerd Hoffmann 92783f7d43aSAndreas Färber static void tcx_register_types(void) 928f40070c3SBlue Swirl { 92939bffca2SAnthony Liguori type_register_static(&tcx_info); 930f40070c3SBlue Swirl } 931f40070c3SBlue Swirl 93283f7d43aSAndreas Färber type_init(tcx_register_types) 933