/* * $Id: xy.c,v 1.59 2005/01/14 18:58:04 phil Exp $ * Simulator and host O/S independent XY display simulator * Phil Budne * September 2003 * * with changes by Douglas A. Gwyn, 21 Jan. 2004 * * started from PDP-8/E simulator vc8e.c; * This PDP8 Emulator was written by Douglas W. Jones at the * University of Iowa. It is distributed as freeware, of * uncertain function and uncertain utility. */ /* * Copyright (c) 2003-2004, Philip L. Budne * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the names of the authors shall * not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization * from the authors. */ #include #include #include #include /* for USHRT_MAX */ #include "ws.h" #include "xy.h" /* * The user may select (at compile time) how big a window is used to * emulate the display. Using smaller windows saves memory and screen space. * * Type 30 has 1024x1024 addressing, but only 512x512 visible points. * VR14 has only 1024x768 visible points; VR17 has 1024x1024 visible points. * VT11 supports 4096x4096 addressing, clipping to the lowest 1024x1024 region. * VR48 has 1024x1024 visible points in the main display area and 128x1024 * visible points in a menu area on the right-hand side (1152x1024 total). * VT48 supports 8192x8192 (signed) main-area addressing, clipping to a * 1024x1024 window which can be located anywhere within that region. * (XXX -- That is what the VT11/VT48 manuals say; however, evidence suggests * that the VT11 may actually support 8192x8192 (signed) addressing too.) */ /* Define the default display type (if display_init() not called) */ #ifndef DISPLAY_TYPE #define DISPLAY_TYPE DIS_TYPE30 #endif /* DISPLAY_TYPE not defined */ /* select a default resolution if display_init() not called */ /* XXX keep in struct display? */ #ifndef PIX_SCALE #define PIX_SCALE RES_HALF #endif /* PIX_SCALE not defined */ /* select a default light-pen hit radius if display_init() not called */ #ifndef PEN_RADIUS #define PEN_RADIUS 4 #endif /* PEN_RADIUS not defined */ /* * note: displays can have up to two different colors (eg VR20) * each color can be made up of any number of phosphors * with different colors and decay characteristics (eg Type 30) */ #define ELEMENTS(X) (sizeof(X)/sizeof(X[0])) struct phosphor { float red, green, blue; float level; /* decay level (0.5 for half life) */ float t_level; /* seconds to decay to level */ }; struct color { struct phosphor *phosphors; int nphosphors; int half_life; /* for refresh calc */ }; struct display { enum display_type type; char *name; struct color *color0, *color1; short xpoints, ypoints; }; /* * P7 phosphor for type 30 (16ADP7(A?) CRT) * original phosphor constants from Raphael Nabet's XMame 0.72.1 PDP-1 sim. * fast blue (.05s half life), and slow green (.2s half life) */ static struct phosphor p7[] = { {0.11, 0.11, 1.0, 0.5, 0.05}, /* fast blue */ {1.0, 1.0, 0.11, 0.5, 0.20} /* slow yellow/green */ }; static struct color color_p7 = { p7, ELEMENTS(p7), 125000 }; /* green phosphor for VR14, VR17, VR20 */ static struct phosphor p29[] = {{0.0260, 1.0, 0.00121, 0.5, 0.025}}; struct color color_p29 = { p29, ELEMENTS(p29), 25000 }; static struct phosphor p40[] = { /* P40 blue-white spot with yellow-green decay (.045s to 10%?) */ {0.4, 0.2, 0.924, 0.5, 0.0135}, {0.5, 0.7, 0.076, 0.5, 0.065} }; static struct color color_p40 = { p40, ELEMENTS(p40), 20000 }; /* "red" -- until real VR20 phosphor type/number/constants known */ static struct phosphor pred[] = { {1.0, 0.37, 0.37, 0.5, 0.10} }; static struct color color_red = { pred, ELEMENTS(pred), 100000 }; static struct display displays[] = { /* * Type 30 * PDP-1/4/5/8/9/10 "Precision CRT" display system * * Raytheon 16ADP7A CRT? * web searches for 16ADP7 finds useful information!! * 16" tube, 14 3/8" square raster * maximum dot size .015" * 50us point plot time (20,000 points/sec) * P7 Phosphor? Two phosphor layers: fast blue, slow yellow/green * 360 lb * 7A at 115+-10V 60Hz */ { DIS_TYPE30, "Type 30", &color_p7, NULL, 1024, 1024 }, /* * VR14 * used w/ GT40/44, AX08, VC8E * * Viewable area 6.75" x 9" * 12" diagonal * brightness >= 30 fL * dot size .02" (20 mils) * settle time: * full screen 18us to +/-1 spot diameter * .1" change 1us to +/-.5 spot diameter * weight 75lb */ { DIS_VR14, "VR14", &color_p29, NULL, 1024, 768 }, /* * VR17 * used w/ GT40/44, AX08, VC8E * * Viewable area 9.25" x 9.25" * 17" diagonal * dot size .02" (20 mils) * brightness >= 25 fL * phosphor: P39 doped for IR light pen use * light pen: Type 375 * weight 85lb */ { DIS_VR17, "VR17", &color_p29, NULL, 1024, 1024 }, /* * VR20 * on VC8E * Two colors!! */ { DIS_VR20, "VR20", &color_p29, &color_red, 1024, 1024 }, /* * VR48 * (on VT48 in VS60) * from Douglas A. Gwyn 23 Nov. 2003 * * Viewable area 12" x 12", plus 1.5" x 12" menu area on right-hand side * 21" diagonal * dot size <= .01" (10 mils) * brightness >= 31 fL * phosphor: P40 (blue-white fluorescence with yellow-green phosphorescence) * light pen: Type 377A (with tip switch) * driving circuitry separate * (normally under table on which CRT is mounted) */ { DIS_VR48, "VR48", &color_p40, NULL, 1024+VR48_GUTTER+128, 1024 }, /* * Type 340 Display system * on PDP-4/6/7/9/10 * * 1024x1024 * 9 3/8" raster (.01" dot pitch) * 0,0 at lower left * 8 intensity levels */ { DIS_TYPE340, "Type 340", &color_p7, NULL, 1024, 1024 } }; /* * Unit time (in microseconds) used to store display point time to * live at current aging level. If this is too small, delay values * cannot fit in an unsigned short. If it is too large all pixels * will age at once. Perhaps a suitable value should be calculated at * run time? When display_init() calculates refresh_interval it * sanity checks for both cases. */ #define DELAY_UNIT 250 /* levels to display in first half-life; determines refresh rate */ #ifndef LEVELS_PER_HALFLIFE #define LEVELS_PER_HALFLIFE 4 #endif /* after 5 half lives (.5**5) remaining intensity is 3% of original */ #ifndef HALF_LIVES_TO_DISPLAY #define HALF_LIVES_TO_DISPLAY 5 #endif /* * refresh_rate is number of times per (simulated) second a pixel is * aged to next lowest intensity level. * * refresh_rate = ((1e6*LEVELS_PER_HALFLIFE)/PHOSPHOR_HALF_LIFE) * refresh_interval = 1e6/DELAY_UNIT/refresh_rate * = PHOSPHOR_HALF_LIFE/LEVELS_PER_HALF_LIFE * intensities = (HALF_LIVES_TO_DISPLAY*PHOSPHOR_HALF_LIFE)/refresh_interval * = HALF_LIVES_TO_DISPLAY*LEVELS_PER_HALFLIFE * * See also comments on display_age() * * Try to keep LEVELS_PER_HALFLIFE*HALF_LIVES_TO_DISPLAY*NLEVELS <= 192 * to run on 8-bit (256 color) displays! */ /* * number of aging periods to display a point for */ #define NTTL (HALF_LIVES_TO_DISPLAY*LEVELS_PER_HALFLIFE) /* * maximum (initial) TTL for a point. * TTL's are stored 1-based * (a stored TTL of zero means the point is off) */ #define MAXTTL NTTL /* * number of drawing intensity levels */ #define NLEVELS (DISPLAY_INT_MAX-DISPLAY_INT_MIN+1) #define MAXLEVEL (NLEVELS-1) /* * Display Device Implementation */ /* * Each point on the display is represented by a "struct point". When * a point isn't dark (intensity > 0), it is linked into a circular, * doubly linked delta queue (a priority queue where "delay" * represents the time difference from the previous entry (if any) in * the queue. * * All points are aged refresh_rate times/second, each time moved to the * next (logarithmically) lower intensity level. When display_age() is * called, only the entries which have expired are processed. Calling * display_age() often allows spreading out the workload. * * An alternative would be to have intensity levels represent linear * decreases in intensity, and have the decay time at each level change. * Inverting the decay function for a multi-component phosphor may be * tricky, and the two different colors would need different time tables. * Furthermore, it would require finding the correct location in the * queue when adding a point (currently only need to add points at end) */ /* * 12 bytes/entry on 32-bit system when REFRESH_RATE > 15 * (requires 3MB for 512x512 display). */ typedef unsigned short delay_t; #define DELAY_T_MAX USHRT_MAX struct point { struct point *next; /* next entry in queue */ struct point *prev; /* prev entry in queue */ delay_t delay; /* delta T in DELAY_UNITs */ unsigned char ttl; /* zero means off, not linked in */ unsigned char level : 7; /* intensity level */ unsigned char color : 1; /* for VR20 (two colors) */ }; static struct point *points; /* allocated array of points */ static struct point _head; #define head (&_head) /* * time span of all entries in queue * should never exceed refresh_interval * (should be possible to make this a delay_t) */ static long queue_interval; /* convert X,Y to a "struct point *" */ #define P(X,Y) (points + (X) + ((Y)*(size_t)xpixels)) /* convert "struct point *" to X and Y */ #define X(P) (((P) - points) % xpixels) #define Y(P) (((P) - points) / xpixels) static int initialized = 0; /* * global set by O/S display level to indicate "light pen tip switch activated" * (This is used only by the VS60 emulation, also by vttest to change patterns) */ unsigned char display_lp_sw = 0; /* * global set by DR11-C simulation when DR device enabled; deactivates * light pen and instead reports mouse coordinates as Talos digitizer * data via DR11-C */ unsigned char display_tablet = 0; /* * can be changed with display_lp_radius() */ static long scaled_pen_radius_squared; /* run-time -- set by display_init() */ static int xpoints, ypoints; static int xpixels, ypixels; static int refresh_rate; static int refresh_interval; static int ncolors; static enum display_type display_type; static int scale; /* * relative brightness for each display level * (all but last must be less than 1.0) */ static float level_scale[NLEVELS]; /* * table of pointer to window system "colors" * for painting each age level, intensity level and beam color */ void *colors[2][NLEVELS][NTTL]; void display_lp_radius(int r) { r /= scale; scaled_pen_radius_squared = r * r; } /* * from display_age and display_point * since all points age at the same rate, * only adds points at end of list. */ static void queue_point(struct point *p) { int d; d = refresh_interval - queue_interval; queue_interval += d; /* queue_interval should now be == refresh_interval */ #ifdef PARANOIA if (p->ttl == 0 || p->ttl > MAXTTL) printf("queuing %d,%d level %d!\n", X(p), Y(p), p->level); if (d > DELAY_T_MAX) printf("queuing %d,%d delay %d!\n", X(p), Y(p), d); if (queue_interval > DELAY_T_MAX) printf("queue_interval (%d) > DELAY_T_MAX (%d)\n", (int)queue_interval, DELAY_T_MAX); #endif /* PARANOIA defined */ p->next = head; p->prev = head->prev; head->prev->next = p; head->prev = p; p->delay = d; } /* * here to to dynamically adjust interval for examination * of elapsed vs. simulated time, and fritter away * any extra wall-clock time without eating CPU */ /* * more parameters! */ /* * upper bound for elapsed time between elapsed time checks. * if more than MAXELAPSED microseconds elapsed while simulating * delay_check simulated microseconds, decrease delay_check. */ #define MAXELAPSED 100000 /* 10Hz */ /* * lower bound for elapsed time between elapsed time checks. * if fewer than MINELAPSED microseconds elapsed while simulating * delay_check simulated microseconds, increase delay_check. */ #define MINELAPSED 50000 /* 20Hz */ /* * upper bound for delay (sleep/poll). * If difference between elapsed time and simulated time is * larger than MAXDELAY microseconds, decrease delay_check. * * since delay is elapsed time - simulated time, MAXDELAY * should be <= MAXELAPSED */ #ifndef MAXDELAY #define MAXDELAY 100000 /* 100ms */ #endif /* MAXDELAY not defined */ /* * lower bound for delay (sleep/poll). * If difference between elapsed time and simulated time is * smaller than MINDELAY microseconds, increase delay_check. * * since delay is elapsed time - simulated time, MINDELAY * should be <= MINELAPSED */ #ifndef MINDELAY #define MINDELAY 50000 /* 50ms */ #endif /* MINDELAY not defined */ /* * Initial amount of simulated time to elapse before polling. * Value is very low to ensure polling occurs on slow systems. * Fast systems should ramp up quickly. */ #ifndef INITIAL_DELAY_CHECK #define INITIAL_DELAY_CHECK 1000 /* 1ms */ #endif /* INITIAL_DELAY_CHECK */ /* * gain factor (2**-GAINSHIFT) for adjustment of adjustment * of delay_check */ #ifndef GAINSHIFT #define GAINSHIFT 3 /* gain=0.125 (12.5%) */ #endif /* GAINSHIFT not defined */ static void display_delay(int t, int slowdown) { /* how often (in simulated us) to poll/check for delay */ static unsigned long delay_check = INITIAL_DELAY_CHECK; /* accumulated simulated time */ static unsigned long sim_time = 0; unsigned long elapsed; long delay; sim_time += t; if (sim_time < delay_check) return; elapsed = os_elapsed(); /* read and reset elapsed timer */ if (elapsed == ~0L) { /* first time thru? */ slowdown = 0; /* no adjustments */ elapsed = sim_time; } /* * get delta between elapsed (real) time, and simulated time. * if simulated time running faster, we need to slow things down (delay) */ if (slowdown) delay = sim_time - elapsed; else delay = 0; /* just poll */ #ifdef DEBUG_DELAY2 printf("sim %d elapsed %d delay %d\r\n", sim_time, elapsed, delay); #endif /* * Try to keep the elapsed (real world) time between checks for * delay (and poll for window system events) bounded between * MAXELAPSED and MINELAPSED. Also tries to keep * delay/poll time bounded between MAXDELAY and MINDELAY -- large * delays make the simulation spastic, while very small ones are * inefficient (too many system calls) and tend to be inaccurate * (operating systems have a certain granularity for time * measurement, and when you try to sleep/poll for very short * amounts of time, the noise will dominate). * * delay_check period may be adjusted often, and oscillate. There * is no single "right value", the important things are to keep * the delay time and max poll intervals bounded, and responsive * to system load. */ if (elapsed > MAXELAPSED || delay > MAXDELAY) { /* too much elapsed time passed, or delay too large; shrink interval */ if (delay_check > 1) { delay_check -= delay_check>>GAINSHIFT; #ifdef DEBUG_DELAY printf("reduced period to %d\r\n", delay_check); #endif /* DEBUG_DELAY defined */ } } else if (elapsed < MINELAPSED || slowdown && delay < MINDELAY) { /* too little elapsed time passed, or delta very small */ int gain = delay_check>>GAINSHIFT; if (gain == 0) gain = 1; /* make sure some change made! */ delay_check += gain; #ifdef DEBUG_DELAY printf("increased period to %d\r\n", delay_check); #endif /* DEBUG_DELAY defined */ } if (delay < 0) delay = 0; /* else if delay < MINDELAY, clamp at MINDELAY??? */ /* poll for window system events and/or delay */ ws_poll(NULL, delay); sim_time = 0; /* reset simulated time clock */ /* * delay (poll/sleep) time included in next "elapsed" period * (clock not reset after a delay) */ } /* display_delay */ /* * here periodically from simulator to age pixels. * * calling often with small values will age a few pixels at a time, * and assist with graceful aging of display, and pixel aging. * * values should be smaller than refresh_interval! * * returns true if anything on screen changed. */ int display_age(int t, /* simulated us since last call */ int slowdown) /* slowdown to simulated speed */ { struct point *p; static int elapsed = 0; int changed; if (!initialized && !display_init(DISPLAY_TYPE, PIX_SCALE)) return 0; display_delay(t, slowdown); changed = 0; elapsed += t; if (elapsed < DELAY_UNIT) return 0; t = elapsed / DELAY_UNIT; elapsed %= DELAY_UNIT; while ((p = head->next) != head) { int x, y; /* look at oldest entry */ if (p->delay > t) { /* further than our reach? */ p->delay -= t; /* update head */ queue_interval -= t; /* update span */ break; /* quit */ } x = X(p); y = Y(p); #ifdef PARANOIA if (p->ttl == 0) printf("BUG: age %d,%d ttl zero\n", x, y); #endif /* PARANOIA defined */ /* dequeue point */ p->prev->next = p->next; p->next->prev = p->prev; t -= p->delay; /* lessen our reach */ queue_interval -= p->delay; /* update queue span */ ws_display_point(x, y, colors[p->color][p->level][--p->ttl]); changed = 1; /* queue it back up, unless we just turned it off! */ if (p->ttl > 0) queue_point(p); } return changed; } /* display_age */ /* here from window system */ void display_repaint(void) { struct point *p; int x, y; /* * bottom to top, left to right. */ for (p = points, y = 0; y < ypixels; y++) for (x = 0; x < xpixels; p++, x++) if (p->ttl) ws_display_point(x, y, colors[p->color][p->level][p->ttl-1]); ws_sync(); } /* (0,0) is lower left */ static int intensify(int x, /* 0..xpixels */ int y, /* 0..ypixels */ int level, /* 0..MAXLEVEL */ int color) /* for VR20! 0 or 1 */ { struct point *p; int bleed; if (x < 0 || x >= xpixels || y < 0 || y >= ypixels) return 0; /* limit to display */ p = P(x,y); if (p->ttl) { /* currently lit? */ #ifdef LOUD printf("%d,%d old level %d ttl %d new %d\r\n", x, y, p->level, p->ttl, level); #endif /* LOUD defined */ /* unlink from delta queue */ p->prev->next = p->next; if (p->next == head) queue_interval -= p->delay; else p->next->delay += p->delay; p->next->prev = p->prev; } bleed = 0; /* no bleeding for now */ /* EXP: doesn't work... yet */ /* if "recently" drawn, same or brighter, same color, make even brighter */ if (p->ttl >= MAXTTL*2/3 && level >= p->level && p->color == color && level < MAXLEVEL) level++; /* * this allows a dim beam to suck light out of * a recently drawn bright spot!! */ if (p->ttl != MAXTTL || p->level != level || p->color != color) { p->ttl = MAXTTL; p->level = level; p->color = color; /* save color even if monochrome */ ws_display_point(x, y, colors[p->color][p->level][p->ttl-1]); } queue_point(p); /* put at end of list */ return bleed; } int display_point(int x, /* 0..xpixels (unscaled) */ int y, /* 0..ypixels (unscaled) */ int level, /* DISPLAY_INT_xxx */ int color) /* for VR20! 0 or 1 */ { long lx, ly; if (!initialized && !display_init(DISPLAY_TYPE, PIX_SCALE)) return 0; /* scale x and y to the displayed number of pixels */ /* handle common cases quickly */ if (scale > 1) { if (scale == 2) { x >>= 1; y >>= 1; } else { x /= scale; y /= scale; } } #if DISPLAY_INT_MIN > 0 level -= DISPLAY_INT_MIN; /* make zero based */ #endif intensify(x, y, level, color); /* no bleeding for now (used to recurse for neighbor points) */ if (ws_lp_x == -1 || ws_lp_y == -1) return 0; lx = x - ws_lp_x; ly = y - ws_lp_y; return lx*lx + ly*ly <= scaled_pen_radius_squared; } /* display_point */ /* * calculate decay color table for a phosphor mixture * must be called AFTER refresh_rate initialized! */ static void phosphor_init(struct phosphor *phosphors, int nphosphors, int color) { int ttl; /* for each display ttl level; newest to oldest */ for (ttl = NTTL-1; ttl > 0; ttl--) { struct phosphor *pp; double rr, rg, rb; /* real values */ /* fractional seconds */ double t = ((double)(NTTL-1-ttl))/refresh_rate; int ilevel; /* intensity levels */ int p; /* sum over all phosphors in mixture */ rr = rg = rb = 0.0; for (pp = phosphors, p = 0; p < nphosphors; pp++, p++) { double decay = pow(pp->level, t/pp->t_level); rr += decay * pp->red; rg += decay * pp->green; rb += decay * pp->blue; } /* scale for brightness for each intensity level */ for (ilevel = MAXLEVEL; ilevel >= 0; ilevel--) { int r, g, b; void *cp; /* * convert to 16-bit integer; clamp at 16 bits. * this allows the sum of brightness factors across phosphors * for each of R G and B to be greater than 1.0 */ r = rr * level_scale[ilevel] * 0xffff; if (r > 0xffff) r = 0xffff; g = rg * level_scale[ilevel] * 0xffff; if (g > 0xffff) g = 0xffff; b = rb * level_scale[ilevel] * 0xffff; if (b > 0xffff) b = 0xffff; cp = ws_color_rgb(r, g, b); if (!cp) { /* allocation failed? */ if (ttl == MAXTTL-1) { /* brand new */ if (ilevel == MAXLEVEL) /* highest intensity? */ cp = ws_color_white(); /* use white */ else cp = colors[color][ilevel+1][ttl]; /* use next lvl */ } /* brand new */ else if (r + g + b >= 0xffff*3/3) /* light-ish? */ cp = colors[color][ilevel][ttl+1]; /* use previous TTL */ else cp = ws_color_black(); } colors[color][ilevel][ttl] = cp; } /* for each intensity level */ } /* for each TTL */ } /* phosphor_init */ static struct display * find_type(enum display_type type) { int i; struct display *dp; for (i = 0, dp = displays; i < ELEMENTS(displays); i++, dp++) if (dp->type == type) return dp; return NULL; } int display_init(enum display_type type, int sf) { static int init_failed = 0; struct display *dp; int half_life; int i; if (initialized) { /* cannot change type once started */ /* XXX say something???? */ return type == display_type; } if (init_failed) return 0; /* avoid thrashing */ init_failed = 1; /* assume the worst */ dp = find_type(type); if (!dp) { fprintf(stderr, "Unknown display type %d\r\n", (int)type); goto failed; } /* Initialize display list */ head->next = head->prev = head; display_type = type; scale = sf; xpoints = dp->xpoints; ypoints = dp->ypoints; /* increase scale factor if won't fit on desktop? */ xpixels = xpoints / scale; ypixels = ypoints / scale; /* set default pen radius now that scale is set */ display_lp_radius(PEN_RADIUS); ncolors = 1; /* * use function to calculate from looking at avg (max?) * of phosphor half lives??? */ #define COLOR_HALF_LIFE(C) ((C)->half_life) half_life = COLOR_HALF_LIFE(dp->color0); if (dp->color1) { if (dp->color1->half_life > half_life) half_life = COLOR_HALF_LIFE(dp->color1); ncolors++; } /* before phosphor_init; */ refresh_rate = (1000000*LEVELS_PER_HALFLIFE)/half_life; refresh_interval = 1000000/DELAY_UNIT/refresh_rate; /* * sanity check refresh_interval * calculating/selecting DELAY_UNIT at runtime might avoid this! */ /* must be non-zero; interval of 1 means all pixels will age at once! */ if (refresh_interval < 1) { /* decrease DELAY_UNIT? */ fprintf(stderr, "NOTE! refresh_interval too small: %d\r\n", refresh_interval); /* dunno if this is a good idea, but might be better than dying */ refresh_interval = 1; } /* point lifetime in DELAY_UNITs will not fit in p->delay field! */ if (refresh_interval > DELAY_T_MAX) { /* increase DELAY_UNIT? */ fprintf(stderr, "bad refresh_interval %d > DELAY_T_MAX %d\r\n", refresh_interval, DELAY_T_MAX); goto failed; } /* * before phosphor_init; * set up relative brightness of display intensity levels * (could differ for different hardware) * * linear for now. boost factor insures low intensities are visible */ #define BOOST 5 for (i = 0; i < NLEVELS; i++) level_scale[i] = ((float)i+1+BOOST)/(NLEVELS+BOOST); points = (struct point *)calloc((size_t)xpixels, ypixels * sizeof(struct point)); if (!points) goto failed; if (!ws_init(dp->name, xpixels, ypixels, ncolors)) goto failed; phosphor_init(dp->color0->phosphors, dp->color0->nphosphors, 0); if (dp->color1) phosphor_init(dp->color1->phosphors, dp->color1->nphosphors, 1); initialized = 1; init_failed = 0; /* hey, we made it! */ return 1; failed: fprintf(stderr, "Display initialization failed\r\n"); return 0; } void display_reset(void) { /* XXX tear down window? just clear it? */ } void display_sync(void) { ws_sync(); } void display_beep(void) { ws_beep(); } int display_xpoints(void) { return xpoints; } int display_ypoints(void) { return ypoints; } int display_scale(void) { return scale; } /* * handle keyboard events * * data switches; 18 -- enough for PDP-1/4/7/9/15 (for munching squares!) * 123 456 789 qwe rty uio * bit toggled on key up * all cleared on space * * spacewar switches; bit high as long as key down * asdf kl;' * just where PDP-1 spacewar expects them! * key mappings same as MIT Media Lab Java PDP-1 simulator * */ unsigned long spacewar_switches = 0; /* here from window system */ void display_keydown(int k) { switch (k) { case 'f': case 'F': spacewar_switches |= 01; break; /* torpedos */ case 'd': case 'D': spacewar_switches |= 02; break; /* engines */ case 'a': case 'A': spacewar_switches |= 04; break; /* rotate R */ case 's': case 'S': spacewar_switches |= 010; break; /* rotate L */ case '\'': case '"': spacewar_switches |= 040000; break; /* torpedos */ case ';': case ':': spacewar_switches |= 0100000; break; /* engines */ case 'k': case 'K': spacewar_switches |= 0200000; break; /* rotate R */ case 'l': case 'L': spacewar_switches |= 0400000; break; /* rotate L */ default: return; } } /* here from window system */ void display_keyup(int k) { unsigned long test_switches = cpu_get_switches(); /* fetch console switches from simulator? */ switch (k) { case 'f': case 'F': spacewar_switches &= ~01; return; case 'd': case 'D': spacewar_switches &= ~02; return; case 'a': case 'A': spacewar_switches &= ~04; return; case 's': case 'S': spacewar_switches &= ~010; return; case '\'': case '"': spacewar_switches &= ~040000; return; case ';': case ':': spacewar_switches &= ~0100000; return; case 'k': case 'K': spacewar_switches &= ~0200000; return; case 'l': case 'L': spacewar_switches &= ~0400000; return; case '1': test_switches ^= 1<<17; break; case '2': test_switches ^= 1<<16; break; case '3': test_switches ^= 1<<15; break; case '4': test_switches ^= 1<<14; break; case '5': test_switches ^= 1<<13; break; case '6': test_switches ^= 1<<12; break; case '7': test_switches ^= 1<<11; break; case '8': test_switches ^= 1<<10; break; case '9': test_switches ^= 1<<9; break; case 'q': case 'Q': test_switches ^= 1<<8; break; case 'w': case 'W': test_switches ^= 1<<7; break; case 'e': case 'E': test_switches ^= 1<<6; break; case 'r': case 'R': test_switches ^= 1<<5; break; case 't': case 'T': test_switches ^= 1<<4; break; case 'y': case 'Y': test_switches ^= 1<<3; break; case 'u': case 'U': test_switches ^= 1<<2; break; case 'i': case 'I': test_switches ^= 1<<1; break; case 'o': case 'O': test_switches ^= 1; break; case ' ': test_switches = 0; break; default: return; } cpu_set_switches(test_switches); }