X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=hacks%2Fbsod.c;h=b2bd384431e0e50ab6b0abc505bc782909e074e2;hp=450f10c18ff0cc90ccac35c96b4d0bb063ba126b;hb=6cee540bdbb571485cd5e519f89f389faebd0495;hpb=40eacb5812ef7c0e3374fb139afbb4f5bc8bbfb5 diff --git a/hacks/bsod.c b/hacks/bsod.c index 450f10c1..b2bd3844 100644 --- a/hacks/bsod.c +++ b/hacks/bsod.c @@ -13,6 +13,7 @@ * this version written by jwz, 4-Jun-98. */ +#include #include "screenhack.h" #include "xpm-pixmap.h" #include @@ -20,6 +21,10 @@ #include #include +#ifdef HAVE_XSHM_EXTENSION +#include "xshm.h" +#endif + #ifdef HAVE_UNAME # include #endif /* HAVE_UNAME */ @@ -30,6 +35,8 @@ #include "images/macbomb.xbm" #include "images/hmac.xpm" +#undef countof +#define countof(x) (sizeof((x))/sizeof((*x))) static int draw_string (Display *dpy, Window window, GC gc, XGCValues *gcv, @@ -195,7 +202,7 @@ bsod_sleep(Display *dpy, int seconds) } -static Bool +static void windows (Display *dpy, Window window, int delay, int which) { XGCValues gcv; @@ -286,13 +293,6 @@ windows (Display *dpy, Window window, int delay, int which) if (which == 2 && (random() % 2)) which = 3; - if (!get_boolean_resource((which == 0 ? "doWindows" : - which == 1 ? "doNT" : - which == 2 ? "doWin2K" : - "doWin2K"), /* "doWinME" ? */ - "DoWindows")) - return False; - XGetWindowAttributes (dpy, window, &xgwa); fontname = get_string_resource ((xgwa.height > 600 @@ -377,12 +377,30 @@ windows (Display *dpy, Window window, int delay, int which) bsod_sleep(dpy, delay); XClearWindow(dpy, window); XFreeFont(dpy, font); - return True; } +static void +windows_31 (Display *dpy, Window window, int delay) +{ + windows (dpy, window, delay, 0); +} + +static void +windows_nt (Display *dpy, Window window, int delay) +{ + windows (dpy, window, delay, 1); +} + +static void +windows_2k (Display *dpy, Window window, int delay) +{ + windows (dpy, window, delay, 2); +} + + /* SCO OpenServer 5 panic, by Tom Kelly */ -static Bool +static void sco (Display *dpy, Window window, int delay) { XGCValues gcv; @@ -408,7 +426,8 @@ sco (Display *dpy, Window window, int delay) "Trying to dump 5023 pages to dumpdev hd (1/41), 63 pages per '.'\n" ); const char *sco_panic_2 = - ("...............................................................................\n" + ("................................................................." + "..............\n" ); const char *sco_panic_3 = ("5023 pages dumped\n" @@ -421,9 +440,6 @@ sco (Display *dpy, Window window, int delay) "** Press Any Key to Reboot **\n" ); - if (!get_boolean_resource("doSCO", "DoSCO")) - return False; - for (s = sco_panic_1; *s; s++) if (*s == '\n') lines_1++; for (s = sco_panic_2; *s; s++) if (*s == '\n') lines_2++; for (s = sco_panic_3; *s; s++) if (*s == '\n') lines_3++; @@ -495,13 +511,12 @@ sco (Display *dpy, Window window, int delay) XClearWindow(dpy, window); XFreeGC(dpy, gc); XFreeFont(dpy, font); - return True; } /* Linux (sparc) panic, by Tom Kelly */ -static Bool +static void sparc_linux (Display *dpy, Window window, int delay) { XGCValues gcv; @@ -535,9 +550,6 @@ sparc_linux (Display *dpy, Window window, int delay) "Instruction DUMP:\n" ); - if (!get_boolean_resource("doSparcLinux", "DoSparcLinux")) - return False; - for (s = linux_panic; *s; s++) if (*s == '\n') lines++; XGetWindowAttributes (dpy, window, &xgwa); @@ -574,13 +586,12 @@ sparc_linux (Display *dpy, Window window, int delay) bsod_sleep(dpy, delay); XClearWindow(dpy, window); XFreeFont(dpy, font); - return True; } /* BSD Panic by greywolf@starwolf.com - modeled after the Linux panic above. By Grey Wolf */ -static Bool +static void bsd (Display *dpy, Window window, int delay) { XGCValues gcv; @@ -610,9 +621,6 @@ bsd (Display *dpy, Window window, int delay) "panic: teleport chamber: out of order", "panic: Brain fried - core dumped"}; - if (!get_boolean_resource("doBSD", "DoBSD")) - return False; - for (i = 0; i < sizeof(syncing); i++) syncing[i] = 0; @@ -718,10 +726,9 @@ bsd (Display *dpy, Window window, int delay) DONE: XClearWindow(dpy, window); XFreeFont(dpy, font); - return True; } -static Bool +static void amiga (Display *dpy, Window window, int delay) { XGCValues gcv; @@ -741,9 +748,6 @@ amiga (Display *dpy, Window window, int delay) ("_Software failure. Press left mouse button to continue.\n" "_Guru Meditation #00000003.00C01570"); - if (!get_boolean_resource("doAmiga", "DoAmiga")) - return False; - XGetWindowAttributes (dpy, window, &xgwa); fontname = get_string_resource ((xgwa.height > 600 @@ -833,7 +837,6 @@ amiga (Display *dpy, Window window, int delay) XSync(dpy, False); XClearWindow(dpy, window); XFreeFont(dpy, font); - return True; } @@ -847,7 +850,7 @@ amiga (Display *dpy, Window window, int delay) Perhaps somebody else can tell you more about it.. its just a quick hack :-} */ -static Bool +static void atari (Display *dpy, Window window, int delay) { @@ -862,9 +865,6 @@ atari (Display *dpy, Window window, int delay) int offset; int i, x, y; - if (!get_boolean_resource("doAtari", "DoAtari")) - return False; - XGetWindowAttributes (dpy, window, &xgwa); font = XLoadQueryFont (dpy, def_font); @@ -914,11 +914,10 @@ atari (Display *dpy, Window window, int delay) XSync(dpy, False); XClearWindow(dpy, window); XFreeFont(dpy, font); - return True; } -static Bool +static void mac (Display *dpy, Window window, int delay) { XGCValues gcv; @@ -936,9 +935,6 @@ mac (Display *dpy, Window window, int delay) const char *string = ("0 0 0 0 0 0 0 F\n" "0 0 0 0 0 0 0 3"); - if (!get_boolean_resource("doMac", "DoMac")) - return False; - XGetWindowAttributes (dpy, window, &xgwa); fontname = get_string_resource ("mac.font", "Mac.Font"); @@ -990,10 +986,9 @@ mac (Display *dpy, Window window, int delay) bsod_sleep(dpy, delay); XClearWindow(dpy, window); XFreeFont(dpy, font); - return True; } -static Bool +static void macsbug (Display *dpy, Window window, int delay) { XGCValues gcv; @@ -1104,9 +1099,6 @@ macsbug (Display *dpy, Window window, int delay) const char *s; int body_lines = 1; - if (!get_boolean_resource("doMacsBug", "DoMacsBug")) - return False; - for (s = body; *s; s++) if (*s == '\n') body_lines++; XGetWindowAttributes (dpy, window, &xgwa); @@ -1208,10 +1200,9 @@ macsbug (Display *dpy, Window window, int delay) XFreeGC(dpy, gc2); XClearWindow(dpy, window); XFreeFont(dpy, font); - return True; } -static Bool +static void mac1 (Display *dpy, Window window, int delay) { XGCValues gcv; @@ -1221,9 +1212,6 @@ mac1 (Display *dpy, Window window, int delay) int pix_w = macbomb_width; int pix_h = macbomb_height; - if (!get_boolean_resource("doMac1", "DoMac1")) - return False; - XGetWindowAttributes (dpy, window, &xgwa); gcv.foreground = get_pixel_resource("mac1.foreground", "Mac.Foreground", @@ -1258,11 +1246,10 @@ mac1 (Display *dpy, Window window, int delay) XSync(dpy, False); bsod_sleep(dpy, delay); XClearWindow(dpy, window); - return True; } -static Bool +static void macx (Display *dpy, Window window, int delay) { XGCValues gcv; @@ -1299,9 +1286,6 @@ macx (Display *dpy, Window window, int delay) "\n" "panic: We are hanging here...\n"); - if (!get_boolean_resource("doMacX", "DoMacX")) - return False; - XGetWindowAttributes (dpy, window, &xgwa); gcv.background = get_pixel_resource("macX.background", @@ -1403,12 +1387,8 @@ macx (Display *dpy, Window window, int delay) XSync(dpy, False); bsod_sleep(dpy, delay); XClearWindow(dpy, window); - return True; } - - - /* blit damage * @@ -1418,7 +1398,7 @@ macx (Display *dpy, Window window, int delay) * Xterms. The parameters for choosing what to copy where might not * be quite right, but it looks about ugly enough. */ -static Bool +static void blitdamage (Display *dpy, Window window, int delay) { XGCValues gcv; @@ -1433,9 +1413,6 @@ blitdamage (Display *dpy, Window window, int delay) int src_x, src_y; int x, y; - if (!get_boolean_resource("doBlitDamage", "DoBlitDamage")) - return False; - XGetWindowAttributes(dpy, window, &xwa); load_random_image (xwa.screen, window, window); @@ -1486,8 +1463,6 @@ blitdamage (Display *dpy, Window window, int delay) DONE: XFreeGC(dpy, gc0); - - return True; } @@ -1619,6 +1594,8 @@ scrolling_putc (scrolling_window* ts, const char aChar) ts->sub_x, ts->sub_y + ts->sub_height - ts->line_height, ts->sub_width, ts->line_height); break; + case '\r': + ts->x = 0; case '\b': if(ts->x > 0) ts->x--; @@ -1656,7 +1633,7 @@ scrolling_puts (scrolling_window *ts, const char* aString, int delay) return False; } -static Bool +static void sparc_solaris (Display* dpy, Window window, int delay) { const char *msg1 = @@ -1699,9 +1676,6 @@ sparc_solaris (Display* dpy, Window window, int delay) int i; char buf[256]; - if (!get_boolean_resource("doSolaris", "DoSolaris")) - return False; - ts = make_scrolling_window (dpy, window, "Solaris", True); scrolling_puts (ts, msg1, 0); @@ -1732,13 +1706,11 @@ sparc_solaris (Display* dpy, Window window, int delay) DONE: free_scrolling_window (ts); - - return True; } /* Linux panic and fsck, by jwz */ -static Bool +static void linux_fsck (Display *dpy, Window window, int delay) { XWindowAttributes xgwa; @@ -1780,9 +1752,6 @@ linux_fsck (Display *dpy, Window window, int delay) 0 }; - if (!get_boolean_resource("doLinux", "DoLinux")) - return False; - XGetWindowAttributes (dpy, window, &xgwa); XSetWindowBackground (dpy, window, get_pixel_resource("Linux.background", @@ -2121,7 +2090,1743 @@ linux_fsck (Display *dpy, Window window, int delay) DONE: free_scrolling_window (ts); XClearWindow(dpy, window); - return True; +} + + + +/* HPUX panic, by Tobias Klausmann + */ + +static void +hpux (Display* dpy, Window window, int delay) +{ + GC gc; + XGCValues gcv; + XWindowAttributes xgwa; + scrolling_window *ts; + const char *sysname; + char buf[2048]; + + const char *msg1 = + "Console Login:\n" + "\n" + " ******* Unexpected HPMC/TOC. Processor HPA FFFFFFFF'" + "FFFA0000 *******\n" + " GENERAL REGISTERS:\n" + "r00/03 00000000'00000000 00000000'00000000 00000000'00000000 00000000'" + "006C76C0\n" + "r04/07 00000000'00000001 00000000'0126E328 00000000'00000000 00000000'" + "0122B640\n" + "r08/11 00000000'00000000 00000000'0198CFC0 00000000'000476FE 00000000'" + "00000001\n" + "r12/15 00000000'40013EE8 00000000'08000080 00000000'4002530C 00000000'" + "4002530C\n" + "r16/19 00000000'7F7F2A00 00000000'00000001 00000000'00000000 00000000'" + "00000000\n" + "r20/23 00000000'006C8048 00000000'00000001 00000000'00000000 00000000'" + "00000000\n" + "r24/27 00000000'00000000 00000000'00000000 00000000'00000000 00000000'" + "00744378\n" + "r28/31 00000000'00000000 00000000'007DD628 00000000'0199F2B0 00000000'" + "00000000\n" + " CONTROL REGISTERS:\n" + "sr0/3 00000000'0F3B4000 00000000'0C2A2000 00000000'016FF800 00000000'" + "00000000\n" + "sr4/7 00000000'00000000 00000000'016FF800 00000000'0DBF1400 00000000'" + "00000000\n" + "pcq = 00000000'00000000.00000000'00104950 00000000'00000000.00000000'" + "00104A14\n" + "isr = 00000000'10240006 ior = 00000000'67D9E220 iir = 08000240 rctr = " + "7FF10BB6\n" + "\n" + "pid reg cr8/cr9 00007700'0000B3A9 00000000'0000C5D8\n" + "pid reg cr12/cr13 00000000'00000000 00000000'00000000\n" + "ipsw = 000000FF'080CFF1F iva = 00000000'0002C000 sar = 3A ccr = C0\n" + "tr0/3 00000000'006C76C0 00000000'00000001 00000000'00000000 00000000'" + "7F7CE000\n" + "tr4/7 00000000'03790000 0000000C'4FB68340 00000000'C07EE13F 00000000'" + "0199F2B0\n" + "eiem = FFFFFFF0'FFFFFFFF eirr = 80000000'00000000 itmr = 0000000C'" + "4FD8EDE1\n" + "cr1/4 00000000'00000000 00000000'00000000 00000000'00000000 00000000'" + "00000000\n" + "cr5/7 00000000'00000000 00000000'00000000 00000000'" + "00000000\n" + " MACHINE CHECK PARAMETERS:\n" + "Check Type = 00000000 CPU STATE = 9E000001 Cache Check = 00000000\n" + "TLB Check = 00000000 Bus Check = 00000000 PIM State = ? SIU " + "Status = ????????\n" + "Assists = 00000000 Processor = 00000000\n" + "Slave Addr = 00000000'00000000 Master Addr = 00000000'00000000\n" + "\n" + "\n" + "TOC, pcsq.pcoq = 0'0.0'104950 , isr.ior = 0'10240006.0'67d9e220\n" + "@(#)B2352B/9245XB HP-UX (B.11.00) #1: Wed Nov 5 22:38:19 PST 1997\n" + "Transfer of control: (display==0xd904, flags==0x0)\n" + "\n" + "\n" + "\n" + "*** A system crash has occurred. (See the above messages for details.)\n" + "*** The system is now preparing to dump physical memory to disk, for use\n" + "*** in debugging the crash.\n" + "\n" + "*** The dump will be a SELECTIVE dump: 40 of 256 megabytes.\n" + "*** To change this dump type, press any key within 10 seconds.\n" + "*** Proceeding with selective dump.\n" + "\n" + "*** The dump may be aborted at any time by pressing ESC.\n"; + const char *msg2 = + "\n*** System rebooting.\n"; + + XGetWindowAttributes (dpy, window, &xgwa); + ts = make_scrolling_window (dpy, window, "HPUX", False); + ts->columns = 10000; /* never wrap */ + ts->sub_x = 0; + ts->sub_y = 0; + ts->sub_width = xgwa.width; + ts->sub_height = xgwa.height; + + sysname = "HPUX"; +# ifdef HAVE_UNAME + { + struct utsname uts; + char *s; + if (uname (&uts) >= 0) + sysname = uts.nodename; + s = strchr (sysname, '.'); + if (s) *s = 0; + } +# endif /* !HAVE_UNAME */ + + gcv.foreground = get_pixel_resource ("HPUX.foreground", "HPUX.Foreground", + dpy, xgwa.colormap); + gc = XCreateGC (dpy, window, GCForeground|GCBackground, &gcv); + XFillRectangle (dpy, window, gc, 0, 0, xgwa.width, xgwa.height); + if (bsod_sleep (dpy, 1)) + goto DONE; + + scrolling_puts (ts, + " " + " " + " \n", + 0); + sprintf (buf, "%.100s [HP Release B.11.00] (see /etc/issue)\n", sysname); + scrolling_puts (ts, buf, 0); + if (bsod_sleep (dpy, 1)) + goto DONE; + scrolling_puts (ts, msg1, 0); + { + int i; + int steps = 11; + int size = 40; + for (i = 0; i <= steps; i++) + { + if (i > steps) i = steps; + sprintf (buf, + "*** Dumping: %3d%% complete (%d of 40 MB) (device 64:0x2)\r", + i * 100 / steps, + i * size / steps); + scrolling_puts (ts, buf, 0); + XSync (dpy, False); + usleep (1500000); + if (bsod_sleep (dpy, 0)) + goto DONE; + } + } + + scrolling_puts (ts, msg2, 0); + + XSync(dpy, False); + bsod_sleep(dpy, delay); + + DONE: + free_scrolling_window (ts); +} + + + +/* IBM OS/390 aka MVS aka z/OS. + Text from Dan Espen . + Apparently this isn't actually a crash, just a random session... + But who can tell. + */ + +static void +os390 (Display* dpy, Window window, int delay) +{ + GC gc; + XGCValues gcv; + XWindowAttributes xgwa; + scrolling_window *ts; + int i; + + const char *msg[] = { + "* ISPF Subtask abend *\n", + "SPF ENDED DUE TO ERROR+\n", + "READY\n", + "\n", + "IEA995I SYMPTOM DUMP OUTPUT\n", + " USER COMPLETION CODE=0222\n", + " TIME=23.00.51 SEQ=03210 CPU=0000 ASID=00AE\n", + " PSW AT TIME OF ERROR 078D1000 859DAF18 ILC 2 INTC 0D\n", + " NO ACTIVE MODULE FOUND\n", + " NAME=UNKNOWN\n", + " DATA AT PSW 059DAF12 - 00181610 0A0D9180 70644710\n", + " AR/GR 0: 00000000/80000000 1: 00000000/800000DE\n", + " 2: 00000000/196504DC 3: 00000000/00037A78\n", + " 4: 00000000/00037B78 5: 00000000/0003351C\n", + " 6: 00000000/0000F0AD 7: 00000000/00012000\n", + " 8: 00000000/059DAF10 9: 00000000/0002D098\n", + " A: 00000000/059D9F10 B: 00000000/059D8F10\n", + " C: 00000000/859D7F10 D: 00000000/00032D60\n", + " E: 00000000/00033005 F: 01000002/00000041\n", + " END OF SYMPTOM DUMP\n", + "ISPS014 - ** Logical screen request failed - abend 0000DE **\n", + "ISPS015 - ** Contact your system programmer or dialog developer.**\n", + "*** ISPF Main task abend ***\n", + "IEA995I SYMPTOM DUMP OUTPUT\n", + " USER COMPLETION CODE=0222\n", + " TIME=23.00.52 SEQ=03211 CPU=0000 ASID=00AE\n", + " PSW AT TIME OF ERROR 078D1000 8585713C ILC 2 INTC 0D\n", + " ACTIVE LOAD MODULE ADDRESS=05855000 OFFSET=0000213C\n", + " NAME=ISPMAIN\n", + " DATA AT PSW 05857136 - 00181610 0A0D9180 D3304770\n", + " GR 0: 80000000 1: 800000DE\n", + " 2: 00015260 3: 00000038\n", + " 4: 00012508 5: 00000000\n", + " 6: 000173AC 7: FFFFFFF8\n", + " 8: 05858000 9: 00012CA0\n", + " A: 05857000 B: 05856000\n", + " C: 85855000 D: 00017020\n", + " E: 85857104 F: 00000000\n", + " END OF SYMPTOM DUMP\n", + "READY\n", + "***_\n" + }; + + XGetWindowAttributes (dpy, window, &xgwa); + ts = make_scrolling_window (dpy, window, "OS390", False); + ts->columns = 10000; /* never wrap */ + ts->sub_x = 0; + ts->sub_y = 0; + ts->sub_width = xgwa.width; + ts->sub_height = xgwa.height; + + gcv.foreground = get_pixel_resource ("390.background", "390.Background", + dpy, xgwa.colormap); + gc = XCreateGC (dpy, window, GCForeground, &gcv); + XFillRectangle (dpy, window, gc, 0, 0, xgwa.width, xgwa.height); + XFreeGC (dpy, gc); + + for (i = 0; i < countof (msg); i++) + { + scrolling_puts (ts, msg[i], 0); + usleep (100000); + if (bsod_sleep(dpy, 0)) goto DONE; + } + + XSync(dpy, False); + bsod_sleep(dpy, delay); +DONE: + free_scrolling_window (ts); +} + + + +/* + * Simulate various Apple II crashes. The memory map encouraged many + * programs to use the primary hi-res video page for various storage, + * and the secondary hi-res page for active display. When it crashed + * into Applesoft or the monitor, it would revert to the primary page + * and you'd see memory garbage on the screen. Also, it was common for + * copy-protected games to use the primary text page for important + * code, because that made it really hard to reverse-engineer them. The + * result often looked like what this generates. + * + * Sometimes an imaginary user types some of the standard commands to + * recover from crashes. You can turn off BSOD*apple2SimulateUser to + * prevent this. + * + * It simulates the following characteristics of standard television + * monitors: + * + * - Realistic rendering of a composite video signal + * - Compression & brightening on the right, as the scan gets truncated + * because of saturation in the flyback transformer + * - Blooming of the picture dependent on brightness + * - Overscan, cutting off a few pixels on the left side. + * - Colored text in mixed graphics/text modes + * + * It's amazing how much it makes your high-end monitor look like at + * large late-70s TV. All you need is to put a big "Solid State" logo + * in curly script on it and you'd be set. + * + * Trevor Blackwell + */ + +/* + * Implementation notes: + * + * There are roughly 3 parts to this hack: + * + * - emulation of A2 Basic and Monitor. Not much more than printing random + * plausible messages. Here we work in the A2 memory space. + * + * - emulation of the A2's video output section, which shifted bits out of main + * memory at a 14 MHz dot clock rate, sort of. You could only read one byte + * per MHz, so there were various schemes for turning 8 bits into 14 screen + * pixels. + * + * - simulation of an NTSC television, which turned the bits into colored + * graphics and text. + * + * The A2 had 3 display modes: text, lores, and hires. Text was 40x24, and it + * disabled color in the TV. Lores gave you 40x48 graphics blocks, using the + * same memory as the text screen. Each could be one of 16 colors. Hires gave + * you 280x192 pixels. Odd pixels were blue or purple, and even pixels were + * orange or green depending on the setting of the high bit in each byte. + * + * The graphics modes could also have 4 lines of text at the bottom. This was + * fairly unreadable if you had a color monitor. + * + * Each mode had 2 different screens using different memory space. In hires + * mode this was sometimes used for double buffering, but more often the lower + * screen was full of code/data and the upper screen was used for display, so + * you got random garbage on the screen. + * + * In DirectColor or TrueColor modes, it generates pixel values directly from + * RGB values it calculates across each scan line. In PseudoColor mode, it + * consider each possible pattern of 5 preceding bit values in each possible + * position modulo 4 and allocates a color for each. A few things, like the + * brightening on the right side as the horizontal trace slows down, aren't + * done in PseudoColor. + * + * The text font is based on X's standard 6x10 font, with a few tweaks like + * putting a slash across the zero. + * + * I'd like to add a bit of visible retrace, but it conflicts with being able + * to bitcopy the image when fast scrolling. After another couple of CPU + * generations, we could probably regenerate the whole image from scratch every + * time. On a P4 2 GHz it can manage this fine for blinking text, but scrolling + * looks too slow. + */ + +static char * apple2_basic_errors[]={ + "BREAK", + "NEXT WITHOUT FOR", + "SYNTAX ERROR", + "RETURN WITHOUT GOSUB", + "ILLEGAL QUANTITY", + "OVERFLOW", + "OUT OF MEMORY", + "BAD SUBSCRIPT ERROR", + "DIVISION BY ZERO", + "STRING TOO LONG", + "FORMULA TOO COMPLEX", + "UNDEF'D FUNCTION", + "OUT OF DATA" +}; +static char * apple2_dos_errors[]={ + "VOLUME MISMATCH", + "I/O ERROR", + "DISK FULL", + "NO BUFFERS AVAILABLE", + "PROGRAM TOO LARGE", +}; + +struct apple2_state { + char hireslines[192][40]; + char textlines[24][40]; + int gr_text; + enum { + A2_GR_FULL=1, + A2_GR_LORES=2, + A2_GR_HIRES=4 + } gr_mode; + int cursx; + int cursy; + int blink; + int rowimage[24]; +}; + +enum { + A2_SP_ROWMASK=1023, + A2_SP_PUT=1024, + A2_SP_COPY=2048, +}; + +static void +a2_scroll(struct apple2_state *st) +{ + int i; + int top=(st->gr_mode&(A2_GR_LORES|A2_GR_HIRES)) ? 20 : 0; + if ((st->gr_mode&A2_GR_FULL) && (st->gr_mode&A2_GR_HIRES)) return; + if (st->gr_mode&A2_GR_FULL) top=0; + for (i=top; i<23; i++) { + if (memcmp(st->textlines[i],st->textlines[i+1],40)) { + memcpy(st->textlines[i],st->textlines[i+1],40); + st->rowimage[i]=st->rowimage[i+1]; + } + } + memset(st->textlines[23],0xe0,40); + st->rowimage[23]=-1; +} + +static void +a2_printc(struct apple2_state *st, char c) +{ + st->textlines[st->cursy][st->cursx] |= 0xc0; /* turn off blink */ + if (c=='\n') { + if (st->cursy==23) { + a2_scroll(st); + } else { + st->rowimage[st->cursy]=-1; + st->cursy++; + st->rowimage[st->cursy]=-1; + } + st->cursx=0; + } else { + st->textlines[st->cursy][st->cursx]=c ^ 0xc0; + st->rowimage[st->cursy]=-1; + st->cursx++; + if (st->cursx==40) { + if (st->cursy==23) { + a2_scroll(st); + } else { + st->rowimage[st->cursy]=-1; + st->cursy++; + st->rowimage[st->cursy]=-1; + } + st->cursx=0; + } + } + st->textlines[st->cursy][st->cursx] &= 0x7f; /* turn on blink */ +} + +static void +a2_goto(struct apple2_state *st, int r, int c) +{ + st->textlines[st->cursy][st->cursx] |= 0xc0; /* turn off blink */ + st->cursy=r; + st->cursx=c; + st->textlines[st->cursy][st->cursx] &= 0x7f; /* turn on blink */ +} + +static void +a2_cls(struct apple2_state *st) +{ + int i; + for (i=0; i<24; i++) { + memset(st->textlines[i],0xe0,40); + st->rowimage[i]=-1; + } +} + +static void +a2_invalidate(struct apple2_state *st) +{ + int i; + for (i=0; i<24; i++) { + st->rowimage[i]=-1; + } +} + +static void +a2_poke(struct apple2_state *st, int addr, int val) +{ + + if (addr>=0x400 && addr<0x800) { + /* text memory */ + int row=((addr&0x380)/0x80) + ((addr&0x7f)/0x28)*8; + int col=(addr&0x7f)%0x28; + if (row<24 && col<40) { + st->textlines[row][col]=val; + if (!(st->gr_mode&(A2_GR_HIRES)) || + (!(st->gr_mode&(A2_GR_FULL)) && row>=20)) { + st->rowimage[row]=-1; + } + } + } + else if (addr>=0x2000 && addr<0x4000) { + int row=(((addr&0x1c00) / 0x400) * 1 + + ((addr&0x0380) / 0x80) * 8 + + ((addr&0x0078) / 0x28) * 64); + int col=((addr&0x07f)%0x28); + if (row<192 && col<40) { + st->hireslines[row][col]=val; + if (st->gr_mode&A2_GR_HIRES) { + st->rowimage[row/8]=-1; + } + } + } +} + +/* This table lists fixes for characters that differ from the standard 6x10 + font. Each encodes a pixel, as (charindex*7 + x) + (y<<10) + (value<<15) + where value is 0 for white and 1 for black. */ +static unsigned short a2_fixfont[] = { + /* Fix $ */ 0x8421, 0x941d, + /* Fix % */ 0x8024, 0x0028, 0x8425, 0x0426, 0x0825, 0x1027, 0x1426, 0x9427, + 0x1824, 0x9828, + /* Fix * */ 0x8049, 0x8449, 0x8849, 0x0c47, 0x0c48, 0x0c4a, 0x0c4b, 0x9049, + 0x9449, 0x9849, + /* Fix , */ 0x9057, 0x1458, 0x9856, 0x1857, 0x1c56, + /* Fix . */ 0x1465, 0x1864, 0x1866, 0x1c65, + /* Fix / */ 0x006e, 0x186a, + /* Fix 0 */ 0x8874, 0x8c73, 0x9072, + /* Fix 1 */ 0x0878, 0x1878, 0x187c, + /* Fix 5 */ 0x8895, 0x0c94, 0x0c95, + /* Fix 6 */ 0x809f, 0x8c9c, 0x109c, + /* Fix 7 */ 0x8ca4, 0x0ca5, 0x90a3, 0x10a4, + /* Fix 9 */ 0x08b3, 0x8cb3, 0x98b0, + /* Fix : */ 0x04b9, 0x08b8, 0x08ba, 0x0cb9, 0x90b9, 0x14b9, 0x18b8, 0x18b9, + 0x18ba, 0x1cb9, + /* Fix ; */ 0x04c0, 0x08bf, 0x08c1, 0x0cc0, 0x90c0, 0x14c1, 0x98bf, 0x18c0, + 0x1cbf, + /* Fix < */ 0x80c8, 0x00c9, 0x84c7, 0x04c8, 0x88c6, 0x08c7, 0x8cc5, 0x0cc6, + 0x90c6, 0x10c7, + 0x94c7, 0x14c8, 0x98c8, 0x18c9, + /* Fix > */ 0x80d3, 0x00d4, 0x84d4, 0x04d5, 0x88d5, 0x08d6, 0x8cd6, 0x0cd7, + 0x90d5, 0x10d6, + 0x94d4, 0x14d5, 0x98d3, 0x18d4, + /* Fix @ */ 0x88e3, 0x08e4, 0x8ce4, 0x98e5, + /* Fix B */ 0x84ef, 0x04f0, 0x88ef, 0x08f0, 0x8cef, 0x90ef, 0x10f0, 0x94ef, + 0x14f0, + /* Fix D */ 0x84fd, 0x04fe, 0x88fd, 0x08fe, 0x8cfd, 0x0cfe, 0x90fd, 0x10fe, + 0x94fd, 0x14fe, + /* Fix G */ 0x8116, 0x0516, 0x9916, + /* Fix J */ 0x0129, 0x012a, 0x052a, 0x852b, 0x092a, 0x892b, 0x0d2a, 0x8d2b, + 0x112a, 0x912b, + 0x152a, 0x952b, 0x992a, + /* Fix M */ 0x853d, 0x853f, 0x093d, 0x893e, 0x093f, + /* Fix Q */ 0x915a, 0x155a, 0x955b, 0x155c, 0x195b, 0x995c, 0x1d5c, + /* Fix V */ 0x8d7b, 0x0d7c, 0x0d7e, 0x8d7f, 0x917b, 0x117c, 0x117e, 0x917f, + /* Fix [ */ 0x819e, 0x81a2, 0x859e, 0x899e, 0x8d9e, 0x919e, 0x959e, 0x999e, + 0x99a2, + /* Fix \ */ 0x01a5, 0x19a9, + /* Fix ] */ 0x81ac, 0x81b0, 0x85b0, 0x89b0, 0x8db0, 0x91b0, 0x95b0, 0x99ac, + 0x99b0, + /* Fix ^ */ 0x01b5, 0x05b4, 0x05b6, 0x09b3, 0x89b5, 0x09b7, 0x8db4, 0x8db6, + 0x91b3, 0x91b7, + /* Fix _ */ 0x9db9, 0x9dbf, + 0, +}; + +struct ntsc_dec { + char pattern[600]; + int ntscy[600]; + int ntsci[600]; + int ntscq[600]; + int multi[600]; + int multq[600]; + int brightness_control; +}; + +/* + First generate the I and Q reference signals, which we'll multiply by the + input signal to accomplish the demodulation. Normally they are shifted 33 + degrees from the colorburst. I think this was convenient for + inductor-capacitor-vacuum tube implementation. + + The tint control, FWIW, just adds a phase shift to the chroma signal, and + the color control controls the amplitude. + + In text modes (colormode==0) the system disabled the color burst, and no + color was detected by the monitor. + + freq_error gives a mismatch between the built-in oscillator and the TV's + colorbust. Older II Plus machines seemed to occasionally get instability + problems -- the crystal oscillator was a single transistor if I remember + correctly -- and the frequency would vary enough that the tint would change + across the width of the screen. The left side would be in correct tint + because it had just gotten resynchronized with the color burst. +*/ +static void +ntsc_set_demod(struct ntsc_dec *it, double tint_control, + double color_control, double brightness_control, + double freq_error, + int colormode) +{ + int i; + + it->brightness_control=(int)(1024.0*brightness_control); + + for (i=0; i<600; i++) { + double phase=90.0-90.0*i + freq_error*i/600.0 + tint_control; + it->multi[i]=(int)(-cos(3.1415926/180.0*(phase-303)) * 65536.0 * + color_control * colormode * 4); + it->multq[i]=(int)(cos(3.1415926/180.0*(phase-33)) * 65536.0 * + color_control * colormode * 4); + } +} + +/* Here we model the analog circuitry of an NTSC television. Basically, it + splits the signal into 3 signals: Y, I and Q. Y corresponds to luminance, + and you get it by low-pass filtering the input signal to below 3.57 MHz. + + I and Q are the in-phase and quadrature components of the 3.57 MHz + subcarrier. We get them by multiplying by cos(3.57 MHz*t) and sin(3.57 + MHz*t), and low-pass filtering. Because the eye has less resolution in some + colors than others, the I component gets low-pass filtered at 1.5 MHz and + the Q at 0.5 MHz. The I component is approximately orange-blue, and Q is + roughly purple-green. See http://www.ntsc-tv.com for details. + */ +static void +ntsc_to_yiq(struct ntsc_dec *it) +{ + int i; + int fyx[10],fyy[10]; + int fix[10],fiy[10]; + int fqx[10],fqy[10]; + int pixghost; + int iny,ini,inq,pix,blank; + + for (i=0; i<10; i++) fyx[i]=fyy[i]=fix[i]=fiy[i]=fqx[i]=fqy[i]=0.0; + pixghost=0; + for (i=0; i<600; i++) { + /* Get the video out signal, and add a teeny bit of ghosting, typical of RF + monitor cables. This corresponds to a pretty long cable, but looks right + to me. */ + pix=it->pattern[i]*1024; + if (i>=20) pixghost += it->pattern[i-20]*15; + if (i>=30) pixghost -= it->pattern[i-30]*15; + pix += pixghost; + + /* Get Y, I, Q before filtering */ + iny=pix; + ini=(pix*it->multi[i])>>16; + inq=(pix*it->multq[i])>>16; + + blank = (i>=7 && i<596 ? it->brightness_control : -200); + + /* Now filter them. These are infinite impulse response filters calculated + by the script at http://www-users.cs.york.ac.uk/~fisher/mkfilter. This + is fixed-point integer DSP, son. No place for wimps. We do it in integer + because you can count on integer being faster on most CPUs. We care + about speed because we need to recalculate every time we blink text, and + when we spew random bytes into screen memory. This is roughly 16.16 + fixed point arithmetic, but we scale some filter values up by a few bits + to avoid some nasty precision errors. */ + + /* Filter y at with a 4-pole low-pass Butterworth filter at 3.5 MHz + with an extra zero at 3.5 MHz, from + mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l + Delay about 2 */ + + fyx[0] = fyx[1]; fyx[1] = fyx[2]; fyx[2] = fyx[3]; + fyx[3] = fyx[4]; fyx[4] = fyx[5]; fyx[5] = fyx[6]; + fyx[6] = (iny * 1897) >> 13; + fyy[0] = fyy[1]; fyy[1] = fyy[2]; fyy[2] = fyy[3]; + fyy[3] = fyy[4]; fyy[4] = fyy[5]; fyy[5] = fyy[6]; + fyy[6] = (fyx[0]+fyx[6]) + 4*(fyx[1]+fyx[5]) + 7*(fyx[2]+fyx[4]) + 8*fyx[3] + + ((-151*fyy[2] + 8115*fyy[3] - 38312*fyy[4] + 36586*fyy[5]) >> 16); + if (i>=2) it->ntscy[i-2] = blank + (fyy[6]>>3); + + /* Filter I and Q at 1.5 MHz. 3 pole Butterworth from + mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 + Delay about 3. + + The NTSC spec says the Q value should be filtered at 0.5 MHz at the + transmit end, But the Apple's video circuitry doesn't any such thing. + AFAIK, oldish televisions (before comb filters) simply applied a 1.5 MHz + filter to both after the demodulator. + */ + + fix[0] = fix[1]; fix[1] = fix[2]; fix[2] = fix[3]; + fix[3] = (ini * 1413) >> 14; + fiy[0] = fiy[1]; fiy[1] = fiy[2]; fiy[2] = fiy[3]; + fiy[3] = (fix[0]+fix[3]) + 3*(fix[1]+fix[2]) + + ((16559*fiy[0] - 72008*fiy[1] + 109682*fiy[2]) >> 16); + if (i>=3) it->ntsci[i-3] = fiy[3]>>2; + + fqx[0] = fqx[1]; fqx[1] = fqx[2]; fqx[2] = fqx[3]; + fqx[3] = (inq * 1413) >> 14; + fqy[0] = fqy[1]; fqy[1] = fqy[2]; fqy[2] = fqy[3]; + fqy[3] = (fqx[0]+fqx[3]) + 3*(fqx[1]+fqx[2]) + + ((16559*fqy[0] - 72008*fqy[1] + 109682*fqy[2]) >> 16); + if (i>=3) it->ntscq[i-3] = fqy[3]>>2; + + } + for (; i<610; i++) { + if (i-2<600) it->ntscy[i-2]=0; + if (i-3<600) it->ntsci[i-3]=0; + if (i-9<600) it->ntscq[i-9]=0; + } +} + +enum { + A2_CMAP_HISTBITS=5, + A2_CMAP_LEVELS=2, + A2_CMAP_OFFSETS=4, +}; + +#define A2_CMAP_INDEX(COLORMODE, LEVEL, HIST, OFFSET) \ +((((COLORMODE)*A2_CMAP_LEVELS+(LEVEL))<class; + red_shift=red_invprec=green_shift=green_invprec=blue_shift=blue_invprec=-1; + if (visclass == TrueColor || xgwa.visual->class == DirectColor) { + use_cmap=0; + use_color=!mono_p; + } + else if (visclass == PseudoColor || visclass == StaticColor) { + use_cmap=1; + use_color=!mono_p; + } + else { + use_cmap=1; + use_color=0; + } + + /* The Apple II screen was 280x192, sort of. We expand the width to 300 + pixels to allow for overscan. We then pick a size within the window + that's an integer multiple of 300x192. The small case happens when + we're displaying in a subwindow. Then it ends up showing the center + of the screen, which is OK. */ + w=xgwa.width; + h = (xgwa.height/192)*192; + if (w<300) w=300; + if (h==0) h=192; + + dec=(struct ntsc_dec *)malloc(sizeof(struct ntsc_dec)); + + if (use_cmap) { + int hist,offset,level; + int colorprec=8; + + cmap_again: + n_colors=0; + /* Typically allocates 214 distinct colors, but will scale back its + ambitions pretty far if it can't get them */ + for (colormode=0; colormode<=use_color; colormode++) { + ntsc_set_demod(dec, tint_control, color_control, brightness_control, + 0.0, colormode); + for (level=0; level<2; level++) { + for (hist=0; hist<(1<pattern[i]=0; + for (i=0; ipattern[64+offset-i]=(hist>>i)&1; + } + + ntsc_to_yiq(dec); + interpy=dec->ntscy[63+offset]; + interpi=dec->ntsci[63+offset]; + interpq=dec->ntscq[63+offset]; + + r=(interpy + ((+68128*interpi+40894*interpq)>>16))*levelmult; + g=(interpy + ((-18087*interpi-41877*interpq)>>16))*levelmult; + b=(interpy + ((-72417*interpi+113312*interpq)>>16))*levelmult; + if (r<0) r=0; + if (r>65535) r=65535; + if (g<0) g=0; + if (g>65535) g=65535; + if (b<0) b=0; + if (b>65535) b=65535; + + col.red=r & precmask; + col.green=g & precmask; + col.blue=b & precmask; + col.pixel=0; + if (!XAllocColor(dpy, xgwa.colormap, &col)) { + XFreeColors(dpy, xgwa.colormap, colors, n_colors, 0L); + n_colors=0; + colorprec--; + if (colorprec<3) { + goto bailout; + } + goto cmap_again; + } + colors[n_colors++]=col.pixel; + } + } + } + } + } else { + /* Is there a standard way to do this? Does this handle all cases? */ + int shift, prec; + for (shift=0; shift<32; shift++) { + for (prec=1; prec<16 && prec<32-shift; prec++) { + unsigned long mask=(0xffffUL>>(16-prec)) << shift; + if (red_shift<0 && mask==xgwa.visual->red_mask) + red_shift=shift, red_invprec=16-prec; + if (green_shift<0 && mask==xgwa.visual->green_mask) + green_shift=shift, green_invprec=16-prec; + if (blue_shift<0 && mask==xgwa.visual->blue_mask) + blue_shift=shift, blue_invprec=16-prec; + } + } + if (red_shift<0 || green_shift<0 || blue_shift<0) { + if (0) fprintf(stderr,"Can't figure out color space\n"); + goto bailout; + } + raw_rgb=(short *)calloc(w*3, sizeof(short)); + } + + gcv.background=0; + gc = XCreateGC(dpy, window, GCBackground, &gcv); + XSetWindowBackground(dpy, window, gcv.background); + XClearWindow(dpy,window); + + screen_xo=(xgwa.width-w)/2; + screen_yo=(xgwa.height-h)/2; + + if (use_shm) { +#ifdef HAVE_XSHM_EXTENSION + image = create_xshm_image (dpy, xgwa.visual, xgwa.depth, ZPixmap, 0, + &shm_info, w, h); +#endif + if (!image) { + fprintf(stderr, "create_xshm_image failed\n"); + use_shm=0; + } + } + if (!image) { + image = XCreateImage(dpy, xgwa.visual, xgwa.depth, ZPixmap, 0, 0, + w, h, 8, 0); + image->data = (char *)calloc(image->height, image->bytes_per_line); + } + + st=(struct apple2_state *)calloc(1,sizeof(struct apple2_state)); + + /* + Generate the font. It used a 5x7 font which looks a lot like the standard X + 6x10 font, with a few differences. So we render up all the uppercase + letters of 6x10, and make a few tweaks (like putting a slash across the + zero) according to fixfont. + */ + { + const char *def_font="6x10"; + XFontStruct *font; + Pixmap text_pm; + GC gc; + + font = XLoadQueryFont (dpy, def_font); + if (!font) { + fprintf(stderr,"Can't load font %s\n",def_font); + goto bailout; + } + + text_pm=XCreatePixmap(dpy, window, 64*7, 8, xgwa.depth); + + gcv.foreground=1; + gcv.background=0; + gcv.font=font->fid; + gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv); + + XSetForeground(dpy, gc, 0); + XFillRectangle(dpy, text_pm, gc, 0, 0, 64*7, 8); + XSetForeground(dpy, gc, 1); + for (i=0; i<64; i++) { + char c=32+i; + int x=7*i+1; + int y=7; + if (c=='0') { + c='O'; + XDrawString(dpy, text_pm, gc, x, y, &c, 1); + } else { + XDrawString(dpy, text_pm, gc, x, y, &c, 1); + } + } + text_im = XGetImage(dpy, text_pm, 0, 0, 64*7, 8, ~0L, ZPixmap); + XFreeGC(dpy, gc); + XFreePixmap(dpy, text_pm); + + for (i=0; a2_fixfont[i]; i++) { + XPutPixel(text_im, a2_fixfont[i]&0x3ff, + (a2_fixfont[i]>>10)&0xf, + (a2_fixfont[i]>>15)&1); + } + } + + /* + Simulate plausible initial memory contents. + */ + { + int addr=0; + while (addr<0x4000) { + int n; + + switch (random()%4) { + case 0: + case 1: + n=random()%500; + for (i=0; iwidth); + for (i=0; i<100; i++) { + for (y=0; y<8; y++) { + c=0; + for (j=0; j<8; j++) { + c |= XGetPixel(text_im, (x+j)%text_im->width, y)<width); + } + break; + + case 3: + if (addr>0x2000) { + n=random()%200; + for (i=0; ibits_per_rgb>=8) { + flutter_tint=1; + } + else if (random()%3==0) { + flutter_horiz_desync=1; + } + + crtload[0]=0.0; + stepno=0; + a2_goto(st,23,0); + gettimeofday(&basetime_tv, NULL); + if (random()%2==0) basetime_tv.tv_sec -= 1; /* random blink phase */ + next_actiontime=0.0; + fillptr=fillbyte=0; + while (1) { + double curtime,blinkphase; + int startdisplayrow=0; + int cheapdisplay=0; + int nodelay=0; + { + struct timeval curtime_tv; + gettimeofday(&curtime_tv, NULL); + curtime=(curtime_tv.tv_sec - basetime_tv.tv_sec) + + 0.000001*(curtime_tv.tv_usec - basetime_tv.tv_usec); + } + if (curtime>delay) goto finished; + + if (bsod_sleep(dpy,0)) goto finished; + + if (flutter_tint && st->gr_mode && !printing) { + /* Oscillator instability. Look for freq_error below. We should only do + this with color depth>=8, since otherwise you see pixels changing. */ + freq_error_inc += (-0.10*freq_error_inc + + ((int)(random()&0xff)-0x80) * 0.01); + freq_error += freq_error_inc; + a2_invalidate(st); + nodelay=1; + } + else if (flutter_horiz_desync) { + /* Horizontal sync during vertical sync instability. */ + horiz_desync += (-0.10*(horiz_desync-3.0) + + ((int)(random()&0xff)-0x80) * + ((int)(random()&0xff)-0x80) * + ((int)(random()&0xff)-0x80) * 0.0000003); + for (i=0; i<3; i++) st->rowimage[i]=-1; + nodelay=1; + } + + /* It's super-important to get the cursor/text flash out at exactly the + right time, or it looks wrong. So if we're almost due for a blink, wait + for it so we don't miss it in the middle of a screen update. */ + blinkphase=curtime/0.8; + if (blinkphase-floor(blinkphase)>0.7 && !printing && !nodelay) { + /* We're about to blink */ + int delay = ((1.0-(blinkphase-floor(blinkphase)))*0.8) * 1000000; + if (delay<1000) delay=1000; + usleep(delay); + continue; + } + + /* The blinking rate was controlled by 555 timer with a resistor/capacitor + time constant. Because the capacitor was electrolytic, the flash rate + varied somewhat between machines. I'm guessing 1.6 seconds/cycle was + reasonable. (I soldered a resistor in mine to make it blink faster.) */ + i=st->blink; + st->blink=((int)blinkphase)&1; + if (st->blink!=i && !(st->gr_mode&A2_GR_FULL)) { + int downcounter=0; + /* For every row with blinking text, set the changed flag. This basically + works great except with random screen garbage in text mode, when we + end up redrawing the whole screen every second */ + for (row=(st->gr_mode ? 20 : 0); row<24; row++) { + for (col=0; col<40; col++) { + c=st->textlines[row][col]; + if ((c & 0xc0) == 0x40) { + downcounter=4; + break; + } + } + if (downcounter>0) { + st->rowimage[row]=-1; + downcounter--; + } + } + st->rowimage[st->cursy]=-1; + startdisplayrow=random()%24; + } + else if (next_actiontime > curtime && !printing && !nodelay) { + int delay = (next_actiontime-curtime)*1000000; + + if (delay>100000) delay=100000; + if (delay<1000) delay=1000; + usleep(delay); + continue; + } + + if (printing) { + cheapdisplay=1; + while (*printing) { + if (*printing=='\001') { /* pause */ + printing++; + for (i=20; i<24; i++) st->rowimage[i]=-1; + break; + } + else if (*printing=='\n') { + a2_printc(st,*printing); + printing++; + break; + } + else { + a2_printc(st,*printing); + printing++; + } + } + if (!*printing) printing=NULL; + } + else if (curtime >= next_actiontime) { + if (typing) { + /* If we're in the midst of typing a string, emit a character with + random timing. */ + a2_printc(st, *typing); + if (*typing=='\n') { + next_actiontime = curtime; + } else { + next_actiontime = curtime + (random()%1000)*0.0003 + 0.3; + } + typing++; + + if (!*typing) typing=NULL; + + } + else { + next_actiontime=curtime; + + switch(stepno) { + case 0: + a2_invalidate(st); + if (0) { + /* + For testing color rendering. The spec is: + red grn blu + 0 black 0 0 0 + 1 red 227 30 96 + 2 dk blue 96 78 189 + 3 purple 255 68 253 + 4 dk green 0 163 96 + 5 gray 156 156 156 + 6 med blue 20 207 253 + 7 lt blue 208 195 255 + 8 brown 96 114 3 + 9 orange 255 106 60 + 10 grey 156 156 156 + 11 pink 255 160 208 + 12 lt green 20 245 60 + 13 yellow 208 221 141 + 14 aqua 114 255 208 + 15 white 255 255 255 + */ + st->gr_mode=A2_GR_LORES; + for (row=0; row<24; row++) { + for (col=0; col<40; col++) { + st->textlines[row][col]=(row&15)*17; + } + } + next_actiontime+=0.4; + stepno=88; + } + else if (random()%3==0) { + st->gr_mode=0; + next_actiontime+=0.4; + stepno=88; + } + else if (random()%4==0) { + st->gr_mode=A2_GR_LORES; + if (random()%3==0) st->gr_mode |= A2_GR_FULL; + next_actiontime+=0.4; + stepno=88; + } + else if (random()%2==0) { + st->gr_mode=A2_GR_HIRES; + stepno=73; + } + else { + st->gr_mode=A2_GR_HIRES; + next_actiontime+=0.4; + stepno=88; + } + break; + + case 88: + /* An illegal instruction or a reset caused it to drop into the + assembly language monitor, where you could disassemble code & view + data in hex. */ + if (random()%3==0) { + char ibytes[128]; + char itext[128]; + int addr=0xd000+random()%0x3000; + sprintf(ibytes, + "%02X",random()%0xff); + sprintf(itext, + "???"); + sprintf(printbuf, + "\n\n" + "%04X: %-15s %s\n" + " A=%02X X=%02X Y=%02X S=%02X F=%02X\n" + "*", + addr,ibytes,itext, + random()%0xff, random()%0xff, + random()%0xff, random()%0xff, + random()%0xff); + printing=printbuf; + a2_goto(st,23,1); + if (st->gr_mode) { + stepno=11; + } else { + stepno=13; + } + prompt='*'; + next_actiontime += 2.0 + (random()%1000)*0.0002; + } + else { + /* Lots of programs had at least their main functionality in + Applesoft Basic, which had a lot of limits (memory, string + length, etc) and would sometimes crash unexpectedly. */ + sprintf(printbuf, + "\n" + "\n" + "\n" + "?%s IN %d\n" + "\001]", + apple2_basic_errors[random() % + (sizeof(apple2_basic_errors) + /sizeof(char *))], + (1000*(random()%(random()%59+1)) + + 100*(random()%(random()%9+1)) + + 5*(random()%(random()%199+1)) + + 1*(random()%(random()%(random()%2+1)+1)))); + printing=printbuf; + a2_goto(st,23,1); + stepno=1; + prompt=']'; + next_actiontime += 2.0 + (random()%1000)*0.0002; + } + break; + + case 1: + if (simulate_user && random()%3==0) { + /* This was how you reset the Basic interpreter. The sort of + incantation you'd have on a little piece of paper taped to the + side of your machine */ + typing="CALL -1370"; + stepno=2; + } + else if (simulate_user && random()%2==0) { + typing="CATALOG\n"; + stepno=22; + } + else { + next_actiontime+=1.0; + stepno=6; + } + break; + + case 2: + stepno=3; + next_actiontime += 0.5; + break; + + case 3: + st->gr_mode=0; + a2_cls(st); + a2_goto(st,0,16); + for (s="APPLE ]["; *s; s++) a2_printc(st,*s); + a2_goto(st,23,0); + a2_printc(st,']'); + next_actiontime+=1.0; + stepno=6; + break; + + case 6: + if (simulate_user && random()%50==0 && 0) { /* disabled, too goofy */ + typing="10 PRINT \"TRS-80S SUCK!!!\"\n" + "]20 GOTO 10\n" + "]RUN"; + stepno=7; + } + else { + stepno=8; + next_actiontime += delay; + } + break; + + case 7: + for (i=0; i<30; i++) { + for (s="\nTRS-80S SUCK"; *s; s++) a2_printc(st,*s); + } + stepno=8; + next_actiontime+=delay; + + case 8: + break; + + case 22: + if (random()%50==0) { + sprintf(printbuf,"\nDISK VOLUME 254\n\n" + " A 002 HELLO\n" + "\n" + "]"); + printing=printbuf; + } + else { + sprintf(printbuf,"\n?%s\n]", + apple2_dos_errors[random()% + (sizeof(apple2_dos_errors) / + sizeof(char *))]); + printing=printbuf; + } + stepno=6; + next_actiontime+=1.0; + break; + + case 11: + if (simulate_user && random()%2==0) { + /* This was how you went back to text mode in the monitor */ + typing="FB4BG"; + stepno=12; + } else { + next_actiontime+=1.0; + stepno=6; + } + break; + + case 12: + st->gr_mode=0; + a2_invalidate(st); + a2_printc(st,'\n'); + a2_printc(st,'*'); + stepno=13; + next_actiontime+=2.0; + break; + + case 13: + /* This reset things into Basic */ + if (simulate_user && random()%2==0) { + typing="FAA6G"; + stepno=2; + } + else { + stepno=8; + next_actiontime+=delay; + } + break; + + case 73: + for (i=0; i<1500; i++) { + a2_poke(st, fillptr, fillbyte); + fillptr++; + fillbyte = (fillbyte+1)&0xff; + } + next_actiontime += 0.08; + /* When you hit c000, it changed video settings */ + if (fillptr>=0xc000) { + a2_invalidate(st); + st->gr_mode=0; + } + /* And it seemed to reset around here, I dunno why */ + if (fillptr>=0xcf00) stepno=3; + break; + } + } + } + + /* Now, we turn the data in the Apple II video into a screen display. This + is interesting because of the interaction with the NTSC color decoding + in a color television. */ + + colormode=use_color && st->gr_mode!=0; + if (!use_cmap) { + ntsc_set_demod(dec, tint_control, color_control, brightness_control, + freq_error, colormode); + } + imgrow=0; + for (textrow=0; textrow<24; textrow++) { + if (st->rowimage[textrow] == textrow) { + screen_plan[textrow]=0; + } + else if (cheapdisplay && st->rowimage[textrow]>=0 && + textrow<21 && st->rowimage[textrow]<21 && + st->rowimage[textrow]>=2 && textrow>=2 && + (st->rowimage[textrow]+1)*h/24 + screen_xo <= xgwa.height) { + screen_plan[textrow]= A2_SP_COPY | st->rowimage[textrow]; + for (i=0; i<8; i++) { + crtload[textrow*8+i]=crtload[st->rowimage[textrow]*8+i]; + } + startdisplayrow=0; + } + else { + st->rowimage[textrow]=imgrow; + screen_plan[textrow]=imgrow | A2_SP_PUT; + + for (row=textrow*8; rowpattern,0,sizeof(dec->pattern)); + pp=dec->pattern+20; + + if ((st->gr_mode&A2_GR_HIRES) && (row<160 || + (st->gr_mode&A2_GR_FULL))) { + + /* Emulate the mysterious pink line, due to a bit getting + stuck in a shift register between the end of the last + row and the beginning of this one. */ + if ((st->hireslines[row][0] & 0x80) && + (st->hireslines[row][39]&0x40)) { + pp[-1]=1; + } + + for (col=0; col<40; col++) { + u_char b=st->hireslines[row][col]; + int shift=(b&0x80)?0:1; + + /* Each of the low 7 bits in hires mode corresponded to 2 dot + clocks, shifted by one if the high bit was set. */ + for (i=0; i<7; i++) { + pp[shift+1] = pp[shift] =(b>>i)&1; + pp+=2; + } + } + } + else if ((st->gr_mode&A2_GR_LORES) && (row<160 || + (st->gr_mode&A2_GR_FULL))) { + for (col=0; col<40; col++) { + u_char nib=(st->textlines[textrow][col] >> (((row/4)&1)*4))&0xf; + /* The low or high nybble was shifted out one bit at a time. */ + for (i=0; i<14; i++) { + *pp = (nib>>((col*14+i)&3))&1; + pp++; + } + } + } + else { + for (col=0; col<40; col++) { + int rev; + c=st->textlines[textrow][col]; + /* hi bits control inverse/blink as follows: + 0x00: inverse + 0x40: blink + 0x80: normal + 0xc0: normal */ + rev=!(c&0x80) && (!(c&0x40) || st->blink); + + for (i=0; i<7; i++) { + for (i=0; i<7; i++) { + unsigned long pix=XGetPixel(text_im, + ((c&0x3f)^0x20)*7+i, row%8); + pp[1] = pp[2] = pix^rev; + pp+=2; + } + } + } + } + + /* + Interpolate the 600-dotclock line into however many horizontal + screen pixels we're using, and convert to RGB. + + We add some 'bloom', variations in the horizontal scan width with + the amount of brightness, extremely common on period TV sets. They + had a single oscillator which generated both the horizontal scan + and (during the horizontal retrace interval) the high voltage for + the electron beam. More brightness meant more load on the + oscillator, which caused an decrease in horizontal deflection. Look + for (bloomthisrow). + + Also, the A2 did a bad job of generating horizontal sync pulses + during the vertical blanking interval. This, and the fact that the + horizontal frequency was a bit off meant that TVs usually went a + bit out of sync during the vertical retrace, and the top of the + screen would be bent a bit to the left or right. Look for + (shiftthisrow). + + We also add a teeny bit of left overscan, just enough to be + annoying, but you can still read the left column of text. + + We also simulate compression & brightening on the right side of the + screen. Most TVs do this, but you don't notice because they + overscan so it's off the right edge of the CRT. But the A2 video + system used so much of the horizontal scan line that you had to + crank the horizontal width down in order to not lose the right few + characters, and you'd see the compression on the right + edge. Associated with compression is brightening; since the + electron beam was scanning slower, the same drive signal hit the + phosphor harder. Look for (squishright_i) and (squishdiv). + */ + + for (i=j=0; i<600; i++) { + j += dec->pattern[i]; + } + crtload[row] = (crtload[row>1 ? row-1 : 0]) * 0.98 + 0.02*(j/600.0) + + (row>180 ? (row-180)*(row-180)*0.0005 : 0.0); + bloomthisrow = -10.0 * crtload[row]; + shiftthisrow=((row<18) ? ((18-row)*(18-row)* 0.002 + (18-row)*0.05) + * horiz_desync : 0.0); + + scanstart_i=(int)((bloomthisrow+shiftthisrow+18.0)*65536.0); + if (scanstart_i<0) scanstart_i=0; + if (scanstart_i>30*65536) scanstart_i=30*65536; + scanend_i=599*65536; + squishright_i=scanstart_i + 530*65536; + squishdiv=w/15; + pixrate=(int)((560.0-2.0*bloomthisrow)*65536.0/w); + + if (use_cmap) { + for (y=ytop; y=3) && + !(y==ybot-1 && ybot-ytop>=5)); + int hist=0; + int histi=0; + + pixmultinc=pixrate; + for (x=0, i=scanstart_i; + x>16); + int offset=pati&3; + while (pati>=histi) { + hist=(((hist<<1) & ((1<pattern[histi]); + histi++; + } + XPutPixel(image, x, y, + colors[A2_CMAP_INDEX(colormode,level,hist,offset)]); + if (i >= squishright_i) { + pixmultinc += pixmultinc/squishdiv; + } + } + for ( ; x>16; + int r,g,b; + + int interpy=((dec->ntscy[pati]*invpixfrac + + dec->ntscy[pati+1]*pixfrac)>>16); + int interpi=((dec->ntsci[pati]*invpixfrac + + dec->ntsci[pati+1]*pixfrac)>>16); + int interpq=((dec->ntscq[pati]*invpixfrac + + dec->ntscq[pati+1]*pixfrac)>>16); + + /* + According to the NTSC spec, Y,I,Q are generated as: + + y=0.30 r + 0.59 g + 0.11 b + i=0.60 r - 0.28 g - 0.32 b + q=0.21 r - 0.52 g + 0.31 b + + So if you invert the implied 3x3 matrix you get what standard + televisions implement with a bunch of resistors (or directly in + the CRT -- don't ask): + + r = y + 0.948 i + 0.624 q + g = y - 0.276 i - 0.639 q + b = y - 1.105 i + 1.729 q + + These coefficients are below in 16.16 format. + */ + + r=((interpy + ((+68128*interpi+40894*interpq)>>16))*pixbright) + >>16; + g=((interpy + ((-18087*interpi-41877*interpq)>>16))*pixbright) + >>16; + b=((interpy + ((-72417*interpi+113312*interpq)>>16))*pixbright) + >>16; + if (r<0) r=0; + if (g<0) g=0; + if (b<0) b=0; + rrp[0]=r; + rrp[1]=g; + rrp[2]=b; + + if (i>=squishright_i) { + pixmultinc += pixmultinc/squishdiv; + pixbright += pixbright/squishdiv; + } + } + for ( ; xformat==ZPixmap && image->bits_per_pixel==32 && + sizeof(unsigned long)==4 && + image->byte_order==localbyteorder) { + unsigned long *pixelptr = + (unsigned long *) (image->data + y * image->bytes_per_line); + for (x=0, rrp=raw_rgb; x65535) ntscri=65535; + if (ntscgi>65535) ntscgi=65535; + if (ntscbi>65535) ntscbi=65535; + *pixelptr++ = ((ntscri>>red_invprec)<>green_invprec)<>blue_invprec)<format==ZPixmap && image->bits_per_pixel==16 && + sizeof(unsigned short)==2 && + image->byte_order==localbyteorder) { + unsigned short *pixelptr = + (unsigned short *)(image->data + y*image->bytes_per_line); + for (x=0, rrp=raw_rgb; x65535) ntscri=65535; + if (ntscgi>65535) ntscgi=65535; + if (ntscbi>65535) ntscbi=65535; + *pixelptr++ = ((ntscri>>red_invprec)<>green_invprec)<>blue_invprec)<65535) ntscri=65535; + if (ntscgi>65535) ntscgi=65535; + if (ntscbi>65535) ntscbi=65535; + pixel = ((ntscri>>red_invprec)<>green_invprec)<>blue_invprec)<rowimage[textrow]=textrow; + } + } + + finished: + XSync(dpy,False); + XClearWindow(dpy, window); + goto cleanup; + + bailout: + ; + + cleanup: + if (image) { + if (use_shm) { +#ifdef HAVE_XSHM_EXTENSION + destroy_xshm_image(dpy, image, &shm_info); +#endif + } else { + XDestroyImage(image); + } + image=NULL; + } + if (text_im) XDestroyImage(text_im); + if (gc) XFreeGC(dpy, gc); + if (st) free(st); + if (raw_rgb) free(raw_rgb); + if (dec) free(dec); + if (n_colors) XFreeColors(dpy, xgwa.colormap, colors, n_colors, 0L); } @@ -2131,6 +3836,7 @@ char *progclass = "BSOD"; char *defaults [] = { "*delay: 30", + "*doOnly: ", "*doWindows: True", "*doNT: True", "*doWin2K: True", @@ -2146,6 +3852,8 @@ char *defaults [] = { "*doSparcLinux: False", /* boring */ "*doBlitDamage: True", "*doSolaris: True", + "*doHPUX: True", + "*doApple2: True", ".Windows.font: -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*", ".Windows.font2: -*-courier-bold-r-*-*-*-180-*-*-m-*-*-*", @@ -2188,31 +3896,52 @@ char *defaults [] = { ".Linux.font: 9x15bold", ".Linux.font2: -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*", - ".Linux.foreground: White", - ".Linux.background: Black", + ".Linux.foreground: White", + ".Linux.background: Black", ".SparcLinux.font: -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*", ".SparcLinux.font2: -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*", ".SparcLinux.foreground: White", ".SparcLinux.background: Black", - ".BSD.font: vga", - ".BSD.font: -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*", - ".BSD.font2: -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*", -/* ".BSD.font2: -sun-console-medium-r-*-*-22-*-*-*-m-*-*-*", */ - ".BSD.foreground: #c0c0c0", - ".BSD.background: Black", - - ".Solaris.font: -sun-gallant-*-*-*-*-19-*-*-*-*-120-*-*", - ".Solaris.font2: -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*", - ".Solaris.foreground: Black", - ".Solaris.background: White", - "*dontClearRoot: True", + ".BSD.font: vga", + ".BSD.font: -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*", + ".BSD.font2: -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*", +/* ".BSD.font2: -sun-console-medium-r-*-*-22-*-*-*-m-*-*-*", */ + ".BSD.foreground: #c0c0c0", + ".BSD.background: Black", + + ".Solaris.font: -sun-gallant-*-*-*-*-19-*-*-*-*-120-*-*", + ".Solaris.font2: -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*", + ".Solaris.foreground: Black", + ".Solaris.background: White", + "*dontClearRoot: True", + + ".HPUX.font: 9x15bold", + ".HPUX.font2: -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*", + ".HPUX.foreground: Black", + ".HPUX.background: White", + + ".OS390.font: 9x15bold", + ".OS390.font2: -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*", + ".OS390.background: Black", + ".OS390.foreground: Red", + + "*apple2TVColor: 50", + "*apple2TVTint: 5", + "*apple2TVBrightness: 10", + "*apple2TVContrast: 90", + "*apple2SimulateUser: True", + +#ifdef HAVE_XSHM_EXTENSION + "*useSHM: True", +#endif 0 }; XrmOptionDescRec options [] = { { "-delay", ".delay", XrmoptionSepArg, 0 }, + { "-only", ".doOnly", XrmoptionSepArg, 0 }, { "-windows", ".doWindows", XrmoptionNoArg, "True" }, { "-no-windows", ".doWindows", XrmoptionNoArg, "False" }, { "-nt", ".doNT", XrmoptionNoArg, "True" }, @@ -2225,16 +3954,19 @@ XrmOptionDescRec options [] = { { "-no-mac", ".doMac", XrmoptionNoArg, "False" }, { "-mac1", ".doMac1", XrmoptionNoArg, "True" }, { "-no-mac1", ".doMac1", XrmoptionNoArg, "False" }, + { "-macx", ".doMacX", XrmoptionNoArg, "True" }, { "-no-macx", ".doMacX", XrmoptionNoArg, "False" }, { "-atari", ".doAtari", XrmoptionNoArg, "True" }, { "-no-atari", ".doAtari", XrmoptionNoArg, "False" }, { "-macsbug", ".doMacsBug", XrmoptionNoArg, "True" }, { "-no-macsbug", ".doMacsBug", XrmoptionNoArg, "False" }, + { "-apple2", ".doApple2", XrmoptionNoArg, "True" }, + { "-no-apple2", ".doApple2", XrmoptionNoArg, "False" }, { "-sco", ".doSCO", XrmoptionNoArg, "True" }, { "-no-sco", ".doSCO", XrmoptionNoArg, "False" }, { "-bsd", ".doBSD", XrmoptionNoArg, "True" }, { "-no-bsd", ".doBSD", XrmoptionNoArg, "False" }, - { "-linux", ".doLinux", XrmoptionNoArg, "True" }, + { "-linux", ".doLinux", XrmoptionNoArg, "True" }, { "-no-linux", ".doLinux", XrmoptionNoArg, "False" }, { "-sparclinux", ".doSparcLinux", XrmoptionNoArg, "True" }, { "-no-sparclinux", ".doSparcLinux", XrmoptionNoArg, "False" }, @@ -2242,19 +3974,66 @@ XrmOptionDescRec options [] = { { "-no-blitdamage", ".doBlitDamage", XrmoptionNoArg, "False" }, { "-solaris", ".doSolaris", XrmoptionNoArg, "True" }, { "-no-solaris", ".doSolaris", XrmoptionNoArg, "False" }, + { "-hpux", ".doHPUX", XrmoptionNoArg, "True" }, + { "-no-hpux", ".doHPUX", XrmoptionNoArg, "False" }, + { "-os390", ".doOS390", XrmoptionNoArg, "True" }, + { "-no-os390", ".doOS390", XrmoptionNoArg, "False" }, { 0, 0, 0, 0 } }; +static struct { + const char *name; + void (*fn) (Display *, Window, int delay); +} all_modes[] = { + { "Windows", windows_31 }, + { "Nt", windows_nt }, + { "2k", windows_2k }, + { "Amiga", amiga }, + { "Mac", mac }, + { "MacsBug", macsbug }, + { "Mac1", mac1 }, + { "MacX", macx }, + { "SCO", sco }, + { "SparcLinux", sparc_linux }, + { "BSD", bsd }, + { "Atari", atari }, + { "BlitDamage", blitdamage }, + { "Solaris", sparc_solaris }, + { "Linux", linux_fsck }, + { "HPUX", hpux }, + { "OS390", os390 }, + { "Apple2", apple2 }, +}; + + void screenhack (Display *dpy, Window window) { int loop = 0; int i = -1; int j = -1; + int only = -1; int delay = get_integer_resource ("delay", "Integer"); if (delay < 3) delay = 3; + { + char *s = get_string_resource("doOnly", "DoOnly"); + if (s && *s) + { + int count = countof(all_modes); + for (only = 0; only < count; only++) + if (!strcasecmp (s, all_modes[only].name)) + break; + if (only >= count) + { + fprintf (stderr, "%s: unknown -only mode: \"%s\"\n", progname, s); + only = -1; + } + } + if (s) free (s); + } + if (!get_boolean_resource ("root", "Boolean")) { XWindowAttributes xgwa; @@ -2266,26 +4045,24 @@ screenhack (Display *dpy, Window window) while (1) { Bool did; - do { i = (random() & 0xFF) % 15; } while (i == j); - switch (i) - { - case 0: did = windows(dpy, window, delay, 0); break; - case 1: did = windows(dpy, window, delay, 1); break; - case 2: did = windows(dpy, window, delay, 2); break; - case 3: did = amiga(dpy, window, delay); break; - case 4: did = mac(dpy, window, delay); break; - case 5: did = macsbug(dpy, window, delay); break; - case 6: did = mac1(dpy, window, delay); break; - case 7: did = macx(dpy, window, delay); break; - case 8: did = sco(dpy, window, delay); break; - case 9: did = sparc_linux(dpy, window, delay); break; - case 10: did = bsd(dpy, window, delay); break; - case 11: did = atari(dpy, window, delay); break; - case 12: did = blitdamage(dpy, window, delay); break; - case 13: did = sparc_solaris(dpy, window, delay); break; - case 14: did = linux_fsck(dpy, window, delay); break; - default: abort(); break; - } + int count = countof(all_modes); + char name[100], class[100]; + + if (only > 0) + i = only; + else + do { i = (random() & 0xFF) % count; } while (i == j); + + sprintf (name, "do%s", all_modes[i].name); + sprintf (class, "Do%s", all_modes[i].name); + + did = False; + if (only > 0 || get_boolean_resource(name, class)) + { + all_modes[i].fn (dpy, window, delay); + did = True; + } + loop++; if (loop > 100) j = -1; if (loop > 200)