|
|
1.1 ! root 1: /* Generator is (c) James Ponder, 1997-2001 http://www.squish.net/generator/ */ ! 2: ! 3: /* user interface for X tcl/tk */ ! 4: ! 5: #include <string.h> ! 6: #include <unistd.h> ! 7: #include <stdlib.h> ! 8: #include <errno.h> ! 9: #include <stdarg.h> ! 10: ! 11: #include <sys/types.h> /* these three are for saving with -save */ ! 12: #include <sys/stat.h> ! 13: #include <fcntl.h> ! 14: ! 15: #include "generator.h" ! 16: ! 17: #ifdef HAVE_TCL8_0_H ! 18: # include <tcl8.0.h> ! 19: #else ! 20: # include <tcl.h> ! 21: #endif ! 22: ! 23: #ifdef HAVE_TK8_0_H ! 24: # include <tk8.0.h> ! 25: #else ! 26: # include <tk.h> ! 27: #endif ! 28: ! 29: #include <X11/Xlib.h> ! 30: #include <X11/Xutil.h> ! 31: ! 32: #include "diss68k.h" ! 33: #include "cpu68k.h" ! 34: #include "cpuz80.h" ! 35: #include "ui.h" ! 36: #include "vdp.h" ! 37: #include "mem68k.h" ! 38: #include "event.h" ! 39: ! 40: #ifdef XF86DGA ! 41: #include <X11/extensions/xf86dga.h> ! 42: #endif ! 43: ! 44: /* ! 45: #ifndef XInitImage ! 46: #define XInitImage _XInitImageFuncPtrs ! 47: Status _XInitImageFuncPtrs(XImage *); ! 48: #endif ! 49: */ ! 50: ! 51: #define MAXDEPTHBYTES 4 ! 52: ! 53: /*** forward reference declarations ***/ ! 54: ! 55: int ui_loadimage(Tcl_Interp *interp, const char *filename); ! 56: int ui_loadsavepos(Tcl_Interp *interp, const char *filename); ! 57: int ui_redrawdump(Tcl_Interp *interp, char *textwidget); ! 58: void ui_updateregs(void); ! 59: void ui_updatestr(char *var, char *val); ! 60: void ui_updateint(char *var, int val); ! 61: int ui_getint(int *cint, char *tclint); ! 62: int ui_setstr(const char *name, const char *value); ! 63: void ui_keypress(ClientData cd, XEvent *event); ! 64: void ui_enterleave(ClientData cd, XEvent *event); ! 65: int ui_topbit(unsigned long int bits); ! 66: void ui_endframe(void); ! 67: void ui_convertdata(uint8 *indata, uint8 *outdata, unsigned int pixels, ! 68: unsigned int lineoffset, unsigned int scale, ! 69: unsigned int depth, unsigned int basepixel); ! 70: void ui_convertdata_smooth(uint8 *indata, uint8 *outdata, unsigned int pixels, ! 71: unsigned int lineoffset, unsigned int depth); ! 72: ! 73: #ifdef XF86DGA ! 74: void ui_fullscreen(int onoff); ! 75: #endif ! 76: ! 77: /*** static variables ***/ ! 78: ! 79: static Tk_Window mainwin; ! 80: static char *display = NULL; ! 81: static Bool xsync = False; ! 82: static Bool save = False; ! 83: static Tcl_Interp *interp; ! 84: static char *imagedata; ! 85: static XImage *image; ! 86: static GC gc = NULL; ! 87: static unsigned int scale = 1; ! 88: static unsigned int smooth = 0; ! 89: static Visual *visual; ! 90: static Colormap cmap; ! 91: static unsigned long visual_bluemask; ! 92: static unsigned long visual_redmask; ! 93: static unsigned long visual_greenmask; ! 94: static int visual_bluepos; ! 95: static int visual_redpos; ! 96: static int visual_greenpos; ! 97: static unsigned int basepixel; ! 98: static unsigned int depth; ! 99: static unsigned int frameskip = 1; ! 100: static Display *ui_display = NULL; ! 101: static Screen *ui_screen = NULL; ! 102: static int ui_screenno = 0; ! 103: static int ui_dga = 0; /* enable/disable availability flag */ ! 104: static int ui_dga_state = 0; /* DGA on/off */ ! 105: static uint32 ui_palcache[192]; ! 106: static int ui_vdpsimple = 1; /* simple vdp enable/disable */ ! 107: static char *ui_initload = NULL; /* filename to load on init */ ! 108: static unsigned int state = 0; /* 0=stop,1=pause,2=play */ ! 109: ! 110: #ifdef XF86DGA ! 111: static int dga_major, dga_minor; ! 112: static int dga_eventb, dga_errorb; ! 113: static int dga_flags; ! 114: static char *dga_baseaddr; ! 115: static int dga_width, dga_banksize, dga_memsize; ! 116: static int dga_xsize, dga_ysize; ! 117: static char *dga_start; ! 118: #endif ! 119: ! 120: /*** Define our own arguments for Tk to process ***/ ! 121: ! 122: static Tk_ArgvInfo argtable[] = { ! 123: /* { "-display", TK_ARGV_STRING, (char*) NULL, (char*) &display, ! 124: "Display to use" }, */ ! 125: { "-sync", TK_ARGV_CONSTANT, (char*) True, (char*) &xsync, ! 126: "Turn on synchronous X" }, ! 127: { "-save", TK_ARGV_CONSTANT, (char*) True, (char*) &save, ! 128: "Save ROM as bin" }, ! 129: { "", TK_ARGV_END, (char*) NULL, (char*) NULL, (char*) NULL } ! 130: }; ! 131: ! 132: /*** Error handler for X protocol errors ***/ ! 133: ! 134: static int errorhandler(ClientData data, XErrorEvent *err) { ! 135: ui_err("X error %d, request %d, minor %d", err->error_code, ! 136: err->request_code, err->minor_code); ! 137: return 0; ! 138: } ! 139: ! 140: /*** Gen_Load type pathname ***/ ! 141: ! 142: int Gen_Load(ClientData cdata, Tcl_Interp *interp, int objc, ! 143: Tcl_Obj * const objv[]) ! 144: { ! 145: int i; ! 146: ! 147: if (objc != 3) { ! 148: Tcl_SetResult(interp, "wrong # args: should be \"Gen_Load type pathName\"", ! 149: TCL_STATIC); ! 150: return TCL_ERROR; ! 151: } ! 152: if (Tcl_GetIntFromObj(interp, objv[1], &i) != TCL_OK) ! 153: return TCL_ERROR; ! 154: switch (i) { ! 155: case 0: ! 156: return ui_loadimage(interp, Tcl_GetStringFromObj(objv[2], NULL)); ! 157: case 1: ! 158: return ui_loadsavepos(interp, Tcl_GetStringFromObj(objv[2], NULL)); ! 159: } ! 160: Tcl_SetResult(interp, "invalid type: should be image (0) or savepos (1)\n", ! 161: TCL_STATIC); ! 162: return TCL_ERROR; ! 163: } ! 164: ! 165: /*** Gen_Save pathname - saves the current state of play in a savegame ***/ ! 166: ! 167: int Gen_Save(ClientData cdata, Tcl_Interp *interp, int objc, ! 168: Tcl_Obj * const objv[]) ! 169: { ! 170: if (objc != 2) { ! 171: Tcl_SetResult(interp, "wrong # args: should be \"Gen_Save pathName\"", ! 172: TCL_STATIC); ! 173: return TCL_ERROR; ! 174: } ! 175: LOG_NORMAL(("Save! %s\n", Tcl_GetStringFromObj(objv[1], NULL))); ! 176: return TCL_OK; ! 177: } ! 178: ! 179: /*** Gen_Dump widgetname yview <params from scrollbar> ***/ ! 180: ! 181: int Gen_Dump(ClientData cdata, Tcl_Interp *interp, int argc, ! 182: char *argv[]) ! 183: { ! 184: int si_line, memtype, dumptype, offset, lines; ! 185: double si_fraction; ! 186: char tmp[256]; ! 187: Tcl_Obj *varobj, *outobj; ! 188: Tk_Window window; ! 189: uint8 *mem_where; ! 190: unsigned int mem_start, mem_len; ! 191: ! 192: if (!cpu68k_rom) { ! 193: Tcl_SetResult(interp, "Nothing to display", TCL_STATIC); ! 194: return TCL_ERROR; ! 195: } ! 196: ! 197: window = Tk_NameToWindow(interp, argv[1], Tk_MainWindow(interp)); ! 198: if (!window) { ! 199: Tcl_SetResult(interp, "bad widget passed to Gen_Dump", TCL_STATIC); ! 200: return TCL_ERROR; ! 201: } ! 202: ! 203: /* get memtype variable */ ! 204: sprintf(tmp, "%s.memtype", argv[1]); ! 205: varobj = Tcl_NewStringObj(tmp, -1); ! 206: Tcl_IncrRefCount(varobj); ! 207: if (!(outobj = Tcl_ObjGetVar2(interp, varobj, NULL, TCL_GLOBAL_ONLY))) { ! 208: Tcl_SetResult(interp, ".memtype variable not set", TCL_STATIC); ! 209: return TCL_ERROR; ! 210: } ! 211: Tcl_IncrRefCount(outobj); ! 212: if (Tcl_GetIntFromObj(interp, outobj, &memtype) != TCL_OK) { ! 213: return TCL_ERROR; ! 214: } ! 215: Tcl_DecrRefCount(varobj); ! 216: Tcl_DecrRefCount(outobj); ! 217: ! 218: /* get dumptype variable */ ! 219: sprintf(tmp, "%s.dumptype", argv[1]); ! 220: varobj = Tcl_NewStringObj(tmp, -1); ! 221: Tcl_IncrRefCount(varobj); ! 222: if (!(outobj = Tcl_ObjGetVar2(interp, varobj, NULL, TCL_GLOBAL_ONLY))) { ! 223: Tcl_SetResult(interp, ".dumptype variable not set", TCL_STATIC); ! 224: return TCL_ERROR; ! 225: } ! 226: Tcl_IncrRefCount(outobj); ! 227: if (Tcl_GetIntFromObj(interp, outobj, &dumptype) != TCL_OK) { ! 228: return TCL_ERROR; ! 229: } ! 230: Tcl_DecrRefCount(varobj); ! 231: Tcl_DecrRefCount(outobj); ! 232: ! 233: /* get offset variable */ ! 234: sprintf(tmp, "%s.offset", argv[1]); ! 235: varobj = Tcl_NewStringObj(tmp, -1); ! 236: Tcl_IncrRefCount(varobj); ! 237: if (!(outobj = Tcl_ObjGetVar2(interp, varobj, NULL, TCL_GLOBAL_ONLY))) { ! 238: Tcl_SetResult(interp, ".offset variable not set", TCL_STATIC); ! 239: return TCL_ERROR; ! 240: } ! 241: Tcl_IncrRefCount(outobj); ! 242: if (Tcl_GetIntFromObj(interp, outobj, &offset) != TCL_OK) { ! 243: return TCL_ERROR; ! 244: } ! 245: Tcl_DecrRefCount(varobj); ! 246: Tcl_DecrRefCount(outobj); ! 247: ! 248: /* get lines variable */ ! 249: sprintf(tmp, "%s.lines", argv[1]); ! 250: varobj = Tcl_NewStringObj(tmp, -1); ! 251: Tcl_IncrRefCount(varobj); ! 252: if (!(outobj = Tcl_ObjGetVar2(interp, varobj, NULL, TCL_GLOBAL_ONLY))) { ! 253: Tcl_SetResult(interp, ".lines variable not set", TCL_STATIC); ! 254: return TCL_ERROR; ! 255: } ! 256: Tcl_IncrRefCount(outobj); ! 257: if (Tcl_GetIntFromObj(interp, outobj, &lines) != TCL_OK) { ! 258: return TCL_ERROR; ! 259: } ! 260: Tcl_DecrRefCount(varobj); ! 261: Tcl_DecrRefCount(outobj); ! 262: ! 263: switch(memtype) { ! 264: case 1: ! 265: mem_where = cpu68k_ram; ! 266: mem_start = 0xFF0000; ! 267: mem_len = 0x10000; ! 268: break; ! 269: case 2: ! 270: mem_where = vdp_vram; ! 271: mem_start = 0x0; ! 272: mem_len = LEN_VRAM; ! 273: break; ! 274: case 3: ! 275: mem_where = vdp_cram; ! 276: mem_start = 0x0; ! 277: mem_len = LEN_CRAM; ! 278: break; ! 279: case 4: ! 280: mem_where = vdp_vsram; ! 281: mem_start = 0x0; ! 282: mem_len = LEN_VSRAM; ! 283: break; ! 284: case 5: ! 285: mem_where = cpuz80_ram; ! 286: mem_start = 0x0; ! 287: mem_len = LEN_SRAM; ! 288: break; ! 289: default: /* 0 */ ! 290: mem_where = cpu68k_rom; ! 291: mem_start = 0; ! 292: mem_len = cpu68k_romlen; ! 293: break; ! 294: } ! 295: ! 296: if (!strcasecmp(argv[2], "yview")) { ! 297: switch (Tk_GetScrollInfo(interp, argc-1, argv+1, &si_fraction, &si_line)) { ! 298: case TK_SCROLL_MOVETO: ! 299: offset = (int)(si_fraction*mem_len) & (~1); ! 300: break; ! 301: case TK_SCROLL_UNITS: ! 302: if (offset < 256 && (si_line > 0)) { ! 303: offset = (si_line*4)+offset; ! 304: offset&= ~3; ! 305: } else if ((offset <= 256) && (si_line < 0)) { ! 306: offset = (si_line*4)+offset; ! 307: offset&= ~3; ! 308: } else { ! 309: offset = (si_line*2)+offset; ! 310: } ! 311: break; ! 312: case TK_SCROLL_PAGES: ! 313: offset = si_line*lines*2+offset; ! 314: break; ! 315: default: ! 316: Tcl_SetResult(interp, "unknown yview command parameter", TCL_STATIC); ! 317: return TCL_ERROR; ! 318: } ! 319: } else if (!strcasecmp(argv[2], "redraw")) { ! 320: return ui_redrawdump(interp, argv[1]); ! 321: } else { ! 322: Tcl_SetResult(interp, "unknown command type (must be yview or redraw)", ! 323: TCL_STATIC); ! 324: return TCL_ERROR; ! 325: } ! 326: if (offset >= (signed int)mem_len) ! 327: offset = mem_len-2; ! 328: if (offset < 0) ! 329: offset = 0; ! 330: sprintf(tmp, "%s.offset", argv[1]); ! 331: varobj = Tcl_NewStringObj(tmp, -1); ! 332: Tcl_IncrRefCount(varobj); ! 333: if (Tcl_ObjSetVar2(interp, varobj, NULL, Tcl_NewIntObj(offset), ! 334: TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL) { ! 335: return TCL_ERROR; ! 336: } ! 337: Tcl_DecrRefCount(varobj); ! 338: ui_redrawdump(interp, argv[1]); ! 339: return TCL_OK; ! 340: } ! 341: ! 342: /*** Gen_Step - step through one instruction ***/ ! 343: ! 344: int Gen_Step(ClientData cdata, Tcl_Interp *interp, int objc, ! 345: Tcl_Obj * const objv[]) ! 346: { ! 347: if (objc != 1) { ! 348: Tcl_SetResult(interp, "wrong # args: should be \"Gen_Step\"", ! 349: TCL_STATIC); ! 350: return TCL_ERROR; ! 351: } ! 352: event_dostep(); ! 353: ui_updateregs(); ! 354: return TCL_OK; ! 355: } ! 356: ! 357: /*** Gen_Cont - start executing ***/ ! 358: ! 359: int Gen_Cont(ClientData cdata, Tcl_Interp *interp, int objc, ! 360: Tcl_Obj * const objv[]) ! 361: { ! 362: char *stopstr; ! 363: uint32 stopaddr; ! 364: Tcl_Obj *obj; ! 365: int i; ! 366: ! 367: if (objc != 1) { ! 368: Tcl_SetResult(interp, "wrong # args: should be \"Gen_Cont\"", ! 369: TCL_STATIC); ! 370: return TCL_ERROR; ! 371: } ! 372: obj = Tcl_ObjGetVar2(interp, Tcl_NewStringObj("regs.stop", -1), NULL, ! 373: TCL_GLOBAL_ONLY); ! 374: stopstr = Tcl_GetStringFromObj(obj, NULL); ! 375: if (!stopstr) { ! 376: Tcl_SetResult(interp, "Cannot read regs.stop", TCL_STATIC); ! 377: return TCL_ERROR; ! 378: } ! 379: stopaddr = strtol(stopstr, NULL, 16); ! 380: gen_quit = 0; ! 381: LOG_NORMAL(("Continuing from %08X", regs.pc)); ! 382: do { ! 383: while (Tcl_DoOneEvent(TCL_ALL_EVENTS | TCL_DONT_WAIT)); ! 384: for (i = 0; i < 128; i++) { ! 385: event_dostep(); ! 386: if ((regs.pc & 0xFFFFFF) == stopaddr || gen_quit) ! 387: break; ! 388: } ! 389: } while((regs.pc & 0xFFFFFF) != stopaddr && !gen_quit); ! 390: LOG_NORMAL(("End of continuation, stopped at %08X", regs.pc)); ! 391: ui_updateregs(); ! 392: if (gen_quit) ! 393: LOG_NORMAL(("Stopped.")); ! 394: gen_quit = 0; ! 395: return TCL_OK; ! 396: } ! 397: ! 398: /*** Gen_FrameStep - step through one frame ***/ ! 399: ! 400: int Gen_FrameStep(ClientData cdata, Tcl_Interp *interp, int objc, ! 401: Tcl_Obj * const objv[]) ! 402: { ! 403: if (objc != 1) { ! 404: Tcl_SetResult(interp, "wrong # args: should be \"Gen_FrameStep\"", ! 405: TCL_STATIC); ! 406: return TCL_ERROR; ! 407: } ! 408: gen_quit = 0; ! 409: do { ! 410: event_doframe(); ! 411: while (Tcl_DoOneEvent(TCL_ALL_EVENTS | TCL_DONT_WAIT)); ! 412: } while(!gen_quit); ! 413: ui_updateregs(); ! 414: if (gen_quit) ! 415: LOG_NORMAL(("Stopped.")); ! 416: gen_quit = 0; ! 417: ! 418: return TCL_OK; ! 419: } ! 420: ! 421: /*** Gen_SpriteList - print sprite information ***/ ! 422: ! 423: int Gen_SpriteList(ClientData cdata, Tcl_Interp *interp, int objc, ! 424: Tcl_Obj * const objv[]) ! 425: { ! 426: if (objc != 1) { ! 427: Tcl_SetResult(interp, "wrong # args: should be \"Gen_SpriteList\"", ! 428: TCL_STATIC); ! 429: return TCL_ERROR; ! 430: } ! 431: vdp_spritelist(); ! 432: return TCL_OK; ! 433: } ! 434: ! 435: /*** Gen_Change - there has been a change in parameter ***/ ! 436: ! 437: int Gen_Change(ClientData cdata, Tcl_Interp *interp, int objc, ! 438: Tcl_Obj * const objv[]) ! 439: { ! 440: Tcl_Obj *varobj, *outobj; ! 441: unsigned int oldscale = scale; ! 442: unsigned int newstate; ! 443: ! 444: if (objc != 1) { ! 445: Tcl_SetResult(interp, "wrong # args: should be \"Gen_Change\"", ! 446: TCL_STATIC); ! 447: return TCL_ERROR; ! 448: } ! 449: ! 450: ui_getint((int *)&ui_vdpsimple, "vdpsimple"); ! 451: ui_getint((int *)&vdp_layerB, "layerB"); ! 452: ui_getint((int *)&vdp_layerBp, "layerBp"); ! 453: ui_getint((int *)&vdp_layerA, "layerA"); ! 454: ui_getint((int *)&vdp_layerAp, "layerAp"); ! 455: ui_getint((int *)&vdp_layerW, "layerW"); ! 456: ui_getint((int *)&vdp_layerWp, "layerWp"); ! 457: ui_getint((int *)&vdp_layerH, "layerH"); ! 458: ui_getint((int *)&vdp_layerS, "layerS"); ! 459: ui_getint((int *)&vdp_layerSp, "layerSp"); ! 460: ui_getint((int *)&frameskip, "skip"); ! 461: ui_getint((int *)&newstate, "state"); ! 462: ui_getint((int *)&gen_loglevel, "loglevel"); ! 463: ! 464: ui_getint(&scale, "scale"); ! 465: ui_getint(&smooth, "smooth"); ! 466: ! 467: if (scale != oldscale) { ! 468: XDestroyImage(image); ! 469: if ((imagedata = malloc(320*240*scale*scale*(depth/8))) == NULL) ! 470: ui_err("%s: Out of memory!"); ! 471: memset(imagedata, 0, 320*240*scale*scale*(depth/8)); ! 472: if ((image = XCreateImage(ui_display, visual, depth, ZPixmap, 0, imagedata, ! 473: 320*scale, 240*scale, 8, 0)) == NULL) { ! 474: Tcl_SetResult(interp, "unable to create image", TCL_STATIC); ! 475: return TCL_ERROR; ! 476: } ! 477: image->bits_per_pixel = depth; ! 478: XInitImage(image); ! 479: } ! 480: #ifdef XF86DGA ! 481: dga_start = dga_baseaddr + ((depth/8) * ! 482: (dga_xsize*((dga_ysize-(240*scale))/2) + ! 483: ((dga_xsize-(320*scale))/2))); ! 484: #endif ! 485: ! 486: if (newstate != state) { ! 487: switch(newstate) { ! 488: case 0: ! 489: /* stop */ ! 490: break; ! 491: case 1: ! 492: /* pause */ ! 493: break; ! 494: case 2: ! 495: /* play */ ! 496: if (cpu68k_rom == NULL) { ! 497: Tcl_SetResult(interp, "No ROM loaded\n", TCL_STATIC); ! 498: return TCL_ERROR; ! 499: } ! 500: if (state == 0) ! 501: gen_reset(); ! 502: break; ! 503: } ! 504: state = newstate; ! 505: } ! 506: return TCL_OK; ! 507: } ! 508: ! 509: /*** Gen_Initialised ***/ ! 510: ! 511: int Gen_Initialised(ClientData cdata, Tcl_Interp *interp, int objc, ! 512: Tcl_Obj * const objv[]) ! 513: { ! 514: char *p; ! 515: int f; ! 516: char buffer[256]; ! 517: ! 518: if (objc != 1) { ! 519: Tcl_SetResult(interp, "wrong # args: should be \"Gen_Change\"", ! 520: TCL_STATIC); ! 521: return TCL_ERROR; ! 522: } ! 523: if (ui_initload) { ! 524: p = gen_loadimage(ui_initload); ! 525: if (p) { ! 526: Tcl_SetResult(interp, p, TCL_STATIC); ! 527: return TCL_ERROR; ! 528: } ! 529: ui_updateregs(); ! 530: if (save) { ! 531: snprintf(buffer, sizeof(buffer)-1, "./%s (%X-%s)", ! 532: gen_cartinfo.name_overseas, gen_cartinfo.checksum, ! 533: gen_cartinfo.country); ! 534: f = 0; ! 535: if ((f = open(buffer, O_CREAT | O_EXCL | O_WRONLY, 0777)) == -1 || ! 536: write(f, cpu68k_rom, cpu68k_romlen) == -1 || close(f) == -1) { ! 537: Tcl_SetResult(interp, strerror(errno), TCL_STATIC); ! 538: return TCL_ERROR; ! 539: } ! 540: LOG_REQUEST(("Saved '%s'", buffer)); ! 541: } ! 542: } ! 543: return TCL_OK; ! 544: } ! 545: ! 546: /*** Gen_Reset ***/ ! 547: ! 548: int Gen_Reset(ClientData cdata, Tcl_Interp *interp, int objc, ! 549: Tcl_Obj * const objv[]) ! 550: { ! 551: int resetitem; ! 552: ! 553: if (objc != 2) { ! 554: Tcl_SetResult(interp, "wrong # args: should be \"Gen_Reset <x>\"", ! 555: TCL_STATIC); ! 556: return TCL_ERROR; ! 557: } ! 558: Tcl_GetIntFromObj(interp, objv[1], &resetitem); ! 559: switch(resetitem) { ! 560: case 1: /* z80 */ ! 561: cpuz80_reset(); ! 562: break; ! 563: default: ! 564: Tcl_SetResult(interp, "unknown reset item", TCL_STATIC); ! 565: return TCL_ERROR; ! 566: } ! 567: return TCL_OK; ! 568: } ! 569: ! 570: /*** ui_getint - get a TCL integer into a C int ***/ ! 571: ! 572: int ui_getint(int *cint, char *tclint) ! 573: { ! 574: Tcl_Obj *valobj, *varobj; ! 575: ! 576: varobj = Tcl_NewStringObj(tclint, -1); ! 577: Tcl_IncrRefCount(varobj); ! 578: if (!(valobj = Tcl_ObjGetVar2(interp, varobj, NULL, TCL_GLOBAL_ONLY))) { ! 579: Tcl_SetResult(interp, "variable not set", TCL_STATIC); ! 580: return TCL_ERROR; ! 581: } ! 582: Tcl_IncrRefCount(valobj); ! 583: if (Tcl_GetIntFromObj(interp, valobj, cint) != TCL_OK) { ! 584: return TCL_ERROR; ! 585: } ! 586: Tcl_DecrRefCount(varobj); ! 587: Tcl_DecrRefCount(valobj); ! 588: return TCL_OK; ! 589: } ! 590: ! 591: /*** ui_setstr - store a string into a TCL variable ***/ ! 592: ! 593: int ui_setstr(const char *name, const char *value) ! 594: { ! 595: Tcl_Obj *strobj, *varobj, *outobj; ! 596: ! 597: varobj = Tcl_NewStringObj((char *)name, -1); ! 598: Tcl_IncrRefCount(varobj); ! 599: strobj = Tcl_NewStringObj((char *)value, -1); ! 600: Tcl_IncrRefCount(strobj); ! 601: if (!(outobj = Tcl_ObjSetVar2(interp, varobj, NULL, strobj, ! 602: TCL_GLOBAL_ONLY))) { ! 603: return -1; ! 604: } ! 605: Tcl_DecrRefCount(varobj); ! 606: Tcl_DecrRefCount(strobj); ! 607: return 0; ! 608: } ! 609: ! 610: /*** Gen_Regs - display registers ***/ ! 611: ! 612: int Gen_Regs(ClientData cdata, Tcl_Interp *interp, int objc, ! 613: Tcl_Obj * const objv[]) ! 614: { ! 615: if (objc != 1) { ! 616: Tcl_SetResult(interp, "wrong # args: should be \"Gen_Regs\"", ! 617: TCL_STATIC); ! 618: return TCL_ERROR; ! 619: } ! 620: vdp_showregs(); ! 621: return TCL_OK; ! 622: } ! 623: ! 624: /*** Gen_VDPDescribe - describe vdp state ***/ ! 625: ! 626: int Gen_VDPDescribe(ClientData cdata, Tcl_Interp *interp, int objc, ! 627: Tcl_Obj * const objv[]) ! 628: { ! 629: if (objc != 1) { ! 630: Tcl_SetResult(interp, "wrong # args: should be \"Gen_VDPDescribe\"", ! 631: TCL_STATIC); ! 632: return TCL_ERROR; ! 633: } ! 634: vdp_describe(); ! 635: return TCL_OK; ! 636: } ! 637: ! 638: int ui_redrawdump(Tcl_Interp *interp, char *textwidget) ! 639: { ! 640: int offset, line, listlen, words, i, memtype; ! 641: char tmp[256], dumpline[128]; ! 642: Tcl_Obj *varobj, *outobj, *paramobj; ! 643: t_ipc ipc; ! 644: uint8 *mem_where; ! 645: unsigned int mem_start, mem_len; ! 646: ! 647: /* get offset variable */ ! 648: sprintf(tmp, "%s.offset", textwidget); ! 649: varobj = Tcl_NewStringObj(tmp, -1); ! 650: Tcl_IncrRefCount(varobj); ! 651: if ((outobj = Tcl_ObjGetVar2(interp, varobj, NULL, TCL_GLOBAL_ONLY | ! 652: TCL_LEAVE_ERR_MSG)) == NULL) { ! 653: return TCL_ERROR; ! 654: } ! 655: Tcl_IncrRefCount(outobj); ! 656: if (Tcl_GetIntFromObj(interp, outobj, &offset) != TCL_OK) { ! 657: return TCL_ERROR; ! 658: } ! 659: Tcl_DecrRefCount(outobj); ! 660: Tcl_DecrRefCount(varobj); ! 661: ! 662: /* get memtype variable */ ! 663: sprintf(tmp, "%s.memtype", textwidget); ! 664: varobj = Tcl_NewStringObj(tmp, -1); ! 665: Tcl_IncrRefCount(varobj); ! 666: if ((outobj = Tcl_ObjGetVar2(interp, varobj, NULL, TCL_GLOBAL_ONLY | ! 667: TCL_LEAVE_ERR_MSG)) == NULL) { ! 668: return TCL_ERROR; ! 669: } ! 670: Tcl_IncrRefCount(outobj); ! 671: if (Tcl_GetIntFromObj(interp, outobj, &memtype) != TCL_OK) { ! 672: return TCL_ERROR; ! 673: } ! 674: Tcl_DecrRefCount(varobj); ! 675: Tcl_DecrRefCount(outobj); ! 676: ! 677: /* clear current text display */ ! 678: sprintf(tmp, "%s.main delete 1.0 end", textwidget); ! 679: varobj = Tcl_NewStringObj(tmp, -1); ! 680: Tcl_IncrRefCount(varobj); ! 681: if (Tcl_EvalObj(interp, varobj) == TCL_ERROR) { ! 682: return TCL_ERROR; ! 683: } ! 684: Tcl_DecrRefCount(varobj); ! 685: ! 686: switch(memtype) { ! 687: case 1: ! 688: mem_where = cpu68k_ram; ! 689: mem_start = 0xFF0000; ! 690: mem_len = 0x10000; ! 691: break; ! 692: case 2: ! 693: mem_where = vdp_vram; ! 694: mem_start = 0x0; ! 695: mem_len = LEN_VRAM; ! 696: break; ! 697: case 3: ! 698: mem_where = vdp_cram; ! 699: mem_start = 0x0; ! 700: mem_len = LEN_CRAM; ! 701: break; ! 702: case 4: ! 703: mem_where = vdp_vsram; ! 704: mem_start = 0x0; ! 705: mem_len = LEN_VSRAM; ! 706: break; ! 707: case 5: ! 708: mem_where = cpuz80_ram; ! 709: mem_start = 0x0; ! 710: mem_len = LEN_SRAM; ! 711: break; ! 712: default: /* 0 */ ! 713: mem_where = cpu68k_rom; ! 714: mem_start = 0; ! 715: mem_len = cpu68k_romlen; ! 716: break; ! 717: } ! 718: ! 719: line = 1; ! 720: while(line <= 100) { /* do dump loop */ ! 721: ! 722: /* first check visibility of line by checking bbox of char 0 on the line */ ! 723: sprintf(tmp, "[%s.main bbox %d.0]", textwidget, line); ! 724: varobj = Tcl_NewStringObj(tmp, -1); ! 725: Tcl_IncrRefCount(varobj); ! 726: if (Tcl_ExprObj(interp, varobj, &outobj) == TCL_ERROR) { ! 727: return TCL_ERROR; ! 728: } ! 729: if (Tcl_ListObjLength(interp, outobj, &listlen) == TCL_ERROR) { ! 730: return TCL_ERROR; ! 731: } ! 732: Tcl_DecrRefCount(outobj); ! 733: Tcl_DecrRefCount(varobj); ! 734: ! 735: if (listlen == 0 || (unsigned int)offset > mem_len) { ! 736: break; ! 737: } ! 738: ! 739: /* this line is visible so disassemble it */ ! 740: if ((unsigned int)offset == mem_len) { ! 741: sprintf(dumpline, "---end\n", offset); ! 742: words = 1; ! 743: } else { ! 744: words = diss68k_getdumpline(mem_start+offset, mem_where+offset, ! 745: dumpline); ! 746: } ! 747: ! 748: /* insert line at end of disassembly */ ! 749: sprintf(tmp, "%s.main insert end {%s}", textwidget, dumpline); ! 750: varobj = Tcl_NewStringObj(tmp, -1); ! 751: Tcl_IncrRefCount(varobj); ! 752: if (Tcl_EvalObj(interp, varobj) == TCL_ERROR) { ! 753: return TCL_ERROR; ! 754: } ! 755: Tcl_DecrRefCount(varobj); ! 756: ! 757: /* increment address and line number and loop */ ! 758: offset+= words*2; ! 759: line++; ! 760: } ! 761: line--; ! 762: ! 763: /* update scrollbar */ ! 764: sprintf(tmp, "%s.bar set %f %f", textwidget, offset/(double)mem_len, ! 765: (offset+line*2)/(double)mem_len); ! 766: varobj = Tcl_NewStringObj(tmp, -1); ! 767: Tcl_IncrRefCount(varobj); ! 768: if (Tcl_EvalObj(interp, varobj) == TCL_ERROR) { ! 769: return TCL_ERROR; ! 770: } ! 771: Tcl_DecrRefCount(varobj); ! 772: ! 773: /* record number of lines for paging function */ ! 774: sprintf(tmp, "%s.lines", textwidget); ! 775: varobj = Tcl_NewStringObj(tmp, -1); ! 776: paramobj = Tcl_NewIntObj(line); ! 777: Tcl_IncrRefCount(varobj); ! 778: Tcl_IncrRefCount(paramobj); ! 779: if (Tcl_ObjSetVar2(interp, varobj, NULL, paramobj, ! 780: TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL) { ! 781: return TCL_ERROR; ! 782: } ! 783: Tcl_DecrRefCount(varobj); ! 784: Tcl_DecrRefCount(paramobj); ! 785: return TCL_OK; ! 786: } ! 787: ! 788: /*** Program entry point ***/ ! 789: ! 790: int ui_init(int argc, char *argv[]) ! 791: { ! 792: int i; ! 793: unsigned int ui; ! 794: unsigned long plane_masks[1]; ! 795: unsigned long colours[256]; ! 796: XColor xcolor; ! 797: ! 798: /* Create tcl interpreter and parse arguments */ ! 799: interp = Tcl_CreateInterp(); ! 800: if (Tk_ParseArgv(interp, NULL, &argc, argv, argtable, 0) != TCL_OK) ! 801: ui_err("%s: %s", argv[0], Tcl_GetStringResult(interp)); ! 802: if (argc < 1 || argc > 2) ! 803: ui_err("Usage: %s [<file>]", argv[0]); ! 804: ! 805: /* Initialise tcl and tk */ ! 806: if (Tcl_Init(interp) != TCL_OK) ! 807: ui_err("%s: Tcl_Init failed: %s", argv[0], Tcl_GetStringResult(interp)); ! 808: if (Tk_Init(interp) != TCL_OK) ! 809: ui_err("%s: Tk_Init failed: %s", argv[0], Tcl_GetStringResult(interp)); ! 810: if ((mainwin = Tk_MainWindow(interp)) == NULL) ! 811: ui_err("%s: No main window!", argv[0]); ! 812: ! 813: ui_display = Tk_Display(mainwin); ! 814: ui_screen = Tk_Screen(mainwin); ! 815: ui_screenno = Tk_ScreenNumber(mainwin); ! 816: ! 817: /* Configure error handling */ ! 818: Tk_CreateErrorHandler(ui_display, -1, -1, -1, errorhandler, ! 819: (char *) NULL); ! 820: if (xsync) ! 821: XSynchronize(ui_display, True); ! 822: ! 823: /* Create new Tcl commands */ ! 824: Tcl_CreateObjCommand(interp, "Gen_Load", Gen_Load, NULL, NULL); ! 825: Tcl_CreateObjCommand(interp, "Gen_Save", Gen_Save, NULL, NULL); ! 826: Tcl_CreateCommand(interp, "Gen_Dump", Gen_Dump, NULL, NULL); ! 827: Tcl_CreateObjCommand(interp, "Gen_Step", Gen_Step, NULL, NULL); ! 828: Tcl_CreateObjCommand(interp, "Gen_FrameStep", Gen_FrameStep, NULL, NULL); ! 829: Tcl_CreateObjCommand(interp, "Gen_Cont", Gen_Cont, NULL, NULL); ! 830: Tcl_CreateObjCommand(interp, "Gen_Change", Gen_Change, NULL, NULL); ! 831: Tcl_CreateObjCommand(interp, "Gen_Regs", Gen_Regs, NULL, NULL); ! 832: Tcl_CreateObjCommand(interp, "Gen_VDPDescribe", Gen_VDPDescribe, NULL, NULL); ! 833: Tcl_CreateObjCommand(interp, "Gen_SpriteList", Gen_SpriteList, NULL, NULL); ! 834: Tcl_CreateObjCommand(interp, "Gen_Initialised", Gen_Initialised, NULL, NULL); ! 835: Tcl_CreateObjCommand(interp, "Gen_Reset", Gen_Reset, NULL, NULL); ! 836: ! 837: if ((visual = Tk_GetVisual(interp, mainwin, "truecolor 24", &depth, ! 838: &cmap)) == NULL) { ! 839: depth = 0; ! 840: } ! 841: LOG_VERBOSE(("Asked for depth 24, got depth %d", depth)); ! 842: if (depth != 16 && depth != 24) { ! 843: if ((visual = Tk_GetVisual(interp, mainwin, "pseudocolor 8", &depth, ! 844: NULL)) == NULL) ! 845: ui_err("%s: GetVisual failed: %s", argv[0], Tcl_GetStringResult(interp)); ! 846: if (depth != 8) ! 847: ui_err("%s: Depth is %d (we want 8, e.g. 256 colours).", argv[0], depth); ! 848: if ((cmap = Tk_GetColormap(interp, mainwin, "new")) == 0) ! 849: ui_err("%s: Unable to create new colormap.", argv[0]); ! 850: if (Tk_SetWindowVisual(mainwin, visual, 8, cmap) != 1) ! 851: ui_err("%s: Unable to set window visual/colormap.", argv[0]); ! 852: if (XAllocColorCells(ui_display, cmap, True, plane_masks, 0, ! 853: colours, 32) == 0) ! 854: ui_err("%s: Unable to allocate colors.", argv[0]); ! 855: for (i = 0; i < 32; i++) { ! 856: xcolor.pixel = i; ! 857: xcolor.flags = 0; ! 858: XQueryColor(ui_display, DefaultColormap(ui_display, ui_screenno), ! 859: &xcolor); ! 860: XStoreColor(ui_display, cmap, &xcolor); ! 861: } ! 862: if (XAllocColorCells(ui_display, cmap, True, plane_masks, 0, ! 863: colours, 64*3) == 0) ! 864: ui_err("%s: Unable to allocate colors.", argv[0]); ! 865: basepixel = colours[0]; ! 866: LOG_VERBOSE(("UI: Base pixel %d", basepixel)); ! 867: } else { ! 868: if (Tk_SetWindowVisual(mainwin, visual, depth, cmap) != 1) ! 869: ui_err("%s: Unable to set window visual/colormap.", argv[0]); ! 870: } ! 871: ! 872: if ((imagedata = malloc(320*240*(depth/8))) == NULL) ! 873: ui_err("%s: Out of memory!", argv[0]); ! 874: memset(imagedata, 0, 320*240*(depth/8)); ! 875: if ((image = XCreateImage(ui_display, visual, depth, ZPixmap, 0, ! 876: imagedata, 320, 240, 8, 0)) == NULL) { ! 877: ui_err("%s: Unable to create image.", argv[0]); ! 878: } ! 879: /* XCreateImage is broken on my system - force these values */ ! 880: image->bytes_per_line = 320*(depth/8); ! 881: image->bits_per_pixel = depth; ! 882: if (depth != 8) { ! 883: visual_bluemask = image->blue_mask; ! 884: visual_redmask = image->red_mask; ! 885: visual_greenmask = image->green_mask; ! 886: visual_bluepos = ui_topbit(visual_bluemask) - 2; ! 887: visual_redpos = ui_topbit(visual_redmask) - 2; ! 888: visual_greenpos = ui_topbit(visual_greenmask) - 2; ! 889: if (visual_bluepos == -1 || visual_redpos == -1 || visual_greenpos == -1) { ! 890: ui_err("%s: Bad colour masks (%x(%d),%x(%d),%x(%d)).", ! 891: argv[0], visual_redmask, visual_redpos, ! 892: visual_greenmask, visual_greenpos, ! 893: visual_bluemask, visual_bluepos); ! 894: } ! 895: } ! 896: ! 897: XInitImage(image); ! 898: ! 899: /* DGA stuff */ ! 900: ! 901: ui_dga = 0; ! 902: ! 903: #ifdef XF86DGA ! 904: if (geteuid()) { ! 905: LOG_NORMAL(("%s: You are not root and so DGA is disabled.", argv[0])); ! 906: } else if (!XF86DGAQueryVersion(ui_display, &dga_major, &dga_minor)) { ! 907: LOG_NORMAL(("%s: XF86DGAQueryVersion failed", argv[0])); ! 908: } else if (!XF86DGAQueryExtension(ui_display, &dga_eventb, &dga_errorb)) { ! 909: LOG_NORMAL(("%s: XF86DGAQueryExtension failed", argv[0])); ! 910: } else if (!XF86DGAQueryDirectVideo(ui_display, ui_screenno, &dga_flags)) { ! 911: LOG_NORMAL(("%s: XF86DGAQueryDirectVideo failed", argv[0])); ! 912: } else if (!(dga_flags & XF86DGADirectPresent)) { ! 913: LOG_NORMAL(("%s: DGA Direct video support is not present", argv[0])); ! 914: } else if (!XF86DGAGetVideo(ui_display, ui_screenno, &dga_baseaddr, ! 915: &dga_width, &dga_banksize, &dga_memsize)) { ! 916: LOG_NORMAL(("%s: XF86DGAGetVideo failed", argv[0])); ! 917: } else if (!XF86DGAGetViewPortSize(ui_display, ui_screenno, &dga_xsize, ! 918: &dga_ysize)) { ! 919: LOG_NORMAL(("%s: XF86DGAGetViewPortsize failed", argv[0])); ! 920: } else { ! 921: ui_dga = 1; ! 922: dga_start = dga_baseaddr + ((depth/8) * ! 923: (dga_xsize*((dga_ysize-(240*scale))/2) + ! 924: ((dga_xsize-(320*scale))/2))); ! 925: LOG_NORMAL(("DGA: Direct full-screen video enabled at " ! 926: "%08X (%d/%d) [%d x %d].", dga_baseaddr, dga_banksize, ! 927: dga_memsize, dga_xsize, dga_ysize)); ! 928: } ! 929: if (!ui_dga) { ! 930: LOG_NORMAL(("DGA has been disabled, no full-screen facilities will " ! 931: "be available.")); ! 932: } ! 933: #else ! 934: LOG_VERBOSE(("DGA: Not compiled in.")); ! 935: #endif ! 936: if (setuid(getuid())) ! 937: ui_err("%s: Failed to setuid: %s", argv[0], strerror(errno)); ! 938: ! 939: /* end DGA stuff */ ! 940: ! 941: ui_updateint("debug", gen_debugmode); ! 942: ! 943: Tk_CreateEventHandler(mainwin, KeyPressMask | KeyReleaseMask, ! 944: ui_keypress, NULL); ! 945: Tk_CreateEventHandler(mainwin, EnterWindowMask | LeaveWindowMask, ! 946: ui_enterleave, NULL); ! 947: ! 948: if (argc == 2) { ! 949: if ((ui_initload = malloc(strlen(argv[1])+1)) == NULL) { ! 950: fprintf(stderr, "Out of memory\n"); ! 951: return 1; ! 952: } ! 953: strcpy(ui_initload, argv[1]); ! 954: } ! 955: return 0; ! 956: } ! 957: ! 958: int ui_topbit(unsigned long int bits) ! 959: { ! 960: long int bit = 31; ! 961: unsigned long int mask = 1<<31; ! 962: ! 963: for (; bit >= 0; bit--, mask>>= 1) { ! 964: if (bits & mask) ! 965: return bit; ! 966: } ! 967: return -1; ! 968: } ! 969: ! 970: void ui_final(void) ! 971: { ! 972: XAutoRepeatOn(ui_display); ! 973: } ! 974: ! 975: void ui_enterleave(ClientData cd, XEvent *event) ! 976: { ! 977: if (event->type == EnterNotify) { ! 978: XAutoRepeatOff(ui_display); ! 979: } else { ! 980: XAutoRepeatOn(ui_display); ! 981: } ! 982: } ! 983: ! 984: void ui_keypress(ClientData cd, XEvent *event) ! 985: { ! 986: char keysym = XLookupKeysym(&event->xkey, 0); ! 987: int type = (event->type == KeyPress); ! 988: ! 989: LOG_DEBUG3(("KEY %d: %d", type, keysym)); ! 990: switch(keysym) { ! 991: case 'a': ! 992: case '1': ! 993: case 'z': ! 994: mem68k_cont1_a = type; ! 995: break; ! 996: case 'b': ! 997: case '2': ! 998: case 's': ! 999: case 'x': ! 1000: mem68k_cont1_b = type; ! 1001: break; ! 1002: case 'c': ! 1003: case '3': ! 1004: case 'd': ! 1005: mem68k_cont1_c = type; ! 1006: break; ! 1007: case 81: ! 1008: mem68k_cont1_left = type; ! 1009: break; ! 1010: case 82: ! 1011: mem68k_cont1_up = type; ! 1012: break; ! 1013: case 83: ! 1014: mem68k_cont1_right = type; ! 1015: break; ! 1016: case 84: ! 1017: mem68k_cont1_down = type; ! 1018: break; ! 1019: case 13: ! 1020: mem68k_cont1_start = type; ! 1021: break; ! 1022: #ifdef XF86DGA ! 1023: case '#': ! 1024: if (type) ! 1025: ui_fullscreen(ui_dga_state ^ 1); ! 1026: break; ! 1027: #endif ! 1028: default: ! 1029: break; ! 1030: } ! 1031: } ! 1032: ! 1033: /*** ui_updateregs - update register information ***/ ! 1034: ! 1035: void ui_updateregs(void) ! 1036: { ! 1037: int i, t; ! 1038: char val[256], var[256]; ! 1039: ! 1040: for (t = 0; t < 2; t++) { ! 1041: for (i = 0; i < 8; i++) { ! 1042: sprintf(val, "%08X", regs.regs[t*8+i]); ! 1043: sprintf(var, "regs.%s%d", t ? "a" : "d", i); ! 1044: ui_updatestr(var, val); ! 1045: } ! 1046: } ! 1047: sprintf(val, "%08X", regs.pc); ui_updatestr("regs.pc", val); ! 1048: sprintf(val, "%04X", regs.sr.sr_int); ui_updatestr("regs.sr", val); ! 1049: sprintf(val, "%08X", regs.sp); ui_updatestr("regs.sp", val); ! 1050: ui_updateint("regs.s", regs.sr.sr_struct.s); ! 1051: ui_updateint("regs.x", regs.sr.sr_struct.x); ! 1052: ui_updateint("regs.n", regs.sr.sr_struct.n); ! 1053: ui_updateint("regs.z", regs.sr.sr_struct.z); ! 1054: ui_updateint("regs.v", regs.sr.sr_struct.v); ! 1055: ui_updateint("regs.c", regs.sr.sr_struct.c); ! 1056: ui_updateint("regs.frames", cpu68k_frames); ! 1057: ui_updateint("regs.clocks", cpu68k_clocks); ! 1058: } ! 1059: ! 1060: void ui_updatestr(char *var, char *val) ! 1061: { ! 1062: Tcl_Obj *valobj, *varobj; ! 1063: ! 1064: valobj = Tcl_NewStringObj(val, -1); ! 1065: Tcl_IncrRefCount(valobj); ! 1066: varobj = Tcl_NewStringObj(var, -1); ! 1067: Tcl_IncrRefCount(varobj); ! 1068: ! 1069: if (Tcl_ObjSetVar2(interp, varobj, NULL, valobj, ! 1070: TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL) { ! 1071: LOG_CRITICAL(("Error setting %s", val)); ! 1072: } ! 1073: Tcl_DecrRefCount(varobj); ! 1074: Tcl_DecrRefCount(valobj); ! 1075: } ! 1076: ! 1077: void ui_updateint(char *var, int val) ! 1078: { ! 1079: Tcl_Obj *valobj, *varobj; ! 1080: ! 1081: valobj = Tcl_NewIntObj(val); ! 1082: Tcl_IncrRefCount(valobj); ! 1083: varobj = Tcl_NewStringObj(var, -1); ! 1084: Tcl_IncrRefCount(varobj); ! 1085: ! 1086: if (Tcl_ObjSetVar2(interp, varobj, NULL, valobj, ! 1087: TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL) { ! 1088: LOG_CRITICAL(("Error setting %s", val)); ! 1089: } ! 1090: Tcl_DecrRefCount(varobj); ! 1091: Tcl_DecrRefCount(valobj); ! 1092: } ! 1093: ! 1094: /*** ui_loop - main UI loop ***/ ! 1095: ! 1096: int ui_loop(void) ! 1097: { ! 1098: char *trace; ! 1099: ! 1100: ui_updateregs(); ! 1101: ! 1102: /* Run Tcl script */ ! 1103: if (Tcl_EvalFile(interp, FNAME_TCLSCRIPT) != TCL_OK) { ! 1104: LOG_CRITICAL(("error: %s", Tcl_GetStringResult(interp))); ! 1105: if ((trace = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY)) != NULL) { ! 1106: LOG_NORMAL(("TCL TRACE: %s", trace)); ! 1107: } ! 1108: return 1; ! 1109: } ! 1110: ! 1111: gen_quit = 0; ! 1112: while(!gen_quit) { ! 1113: switch(state) { ! 1114: case 0: /* stopped */ ! 1115: Tcl_DoOneEvent(TCL_ALL_EVENTS); ! 1116: break; ! 1117: case 1: /* paused */ ! 1118: Tcl_DoOneEvent(TCL_ALL_EVENTS); ! 1119: break; ! 1120: case 2: /* playing */ ! 1121: event_doframe(); ! 1122: Tcl_DoOneEvent(TCL_ALL_EVENTS | TCL_DONT_WAIT); ! 1123: break; ! 1124: } ! 1125: } ! 1126: return 0; ! 1127: } ! 1128: ! 1129: /*** ui_loadimage - load ROM image ***/ ! 1130: ! 1131: int ui_loadimage(Tcl_Interp *interp, const char *filename) ! 1132: { ! 1133: char *p; ! 1134: ! 1135: p = gen_loadimage(filename); ! 1136: if (p) { ! 1137: Tcl_SetResult(interp, p, TCL_STATIC); ! 1138: return TCL_ERROR; ! 1139: } ! 1140: ui_updateregs(); ! 1141: return TCL_OK; ! 1142: } ! 1143: ! 1144: /*** ui_loadsavepos - load saved position ***/ ! 1145: ! 1146: int ui_loadsavepos(Tcl_Interp *interp, const char *filename) ! 1147: { ! 1148: char *p; ! 1149: ! 1150: p = gen_loadsavepos(filename); ! 1151: if (p) { ! 1152: Tcl_SetResult(interp, p, TCL_STATIC); ! 1153: return TCL_ERROR; ! 1154: } ! 1155: return TCL_OK; ! 1156: } ! 1157: ! 1158: /*** ui_setinfo - set information window with name/copyright/etc ***/ ! 1159: ! 1160: char *ui_setinfo(t_cartinfo *cartinfo) ! 1161: { ! 1162: char buffer[128]; ! 1163: ! 1164: if (ui_setstr("i_console", cartinfo->console) || ! 1165: ui_setstr("i_copyright", cartinfo->copyright) || ! 1166: ui_setstr("i_name_domestic", cartinfo->name_domestic) || ! 1167: ui_setstr("i_name_overseas", cartinfo->name_overseas) || ! 1168: ui_setstr("i_version", cartinfo->version) || ! 1169: ui_setstr("i_memo", cartinfo->memo)) ! 1170: return "Unable to set tcl variables"; ! 1171: sprintf(buffer, "%x", cartinfo->checksum); ! 1172: if (ui_setstr("i_checksum", buffer)) ! 1173: return "Unable to set checksum tcl variable"; ! 1174: if (cartinfo->prodtype == pt_game) { ! 1175: if (ui_setstr("i_prodtype", "Game")) ! 1176: return "Unable to set checksum tcl variable"; ! 1177: } else if (cartinfo->prodtype == pt_education) { ! 1178: if (ui_setstr("i_prodtype", "Education")) ! 1179: return "Unable to set checksum tcl variable"; ! 1180: } else { ! 1181: if (ui_setstr("i_prodtype", "Unknown")) ! 1182: return "Unable to set checksum tcl variable"; ! 1183: } ! 1184: return NULL; ! 1185: } ! 1186: ! 1187: /*** ui_checkpalcache - make palette cache up-to-date ***/ ! 1188: ! 1189: void ui_checkpalcache(int flag) ! 1190: { ! 1191: uint32 pal; ! 1192: unsigned int col; ! 1193: uint8 *p; ! 1194: ! 1195: /* this code requires that there be at least 4 bits per colour, that ! 1196: is, three bits that come from the console's palette, and one more bit ! 1197: when we do a dim or bright colour, i.e. this code works with 12bpp ! 1198: upwards */ ! 1199: ! 1200: for (col = 0; col < 64; col++) { ! 1201: if (!flag && !vdp_cramf[col]) ! 1202: continue; ! 1203: vdp_cramf[col] = 0; ! 1204: p = (uint8 *)vdp_cram+2*col; ! 1205: ui_palcache[col] = ! 1206: (((p[0]>>1)&7)<<visual_bluepos) | ! 1207: (((p[1]>>1)&7)<<visual_redpos) | ! 1208: (((p[1]>>5)&7)<<visual_greenpos); ! 1209: ui_palcache[col+64] = ! 1210: (((p[0]>>1)&7)<<(visual_bluepos-1)) | ! 1211: (((p[1]>>1)&7)<<(visual_redpos-1)) | ! 1212: (((p[1]>>5)&7)<<(visual_greenpos-1)) | ! 1213: (4 << visual_bluepos) | (4 << visual_redpos) | ! 1214: (4 << visual_greenpos); ! 1215: ui_palcache[col+128] = ! 1216: (((p[0]>>1)&7)<<(visual_bluepos-1)) | ! 1217: (((p[1]>>1)&7)<<(visual_redpos-1)) | ! 1218: (((p[1]>>5)&7)<<(visual_greenpos-1)); ! 1219: } ! 1220: } ! 1221: ! 1222: /*** ui_line - it is time to render a line ***/ ! 1223: ! 1224: void ui_line(unsigned int line) ! 1225: { ! 1226: static uint8 gfx[320]; ! 1227: uint8 *cram = (uint8 *)vdp_cram; ! 1228: char *p, *pp, *ppp; ! 1229: int lineoffset; ! 1230: ! 1231: if (ui_vdpsimple) ! 1232: return; ! 1233: ! 1234: #ifdef XF86DGA ! 1235: if (ui_dga_state) { ! 1236: lineoffset = dga_xsize*(depth/8); ! 1237: p = dga_start + line*lineoffset*scale; ! 1238: } else { ! 1239: lineoffset = 320*(depth/8)*scale; ! 1240: p = imagedata + line*lineoffset*scale; ! 1241: } ! 1242: #else ! 1243: lineoffset = 320*(depth/8)*scale; ! 1244: p = imagedata + line*lineoffset*scale; ! 1245: #endif ! 1246: ! 1247: if (cpu68k_frames % frameskip != 0) ! 1248: return; ! 1249: ! 1250: LOG_DEBUG2(("line %d: ", line)); ! 1251: if (((vdp_reg[12]>>1) & 3) == 3) ! 1252: vdp_renderline(line, gfx, vdp_oddframe); /* interlace */ ! 1253: else ! 1254: vdp_renderline(line, gfx, 0); ! 1255: if (scale == 2 && smooth) { ! 1256: ui_convertdata_smooth(gfx, p, 320, lineoffset, depth); ! 1257: } else { ! 1258: ui_convertdata(gfx, p, 320, lineoffset, scale, depth, basepixel); ! 1259: } ! 1260: } ! 1261: ! 1262: void ui_convertdata(uint8 *indata, uint8 *outdata, unsigned int pixels, ! 1263: unsigned int lineoffset, unsigned int scale, ! 1264: unsigned int depth, unsigned int basepixel) ! 1265: { ! 1266: char *p, *pp; ! 1267: unsigned int ui; ! 1268: unsigned int t, s; ! 1269: uint32 data; ! 1270: ! 1271: if (depth == 8) { ! 1272: if (scale == 1) { ! 1273: /* not scaled, 8bpp */ ! 1274: for (ui = 0; ui < pixels; ui++) { ! 1275: outdata[ui] = indata[ui] + basepixel; ! 1276: } ! 1277: } else { ! 1278: /* scaled, 8bpp */ ! 1279: p = outdata; ! 1280: for (ui = 0; ui < pixels; ui++, p+=ui*scale) { ! 1281: pp = p; ! 1282: for (s = 0; s < scale; s++, pp+=lineoffset) { ! 1283: for (t = 0; t < scale; t++) { ! 1284: pp[t] = indata[ui] + basepixel; ! 1285: } ! 1286: } ! 1287: } ! 1288: } ! 1289: } else { ! 1290: ui_checkpalcache(0); ! 1291: if (scale == 1) { ! 1292: if (depth == 16) { ! 1293: /* not scaled, 16bpp */ ! 1294: for (ui = 0; ui < pixels; ui++) { ! 1295: data = ui_palcache[indata[ui]]; ! 1296: ((uint16 *)outdata)[ui] = data; /* already in local endian */ ! 1297: } ! 1298: } else if (depth == 24) { ! 1299: /* not scaled, 24bpp */ ! 1300: p = outdata; ! 1301: for (ui = 0; ui < pixels; ui++, p+=3) { ! 1302: data = ui_palcache[indata[ui]]; ! 1303: #ifdef WORDS_BIGENDIAN ! 1304: p[0] = (uint8)(data >> 16); ! 1305: p[1] = (uint8)(data >> 8); ! 1306: p[2] = (uint8)(data); ! 1307: #else ! 1308: p[0] = (uint8)(data); ! 1309: p[1] = (uint8)(data >> 8); ! 1310: p[2] = (uint8)(data >> 16); ! 1311: #endif ! 1312: } ! 1313: } else { ! 1314: ui_err("unknown depth %d", depth); ! 1315: } ! 1316: } else if (scale == 2) { ! 1317: if (depth == 16) { ! 1318: /* scaled by 2, 16bpp */ ! 1319: p = outdata; ! 1320: for (ui = 0; ui < pixels; ui++, p+=4) { ! 1321: data = ui_palcache[indata[ui]]; ! 1322: ((uint16 *)p)[0] = data; /* already in local endian */ ! 1323: ((uint16 *)p)[1] = data; ! 1324: ((uint16 *)(p+lineoffset))[0] = data; ! 1325: ((uint16 *)(p+lineoffset))[1] = data; ! 1326: } ! 1327: } else if (depth == 24) { ! 1328: /* scaled by 2, 24bpp */ ! 1329: p = outdata; ! 1330: for (ui = 0; ui < pixels; ui++, p+=6) { ! 1331: data = ui_palcache[indata[ui]]; ! 1332: #ifdef WORDS_BIGENDIAN ! 1333: p[0] = (uint8)(data >> 16); ! 1334: p[3] = (uint8)(data >> 16); ! 1335: p[lineoffset+0] = (uint8)(data >> 16); ! 1336: p[lineoffset+3] = (uint8)(data >> 16); ! 1337: p[1] = (uint8)(data >> 8); ! 1338: p[4] = (uint8)(data >> 8); ! 1339: p[lineoffset+1] = (uint8)(data >> 8); ! 1340: p[lineoffset+4] = (uint8)(data >> 8); ! 1341: p[2] = (uint8)(data); ! 1342: p[5] = (uint8)(data); ! 1343: p[lineoffset+2] = (uint8)(data); ! 1344: p[lineoffset+5] = (uint8)(data); ! 1345: #else ! 1346: p[0] = (uint8)(data); ! 1347: p[3] = (uint8)(data); ! 1348: p[lineoffset+0] = (uint8)(data); ! 1349: p[lineoffset+3] = (uint8)(data); ! 1350: p[1] = (uint8)(data >> 8); ! 1351: p[4] = (uint8)(data >> 8); ! 1352: p[lineoffset+1] = (uint8)(data >> 8); ! 1353: p[lineoffset+4] = (uint8)(data >> 8); ! 1354: p[2] = (uint8)(data >> 16); ! 1355: p[5] = (uint8)(data >> 16); ! 1356: p[lineoffset+2] = (uint8)(data >> 16); ! 1357: p[lineoffset+5] = (uint8)(data >> 16); ! 1358: #endif ! 1359: } ! 1360: } else { ! 1361: ui_err("unknown depth %d", depth); ! 1362: } ! 1363: } else { ! 1364: /* scaled by more than 2 */ ! 1365: for (ui = 0; ui < pixels; ui++) { ! 1366: data = ui_palcache[indata[ui]]; ! 1367: p = outdata+ui*(depth/8)*scale; ! 1368: for (s = 0; s < scale; s++, p+=lineoffset) { ! 1369: for (t = 0; t < scale; t++) { ! 1370: if (depth == 16) { ! 1371: ((uint16 *)p)[t] = data; /* already in local endian */ ! 1372: } else if (depth == 24) { ! 1373: pp = p+t*3; ! 1374: #ifdef WORDS_BIGENDIAN ! 1375: pp[0] = (uint8)(data >> 16); ! 1376: pp[1] = (uint8)(data >> 8); ! 1377: pp[2] = (uint8)(data); ! 1378: #else ! 1379: pp[0] = (uint8)(data); ! 1380: pp[1] = (uint8)(data >> 8); ! 1381: pp[2] = (uint8)(data >> 16); ! 1382: #endif ! 1383: } else { ! 1384: ui_err("unknown depth %d", depth); ! 1385: } ! 1386: } ! 1387: } ! 1388: } ! 1389: } ! 1390: } ! 1391: } ! 1392: ! 1393: void ui_convertdata_smooth(uint8 *indata, uint8 *outdata, unsigned int pixels, ! 1394: unsigned int lineoffset, unsigned int depth) ! 1395: { ! 1396: char *p, *pp; ! 1397: unsigned int ui; ! 1398: int t, s; ! 1399: uint32 data1, data2, datam; ! 1400: ! 1401: ui_checkpalcache(0); ! 1402: if (depth == 16) { ! 1403: /* scaled by 2, 16bpp */ ! 1404: p = outdata; ! 1405: data1 = ui_palcache[indata[0]]; ! 1406: for (ui = 0; ui < pixels-1; ui++, p+=4) { ! 1407: data2 = ui_palcache[indata[ui+1]]; ! 1408: datam = (((((data1 & visual_redmask) + ! 1409: (data2 & visual_redmask)) >> 1) & visual_redmask) | ! 1410: ((((data1 & visual_greenmask) + ! 1411: (data2 & visual_greenmask)) >> 1) & visual_greenmask) | ! 1412: ((((data1 & visual_bluemask) + ! 1413: (data2 & visual_bluemask)) >> 1) & visual_bluemask)); ! 1414: ((uint16 *)p)[0] = data1; /* already in local endian */ ! 1415: ((uint16 *)p)[1] = datam; ! 1416: ((uint16 *)(p+lineoffset))[0] = data1; ! 1417: ((uint16 *)(p+lineoffset))[1] = datam; ! 1418: data1 = data2; ! 1419: } ! 1420: } else { ! 1421: /* scaled by 2, 24bpp */ ! 1422: p = outdata; ! 1423: data1 = ui_palcache[indata[0]]; ! 1424: for (ui = 0; ui < pixels; ui++, p+=6) { ! 1425: data2 = ui_palcache[indata[ui+1]]; ! 1426: datam = (((((data1 & visual_redmask) + ! 1427: (data2 & visual_redmask)) >> 1) & visual_redmask) | ! 1428: ((((data1 & visual_greenmask) + ! 1429: (data2 & visual_greenmask)) >> 1) & visual_greenmask) | ! 1430: ((((data1 & visual_bluemask) + ! 1431: (data2 & visual_bluemask)) >> 1) & visual_bluemask)); ! 1432: #ifdef WORDS_BIGENDIAN ! 1433: p[0] = (uint8)(data1 >> 16); ! 1434: p[lineoffset+0] = (uint8)(data1 >> 16); ! 1435: p[lineoffset+3] = (uint8)(datam >> 16); ! 1436: p[3] = (uint8)(datam >> 16); ! 1437: p[1] = (uint8)(data1 >> 8); ! 1438: p[lineoffset+1] = (uint8)(data1 >> 8); ! 1439: p[lineoffset+4] = (uint8)(datam >> 8); ! 1440: p[4] = (uint8)(datam >> 8); ! 1441: p[2] = (uint8)(data1); ! 1442: p[lineoffset+2] = (uint8)(data1); ! 1443: p[lineoffset+5] = (uint8)(datam); ! 1444: p[5] = (uint8)(datam); ! 1445: #else ! 1446: p[0] = (uint8)(data1); ! 1447: p[lineoffset+0] = (uint8)(data1); ! 1448: p[lineoffset+3] = (uint8)(datam); ! 1449: p[3] = (uint8)(datam); ! 1450: p[1] = (uint8)(data1 >> 8); ! 1451: p[lineoffset+1] = (uint8)(data1 >> 8); ! 1452: p[lineoffset+4] = (uint8)(datam >> 8); ! 1453: p[4] = (uint8)(datam >> 8); ! 1454: p[2] = (uint8)(data1 >> 16); ! 1455: p[lineoffset+2] = (uint8)(data1 >> 16); ! 1456: p[lineoffset+5] = (uint8)(datam >> 16); ! 1457: p[5] = (uint8)(datam >> 16); ! 1458: #endif ! 1459: data1 = data2; ! 1460: } ! 1461: } ! 1462: } ! 1463: ! 1464: /*** ui_endfield - end of field reached ***/ ! 1465: ! 1466: void ui_endfield(void) ! 1467: { ! 1468: ui_endframe(); ! 1469: } ! 1470: ! 1471: /*** ui_endframe - end of frame reached ***/ ! 1472: ! 1473: void ui_endframe(void) ! 1474: { ! 1475: int y, x, i; ! 1476: XColor xcolor; ! 1477: uint16 col; ! 1478: static uint8 gfx[(320+16)*(240+16)]; ! 1479: char *p, *framestart; ! 1480: unsigned int lineoffset, line; ! 1481: ! 1482: if (cpu68k_frames % frameskip != 0) ! 1483: return; ! 1484: ! 1485: if (ui_vdpsimple) { ! 1486: /* simple mode - entire frame done here */ ! 1487: framestart = gfx+(8*320)+8; ! 1488: vdp_renderframe(framestart, 320+16); /* plot frame */ ! 1489: #ifdef XF86DGA ! 1490: if (ui_dga_state) { ! 1491: lineoffset = dga_xsize*(depth/8); ! 1492: p = dga_start; ! 1493: } else { ! 1494: lineoffset = 320*(depth/8)*scale; ! 1495: p = imagedata; ! 1496: } ! 1497: #else ! 1498: lineoffset = 320*(depth/8)*scale; ! 1499: p = imagedata; ! 1500: #endif ! 1501: for (line = 0; line < 224; line++) { ! 1502: if (scale == 2 && smooth) { ! 1503: ui_convertdata_smooth(framestart+(320+16)*line, ! 1504: p+lineoffset*line*scale, 320, lineoffset, ! 1505: depth); ! 1506: } else { ! 1507: ui_convertdata(framestart+(320+16)*line, ! 1508: p+lineoffset*line*scale, 320, lineoffset, scale, ! 1509: depth, basepixel); ! 1510: } ! 1511: } ! 1512: } ! 1513: if (!ui_dga_state) { ! 1514: /* if ui_dga_state state is set, data has already gone to screen */ ! 1515: if (!gc) ! 1516: gc = XCreateGC(ui_display, Tk_WindowId(mainwin), 0, NULL); ! 1517: XPutImage(ui_display, ! 1518: Tk_WindowId(Tk_NameToWindow(interp, ".main", mainwin)), ! 1519: gc, image, 0, 0, 0, 0, 320*scale, 240*scale); ! 1520: } ! 1521: if (depth == 8) { ! 1522: for (i = 0; i < 64; i++) { ! 1523: if (vdp_cramf[i]) { ! 1524: xcolor.pixel = basepixel+i; ! 1525: xcolor.flags = DoRed | DoGreen | DoBlue; ! 1526: xcolor.blue = ((((uint8 *)vdp_cram)[2*i]) & (7<<1))<<12; ! 1527: xcolor.red = ((((uint8 *)vdp_cram)[2*i+1]) & (7<<1))<<12; ! 1528: xcolor.green = ((((uint8 *)vdp_cram)[2*i+1]) & (7<<5))<<8; ! 1529: XStoreColor(ui_display, cmap, &xcolor); ! 1530: xcolor.pixel = basepixel+i+64; ! 1531: xcolor.blue = 1<<15 | ((((uint8 *)vdp_cram)[2*i]) & (7<<1))<<11; ! 1532: xcolor.red = 1<<15 | ((((uint8 *)vdp_cram)[2*i+1]) & (7<<1))<<11; ! 1533: xcolor.green = 1<<15 | ((((uint8 *)vdp_cram)[2*i+1]) & (7<<5))<<7; ! 1534: XStoreColor(ui_display, cmap, &xcolor); ! 1535: xcolor.pixel = basepixel+i+128; ! 1536: xcolor.blue = ((((uint8 *)vdp_cram)[2*i]) & (7<<1))<<11; ! 1537: xcolor.red = ((((uint8 *)vdp_cram)[2*i+1]) & (7<<1))<<11; ! 1538: xcolor.green = ((((uint8 *)vdp_cram)[2*i+1]) & (7<<5))<<7; ! 1539: XStoreColor(ui_display, cmap, &xcolor); ! 1540: vdp_cramf[i] = 0; ! 1541: } ! 1542: } ! 1543: } ! 1544: } ! 1545: ! 1546: /* ! 1547: ! 1548: void ui_run(void) ! 1549: { ! 1550: gen_quit = 0; ! 1551: do { ! 1552: event_doframe(); ! 1553: while (Tcl_DoOneEvent(TCL_ALL_EVENTS | TCL_DONT_WAIT)); ! 1554: } while(!gen_quit); ! 1555: if (gen_debugmode) { ! 1556: ui_updateregs(); ! 1557: if (gen_quit) ! 1558: LOG_NORMAL(("Stopped.")); ! 1559: } ! 1560: gen_quit = 0; ! 1561: } ! 1562: ! 1563: */ ! 1564: ! 1565: #ifdef XF86DGA ! 1566: void ui_fullscreen(int onoff) ! 1567: { ! 1568: static int forkflag = 0; ! 1569: int i; ! 1570: ! 1571: ui_dga_state = 0; ! 1572: ! 1573: if (!ui_dga) { ! 1574: LOG_CRITICAL(("DGA has been disabled, see error message " ! 1575: "on startup.")); ! 1576: return; ! 1577: } ! 1578: if (onoff) { ! 1579: if (!XF86DGADirectVideo(ui_display, ui_screenno, XF86DGADirectGraphics)) { ! 1580: LOG_CRITICAL(("Switch to DGA direct video failed")); ! 1581: return; ! 1582: } ! 1583: if (!XF86DGASetViewPort(ui_display, ui_screenno, 0, 0)) { ! 1584: LOG_CRITICAL(("Setting of DGA view port failed")); ! 1585: return; ! 1586: } ! 1587: memset(dga_baseaddr, 0, dga_banksize); ! 1588: ui_dga_state = 1; ! 1589: } else { ! 1590: if (!XF86DGADirectVideo(ui_display, ui_screenno, 0)) { ! 1591: LOG_CRITICAL(("Switch out of DGA mode failed")); ! 1592: return; ! 1593: } ! 1594: } ! 1595: } ! 1596: #endif ! 1597: ! 1598: /*** logging functions ***/ ! 1599: ! 1600: /* logging is done this way because this was the best I could come up with ! 1601: whilst battling with macros that can only take fixed numbers of arguments */ ! 1602: ! 1603: #define LOG_FUNC(name,level,txt) void ui_log_ ## name ## (const char *text, ...) \ ! 1604: { \ ! 1605: va_list ap; \ ! 1606: if (gen_loglevel >= level) { \ ! 1607: printf("%s (%05X) ", txt, cpu68k_clocks); \ ! 1608: va_start(ap, text); \ ! 1609: vprintf(text, ap); \ ! 1610: va_end(ap); \ ! 1611: putchar(10); \ ! 1612: } \ ! 1613: } ! 1614: ! 1615: LOG_FUNC(debug3, 7, "DEBG "); ! 1616: LOG_FUNC(debug2, 6, "DEBG "); ! 1617: LOG_FUNC(debug1, 5, "DEBG "); ! 1618: LOG_FUNC(user, 4, "USER "); ! 1619: LOG_FUNC(verbose, 3, "---- "); ! 1620: LOG_FUNC(normal, 2, "---- "); ! 1621: LOG_FUNC(critical, 1, "CRIT "); ! 1622: LOG_FUNC(request, 0, "---- "); ! 1623: ! 1624: /*** ui_err - log error message and quit ***/ ! 1625: ! 1626: void ui_err(const char *text, ...) ! 1627: { ! 1628: va_list ap; ! 1629: ! 1630: printf("ERROR: "); ! 1631: ! 1632: va_start(ap, text); ! 1633: vfprintf(stderr, text, ap); ! 1634: va_end(ap); ! 1635: putc(10, stderr); ! 1636: exit(1); ! 1637: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.