1 /* $OpenBSD: cfxga.c,v 1.33 2022/07/15 17:57:26 kettenis Exp $ */ 2 3 /* 4 * Copyright (c) 2005, 2006, Matthieu Herrb and Miodrag Vallat 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * Display driver for the Colorgraphic CompactFlash ``VoyagerVGA'' card. 21 * based upon the Epson S1D13806 graphics chip. 22 * 23 * Our goals are: 24 * - to provide a somewhat usable emulation mode for extra text display. 25 * - to let an application (such as an X server) map the controller registers 26 * in order to do its own display game. 27 * 28 * Driving this card is somewhat a challenge since: 29 * - its video memory is not directly accessible. 30 * - no operation can make use of DMA. 31 */ 32 33 #include <sys/param.h> 34 #include <sys/kernel.h> 35 #include <sys/device.h> 36 #include <sys/systm.h> 37 #include <sys/malloc.h> 38 #include <sys/conf.h> 39 40 #include <dev/pcmcia/pcmciavar.h> 41 #include <dev/pcmcia/pcmciareg.h> 42 43 #include <dev/wscons/wsconsio.h> 44 #include <dev/wscons/wsdisplayvar.h> 45 #include <dev/rasops/rasops.h> 46 47 #include <dev/pcmcia/cfxgareg.h> 48 49 /* 50 #define CFXGADEBUG 51 #define ENABLE_8BIT_MODES 52 */ 53 54 #ifdef CFXGADEBUG 55 #define DPRINTF(arg) printf arg 56 #else 57 #define DPRINTF(arg) 58 #endif 59 60 struct cfxga_screen; 61 62 #define CFXGA_MODE_640x480x16 0 63 #define CFXGA_MODE_800x600x16 1 64 #ifdef ENABLE_8BIT_MODES 65 #define CFXGA_MODE_640x480x8 2 66 #define CFXGA_MODE_800x600x8 3 67 #define CFXGA_NMODES 4 68 #else 69 #define CFXGA_NMODES 2 70 #endif 71 72 struct cfxga_softc { 73 struct device sc_dev; 74 struct pcmcia_function *sc_pf; 75 int sc_state; 76 #define CS_MAPPED 0x0001 77 #define CS_RESET 0x0002 78 79 struct pcmcia_mem_handle sc_pmemh; 80 int sc_memwin; 81 bus_addr_t sc_offset; 82 83 int sc_mode; 84 85 int sc_nscreens; 86 LIST_HEAD(, cfxga_screen) sc_scr; 87 struct cfxga_screen *sc_active; 88 89 /* wsdisplay glue */ 90 struct wsscreen_descr sc_wsd[CFXGA_NMODES]; 91 struct wsscreen_list sc_wsl; 92 struct wsscreen_descr *sc_scrlist[CFXGA_NMODES]; 93 struct wsdisplay_emulops sc_ops; 94 struct device *sc_wsdisplay; 95 }; 96 97 int cfxga_match(struct device *, void *, void *); 98 void cfxga_attach(struct device *, struct device *, void *); 99 int cfxga_detach(struct device *, int); 100 int cfxga_activate(struct device *, int); 101 102 const struct cfattach cfxga_ca = { 103 sizeof(struct cfxga_softc), cfxga_match, cfxga_attach, 104 cfxga_detach, cfxga_activate 105 }; 106 107 struct cfdriver cfxga_cd = { 108 NULL, "cfxga", DV_DULL 109 }; 110 111 int cfxga_alloc_screen(void *, const struct wsscreen_descr *, void **, 112 int *, int *, uint32_t *); 113 void cfxga_burner(void *, u_int, u_int); 114 void cfxga_free_screen(void *, void *); 115 int cfxga_ioctl(void *, u_long, caddr_t, int, struct proc *); 116 paddr_t cfxga_mmap(void *, off_t, int); 117 int cfxga_load_font(void *, void *, struct wsdisplay_font *); 118 int cfxga_list_font(void *, struct wsdisplay_font *); 119 int cfxga_show_screen(void *, void *, int, void (*)(void *, int, int), 120 void *); 121 122 struct wsdisplay_accessops cfxga_accessops = { 123 .ioctl = cfxga_ioctl, 124 .mmap = cfxga_mmap, 125 .alloc_screen = cfxga_alloc_screen, 126 .free_screen = cfxga_free_screen, 127 .show_screen = cfxga_show_screen, 128 .load_font = cfxga_load_font, 129 .list_font = cfxga_list_font, 130 .burn_screen = cfxga_burner 131 }; 132 133 /* 134 * Per-screen structure 135 */ 136 137 struct cfxga_screen { 138 LIST_ENTRY(cfxga_screen) scr_link; 139 struct cfxga_softc *scr_sc; /* parent reference */ 140 struct rasops_info scr_ri; /* raster op glue */ 141 struct wsdisplay_charcell *scr_mem; /* backing memory */ 142 }; 143 144 int cfxga_copycols(void *, int, int, int, int); 145 int cfxga_copyrows(void *, int, int, int); 146 int cfxga_do_cursor(struct rasops_info *); 147 int cfxga_erasecols(void *, int, int, int, uint32_t); 148 int cfxga_eraserows(void *, int, int, uint32_t); 149 int cfxga_putchar(void *, int, int, u_int, uint32_t); 150 151 int cfxga_install_function(struct pcmcia_function *); 152 void cfxga_remove_function(struct pcmcia_function *); 153 154 int cfxga_expand_char(struct cfxga_screen *, u_int, int, int, uint32_t); 155 int cfxga_repaint_screen(struct cfxga_screen *); 156 void cfxga_reset_video(struct cfxga_softc *); 157 void cfxga_reset_and_repaint(struct cfxga_softc *); 158 int cfxga_solid_fill(struct cfxga_screen *, int, int, int, int, int32_t); 159 int cfxga_standalone_rop(struct cfxga_screen *, u_int, 160 int, int, int, int, int, int); 161 int cfxga_synchronize(struct cfxga_softc *); 162 u_int cfxga_wait(struct cfxga_softc *, u_int, u_int); 163 164 #define cfxga_clear_screen(scr) \ 165 cfxga_solid_fill(scr, 0, 0, scr->scr_ri.ri_width, \ 166 scr->scr_ri.ri_height, scr->scr_ri.ri_devcmap[WSCOL_BLACK]) 167 168 #define cfxga_read_1(sc, addr) \ 169 bus_space_read_1((sc)->sc_pmemh.memt, (sc)->sc_pmemh.memh, \ 170 (sc)->sc_offset + (addr)) 171 #define cfxga_read_2(sc, addr) \ 172 bus_space_read_2((sc)->sc_pmemh.memt, (sc)->sc_pmemh.memh, \ 173 (sc)->sc_offset + (addr)) 174 #define cfxga_write_1(sc, addr, val) \ 175 bus_space_write_1((sc)->sc_pmemh.memt, (sc)->sc_pmemh.memh, \ 176 (sc)->sc_offset + (addr), (val)) 177 #define cfxga_write_2(sc, addr, val) \ 178 bus_space_write_2((sc)->sc_pmemh.memt, (sc)->sc_pmemh.memh, \ 179 (sc)->sc_offset + (addr), (val)) 180 181 #define cfxga_stop_memory_blt(sc) \ 182 (void)cfxga_read_2(sc, CFREG_BITBLT_DATA) 183 184 const char *cfxga_modenames[CFXGA_NMODES] = { 185 "640x480x16", 186 "800x600x16", 187 #ifdef ENABLE_8BIT_MODES 188 "640x480x8", 189 "800x600x8" 190 #endif 191 }; 192 193 /* 194 * This card is very poorly engineered, specificationwise. It does not 195 * provide any CIS information, and has no vendor/product numbers as 196 * well: as such, there is no easy way to differentiate it from any 197 * other cheapo PCMCIA card. 198 * 199 * The best we can do is probe for a chip ID. This is not perfect but better 200 * than matching blindly. Of course this requires us to play some nasty games 201 * behind the PCMCIA framework to be able to do this probe, and correctly fail 202 * if this is not the card we are looking for. 203 * 204 * In shorter words: some card designers ought to be shot, as a service 205 * to the community. 206 */ 207 208 /* 209 * Create the necessary pcmcia function structures to alleviate the lack 210 * of any CIS information on this device. 211 * Actually, we hijack the fake function created by the pcmcia framework. 212 */ 213 int 214 cfxga_install_function(struct pcmcia_function *pf) 215 { 216 struct pcmcia_config_entry *cfe; 217 218 /* Get real. */ 219 pf->pf_flags &= ~PFF_FAKE; 220 221 /* Tell the pcmcia framework where the CCR is. */ 222 pf->ccr_base = 0x800; 223 pf->ccr_mask = 0x67; 224 225 /* Create a simple cfe. */ 226 cfe = (struct pcmcia_config_entry *)malloc(sizeof *cfe, 227 M_DEVBUF, M_NOWAIT | M_ZERO); 228 if (cfe == NULL) { 229 DPRINTF(("%s: cfe allocation failed\n", __func__)); 230 return (ENOMEM); 231 } 232 233 cfe->number = 42; /* have to put some value... */ 234 cfe->flags = PCMCIA_CFE_IO16; 235 cfe->iftype = PCMCIA_IFTYPE_MEMORY; 236 237 SIMPLEQ_INSERT_TAIL(&pf->cfe_head, cfe, cfe_list); 238 239 pcmcia_function_init(pf, cfe); 240 return (0); 241 } 242 243 /* 244 * Undo the changes done above. 245 * Such a function is necessary since we need a full-blown pcmcia world 246 * set up in order to do the device probe, but if we don't match the card, 247 * leaving this state will cause trouble during other probes. 248 */ 249 void 250 cfxga_remove_function(struct pcmcia_function *pf) 251 { 252 struct pcmcia_config_entry *cfe; 253 254 /* we are the first and only entry... */ 255 cfe = SIMPLEQ_FIRST(&pf->cfe_head); 256 SIMPLEQ_REMOVE_HEAD(&pf->cfe_head, cfe_list); 257 free(cfe, M_DEVBUF, 0); 258 259 /* And we're a figment of the kernel's imagination again. */ 260 pf->pf_flags |= PFF_FAKE; 261 } 262 263 int 264 cfxga_match(struct device *parent, void *match, void *aux) 265 { 266 struct pcmcia_attach_args *pa = aux; 267 struct pcmcia_function *pf = pa->pf; 268 struct pcmcia_mem_handle h; 269 int rc; 270 int win; 271 bus_addr_t ptr; 272 u_int8_t id = 0; 273 274 if (pa->product != PCMCIA_PRODUCT_INVALID || 275 pa->manufacturer != PCMCIA_VENDOR_INVALID) 276 return (0); 277 278 /* Only a card with no CIS will have a fake function... */ 279 if ((pf->pf_flags & PFF_FAKE) == 0) 280 return (0); 281 282 if (cfxga_install_function(pf) != 0) 283 return (0); 284 285 if (pcmcia_function_enable(pf) != 0) { 286 DPRINTF(("%s: function enable failed\n", __func__)); 287 return (0); 288 } 289 290 rc = pcmcia_mem_alloc(pf, CFXGA_MEM_RANGE, &h); 291 if (rc != 0) 292 goto out; 293 294 rc = pcmcia_mem_map(pf, PCMCIA_MEM_ATTR, 0, CFXGA_MEM_RANGE, 295 &h, &ptr, &win); 296 if (rc != 0) 297 goto out2; 298 299 id = (bus_space_read_1(h.memt, h.memh, ptr + CFREG_REV) & 300 CR_PRODUCT_MASK) >> CR_PRODUCT_SHIFT; 301 302 pcmcia_mem_unmap(pa->pf, win); 303 out2: 304 pcmcia_mem_free(pa->pf, &h); 305 out: 306 pcmcia_function_disable(pf); 307 cfxga_remove_function(pf); 308 309 /* 310 * Be sure to return a value greater than com's if we match, 311 * otherwise it can win due to the way config(8) will order devices... 312 */ 313 return (id == PRODUCT_S1D13806 ? 10 : 0); 314 } 315 316 int 317 cfxga_activate(struct device *dev, int act) 318 { 319 struct cfxga_softc *sc = (void *)dev; 320 int rv = 0; 321 322 switch (act) { 323 case DVACT_DEACTIVATE: 324 pcmcia_function_disable(sc->sc_pf); 325 break; 326 default: 327 rv = config_activate_children(self, act); 328 break; 329 } 330 return (rv); 331 } 332 333 void 334 cfxga_attach(struct device *parent, struct device *self, void *aux) 335 { 336 struct cfxga_softc *sc = (void *)self; 337 struct pcmcia_attach_args *pa = aux; 338 struct pcmcia_function *pf = pa->pf; 339 struct wsemuldisplaydev_attach_args waa; 340 struct wsscreen_descr *wsd; 341 u_int i; 342 343 LIST_INIT(&sc->sc_scr); 344 sc->sc_nscreens = 0; 345 sc->sc_pf = pf; 346 347 if (cfxga_install_function(pf) != 0) { 348 printf(": pcmcia function setup failed\n"); 349 return; 350 } 351 352 if (pcmcia_function_enable(pf)) { 353 printf(": function enable failed\n"); 354 return; 355 } 356 357 if (pcmcia_mem_alloc(pf, CFXGA_MEM_RANGE, &sc->sc_pmemh) != 0) { 358 printf(": can't allocate memory space\n"); 359 return; 360 } 361 362 if (pcmcia_mem_map(pf, PCMCIA_MEM_ATTR, 0, CFXGA_MEM_RANGE, 363 &sc->sc_pmemh, &sc->sc_offset, &sc->sc_memwin) != 0) { 364 printf(": can't map frame buffer registers\n"); 365 pcmcia_mem_free(pf, &sc->sc_pmemh); 366 return; 367 } 368 369 SET(sc->sc_state, CS_MAPPED); 370 371 printf("\n"); 372 373 sc->sc_mode = WSDISPLAYIO_MODE_EMUL; 374 375 /* 376 * We actually defer real initialization to the creation of the 377 * first wsdisplay screen, since we do not know which mode to pick 378 * yet. 379 */ 380 381 for (wsd = sc->sc_wsd, i = 0; i < CFXGA_NMODES; wsd++, i++) { 382 strlcpy(wsd->name, cfxga_modenames[i], sizeof(wsd->name)); 383 wsd->textops = &sc->sc_ops; 384 sc->sc_scrlist[i] = wsd; 385 } 386 sc->sc_wsl.nscreens = CFXGA_NMODES; 387 sc->sc_wsl.screens = (const struct wsscreen_descr **)sc->sc_scrlist; 388 389 waa.console = 0; 390 waa.scrdata = &sc->sc_wsl; 391 waa.accessops = &cfxga_accessops; 392 waa.accesscookie = sc; 393 waa.defaultscreens = 1; 394 395 if ((sc->sc_wsdisplay = 396 config_found(self, &waa, wsemuldisplaydevprint)) == NULL) { 397 /* otherwise wscons will do this */ 398 if (sc->sc_active != NULL) 399 cfxga_clear_screen(sc->sc_active); 400 else 401 cfxga_burner(sc, 0, 0); 402 } 403 } 404 405 int 406 cfxga_detach(struct device *dev, int flags) 407 { 408 struct cfxga_softc *sc = (void *)dev; 409 410 /* 411 * Detach all children, and hope wsdisplay detach code is correct... 412 */ 413 if (sc->sc_wsdisplay != NULL) { 414 config_detach(sc->sc_wsdisplay, DETACH_FORCE); 415 /* sc->sc_wsdisplay = NULL; */ 416 } 417 418 if (ISSET(sc->sc_state, CS_MAPPED)) { 419 pcmcia_mem_unmap(sc->sc_pf, sc->sc_memwin); 420 pcmcia_mem_free(sc->sc_pf, &sc->sc_pmemh); 421 /* CLR(sc->sc_state, CS_MAPPED); */ 422 } 423 424 return (0); 425 } 426 427 /* 428 * Wscons operations 429 */ 430 431 int 432 cfxga_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep, 433 int *curxp, int *curyp, uint32_t *attrp) 434 { 435 struct cfxga_softc *sc = v; 436 struct cfxga_screen *scr; 437 struct rasops_info *ri; 438 u_int mode, width, height, depth, scrsize; 439 440 scr = malloc(sizeof *scr, M_DEVBUF, 441 (cold ? M_NOWAIT : M_WAITOK) | M_ZERO); 442 if (scr == NULL) 443 return (ENOMEM); 444 445 mode = type - sc->sc_wsd; 446 #ifdef DIAGNOSTIC 447 if (mode >= CFXGA_NMODES) 448 mode = CFXGA_MODE_640x480x16; 449 #endif 450 switch (mode) { 451 default: 452 case CFXGA_MODE_640x480x16: 453 width = 640; 454 height = 480; 455 depth = 16; 456 break; 457 case CFXGA_MODE_800x600x16: 458 width = 800; 459 height = 600; 460 depth = 16; 461 break; 462 #ifdef ENABLE_8BIT_MODES 463 case CFXGA_MODE_640x480x8: 464 width = 640; 465 height = 480; 466 depth = 8; 467 break; 468 case CFXGA_MODE_800x600x8: 469 width = 800; 470 height = 600; 471 depth = 8; 472 break; 473 #endif 474 } 475 476 ri = &scr->scr_ri; 477 ri->ri_hw = (void *)scr; 478 ri->ri_bits = NULL; 479 ri->ri_depth = depth; 480 ri->ri_width = width; 481 ri->ri_height = height; 482 ri->ri_stride = width * depth / 8; 483 ri->ri_flg = 0; 484 485 /* swap B and R at 16 bpp */ 486 if (depth == 16) { 487 ri->ri_rnum = 5; 488 ri->ri_rpos = 11; 489 ri->ri_gnum = 6; 490 ri->ri_gpos = 5; 491 ri->ri_bnum = 5; 492 ri->ri_bpos = 0; 493 } 494 495 if (type->nrows == 0) /* first screen creation */ 496 rasops_init(ri, 100, 100); 497 else 498 rasops_init(ri, type->nrows, type->ncols); 499 500 /* 501 * Allocate backing store to remember non-visible screen contents in 502 * emulation mode. 503 */ 504 scr->scr_mem = mallocarray(ri->ri_rows, 505 ri->ri_cols * sizeof(struct wsdisplay_charcell), M_DEVBUF, 506 (cold ? M_NOWAIT : M_WAITOK) | M_ZERO); 507 if (scr->scr_mem == NULL) { 508 free(scr, M_DEVBUF, 0); 509 return (ENOMEM); 510 } 511 scrsize = ri->ri_rows * ri->ri_cols * sizeof(struct wsdisplay_charcell); 512 513 ri->ri_ops.copycols = cfxga_copycols; 514 ri->ri_ops.copyrows = cfxga_copyrows; 515 ri->ri_ops.erasecols = cfxga_erasecols; 516 ri->ri_ops.eraserows = cfxga_eraserows; 517 ri->ri_ops.putchar = cfxga_putchar; 518 ri->ri_do_cursor = cfxga_do_cursor; 519 520 /* 521 * Finish initializing our screen descriptions, now that we know 522 * the actual console emulation parameters. 523 */ 524 if (type->nrows == 0) { 525 struct wsscreen_descr *wsd = (struct wsscreen_descr *)type; 526 527 wsd->nrows = ri->ri_rows; 528 wsd->ncols = ri->ri_cols; 529 bcopy(&ri->ri_ops, &sc->sc_ops, sizeof(sc->sc_ops)); 530 wsd->fontwidth = ri->ri_font->fontwidth; 531 wsd->fontheight = ri->ri_font->fontheight; 532 wsd->capabilities = ri->ri_caps; 533 } 534 535 scr->scr_sc = sc; 536 LIST_INSERT_HEAD(&sc->sc_scr, scr, scr_link); 537 sc->sc_nscreens++; 538 539 ri->ri_ops.pack_attr(ri, 0, 0, 0, attrp); 540 541 *cookiep = ri; 542 *curxp = *curyp = 0; 543 544 return (0); 545 } 546 547 void 548 cfxga_burner(void *v, u_int on, u_int flags) 549 { 550 struct cfxga_softc *sc = (void *)v; 551 u_int8_t mode; 552 553 mode = cfxga_read_1(sc, CFREG_MODE) & LCD_MODE_SWIVEL_BIT_0; 554 555 if (on) 556 cfxga_write_1(sc, CFREG_MODE, mode | MODE_CRT); 557 else 558 cfxga_write_1(sc, CFREG_MODE, mode | MODE_NO_DISPLAY); 559 } 560 561 void 562 cfxga_free_screen(void *v, void *cookie) 563 { 564 struct cfxga_softc *sc = v; 565 struct rasops_info *ri = cookie; 566 struct cfxga_screen *scr = ri->ri_hw; 567 568 LIST_REMOVE(scr, scr_link); 569 sc->sc_nscreens--; 570 571 if (scr == sc->sc_active) { 572 sc->sc_active = NULL; 573 cfxga_burner(sc, 0, 0); 574 } 575 576 free(scr->scr_mem, M_DEVBUF, 0); 577 free(scr, M_DEVBUF, 0); 578 } 579 580 int 581 cfxga_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 582 { 583 struct cfxga_softc *sc = v; 584 struct cfxga_screen *scr; 585 struct wsdisplay_fbinfo *wdf; 586 int mode; 587 588 switch (cmd) { 589 case WSDISPLAYIO_GTYPE: 590 *(u_int *)data = WSDISPLAY_TYPE_CFXGA; 591 break; 592 593 case WSDISPLAYIO_GINFO: 594 wdf = (struct wsdisplay_fbinfo *)data; 595 scr = sc->sc_active; 596 if (scr == NULL) { 597 /* try later...after running wsconscfg to add screens */ 598 wdf->height = wdf->width = wdf->depth = 0; 599 wdf->stride = wdf->offset = wdf->cmsize = 0; 600 } else { 601 wdf->height = scr->scr_ri.ri_height; 602 wdf->width = scr->scr_ri.ri_width; 603 wdf->depth = scr->scr_ri.ri_depth; 604 wdf->stride = scr->scr_ri.ri_stride; 605 wdf->offset = 0; 606 wdf->cmsize = scr->scr_ri.ri_depth <= 8 ? 607 (1 << scr->scr_ri.ri_depth) : 0; 608 } 609 break; 610 611 case WSDISPLAYIO_SMODE: 612 mode = *(u_int *)data; 613 if (mode == sc->sc_mode) 614 break; 615 switch (mode) { 616 case WSDISPLAYIO_MODE_EMUL: 617 cfxga_reset_and_repaint(sc); 618 break; 619 case WSDISPLAYIO_MODE_MAPPED: 620 break; 621 default: 622 return (EINVAL); 623 } 624 sc->sc_mode = mode; 625 break; 626 627 /* these operations are handled by the wscons code... */ 628 case WSDISPLAYIO_GVIDEO: 629 case WSDISPLAYIO_SVIDEO: 630 break; 631 632 /* these operations are not supported... */ 633 case WSDISPLAYIO_GETCMAP: 634 case WSDISPLAYIO_PUTCMAP: 635 case WSDISPLAYIO_LINEBYTES: 636 case WSDISPLAYIO_GCURPOS: 637 case WSDISPLAYIO_SCURPOS: 638 case WSDISPLAYIO_GCURMAX: 639 case WSDISPLAYIO_GCURSOR: 640 case WSDISPLAYIO_SCURSOR: 641 default: 642 return (-1); 643 } 644 645 return (0); 646 } 647 648 paddr_t 649 cfxga_mmap(void *v, off_t off, int prot) 650 { 651 return (-1); 652 } 653 654 int 655 cfxga_show_screen(void *v, void *cookie, int waitok, 656 void (*cb)(void *, int, int), void *cbarg) 657 { 658 struct cfxga_softc *sc = v; 659 struct rasops_info *ri = cookie; 660 struct cfxga_screen *scr = ri->ri_hw, *old; 661 662 old = sc->sc_active; 663 if (old == scr) 664 return (0); 665 666 sc->sc_active = scr; 667 cfxga_reset_and_repaint(sc); /* will turn video on if scr != NULL */ 668 669 return (0); 670 } 671 672 int 673 cfxga_load_font(void *v, void *emulcookie, struct wsdisplay_font *font) 674 { 675 struct cfxga_softc *sc = v; 676 struct cfxga_screen *scr = sc->sc_active; 677 678 if (scr == NULL) 679 return ENXIO; 680 681 return rasops_load_font(&scr->scr_ri, emulcookie, font); 682 } 683 684 int 685 cfxga_list_font(void *v, struct wsdisplay_font *font) 686 { 687 struct cfxga_softc *sc = v; 688 struct cfxga_screen *scr = sc->sc_active; 689 690 if (scr == NULL) 691 return ENXIO; 692 693 return rasops_list_font(&scr->scr_ri, font); 694 } 695 696 /* 697 * Real frame buffer operations 698 */ 699 700 void 701 cfxga_reset_video(struct cfxga_softc *sc) 702 { 703 struct cfxga_screen *scr = sc->sc_active; 704 struct rasops_info *ri; 705 #ifdef ENABLE_8BIT_MODES 706 const u_int8_t *cmap; 707 u_int i; 708 #endif 709 710 /* 711 * Reset controller 712 */ 713 714 /* need to write to both REV and MISC at the same time */ 715 cfxga_write_2(sc, CFREG_REV, 0x80 | (CM_REGSEL << 8)); 716 delay(25000); /* maintain reset for a short while */ 717 /* need to write to both REV and MISC at the same time */ 718 cfxga_write_2(sc, CFREG_REV, 0 | (CM_MEMSEL << 8)); 719 delay(25000); 720 /* stop any pending blt operation */ 721 cfxga_write_2(sc, CFREG_BITBLT_CONTROL, 0); 722 cfxga_stop_memory_blt(sc); 723 cfxga_write_1(sc, CFREG_MODE, 0); /* disable all displays */ 724 725 /* 726 * Setup common video mode parameters. 727 */ 728 729 cfxga_write_2(sc, CFREG_MEMCLK, MEMCLK_SRC_CLK3); 730 #if 0 731 cfxga_write_1(sc, CFREG_LCD_PCLK, LCD_PCLK_SRC_CLKI | LCD_PCLK_DIV_1); 732 cfxga_write_1(sc, CFREG_MPLUG_CLK, 733 MPLUG_PCLK_SRC_CLKI2 | MPLUG_PCLK_DIV_1); 734 #endif 735 cfxga_write_2(sc, CFREG_CRTTV_PCLK, CRT_PCLK_SRC_CLKI | CRT_PCLK_DIV_1); 736 cfxga_write_2(sc, CFREG_WSTATE, WSTATE_MCLK); 737 738 /* MEMCNF and DRAM_RFRSH need to be programmed at the same time */ 739 cfxga_write_2(sc, CFREG_MEMCNF, 740 MEMCNF_SDRAM_INIT | (DRAM_RFRSH_50MHZ << 8)); 741 delay(250); 742 cfxga_write_2(sc, CFREG_DRAM_TIMING, DRAM_TIMING_50MHZ); 743 744 /* 745 * Setup mode-dependent parameters. 746 */ 747 if (scr == NULL) 748 return; 749 750 ri = &scr->scr_ri; 751 switch (scr->scr_ri.ri_width) { 752 default: 753 case 640: 754 cfxga_write_1(sc, CFREG_CRT_HWIDTH, (640 / 8) - 1); 755 /* HNDISP and HSTART need to be programmed at the same time */ 756 cfxga_write_2(sc, CFREG_CRT_HNDISP, 23 | (2 << 8)); 757 cfxga_write_1(sc, CFREG_CRT_HPULSE, 4); 758 cfxga_write_2(sc, CFREG_CRT_VHEIGHT, 480 - 1); 759 /* VNDISP and VSTART need to be programmed at the same time */ 760 cfxga_write_2(sc, CFREG_CRT_VNDISP, 39 | (8 << 8)); 761 cfxga_write_1(sc, CFREG_CRT_VPULSE, 2); 762 break; 763 case 800: 764 cfxga_write_1(sc, CFREG_CRT_HWIDTH, (800 / 8) - 1); 765 /* HNDISP and HSTART need to be programmed at the same time */ 766 cfxga_write_2(sc, CFREG_CRT_HNDISP, 27 | (2 << 8)); 767 cfxga_write_1(sc, CFREG_CRT_HPULSE, 4); 768 cfxga_write_2(sc, CFREG_CRT_VHEIGHT, 600 - 1); 769 /* VNDISP and VSTART need to be programmed at the same time */ 770 cfxga_write_2(sc, CFREG_CRT_VNDISP, 25 | (8 << 8)); 771 cfxga_write_1(sc, CFREG_CRT_VPULSE, 2); 772 break; 773 } 774 cfxga_write_1(sc, CFREG_CRT_MODE, 775 ri->ri_depth == 16 ? CRT_MODE_16BPP : CRT_MODE_8BPP); 776 cfxga_write_2(sc, CFREG_CRT_START_LOW, 0); 777 cfxga_write_1(sc, CFREG_CRT_START_HIGH, 0); 778 cfxga_write_2(sc, CFREG_CRT_MEMORY, ri->ri_width * ri->ri_depth / 16); 779 cfxga_write_1(sc, CFREG_CRT_PANNING, 0); 780 cfxga_write_1(sc, CFREG_CRT_FIFO_THRESHOLD_HIGH, 0); 781 cfxga_write_1(sc, CFREG_CRT_FIFO_THRESHOLD_LOW, 0); 782 cfxga_write_1(sc, CFREG_CRT_CURSOR_CONTROL, CURSOR_INACTIVE); 783 784 #ifdef ENABLE_8BIT_MODES 785 /* 786 * On 8bpp video modes, program the LUT 787 */ 788 if (ri->ri_depth == 8) { 789 #if 0 790 /* Wait for retrace */ 791 while ((cfxga_read_1(sc, CFREG_CRT_VNDISP) & 792 CRT_VNDISP_STATUS) == 0) 793 delay(1); 794 #endif 795 cfxga_write_1(sc, CFREG_LUT_MODE, LUT_CRT); 796 cfxga_write_1(sc, CFREG_LUT_ADDRESS, 0); /* autoincrements */ 797 cmap = rasops_cmap; 798 for (i = 256 * 3; i != 0; i--) 799 cfxga_write_1(sc, CFREG_LUT_DATA, *cmap++ & 0xf0); 800 } 801 #endif 802 803 cfxga_write_1(sc, CFREG_TV_CONTROL, 804 TV_LUMINANCE_FILTER | TV_SVIDEO_OUTPUT | TV_NTSC_OUTPUT); 805 806 cfxga_write_1(sc, CFREG_POWER_CONF, POWERSAVE_MBO); 807 cfxga_write_1(sc, CFREG_WATCHDOG, 0); 808 809 cfxga_write_1(sc, CFREG_MODE, MODE_CRT); 810 delay(25000); 811 } 812 813 void 814 cfxga_reset_and_repaint(struct cfxga_softc *sc) 815 { 816 cfxga_reset_video(sc); 817 818 if (sc->sc_active != NULL) 819 cfxga_repaint_screen(sc->sc_active); 820 else 821 cfxga_burner(sc, 0, 0); 822 } 823 824 /* 825 * Wait for the blitter to be in a given state. 826 */ 827 u_int 828 cfxga_wait(struct cfxga_softc *sc, u_int mask, u_int result) 829 { 830 u_int tries; 831 832 for (tries = 10000; tries != 0; tries--) { 833 if ((cfxga_read_1(sc, CFREG_BITBLT_CONTROL) & mask) == result) 834 break; 835 delay(10); 836 } 837 838 return (tries); 839 } 840 841 /* 842 * Wait for all pending blitter operations to be complete. 843 * Returns non-zero if the blitter got stuck. 844 */ 845 int 846 cfxga_synchronize(struct cfxga_softc *sc) 847 { 848 /* Wait for previous operations to complete */ 849 if (cfxga_wait(sc, BITBLT_ACTIVE, 0) == 0) { 850 DPRINTF(("%s: not ready\n", __func__)); 851 if (ISSET(sc->sc_state, CS_RESET)) 852 return (EAGAIN); 853 else { 854 DPRINTF(("%s: resetting...\n", sc->sc_dev.dv_xname)); 855 SET(sc->sc_state, CS_RESET); 856 cfxga_reset_and_repaint(sc); 857 CLR(sc->sc_state, CS_RESET); 858 } 859 } 860 cfxga_stop_memory_blt(sc); 861 return (0); 862 } 863 864 /* 865 * Display a character. 866 */ 867 int 868 cfxga_expand_char(struct cfxga_screen *scr, u_int uc, int x, int y, 869 uint32_t attr) 870 { 871 struct cfxga_softc *sc = scr->scr_sc; 872 struct rasops_info *ri = &scr->scr_ri; 873 struct wsdisplay_font *font = ri->ri_font; 874 u_int pos, sts, fifo_avail, chunk; 875 u_int8_t *fontbits; 876 int bg, fg, ul; 877 u_int i; 878 int rc; 879 880 pos = (y * ri->ri_width + x) * ri->ri_depth / 8; 881 fontbits = (u_int8_t *)(font->data + (uc - font->firstchar) * 882 ri->ri_fontscale); 883 ri->ri_ops.unpack_attr(ri, attr, &fg, &bg, &ul); 884 885 /* Wait for previous operations to complete */ 886 if ((rc = cfxga_synchronize(sc)) != 0) 887 return (rc); 888 889 cfxga_write_2(sc, CFREG_COLOR_EXPANSION, 890 ((font->fontwidth - 1) & 7) | (OP_COLOR_EXPANSION << 8)); 891 cfxga_write_2(sc, CFREG_BITBLT_SRC_LOW, font->fontwidth <= 8 ? 0 : 1); 892 cfxga_write_2(sc, CFREG_BITBLT_SRC_HIGH, 0); 893 cfxga_write_2(sc, CFREG_BITBLT_DST_LOW, pos); 894 cfxga_write_2(sc, CFREG_BITBLT_DST_HIGH, pos >> 16); 895 cfxga_write_2(sc, CFREG_BITBLT_OFFSET, 896 ri->ri_width * ri->ri_depth / 16); 897 cfxga_write_2(sc, CFREG_BITBLT_WIDTH, font->fontwidth - 1); 898 cfxga_write_2(sc, CFREG_BITBLT_HEIGHT, font->fontheight - 1); 899 cfxga_write_2(sc, CFREG_BITBLT_FG, ri->ri_devcmap[fg]); 900 cfxga_write_2(sc, CFREG_BITBLT_BG, ri->ri_devcmap[bg]); 901 cfxga_write_2(sc, CFREG_BITBLT_CONTROL, BITBLT_ACTIVE | 902 (ri->ri_depth > 8 ? BITBLT_COLOR_16 : BITBLT_COLOR_8)); 903 904 if (cfxga_wait(sc, BITBLT_ACTIVE, BITBLT_ACTIVE) == 0) 905 goto fail; /* unlikely */ 906 fifo_avail = 0; 907 908 for (i = font->fontheight; i != 0; i--) { 909 /* 910 * Find out how much words we can feed before 911 * a FIFO check is needed. 912 */ 913 if (fifo_avail == 0) { 914 sts = cfxga_read_1(sc, CFREG_BITBLT_CONTROL); 915 if ((sts & BITBLT_FIFO_NOT_EMPTY) == 0) 916 fifo_avail = font->fontwidth <= 8 ? 2 : 1; 917 else if ((sts & BITBLT_FIFO_HALF_FULL) == 0) 918 fifo_avail = font->fontwidth <= 8 ? 1 : 0; 919 else { 920 /* 921 * Let the cheap breathe for a short while. 922 * If this is not enough to free some FIFO 923 * entries, abort the operation. 924 */ 925 if (cfxga_wait(sc, BITBLT_FIFO_FULL, 0) == 0) 926 goto fail; 927 } 928 } 929 930 if (font->fontwidth <= 8) { 931 chunk = *fontbits; 932 if (ul && i == 1) 933 chunk = 0xff; 934 } else { 935 chunk = *(u_int16_t *)fontbits; 936 if (ul && i == 1) 937 chunk = 0xffff; 938 } 939 cfxga_write_2(sc, CFREG_BITBLT_DATA, chunk); 940 fontbits += font->stride; 941 fifo_avail--; 942 } 943 944 return (0); 945 946 fail: 947 DPRINTF(("%s: abort\n", __func__)); 948 cfxga_write_2(sc, CFREG_BITBLT_CONTROL, 0); 949 cfxga_stop_memory_blt(sc); 950 return (EINTR); 951 } 952 953 /* 954 * Copy a memory bitmap to the frame buffer. 955 * 956 * This is slow - we only use this to repaint the whole frame buffer on 957 * screen switches. 958 */ 959 int 960 cfxga_repaint_screen(struct cfxga_screen *scr) 961 { 962 struct wsdisplay_charcell *cell = scr->scr_mem; 963 struct rasops_info *ri = &scr->scr_ri; 964 int x, y, cx, cy, lx, ly; 965 int fg, bg; 966 int rc; 967 968 cfxga_clear_screen(scr); 969 970 cx = ri->ri_font->fontwidth; 971 cy = ri->ri_font->fontheight; 972 973 for (ly = 0, y = ri->ri_yorigin; ly < ri->ri_rows; ly++, y += cy) { 974 for (lx = 0, x = ri->ri_xorigin; lx < ri->ri_cols; 975 lx++, x += cx) { 976 if (cell->uc == 0 || cell->uc == ' ') { 977 ri->ri_ops.unpack_attr(ri, cell->attr, 978 &fg, &bg, NULL); 979 rc = cfxga_solid_fill(scr, x, y, cx, cy, 980 ri->ri_devcmap[bg]); 981 } else { 982 rc = cfxga_expand_char(scr, cell->uc, 983 x, y, cell->attr); 984 } 985 cell++; 986 if (rc != 0) 987 return (rc); 988 } 989 } 990 991 return (0); 992 } 993 994 /* 995 * Perform a solid fill operation. 996 */ 997 int 998 cfxga_solid_fill(struct cfxga_screen *scr, int x, int y, int cx, int cy, 999 int32_t srccolor) 1000 { 1001 struct cfxga_softc *sc = scr->scr_sc; 1002 struct rasops_info *ri = &scr->scr_ri; 1003 u_int pos; 1004 int rc; 1005 1006 pos = (y * ri->ri_width + x) * ri->ri_depth / 8; 1007 1008 /* Wait for previous operations to complete */ 1009 if ((rc = cfxga_synchronize(sc)) != 0) 1010 return (rc); 1011 1012 cfxga_write_2(sc, CFREG_BITBLT_ROP, 0 | (OP_SOLID_FILL << 8)); 1013 cfxga_write_2(sc, CFREG_BITBLT_SRC_LOW, pos); 1014 cfxga_write_2(sc, CFREG_BITBLT_SRC_HIGH, pos >> 16); 1015 cfxga_write_2(sc, CFREG_BITBLT_DST_LOW, pos); 1016 cfxga_write_2(sc, CFREG_BITBLT_DST_HIGH, pos >> 16); 1017 cfxga_write_2(sc, CFREG_BITBLT_OFFSET, 1018 ri->ri_width * ri->ri_depth / 16); 1019 cfxga_write_2(sc, CFREG_BITBLT_WIDTH, cx - 1); 1020 cfxga_write_2(sc, CFREG_BITBLT_HEIGHT, cy - 1); 1021 cfxga_write_2(sc, CFREG_BITBLT_FG, (u_int16_t)srccolor); 1022 cfxga_write_2(sc, CFREG_BITBLT_CONTROL, BITBLT_ACTIVE | 1023 (ri->ri_depth > 8 ? BITBLT_COLOR_16 : BITBLT_COLOR_8)); 1024 1025 return (0); 1026 } 1027 1028 /* 1029 * Perform an internal frame buffer operation. 1030 */ 1031 int 1032 cfxga_standalone_rop(struct cfxga_screen *scr, u_int rop, int sx, int sy, 1033 int dx, int dy, int cx, int cy) 1034 { 1035 struct cfxga_softc *sc = scr->scr_sc; 1036 struct rasops_info *ri = &scr->scr_ri; 1037 u_int srcpos, dstpos; 1038 u_int opcode; 1039 int rc; 1040 1041 srcpos = (sy * ri->ri_width + sx) * ri->ri_depth / 8; 1042 dstpos = (dy * ri->ri_width + dx) * ri->ri_depth / 8; 1043 1044 if (dstpos <= srcpos) 1045 opcode = (OP_MOVE_POSITIVE_ROP << 8) | rop; 1046 else 1047 opcode = (OP_MOVE_NEGATIVE_ROP << 8) | rop; 1048 1049 /* Wait for previous operations to complete */ 1050 if ((rc = cfxga_synchronize(sc)) != 0) 1051 return (rc); 1052 1053 cfxga_write_2(sc, CFREG_BITBLT_ROP, opcode); 1054 cfxga_write_2(sc, CFREG_BITBLT_SRC_LOW, srcpos); 1055 cfxga_write_2(sc, CFREG_BITBLT_SRC_HIGH, srcpos >> 16); 1056 cfxga_write_2(sc, CFREG_BITBLT_DST_LOW, dstpos); 1057 cfxga_write_2(sc, CFREG_BITBLT_DST_HIGH, dstpos >> 16); 1058 cfxga_write_2(sc, CFREG_BITBLT_OFFSET, 1059 ri->ri_width * ri->ri_depth / 16); 1060 cfxga_write_2(sc, CFREG_BITBLT_WIDTH, cx - 1); 1061 cfxga_write_2(sc, CFREG_BITBLT_HEIGHT, cy - 1); 1062 cfxga_write_2(sc, CFREG_BITBLT_CONTROL, BITBLT_ACTIVE | 1063 (ri->ri_depth > 8 ? BITBLT_COLOR_16 : BITBLT_COLOR_8)); 1064 1065 return (0); 1066 } 1067 1068 /* 1069 * Text console raster operations. 1070 * 1071 * We shadow all these operations on a memory copy of the frame buffer. 1072 * Since we are running in emulation mode only, this could be optimized 1073 * by only storing actual character cell values (a la mda). 1074 */ 1075 1076 int 1077 cfxga_copycols(void *cookie, int row, int src, int dst, int num) 1078 { 1079 struct rasops_info *ri = cookie; 1080 struct cfxga_screen *scr = ri->ri_hw; 1081 int sx, dx, y, cx, cy; 1082 1083 /* Copy columns in backing store. */ 1084 memmove(scr->scr_mem + row * ri->ri_cols + dst, 1085 scr->scr_mem + row * ri->ri_cols + src, 1086 num * sizeof(struct wsdisplay_charcell)); 1087 1088 if (scr != scr->scr_sc->sc_active) 1089 return 0; 1090 1091 sx = src * ri->ri_font->fontwidth + ri->ri_xorigin; 1092 dx = dst * ri->ri_font->fontwidth + ri->ri_xorigin; 1093 y = row * ri->ri_font->fontheight + ri->ri_yorigin; 1094 cx = num * ri->ri_font->fontwidth; 1095 cy = ri->ri_font->fontheight; 1096 return cfxga_standalone_rop(scr, ROP_SRC, sx, y, dx, y, cx, cy); 1097 } 1098 1099 int 1100 cfxga_copyrows(void *cookie, int src, int dst, int num) 1101 { 1102 struct rasops_info *ri = cookie; 1103 struct cfxga_screen *scr = ri->ri_hw; 1104 int x, sy, dy, cx, cy; 1105 1106 /* Copy rows in backing store. */ 1107 memmove(scr->scr_mem + dst * ri->ri_cols, 1108 scr->scr_mem + src * ri->ri_cols, 1109 num * ri->ri_cols * sizeof(struct wsdisplay_charcell)); 1110 1111 if (scr != scr->scr_sc->sc_active) 1112 return 0; 1113 1114 x = ri->ri_xorigin; 1115 sy = src * ri->ri_font->fontheight + ri->ri_yorigin; 1116 dy = dst * ri->ri_font->fontheight + ri->ri_yorigin; 1117 cx = ri->ri_emuwidth; 1118 cy = num * ri->ri_font->fontheight; 1119 return cfxga_standalone_rop(scr, ROP_SRC, x, sy, x, dy, cx, cy); 1120 } 1121 1122 int 1123 cfxga_do_cursor(struct rasops_info *ri) 1124 { 1125 struct cfxga_screen *scr = ri->ri_hw; 1126 int x, y, cx, cy; 1127 1128 if (scr != scr->scr_sc->sc_active) 1129 return 0; 1130 1131 x = ri->ri_ccol * ri->ri_font->fontwidth + ri->ri_xorigin; 1132 y = ri->ri_crow * ri->ri_font->fontheight + ri->ri_yorigin; 1133 cx = ri->ri_font->fontwidth; 1134 cy = ri->ri_font->fontheight; 1135 return cfxga_standalone_rop(scr, ROP_ONES ^ ROP_SRC /* i.e. not SRC */, 1136 x, y, x, y, cx, cy); 1137 } 1138 1139 int 1140 cfxga_erasecols(void *cookie, int row, int col, int num, uint32_t attr) 1141 { 1142 struct rasops_info *ri = cookie; 1143 struct cfxga_screen *scr = ri->ri_hw; 1144 int fg, bg; 1145 int x, y, cx, cy; 1146 1147 /* Erase columns in backing store. */ 1148 for (x = col; x < col + num; x++) { 1149 scr->scr_mem[row * ri->ri_cols + x].uc = 0; 1150 scr->scr_mem[row * ri->ri_cols + x].attr = attr; 1151 } 1152 1153 if (scr != scr->scr_sc->sc_active) 1154 return 0; 1155 1156 ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL); 1157 x = col * ri->ri_font->fontwidth + ri->ri_xorigin; 1158 y = row * ri->ri_font->fontheight + ri->ri_yorigin; 1159 cx = num * ri->ri_font->fontwidth; 1160 cy = ri->ri_font->fontheight; 1161 return cfxga_solid_fill(scr, x, y, cx, cy, ri->ri_devcmap[bg]); 1162 } 1163 1164 int 1165 cfxga_eraserows(void *cookie, int row, int num, uint32_t attr) 1166 { 1167 struct rasops_info *ri = cookie; 1168 struct cfxga_screen *scr = ri->ri_hw; 1169 int fg, bg; 1170 int x, y, cx, cy; 1171 1172 /* Erase rows in backing store. */ 1173 for (x = 0; x < ri->ri_cols; x++) { 1174 scr->scr_mem[row * ri->ri_cols + x].uc = 0; 1175 scr->scr_mem[row * ri->ri_cols + x].attr = attr; 1176 } 1177 for (y = 1; y < num; y++) 1178 memmove(scr->scr_mem + (row + y) * ri->ri_cols, 1179 scr->scr_mem + row * ri->ri_cols, 1180 ri->ri_cols * sizeof(struct wsdisplay_charcell)); 1181 1182 if (scr != scr->scr_sc->sc_active) 1183 return 0; 1184 1185 ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL); 1186 x = ri->ri_xorigin; 1187 y = row * ri->ri_font->fontheight + ri->ri_yorigin; 1188 cx = ri->ri_emuwidth; 1189 cy = num * ri->ri_font->fontheight; 1190 return cfxga_solid_fill(scr, x, y, cx, cy, ri->ri_devcmap[bg]); 1191 } 1192 1193 int 1194 cfxga_putchar(void *cookie, int row, int col, u_int uc, uint32_t attr) 1195 { 1196 struct rasops_info *ri = cookie; 1197 struct cfxga_screen *scr = ri->ri_hw; 1198 int x, y; 1199 1200 scr->scr_mem[row * ri->ri_cols + col].uc = uc; 1201 scr->scr_mem[row * ri->ri_cols + col].attr = attr; 1202 1203 if (scr != scr->scr_sc->sc_active) 1204 return 0; 1205 1206 x = col * ri->ri_font->fontwidth + ri->ri_xorigin; 1207 y = row * ri->ri_font->fontheight + ri->ri_yorigin; 1208 1209 if (uc == ' ') { 1210 int cx, cy, fg, bg; 1211 1212 ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL); 1213 cx = ri->ri_font->fontwidth; 1214 cy = ri->ri_font->fontheight; 1215 return cfxga_solid_fill(scr, x, y, cx, cy, ri->ri_devcmap[bg]); 1216 } else { 1217 return cfxga_expand_char(scr, uc, x, y, attr); 1218 } 1219 } 1220