From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / m6502.c
1 /* -*- indent-tabs-mode:nil -*-
2  * Copyright (c) 2007 Jeremy English <jhe@jeremyenglish.org>
3  * 
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  * 
12  * Created: 07-May-2007 
13  */
14
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
18 #if defined(HAVE_STDINT_H)
19 #include <stdint.h> 
20 #elif defined(HAVE_INTTYPES_H)
21 #include <inttypes.h>
22 #endif
23 #include <string.h>
24 #include "screenhack.h"
25 #include "analogtv.h"
26 #include "asm6502.h"
27
28 # ifdef __GNUC__
29   __extension__  /* don't warn about "string length is greater than the length
30                     ISO C89 compilers are required to support" when includng
31                     the following data file... */
32 # endif
33 static const char * const demo_files[] = {
34 # include "m6502.h"
35 };
36
37
38 #ifndef HAVE_MOBILE
39 # define READ_FILES
40 #endif
41
42
43 /* We want to paint on a 32 by 32 grid of pixels. We will needed to
44    divided the screen up into chuncks */
45 enum {
46   SCREEN_W = ANALOGTV_VIS_LEN,
47   SCREEN_H = ANALOGTV_VISLINES,
48   NUM_PROGS = 9
49 };
50
51 struct state {
52   Display *dpy;
53   Window window;
54   
55   Bit8 pixels[32][32];
56
57   machine_6502 *machine;
58
59   analogtv *tv;
60   analogtv_input *inp;
61   analogtv_reception reception;
62   int pixw; /* pixel width */
63   int pixh;/* pixel height */
64   int topb;/* top boarder */
65   int field_ntsc[4];/* used for clearing the screen*/ 
66   int dt;/* how long to wait before changing the demo*/
67   int which;/* the program to run*/
68   int demos;/* number of demos included */
69   struct timeval start_time; 
70   int reset_p;
71 };
72
73 static void
74 plot6502(Bit8 x, Bit8 y, Bit8 color, void *closure)
75 {
76   struct state *st = (struct state *) closure;
77   st->pixels[x][y] = color;
78 }
79
80 #undef countof
81 #define countof(x) (sizeof((x))/sizeof((*x)))
82
83
84 static void 
85 start_rand_bin_prog(machine_6502 *machine, struct state *st){
86   int n = st->which;
87   while(n == st->which)
88     n = random() % st->demos;
89   st->which = n;
90   m6502_start_eval_string(machine, demo_files[st->which], plot6502, st);
91 }
92
93 \f
94 /*
95  * get_time ()
96  *
97  * returns the total time elapsed since the beginning of the demo
98  */
99 static double get_time(struct state *st) {
100   struct timeval t;
101   float f;
102 #if GETTIMEOFDAY_TWO_ARGS
103   gettimeofday(&t, NULL);
104 #else
105   gettimeofday(&t);
106 #endif
107   t.tv_sec -= st->start_time.tv_sec;
108   f = ((double)t.tv_sec) + t.tv_usec*1e-6;
109   return f;
110 }
111
112 /*
113  * init_time ()
114  *
115  * initialises the timing structures
116  */
117 static void init_time(struct state *st) {
118 #if GETTIMEOFDAY_TWO_ARGS
119   gettimeofday(&st->start_time, NULL);
120 #else
121   gettimeofday(&st->start_time);
122 #endif
123 }
124
125 static void *
126 m6502_init (Display *dpy, Window window)
127 {
128   struct state *st = (struct state *) calloc (1, sizeof(*st));
129   unsigned int x, y;
130   int n = get_integer_resource(dpy, "displaytime", "Displaytime");
131   int dh;
132   st->demos = countof(demo_files);
133   st->which = random() % st->demos;
134   st->dt = n;
135   st->dpy = dpy;
136   st->window = window;
137   st->tv=analogtv_allocate(st->dpy, st->window);
138   analogtv_set_defaults(st->tv, "");
139   
140   st->machine = m6502_build();
141   st->inp=analogtv_input_allocate();
142   analogtv_setup_sync(st->inp, 1, 0);
143   
144   st->reception.input = st->inp;
145   st->reception.level = 2.0;
146   st->reception.ofs=0;
147   
148   st->reception.multipath=0.0;
149   st->pixw = SCREEN_W / 32;
150   st->pixh = SCREEN_H / 32;
151   dh = SCREEN_H % 32;
152   st->topb = dh / 2;
153
154   init_time(st);
155   
156   {
157 #ifdef READ_FILES
158     char *s = get_string_resource (dpy, "file", "File");
159     if (strlen(s) > 0)
160       m6502_start_eval_file(st->machine,s, plot6502, st);
161   else
162 #endif
163     start_rand_bin_prog(st->machine,st);
164   }
165
166   analogtv_lcp_to_ntsc(ANALOGTV_BLACK_LEVEL, 0.0, 0.0, st->field_ntsc);
167
168   analogtv_draw_solid(st->inp,
169                       ANALOGTV_VIS_START, ANALOGTV_VIS_END,
170                       ANALOGTV_TOP, ANALOGTV_BOT,
171                       st->field_ntsc);
172
173   for(x = 0; x < 32; x++)
174     for(y = 0; y < 32; y++)
175       st->pixels[x][y] = 0;
176
177   return st;
178 }
179
180 static void
181 paint_pixel(struct state *st, int x, int y, int idx)
182 {
183   double clr_tbl[16][3] = {
184     {  0,   0,   0},
185     {255, 255, 255},
186     {136,   0,   0},
187     {170, 255, 238},
188     {204,  68, 204},
189     {  0, 204,  85},
190     {  0,   0, 170},
191     {238, 238, 119},
192     {221, 136,  85},
193     {102,  68,   0},
194     {255, 119, 119},
195     { 51,  51,  51},
196     {119, 119, 119},
197     {170, 255, 102},
198     {  0, 136, 255},
199     {187, 187, 187}
200   };
201   int ntsc[4], i;
202   int rawy,rawi,rawq;
203   /* RGB conversion taken from analogtv draw xpm */
204   rawy=( 5*clr_tbl[idx][0] + 11*clr_tbl[idx][1] + 2*clr_tbl[idx][2]) / 64;
205   rawi=(10*clr_tbl[idx][0] -  4*clr_tbl[idx][1] - 5*clr_tbl[idx][2]) / 64;
206   rawq=( 3*clr_tbl[idx][0] -  8*clr_tbl[idx][1] + 5*clr_tbl[idx][2]) / 64;
207
208   ntsc[0]=rawy+rawq;
209   ntsc[1]=rawy-rawi;
210   ntsc[2]=rawy-rawq;
211   ntsc[3]=rawy+rawi;
212
213   for (i=0; i<4; i++) {
214     if (ntsc[i]>ANALOGTV_WHITE_LEVEL) ntsc[i]=ANALOGTV_WHITE_LEVEL;
215     if (ntsc[i]<ANALOGTV_BLACK_LEVEL) ntsc[i]=ANALOGTV_BLACK_LEVEL;
216   }
217
218       
219   x *= st->pixw;
220   y *= st->pixh;
221   y += st->topb;
222   analogtv_draw_solid(st->inp,
223                       ANALOGTV_VIS_START + x, ANALOGTV_VIS_START + x + st->pixw,
224                       ANALOGTV_TOP + y, ANALOGTV_TOP + y + st->pixh, ntsc);                           
225 }
226
227 static unsigned long
228 m6502_draw (Display *dpy, Window window, void *closure)
229 {
230   struct state *st = (struct state *) closure;
231   unsigned int x = 0, y = 0;
232   double te;
233   const analogtv_reception *reception = &st->reception;
234
235   m6502_next_eval(st->machine,500);
236
237   for (x = 0; x < 32; x++)
238     for (y = 0; y < 32; y++)
239       paint_pixel(st,x,y,st->pixels[x][y]);
240   
241   analogtv_reception_update(&st->reception);
242   analogtv_draw(st->tv, 0.04, &reception, 1);
243   te = get_time(st);
244   
245   if (st->reset_p || te > st->dt){ /* do something more interesting here XXX */
246     st->reset_p = 0;
247     for(x = 0; x < 32; x++)
248       for(y = 0; y < 32; y++)
249         st->pixels[x][y] = 0;
250     init_time(st);
251     start_rand_bin_prog(st->machine,st);
252   }
253
254 #ifdef HAVE_MOBILE
255   return 0;
256 #else
257   return 5000;
258 #endif
259 }
260
261
262 \f
263
264 static const char *m6502_defaults [] = {
265   ".background:      black",
266   ".foreground:      white",
267   "*file:",
268   "*displaytime:     20",
269   ANALOGTV_DEFAULTS
270   0
271 };
272
273 static XrmOptionDescRec m6502_options [] = {
274   { "-file",           ".file",     XrmoptionSepArg, 0 },
275   { "-displaytime",    ".displaytime", XrmoptionSepArg, 0},
276   ANALOGTV_OPTIONS
277   { 0, 0, 0, 0 }
278 };
279
280 static void
281 m6502_reshape (Display *dpy, Window window, void *closure, 
282                  unsigned int w, unsigned int h)
283 {
284   struct state *st = (struct state *) closure;
285   analogtv_reconfigure (st->tv);
286 }
287
288 static Bool
289 m6502_event (Display *dpy, Window window, void *closure, XEvent *event)
290 {
291   struct state *st = (struct state *) closure;
292   if (screenhack_event_helper (dpy, window, event))
293     {
294       st->reset_p = 1;
295       return True;
296     }
297   return False;
298 }
299
300 static void
301 m6502_free (Display *dpy, Window window, void *closure)
302 {
303   struct state *st = (struct state *) closure;
304   analogtv_release(st->tv);
305   free (st);
306 }
307
308 XSCREENSAVER_MODULE ("m6502", m6502)