diff --git a/display/gmakefile b/display/gmakefile index 4b5b9813..af89be1c 100644 --- a/display/gmakefile +++ b/display/gmakefile @@ -53,34 +53,34 @@ ALL: $(ALL) # munching squares; see README file for # how to use console switches -MUNCH=$(DRIVER) xy.o test.o +MUNCH=$(DRIVER) display.o test.o munch$(EXT): $(MUNCH) - $(CC) $(LDFLAGS) -o munch$(EXT) $(MUNCH) $(LIBS) + $(CC) $(LDFLAGS) -o munch$(EXT) $(MUNCH) $(LIBS) -VT11=$(DRIVER) vt11.o vttest.o xy.o +VT11=$(DRIVER) vt11.o vttest.o display.o vt11$(EXT): $(VT11) - $(CC) $(LDFLAGS) -o vt11$(EXT) $(VT11) $(LIBS) + $(CC) $(LDFLAGS) -o vt11$(EXT) $(VT11) $(LIBS) -xy.o: xy.h ws.h -vt11.o: xy.h vt11.h -x11.o: ws.h xy.h +display.o: display.h ws.h +vt11.o: display.h vt11.h +x11.o: ws.h display.h carbon.o: ws.h win32.o: ws.h -test.o: xy.h vt11.h -vttest.o: xy.h vt11.h vtmacs.h +test.o: display.h vt11.h +vttest.o: display.h vt11.h vtmacs.h clean: ifeq ($(WIN32),) - rm -f *.o *~ .#* + rm -f *.o *~ .#* else - if exist *.o del /q *.o - if exist *~ del /q *~ - if exist .#* del /q .#* + if exist *.o del /q *.o + if exist *~ del /q *~ + if exist .#* del /q .#* endif clobber: clean ifeq ($(WIN32),) - rm -f $(ALL) + rm -f $(ALL) else - if exist *.exe del /q *.exe + if exist *.exe del /q *.exe endif diff --git a/display/smakefile b/display/smakefile index 10d6f2cc..992ef7ec 100644 --- a/display/smakefile +++ b/display/smakefile @@ -64,28 +64,28 @@ ALL: $(ALL) # munching squares; see README file for # how to use console switches -MUNCH=$(DRIVER) xy.o test.o +MUNCH=$(DRIVER) display.o test.o munch$(EXT): $(MUNCH) - $(CC) $(LDFLAGS) -o munch$(EXT) $(MUNCH) $(LIBS) + $(CC) $(LDFLAGS) -o munch$(EXT) $(MUNCH) $(LIBS) -VT11=$(DRIVER) vt11.o vttest.o xy.o +VT11=$(DRIVER) vt11.o vttest.o display.o vt11$(EXT): $(VT11) - $(CC) $(LDFLAGS) -o vt11$(EXT) $(VT11) $(LIBS) + $(CC) $(LDFLAGS) -o vt11$(EXT) $(VT11) $(LIBS) -xy.o: xy.h ws.h -vt11.o: xy.h vt11.h -x11.o: ws.h xy.h +display.o: display.h ws.h +vt11.o: display.h vt11.h +x11.o: ws.h display.h carbon.o: ws.h win32.o: ws.h -test.o: xy.h vt11.h -vttest.o: xy.h vt11.h vtmacs.h +test.o: display.h vt11.h +vttest.o: display.h vt11.h vtmacs.h clean: - rm -f *.o *~ .#* # Unix + rm -f *.o *~ .#* # Unix # if exist *.o del /q *.o # Win32 # if exist *~ del /q *~ # Win32 # if exist .#* del /q .#* # Win32 clobber: clean - rm -f $(ALL) # Unix + rm -f $(ALL) # Unix # if exist *.exe del /q *.exe # Win32 diff --git a/display/test.c b/display/test.c index 1c3273f9..72943b84 100644 --- a/display/test.c +++ b/display/test.c @@ -11,7 +11,7 @@ */ /* - * Copyright (c) 2003-2004, Philip L. Budne + * Copyright (c) 2003-2018, 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"), @@ -57,18 +57,17 @@ static unsigned long test_switches = 0; /* called from display code: */ -unsigned long -cpu_get_switches(void) { - return test_switches; +void +cpu_get_switches(unsigned long *p1, unsigned long *p2) { + *p1 = test_switches; + *p2 = 0; } /* called from display code: */ void -cpu_set_switches(bits) - unsigned long bits; -{ - printf("switches: %06lo\n", bits); - test_switches = bits; +cpu_set_switches(unsigned long w1, unsigned long w2) { + test_switches = w1 ^ w2; + printf("switches: %06lo\n", test_switches); } void @@ -178,7 +177,7 @@ main(void) { if (!display_init(TEST_DIS, TEST_RES, NULL)) exit(EXIT_FAILURE); - cpu_set_switches(04000UL); /* classic starting value */ + cpu_set_switches(04000UL,0); /* classic starting value */ for (;;) { #ifdef T2 t2(); diff --git a/display/vttest.c b/display/vttest.c index 4922693d..1131f2cc 100644 --- a/display/vttest.c +++ b/display/vttest.c @@ -1380,13 +1380,13 @@ main(void) { /* * callbacks from display.c */ -unsigned long -cpu_get_switches(void) { - return 0; +void +cpu_get_switches(unsigned long *p1, unsigned long *p2) { + *p1 = *p2 = 0; } void -cpu_set_switches(unsigned long bits) { +cpu_set_switches(unsigned long w1, unsigned long w2) { } /* diff --git a/display/xy.c b/display/xy.c deleted file mode 100644 index 79ccf271..00000000 --- a/display/xy.c +++ /dev/null @@ -1,1036 +0,0 @@ -/* - * $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); -}