]> git.sur5r.net Git - cc65/blob - src/sim65/chips/console.c
Removed (pretty inconsistently used) tab chars from source code base.
[cc65] / src / sim65 / chips / console.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 console.c                                 */
4 /*                                                                           */
5 /*                  Console plugin for the sim65 simulator                   */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2003-2012, Ullrich von Bassewitz                                      */
10 /*                Roemerstrasse 52                                           */
11 /*                D-70794 Filderstadt                                        */
12 /* EMail:         uz@cc65.org                                                */
13 /*                                                                           */
14 /*                                                                           */
15 /* This software is provided 'as-is', without any expressed or implied       */
16 /* warranty.  In no event will the authors be held liable for any damages    */
17 /* arising from the use of this software.                                    */
18 /*                                                                           */
19 /* Permission is granted to anyone to use this software for any purpose,     */
20 /* including commercial applications, and to alter it and redistribute it    */
21 /* freely, subject to the following restrictions:                            */
22 /*                                                                           */
23 /* 1. The origin of this software must not be misrepresented; you must not   */
24 /*    claim that you wrote the original software. If you use this software   */
25 /*    in a product, an acknowledgment in the product documentation would be  */
26 /*    appreciated but is not required.                                       */
27 /* 2. Altered source versions must be plainly marked as such, and must not   */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or altered from any source              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <errno.h>
40
41 #include <X11/Xlib.h>
42 #include <X11/Xutil.h>
43 #include <X11/Xatom.h>
44 #include <X11/cursorfont.h>
45
46 /* common */
47 #include "attrib.h"
48
49 /* sim65 */
50 #include "chipif.h"
51
52
53
54 /*****************************************************************************/
55 /*                                   Forwards                                */
56 /*****************************************************************************/
57
58
59
60 static int ScreenInitChip (const struct SimData* Data);
61 /* Initialize the chip, return an error code */
62
63 static void* ScreenCreateInstance (unsigned Addr, unsigned Range, void* CfgInfo);
64 /* Create a new chip instance */
65
66 static void ScreenDestroyInstance (void* Data);
67 /* Destroy a chip instance */
68
69 static void ScreenWrite (void* Data, unsigned Offs, unsigned char Val);
70 /* Write user data */
71
72 static unsigned char ScreenRead (void* Data, unsigned Offs);
73 /* Read user data */
74
75 static void ScreenDrawBorder (void);
76 /* Draw the complete border */
77
78 static void ScreenDrawChar (unsigned Offs);
79 /* Draw one character at the given position */
80
81 static void ScreenDrawAllChars (void);
82 /* Redraw the complete interior screen */
83
84 static void ScreenEventLoop (void);
85 /* Get all waiting events and handle them */
86
87
88
89 /*****************************************************************************/
90 /*                                Global data                                */
91 /*****************************************************************************/
92
93
94
95 /* The SimData pointer we get when InitChip is called */
96 static const SimData* Sim;
97
98 /* Control data passed to the main program */
99 static const struct ChipData CData[] = {
100     {
101         "VIDEOSCREEN",          /* Name of the chip */
102         CHIPDATA_TYPE_CHIP,     /* Type of the chip */
103         CHIPDATA_VER_MAJOR,     /* Version information */
104         CHIPDATA_VER_MINOR,
105
106         /* -- Exported functions -- */
107         ScreenInitChip,
108         ScreenCreateInstance,
109         ScreenDestroyInstance,
110         ScreenWrite,
111         ScreenWrite,
112         ScreenRead,
113         ScreenRead
114     },
115 };
116
117 /* Defines for console screen */
118 static const XColor GreenColor = {
119     0,  32*256, 141*256, 32*256, 0, 0           /* green */
120 };
121 static const XColor AmberColor = {
122     0,  255*256, 204*256, 51*256, 0, 0          /* amber */
123 };
124 static const XColor WhiteColor = {
125     0,  224*256, 224*256, 224*256, 0, 0         /* white */
126 };
127 static const XColor BgColor = {
128     0,  0*256, 0*256, 0*256, 0, 0               /* black */
129 };
130
131
132 /*****************************************************************************/
133 /*                                     Data                                  */
134 /*****************************************************************************/
135
136
137
138 /* Screen instance data */
139 typedef struct ScreenInstance ScreenInstance;
140 struct ScreenInstance {
141
142     /* Settings passed from the simulator */
143     unsigned            Addr;           /* Address of the chip */
144     unsigned            Range;          /* Memory range */
145
146     /* X variables */
147     Display*            ScreenDisplay;
148     Window              ScreenWindow;
149     int                 Screen;
150     GC                  ScreenGC;
151
152     /* Windows rows and columns */
153     unsigned            Rows;
154     unsigned            Cols;
155
156     /* Window dimensions, determined by char resolution and char set */
157     unsigned            XTotal;
158     unsigned            YTotal;
159
160     /* Usable area within the window */
161     unsigned            XSize;
162     unsigned            YSize;
163
164     /* Offset of the usable area */
165     unsigned            XOffs;
166     unsigned            YOffs;
167
168     /* Character height */
169     unsigned            CharHeight;
170
171     /* Fore- and background color */
172     XColor              FgColor;
173     XColor              BgColor;
174
175     /* A list of 4 rectangles used to draw the border */
176     XRectangle          Border[4];
177
178     /* The virtual screen we are writing to. */
179     unsigned            MemSize;
180     unsigned char*      Mem;
181
182     /* The font data */
183     unsigned            FontDataSize;
184     unsigned char*      FontData;
185
186 };
187
188 /* If we have a video ram window, place it's instance data here */
189 static ScreenInstance* VScreen = 0;
190
191
192
193 /*****************************************************************************/
194 /*                               Exported function                           */
195 /*****************************************************************************/
196
197
198
199 int GetChipData (const ChipData** Data, unsigned* Count)
200 {
201     /* Pass the control structure to the caller */
202     *Data  = CData;
203     *Count = sizeof (CData) / sizeof (CData[0]);
204
205     /* Call was successful */
206     return 0;
207 }
208
209
210
211 /*****************************************************************************/
212 /*                             Helper functions                              */
213 /*****************************************************************************/
214
215
216
217 static long CfgGetNum (void* CfgInfo, const char* AttrName, long Min, long Max, long Def)
218 /* Read a number from the attributes. Check against Min/Max. Return the
219  * number or Def if it doesn't exist.
220  */
221 {
222     long Val;
223
224     /* Read the attribute if it does exist */
225     if (Sim->GetCfgNum (CfgInfo, AttrName, &Val)) {
226         /* Check it */
227         if (Val < Min || Val > Max) {
228             Sim->Error ("Range error for attribute `%s'", AttrName);
229         }
230
231         /* Return it */
232         return Val;
233
234     } else {
235
236         /* Return the default */
237         return Def;
238
239     }
240 }
241
242
243
244 /*****************************************************************************/
245 /*                              Console screen                               */
246 /*****************************************************************************/
247
248
249
250 static int ScreenInitChip (const struct SimData* Data)
251 /* Initialize the chip, return an error code */
252 {
253     /* Remember the pointer */
254     Sim = Data;
255
256     /* Always successful */
257     return 0;
258 }
259
260
261
262 static void* ScreenCreateInstance (unsigned Addr, unsigned Range, void* CfgInfo)
263 /* Create a new chip instance */
264 {
265     char*       Name;
266     FILE*       F;
267     unsigned    ColorDepth;
268     Colormap    CM;
269     XSizeHints  SizeHints;
270     XWMHints    WMHints;
271     Cursor      C;
272     unsigned    CharColor;
273
274
275     /* Allocate the instance data */
276     ScreenInstance* V = VScreen = Sim->Malloc (sizeof (ScreenInstance));
277
278     /* Remember a few settings */
279     V->Addr  = Addr;
280     V->Range = Range;
281
282     /* Character height is 8 or given as attribute */
283     V->CharHeight = (unsigned) CfgGetNum (CfgInfo, "charheight", 8, 16, 8);
284
285     /* Allocate memory for the font */
286     V->FontDataSize = V->CharHeight * 256;
287     V->FontData = Sim->Malloc (V->FontDataSize);
288
289     /* We must have a "fontdata" attribute. Get it. */
290     if (Sim->GetCfgStr (CfgInfo, "fontdata", &Name) == 0) {
291         /* Attribute not found */
292         Sim->Error ("Attribute `fontdata' missing");        /* ### */
293     }
294
295     /* Open the file with the given name */
296     F = fopen (Name, "rb");
297     if (F == 0) {
298         Sim->Error ("Cannot open `%s': %s", Name, strerror (errno));
299     }
300
301     /* Read the file into the memory */
302     if (fread (V->FontData, 1, V->FontDataSize, F) != V->FontDataSize) {
303         Sim->Warning ("Font data file `%s' seems to be corrupt", Name);
304     }
305
306     /* Close the file */
307     fclose (F);
308
309     /* Free the file name */
310     Sim->Free (Name);
311
312     /* Read screen rows and columns */
313     V->Rows = (unsigned) CfgGetNum (CfgInfo, "rows", 15, 75, 25);
314     V->Cols = (unsigned) CfgGetNum (CfgInfo, "cols", 32, 132, 80);
315
316     /* Allocate screen memory and clear it */
317     V->MemSize = V->Rows * V->Cols;
318     V->Mem = Sim->Malloc (V->MemSize);
319     memset (V->Mem, ' ', V->MemSize);
320
321     /* Setup the window geometry */
322     V->XSize  = V->Cols * 8;
323     V->YSize  = V->Rows * V->CharHeight;
324     V->XTotal = V->XSize + 20;
325     V->YTotal = V->YSize + 20;
326     V->XOffs  = (V->XTotal - V->XSize) / 2;
327     V->YOffs  = (V->YTotal - V->YSize) / 2;
328
329     /* Setup the rectanges used to draw the exterior */
330     V->Border[0].x      = 0;
331     V->Border[0].y      = 0;
332     V->Border[0].width  = V->XTotal;
333     V->Border[0].height = V->YOffs;
334     V->Border[1].x      = 0;
335     V->Border[1].y      = V->YOffs + V->YSize;
336     V->Border[1].width  = V->XTotal;
337     V->Border[1].height = V->YOffs;
338     V->Border[2].x      = 0;
339     V->Border[2].y      = V->YOffs;
340     V->Border[2].width  = V->XOffs;
341     V->Border[2].height = V->YSize;
342     V->Border[3].x      = V->XOffs + V->XSize;
343     V->Border[3].y      = V->YOffs;
344     V->Border[3].width  = V->XOffs;
345     V->Border[3].height = V->YSize;
346
347     /* Open the X display. */
348     V->ScreenDisplay = XOpenDisplay ("");
349     if (V->ScreenDisplay == NULL) {
350         Sim->Error ("Screen: Cannot open X display");
351     }
352
353     /* Get a screen */
354     V->Screen = DefaultScreen (V->ScreenDisplay);
355
356     /* Check the available colors. For now, we expect direct colors, so we
357      * will check for a color depth of at least 16.
358      */
359     ColorDepth = XDefaultDepth (V->ScreenDisplay, V->Screen);
360     if (ColorDepth < 16) {
361         /* OOPS */
362         Sim->Error ("Screen: Need color display");
363     }
364
365     /* Determine the character color */
366     CharColor = (unsigned) CfgGetNum (CfgInfo, "charcolor", 0, 2, 0);
367
368     /* Get all needed colors */
369     switch (CharColor) {
370         case 1:         V->FgColor = AmberColor;        break;
371         case 2:         V->FgColor = WhiteColor;        break;
372         default:        V->FgColor = GreenColor;        break;
373     }
374     V->BgColor = BgColor;
375     CM = DefaultColormap (V->ScreenDisplay, V->Screen);
376     if (XAllocColor (V->ScreenDisplay, CM, &V->FgColor) == 0) {
377         Sim->Error ("Screen: Cannot allocate foreground color");
378     }
379     if (XAllocColor (V->ScreenDisplay, CM, &V->BgColor) == 0) {
380         Sim->Error ("Screen: Cannot allocate background color");
381     }
382
383     /* Set up the size hints structure */
384     SizeHints.x          = 0;
385     SizeHints.y          = 0;
386     SizeHints.flags      = PPosition | PSize | PMinSize | PMaxSize | PResizeInc;
387     SizeHints.width      = V->XTotal;
388     SizeHints.height     = V->YTotal;
389     SizeHints.min_width  = V->XTotal;
390     SizeHints.min_height = V->YTotal;
391     SizeHints.max_width  = V->XTotal;
392     SizeHints.max_height = V->YTotal;
393     SizeHints.width_inc  = 0;
394     SizeHints.height_inc = 0;
395     WMHints.flags        = InputHint;
396     WMHints.input        = True;
397
398     /* Create the window */
399     V->ScreenWindow = XCreateSimpleWindow (V->ScreenDisplay,
400                                            DefaultRootWindow (V->ScreenDisplay),
401                                            SizeHints.x,
402                                            SizeHints.y,
403                                            SizeHints.width,
404                                            SizeHints.height,
405                                            5,
406                                            V->FgColor.pixel,
407                                            V->BgColor.pixel);
408
409     /* Set the standard window properties */
410     XSetStandardProperties (V->ScreenDisplay,           /* Display */
411                             V->ScreenWindow,            /* Window */
412                             "sim65 console screen",     /* Window name */
413                             "sim65 console screen",     /* Icon name */
414                             None,                       /* Icon Pixmap */
415                             0,                          /* argv */
416                             0,                          /* argc */
417                             &SizeHints);                /* Hints */
418     XSetWMHints (V->ScreenDisplay, V->ScreenWindow, &WMHints);
419
420     /* GC creation and initialization */
421     V->ScreenGC = XCreateGC (V->ScreenDisplay, V->ScreenWindow, 0, 0);
422
423     /* Set the cursor to show over the console window */
424     C = XCreateFontCursor (V->ScreenDisplay, XC_pirate);
425     XDefineCursor (V->ScreenDisplay, V->ScreenWindow, C);
426
427     /* Select input events */
428     XSelectInput (V->ScreenDisplay, V->ScreenWindow, ExposureMask | StructureNotifyMask | KeyPressMask);
429
430     /* Show the window */
431     XMapRaised (V->ScreenDisplay, V->ScreenWindow);
432
433     /* Handle events */
434     ScreenEventLoop ();
435
436     /* Return the instance data */
437     return V;
438 }
439
440
441
442 static void ScreenDestroyInstance (void* Data)
443 /* Destroy a chip instance */
444 {
445     /* Cast the data pointer */
446     ScreenInstance* V = Data;
447
448     /* Free X resources */
449     XUndefineCursor (V->ScreenDisplay, V->ScreenWindow);
450     XFreeGC (V->ScreenDisplay, V->ScreenGC);
451     XDestroyWindow (V->ScreenDisplay, V->ScreenWindow);
452     XCloseDisplay (V->ScreenDisplay);
453
454     /* Clear the global pointer */
455     VScreen = 0;
456
457     /* Free the instance data */
458     Sim->Free (V->FontData);
459     Sim->Free (V->Mem);
460     Sim->Free (V);
461 }
462
463
464
465 static void ScreenWrite (void* Data, unsigned Offs, unsigned char Val)
466 /* Write user data */
467 {
468     /* Cast the data pointer */
469     ScreenInstance* V = Data;
470
471     /* Check the offset */
472     if (Offs >= V->MemSize) {
473         Sim->Break ("Screen: Accessing invalid memory at $%06X", V->Addr + Offs);
474         return;
475     }
476
477     /* Write the value */
478     V->Mem[Offs] = Val;
479
480     /* Schedule a redraw */
481     ScreenDrawChar (Offs);
482
483     /* Call the event loop */
484     ScreenEventLoop ();
485 }
486
487
488
489 static unsigned char ScreenRead (void* Data, unsigned Offs)
490 /* Read user data */
491 {
492     /* Cast the data pointer */
493     ScreenInstance* V = Data;
494
495     /* Check the offset */
496     if (Offs >= sizeof (V->Mem)) {
497         Sim->Break ("Screen: Accessing invalid memory at $%06X", V->Addr + Offs);
498         return 0xFF;
499     } else {
500         return V->Mem[Offs];
501     }
502 }
503
504
505
506 static void ScreenDrawBorder (void)
507 /* Draw the complete border */
508 {
509     if (VScreen) {
510         /* Set the border color */
511         XSetForeground (VScreen->ScreenDisplay, VScreen->ScreenGC, VScreen->BgColor.pixel);
512
513         /* Fill all rectangles that make the border */
514         XFillRectangles (VScreen->ScreenDisplay, VScreen->ScreenWindow, VScreen->ScreenGC,
515                          VScreen->Border, sizeof (VScreen->Border) / sizeof (VScreen->Border[0]));
516     }
517 }
518
519
520
521 static void ScreenDrawChar (unsigned Offs)
522 /* Draw one character at the given position */
523 {
524     unsigned    Row, Col;
525     XPoint      Points[128];
526     unsigned    PCount;
527
528     /* Get the character from the video RAM */
529     unsigned char C = VScreen->Mem[Offs];
530
531     /* Calculate the offset for the character data in the character ROM */
532     unsigned char* D = VScreen->FontData + (C * VScreen->CharHeight);
533
534     /* Calculate the coords for the output */
535     unsigned X = VScreen->XOffs + (Offs % VScreen->Cols) * 8;
536     unsigned Y = VScreen->YOffs + (Offs / VScreen->Cols) * VScreen->CharHeight;
537
538     /* Clear the character area with the background color */
539     XSetForeground (VScreen->ScreenDisplay, VScreen->ScreenGC, VScreen->BgColor.pixel);
540     XFillRectangle (VScreen->ScreenDisplay, VScreen->ScreenWindow, VScreen->ScreenGC, X, Y, 8, VScreen->CharHeight);
541
542     /* Prepare the foreground pixels */
543     PCount = 0;
544     for (Row = 0; Row < VScreen->CharHeight; ++Row) {
545
546         /* Get next byte from char rom */
547         unsigned Data = *D++;
548
549         /* Make pixels from this byte */
550         for (Col = 0; Col < 8; ++Col) {
551             if (Data & 0x80) {
552                 /* Foreground pixel */
553                 Points[PCount].x = X + Col;
554                 Points[PCount].y = Y + Row;
555                 ++PCount;
556             }
557             Data <<= 1;
558         }
559     }
560     if (PCount) {
561         /* Set the character color */
562         XSetForeground (VScreen->ScreenDisplay, VScreen->ScreenGC, VScreen->FgColor.pixel);
563
564         /* Draw the pixels */
565         XDrawPoints (VScreen->ScreenDisplay, VScreen->ScreenWindow, VScreen->ScreenGC,
566                      Points, PCount, CoordModeOrigin);
567     }
568 }
569
570
571
572 static void ScreenDrawArea (unsigned X1, unsigned Y1, unsigned X2, unsigned Y2)
573 /* Update an area of the interior screen */
574 {
575     unsigned X, Y;
576
577     /* Check if we have to draw anything */
578     if (X2 < VScreen->XOffs || Y2 < VScreen->YOffs ||
579         X1 >= VScreen->XOffs + VScreen->XSize ||
580         Y1 >= VScreen->YOffs + VScreen->YSize) {
581         /* Completely outside */
582         return;
583     }
584
585     /* Make the coordinates relative to the interior */
586     X1 -= VScreen->XOffs;
587     Y1 -= VScreen->YOffs;
588     X2 -= VScreen->XOffs;
589     Y2 -= VScreen->YOffs;
590
591     /* Loop updating characters */
592     for (Y = Y1; Y <= Y2; Y += 8) {
593         for (X = X1; X <= X2; X += 8) {
594             ScreenDrawChar ((Y / 8) * 40 + (X / 8));
595         }
596     }
597 }
598
599
600
601 static void ScreenDrawAllChars (void)
602 /* Redraw the complete interior screen */
603 {
604     unsigned I;
605     for (I = 0; I < 25*40; ++I) {
606         ScreenDrawChar (I);
607     }
608 }
609
610
611
612 static void ScreenEventLoop (void)
613 /* Get all waiting events and handle them */
614 {
615     unsigned X1, Y1, X2, Y2;
616
617     /* Read input events */
618     while (XEventsQueued (VScreen->ScreenDisplay, QueuedAfterFlush) != 0) {
619
620         /* Read an event */
621         XEvent Event;
622         XNextEvent (VScreen->ScreenDisplay, &Event);
623
624         switch (Event.type) {
625
626             case Expose:
627                 /* Calculate the area to redraw, then update the screen */
628                 X1 = Event.xexpose.x;
629                 Y1 = Event.xexpose.y;
630                 X2 = Event.xexpose.x + Event.xexpose.width - 1;
631                 Y2 = Event.xexpose.y + Event.xexpose.height - 1;
632                 if (X1 < VScreen->XOffs || X2 > VScreen->XOffs + VScreen->XSize ||
633                     Y1 < VScreen->YOffs || Y2 > VScreen->YOffs + VScreen->YSize) {
634                     /* Update the border */
635                     ScreenDrawBorder ();
636                 }
637                 ScreenDrawArea (X1, Y1, X2, Y2);
638                 break;
639
640             case MappingNotify:
641                 XRefreshKeyboardMapping (&Event.xmapping);
642                 break;
643
644             case KeyPress:
645                 break;
646
647             default:
648                 /* Ignore anything else */
649                 break;
650
651         }
652     }
653
654     /* Flush the outgoing event queue */
655     XFlush (VScreen->ScreenDisplay);
656 }
657
658
659