|
|
1.1 ! root 1: /* ! 2: * QEMU G364 framebuffer Emulator. ! 3: * ! 4: * Copyright (c) 2007-2009 Herve Poussineau ! 5: * ! 6: * This program is free software; you can redistribute it and/or ! 7: * modify it under the terms of the GNU General Public License as ! 8: * published by the Free Software Foundation; either version 2 of ! 9: * the License, or (at your option) any later version. ! 10: * ! 11: * This program is distributed in the hope that it will be useful, ! 12: * but WITHOUT ANY WARRANTY; without even the implied warranty of ! 13: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! 14: * GNU General Public License for more details. ! 15: * ! 16: * You should have received a copy of the GNU General Public License along ! 17: * with this program; if not, write to the Free Software Foundation, Inc., ! 18: * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ! 19: */ ! 20: ! 21: #include "hw.h" ! 22: #include "mips.h" ! 23: #include "console.h" ! 24: #include "pixel_ops.h" ! 25: ! 26: //#define DEBUG_G364 ! 27: ! 28: #ifdef DEBUG_G364 ! 29: #define DPRINTF(fmt, args...) \ ! 30: do { printf("g364: " fmt , ##args); } while (0) ! 31: #else ! 32: #define DPRINTF(fmt, args...) do {} while (0) ! 33: #endif ! 34: #define BADF(fmt, args...) \ ! 35: do { fprintf(stderr, "g364 ERROR: " fmt , ##args);} while (0) ! 36: ! 37: typedef struct G364State { ! 38: /* hardware */ ! 39: uint8_t *vram; ! 40: ram_addr_t vram_offset; ! 41: int vram_size; ! 42: qemu_irq irq; ! 43: /* registers */ ! 44: uint8_t color_palette[256][3]; ! 45: uint8_t cursor_palette[3][3]; ! 46: uint16_t cursor[512]; ! 47: uint32_t cursor_position; ! 48: uint32_t ctla; ! 49: uint32_t top_of_screen; ! 50: uint32_t width, height; /* in pixels */ ! 51: /* display refresh support */ ! 52: DisplayState *ds; ! 53: int depth; ! 54: int blanked; ! 55: } G364State; ! 56: ! 57: #define REG_ID 0x000000 ! 58: #define REG_BOOT 0x080000 ! 59: #define REG_DISPLAY 0x080118 ! 60: #define REG_VDISPLAY 0x080150 ! 61: #define REG_CTLA 0x080300 ! 62: #define REG_TOP 0x080400 ! 63: #define REG_CURS_PAL 0x080508 ! 64: #define REG_CURS_POS 0x080638 ! 65: #define REG_CLR_PAL 0x080800 ! 66: #define REG_CURS_PAT 0x081000 ! 67: #define REG_RESET 0x180000 ! 68: ! 69: #define CTLA_FORCE_BLANK 0x00000400 ! 70: #define CTLA_NO_CURSOR 0x00800000 ! 71: ! 72: static inline int check_dirty(ram_addr_t page) ! 73: { ! 74: return cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG); ! 75: } ! 76: ! 77: static inline void reset_dirty(G364State *s, ! 78: ram_addr_t page_min, ram_addr_t page_max) ! 79: { ! 80: cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE - 1, ! 81: VGA_DIRTY_FLAG); ! 82: } ! 83: ! 84: static void g364fb_draw_graphic8(G364State *s) ! 85: { ! 86: int i, w; ! 87: uint8_t *vram; ! 88: uint8_t *data_display, *dd; ! 89: ram_addr_t page, page_min, page_max; ! 90: int x, y; ! 91: int xmin, xmax; ! 92: int ymin, ymax; ! 93: int xcursor, ycursor; ! 94: unsigned int (*rgb_to_pixel)(unsigned int r, unsigned int g, unsigned int b); ! 95: ! 96: switch (ds_get_bits_per_pixel(s->ds)) { ! 97: case 8: ! 98: rgb_to_pixel = rgb_to_pixel8; ! 99: w = 1; ! 100: break; ! 101: case 15: ! 102: rgb_to_pixel = rgb_to_pixel15; ! 103: w = 2; ! 104: break; ! 105: case 16: ! 106: rgb_to_pixel = rgb_to_pixel16; ! 107: w = 2; ! 108: break; ! 109: case 32: ! 110: rgb_to_pixel = rgb_to_pixel32; ! 111: w = 4; ! 112: break; ! 113: default: ! 114: BADF("unknown host depth %d\n", ds_get_bits_per_pixel(s->ds)); ! 115: return; ! 116: } ! 117: ! 118: page = s->vram_offset; ! 119: page_min = (ram_addr_t)-1; ! 120: page_max = 0; ! 121: ! 122: x = y = 0; ! 123: xmin = s->width; ! 124: xmax = 0; ! 125: ymin = s->height; ! 126: ymax = 0; ! 127: ! 128: if (!(s->ctla & CTLA_NO_CURSOR)) { ! 129: xcursor = s->cursor_position >> 12; ! 130: ycursor = s->cursor_position & 0xfff; ! 131: } else { ! 132: xcursor = ycursor = -65; ! 133: } ! 134: ! 135: vram = s->vram + s->top_of_screen; ! 136: /* XXX: out of range in vram? */ ! 137: data_display = dd = ds_get_data(s->ds); ! 138: while (y < s->height) { ! 139: if (check_dirty(page)) { ! 140: if (y < ymin) ! 141: ymin = ymax = y; ! 142: if (page_min == (ram_addr_t)-1) ! 143: page_min = page; ! 144: page_max = page; ! 145: if (x < xmin) ! 146: xmin = x; ! 147: for (i = 0; i < TARGET_PAGE_SIZE; i++) { ! 148: uint8_t index; ! 149: unsigned int color; ! 150: if (unlikely((y >= ycursor && y < ycursor + 64) && ! 151: (x >= xcursor && x < xcursor + 64))) { ! 152: /* pointer area */ ! 153: int xdiff = x - xcursor; ! 154: uint16_t curs = s->cursor[(y - ycursor) * 8 + xdiff / 8]; ! 155: int op = (curs >> ((xdiff & 7) * 2)) & 3; ! 156: if (likely(op == 0)) { ! 157: /* transparent */ ! 158: index = *vram; ! 159: color = (*rgb_to_pixel)( ! 160: s->color_palette[index][0], ! 161: s->color_palette[index][1], ! 162: s->color_palette[index][2]); ! 163: } else { ! 164: /* get cursor color */ ! 165: index = op - 1; ! 166: color = (*rgb_to_pixel)( ! 167: s->cursor_palette[index][0], ! 168: s->cursor_palette[index][1], ! 169: s->cursor_palette[index][2]); ! 170: } ! 171: } else { ! 172: /* normal area */ ! 173: index = *vram; ! 174: color = (*rgb_to_pixel)( ! 175: s->color_palette[index][0], ! 176: s->color_palette[index][1], ! 177: s->color_palette[index][2]); ! 178: } ! 179: memcpy(dd, &color, w); ! 180: dd += w; ! 181: x++; ! 182: vram++; ! 183: if (x == s->width) { ! 184: xmax = s->width - 1; ! 185: y++; ! 186: if (y == s->height) { ! 187: ymax = s->height - 1; ! 188: goto done; ! 189: } ! 190: data_display = dd = data_display + ds_get_linesize(s->ds); ! 191: xmin = 0; ! 192: x = 0; ! 193: } ! 194: } ! 195: if (x > xmax) ! 196: xmax = x; ! 197: if (y > ymax) ! 198: ymax = y; ! 199: } else { ! 200: int dy; ! 201: if (page_min != (ram_addr_t)-1) { ! 202: reset_dirty(s, page_min, page_max); ! 203: page_min = (ram_addr_t)-1; ! 204: page_max = 0; ! 205: dpy_update(s->ds, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1); ! 206: xmin = s->width; ! 207: xmax = 0; ! 208: ymin = s->height; ! 209: ymax = 0; ! 210: } ! 211: x += TARGET_PAGE_SIZE; ! 212: dy = x / s->width; ! 213: x = x % s->width; ! 214: y += dy; ! 215: vram += TARGET_PAGE_SIZE; ! 216: data_display += dy * ds_get_linesize(s->ds); ! 217: dd = data_display + x * w; ! 218: } ! 219: page += TARGET_PAGE_SIZE; ! 220: } ! 221: ! 222: done: ! 223: if (page_min != (ram_addr_t)-1) { ! 224: dpy_update(s->ds, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1); ! 225: reset_dirty(s, page_min, page_max); ! 226: } ! 227: } ! 228: ! 229: static void g364fb_draw_blank(G364State *s) ! 230: { ! 231: int i, w; ! 232: uint8_t *d; ! 233: ! 234: if (s->blanked) { ! 235: /* Screen is already blank. No need to redraw it */ ! 236: return; ! 237: } ! 238: ! 239: w = s->width * ((ds_get_bits_per_pixel(s->ds) + 7) >> 3); ! 240: d = ds_get_data(s->ds); ! 241: for (i = 0; i < s->height; i++) { ! 242: memset(d, 0, w); ! 243: d += ds_get_linesize(s->ds); ! 244: } ! 245: ! 246: dpy_update(s->ds, 0, 0, s->width, s->height); ! 247: s->blanked = 1; ! 248: } ! 249: ! 250: static void g364fb_update_display(void *opaque) ! 251: { ! 252: G364State *s = opaque; ! 253: ! 254: if (s->width == 0 || s->height == 0) ! 255: return; ! 256: ! 257: if (s->width != ds_get_width(s->ds) || s->height != ds_get_height(s->ds)) { ! 258: qemu_console_resize(s->ds, s->width, s->height); ! 259: } ! 260: ! 261: if (s->ctla & CTLA_FORCE_BLANK) { ! 262: g364fb_draw_blank(s); ! 263: } else if (s->depth == 8) { ! 264: g364fb_draw_graphic8(s); ! 265: } else { ! 266: BADF("unknown guest depth %d\n", s->depth); ! 267: } ! 268: ! 269: qemu_irq_raise(s->irq); ! 270: } ! 271: ! 272: static void inline g364fb_invalidate_display(void *opaque) ! 273: { ! 274: G364State *s = opaque; ! 275: int i; ! 276: ! 277: s->blanked = 0; ! 278: for (i = 0; i < s->vram_size; i += TARGET_PAGE_SIZE) { ! 279: cpu_physical_memory_set_dirty(s->vram_offset + i); ! 280: } ! 281: } ! 282: ! 283: static void g364fb_reset(void *opaque) ! 284: { ! 285: G364State *s = opaque; ! 286: qemu_irq_lower(s->irq); ! 287: ! 288: memset(s->color_palette, 0, sizeof(s->color_palette)); ! 289: memset(s->cursor_palette, 0, sizeof(s->cursor_palette)); ! 290: memset(s->cursor, 0, sizeof(s->cursor)); ! 291: s->cursor_position = 0; ! 292: s->ctla = 0; ! 293: s->top_of_screen = 0; ! 294: s->width = s->height = 0; ! 295: memset(s->vram, 0, s->vram_size); ! 296: g364fb_invalidate_display(opaque); ! 297: } ! 298: ! 299: static void g364fb_screen_dump(void *opaque, const char *filename) ! 300: { ! 301: G364State *s = opaque; ! 302: int y, x; ! 303: uint8_t index; ! 304: uint8_t *data_buffer; ! 305: FILE *f; ! 306: ! 307: if (s->depth != 8) { ! 308: BADF("unknown guest depth %d\n", s->depth); ! 309: return; ! 310: } ! 311: ! 312: f = fopen(filename, "wb"); ! 313: if (!f) ! 314: return; ! 315: ! 316: if (s->ctla & CTLA_FORCE_BLANK) { ! 317: /* blank screen */ ! 318: fprintf(f, "P4\n%d %d\n", ! 319: s->width, s->height); ! 320: for (y = 0; y < s->height; y++) ! 321: for (x = 0; x < s->width; x++) ! 322: fputc(0, f); ! 323: } else { ! 324: data_buffer = s->vram + s->top_of_screen; ! 325: fprintf(f, "P6\n%d %d\n%d\n", ! 326: s->width, s->height, 255); ! 327: for (y = 0; y < s->height; y++) ! 328: for (x = 0; x < s->width; x++, data_buffer++) { ! 329: index = *data_buffer; ! 330: fputc(s->color_palette[index][0], f); ! 331: fputc(s->color_palette[index][1], f); ! 332: fputc(s->color_palette[index][2], f); ! 333: } ! 334: } ! 335: ! 336: fclose(f); ! 337: } ! 338: ! 339: /* called for accesses to io ports */ ! 340: static uint32_t g364fb_ctrl_readl(void *opaque, target_phys_addr_t addr) ! 341: { ! 342: G364State *s = opaque; ! 343: uint32_t val; ! 344: ! 345: if (addr >= REG_CURS_PAT && addr < REG_CURS_PAT + 0x1000) { ! 346: /* cursor pattern */ ! 347: int idx = (addr - REG_CURS_PAT) >> 3; ! 348: val = s->cursor[idx]; ! 349: } else if (addr >= REG_CURS_PAL && addr < REG_CURS_PAL + 0x18) { ! 350: /* cursor palette */ ! 351: int idx = (addr - REG_CURS_PAL) >> 3; ! 352: val = ((uint32_t)s->cursor_palette[idx][0] << 16); ! 353: val |= ((uint32_t)s->cursor_palette[idx][1] << 8); ! 354: val |= ((uint32_t)s->cursor_palette[idx][2] << 0); ! 355: } else { ! 356: switch (addr) { ! 357: case REG_ID: ! 358: val = 0x10; /* Mips G364 */ ! 359: break; ! 360: case REG_DISPLAY: ! 361: val = s->width / 4; ! 362: break; ! 363: case REG_VDISPLAY: ! 364: val = s->height * 2; ! 365: break; ! 366: case REG_CTLA: ! 367: val = s->ctla; ! 368: break; ! 369: default: ! 370: { ! 371: BADF("invalid read at [" TARGET_FMT_plx "]\n", addr); ! 372: val = 0; ! 373: break; ! 374: } ! 375: } ! 376: } ! 377: ! 378: DPRINTF("read 0x%08x at [" TARGET_FMT_plx "]\n", val, addr); ! 379: ! 380: return val; ! 381: } ! 382: ! 383: static uint32_t g364fb_ctrl_readw(void *opaque, target_phys_addr_t addr) ! 384: { ! 385: uint32_t v = g364fb_ctrl_readl(opaque, addr & ~0x3); ! 386: if (addr & 0x2) ! 387: return v >> 16; ! 388: else ! 389: return v & 0xffff; ! 390: } ! 391: ! 392: static uint32_t g364fb_ctrl_readb(void *opaque, target_phys_addr_t addr) ! 393: { ! 394: uint32_t v = g364fb_ctrl_readl(opaque, addr & ~0x3); ! 395: return (v >> (8 * (addr & 0x3))) & 0xff; ! 396: } ! 397: ! 398: static void g364fb_update_depth(G364State *s) ! 399: { ! 400: const static int depths[8] = { 1, 2, 4, 8, 15, 16, 0 }; ! 401: s->depth = depths[(s->ctla & 0x00700000) >> 20]; ! 402: } ! 403: ! 404: static void g364_invalidate_cursor_position(G364State *s) ! 405: { ! 406: int ymin, ymax, start, end, i; ! 407: ! 408: /* invalidate only near the cursor */ ! 409: ymin = s->cursor_position & 0xfff; ! 410: ymax = MIN(s->height, ymin + 64); ! 411: start = ymin * ds_get_linesize(s->ds); ! 412: end = (ymax + 1) * ds_get_linesize(s->ds); ! 413: ! 414: for (i = start; i < end; i += TARGET_PAGE_SIZE) { ! 415: cpu_physical_memory_set_dirty(s->vram_offset + i); ! 416: } ! 417: } ! 418: ! 419: static void g364fb_ctrl_writel(void *opaque, target_phys_addr_t addr, uint32_t val) ! 420: { ! 421: G364State *s = opaque; ! 422: ! 423: DPRINTF("write 0x%08x at [" TARGET_FMT_plx "]\n", val, addr); ! 424: ! 425: if (addr >= REG_CLR_PAL && addr < REG_CLR_PAL + 0x800) { ! 426: /* color palette */ ! 427: int idx = (addr - REG_CLR_PAL) >> 3; ! 428: s->color_palette[idx][0] = (val >> 16) & 0xff; ! 429: s->color_palette[idx][1] = (val >> 8) & 0xff; ! 430: s->color_palette[idx][2] = val & 0xff; ! 431: g364fb_invalidate_display(s); ! 432: } else if (addr >= REG_CURS_PAT && addr < REG_CURS_PAT + 0x1000) { ! 433: /* cursor pattern */ ! 434: int idx = (addr - REG_CURS_PAT) >> 3; ! 435: s->cursor[idx] = val; ! 436: g364fb_invalidate_display(s); ! 437: } else if (addr >= REG_CURS_PAL && addr < REG_CURS_PAL + 0x18) { ! 438: /* cursor palette */ ! 439: int idx = (addr - REG_CURS_PAL) >> 3; ! 440: s->cursor_palette[idx][0] = (val >> 16) & 0xff; ! 441: s->cursor_palette[idx][1] = (val >> 8) & 0xff; ! 442: s->cursor_palette[idx][2] = val & 0xff; ! 443: g364fb_invalidate_display(s); ! 444: } else { ! 445: switch (addr) { ! 446: case REG_ID: /* Card identifier; read-only */ ! 447: case REG_BOOT: /* Boot timing */ ! 448: case 0x80108: /* Line timing: half sync */ ! 449: case 0x80110: /* Line timing: back porch */ ! 450: case 0x80120: /* Line timing: short display */ ! 451: case 0x80128: /* Frame timing: broad pulse */ ! 452: case 0x80130: /* Frame timing: v sync */ ! 453: case 0x80138: /* Frame timing: v preequalise */ ! 454: case 0x80140: /* Frame timing: v postequalise */ ! 455: case 0x80148: /* Frame timing: v blank */ ! 456: case 0x80158: /* Line timing: line time */ ! 457: case 0x80160: /* Frame store: line start */ ! 458: case 0x80168: /* vram cycle: mem init */ ! 459: case 0x80170: /* vram cycle: transfer delay */ ! 460: case 0x80200: /* vram cycle: mask register */ ! 461: /* ignore */ ! 462: break; ! 463: case REG_TOP: ! 464: s->top_of_screen = val; ! 465: g364fb_invalidate_display(s); ! 466: break; ! 467: case REG_DISPLAY: ! 468: s->width = val * 4; ! 469: break; ! 470: case REG_VDISPLAY: ! 471: s->height = val / 2; ! 472: break; ! 473: case REG_CTLA: ! 474: s->ctla = val; ! 475: g364fb_update_depth(s); ! 476: g364fb_invalidate_display(s); ! 477: break; ! 478: case REG_CURS_POS: ! 479: g364_invalidate_cursor_position(s); ! 480: s->cursor_position = val; ! 481: g364_invalidate_cursor_position(s); ! 482: break; ! 483: case REG_RESET: ! 484: g364fb_reset(s); ! 485: break; ! 486: default: ! 487: BADF("invalid write of 0x%08x at [" TARGET_FMT_plx "]\n", val, addr); ! 488: break; ! 489: } ! 490: } ! 491: qemu_irq_lower(s->irq); ! 492: } ! 493: ! 494: static void g364fb_ctrl_writew(void *opaque, target_phys_addr_t addr, uint32_t val) ! 495: { ! 496: uint32_t old_val = g364fb_ctrl_readl(opaque, addr & ~0x3); ! 497: ! 498: if (addr & 0x2) ! 499: val = (val << 16) | (old_val & 0x0000ffff); ! 500: else ! 501: val = val | (old_val & 0xffff0000); ! 502: g364fb_ctrl_writel(opaque, addr & ~0x3, val); ! 503: } ! 504: ! 505: static void g364fb_ctrl_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) ! 506: { ! 507: uint32_t old_val = g364fb_ctrl_readl(opaque, addr & ~0x3); ! 508: ! 509: switch (addr & 3) { ! 510: case 0: ! 511: val = val | (old_val & 0xffffff00); ! 512: break; ! 513: case 1: ! 514: val = (val << 8) | (old_val & 0xffff00ff); ! 515: break; ! 516: case 2: ! 517: val = (val << 16) | (old_val & 0xff00ffff); ! 518: break; ! 519: case 3: ! 520: val = (val << 24) | (old_val & 0x00ffffff); ! 521: break; ! 522: } ! 523: g364fb_ctrl_writel(opaque, addr & ~0x3, val); ! 524: } ! 525: ! 526: static CPUReadMemoryFunc *g364fb_ctrl_read[3] = { ! 527: g364fb_ctrl_readb, ! 528: g364fb_ctrl_readw, ! 529: g364fb_ctrl_readl, ! 530: }; ! 531: ! 532: static CPUWriteMemoryFunc *g364fb_ctrl_write[3] = { ! 533: g364fb_ctrl_writeb, ! 534: g364fb_ctrl_writew, ! 535: g364fb_ctrl_writel, ! 536: }; ! 537: ! 538: static int g364fb_load(QEMUFile *f, void *opaque, int version_id) ! 539: { ! 540: G364State *s = opaque; ! 541: unsigned int i, vram_size; ! 542: ! 543: if (version_id != 1) ! 544: return -EINVAL; ! 545: ! 546: vram_size = qemu_get_be32(f); ! 547: if (vram_size < s->vram_size) ! 548: return -EINVAL; ! 549: qemu_get_buffer(f, s->vram, s->vram_size); ! 550: for (i = 0; i < 256; i++) ! 551: qemu_get_buffer(f, s->color_palette[i], 3); ! 552: for (i = 0; i < 3; i++) ! 553: qemu_get_buffer(f, s->cursor_palette[i], 3); ! 554: qemu_get_buffer(f, (uint8_t *)s->cursor, sizeof(s->cursor)); ! 555: s->cursor_position = qemu_get_be32(f); ! 556: s->ctla = qemu_get_be32(f); ! 557: s->top_of_screen = qemu_get_be32(f); ! 558: s->width = qemu_get_be32(f); ! 559: s->height = qemu_get_be32(f); ! 560: ! 561: /* force refresh */ ! 562: g364fb_update_depth(s); ! 563: g364fb_invalidate_display(s); ! 564: ! 565: return 0; ! 566: } ! 567: ! 568: static void g364fb_save(QEMUFile *f, void *opaque) ! 569: { ! 570: G364State *s = opaque; ! 571: int i; ! 572: ! 573: qemu_put_be32(f, s->vram_size); ! 574: qemu_put_buffer(f, s->vram, s->vram_size); ! 575: for (i = 0; i < 256; i++) ! 576: qemu_put_buffer(f, s->color_palette[i], 3); ! 577: for (i = 0; i < 3; i++) ! 578: qemu_put_buffer(f, s->cursor_palette[i], 3); ! 579: qemu_put_buffer(f, (uint8_t *)s->cursor, sizeof(s->cursor)); ! 580: qemu_put_be32(f, s->cursor_position); ! 581: qemu_put_be32(f, s->ctla); ! 582: qemu_put_be32(f, s->top_of_screen); ! 583: qemu_put_be32(f, s->width); ! 584: qemu_put_be32(f, s->height); ! 585: } ! 586: ! 587: int g364fb_mm_init(uint8_t *vram, ram_addr_t vram_offset, ! 588: int vram_size, target_phys_addr_t vram_base, ! 589: target_phys_addr_t ctrl_base, int it_shift, ! 590: qemu_irq irq) ! 591: { ! 592: G364State *s; ! 593: int io_ctrl; ! 594: ! 595: s = qemu_mallocz(sizeof(G364State)); ! 596: ! 597: s->vram = vram; ! 598: s->vram_offset = vram_offset; ! 599: s->vram_size = vram_size; ! 600: s->irq = irq; ! 601: ! 602: qemu_register_reset(g364fb_reset, s); ! 603: register_savevm("g364fb", 0, 1, g364fb_save, g364fb_load, s); ! 604: g364fb_reset(s); ! 605: ! 606: s->ds = graphic_console_init(g364fb_update_display, ! 607: g364fb_invalidate_display, ! 608: g364fb_screen_dump, NULL, s); ! 609: ! 610: cpu_register_physical_memory(vram_base, s->vram_size, s->vram_offset); ! 611: ! 612: io_ctrl = cpu_register_io_memory(0, g364fb_ctrl_read, g364fb_ctrl_write, s); ! 613: cpu_register_physical_memory(ctrl_base, 0x200000, io_ctrl); ! 614: ! 615: return 0; ! 616: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.