]> git.sur5r.net Git - cc65/blob - src/sim65/chips/console.c
ad520c5d49df1aacbe494bf5910313526594469b
[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-2009, 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 FgColor = {
119     0,  32*256, 141*256, 32*256, 0, 0          /* green */
120 };
121 static const XColor BgColor = {
122     0,  0*256, 0*256, 0*256, 0, 0               /* black */
123 };
124
125
126 /*****************************************************************************/
127 /*                                     Data                                  */
128 /*****************************************************************************/
129
130
131
132 /* Screen instance data */
133 typedef struct ScreenInstance ScreenInstance;
134 struct ScreenInstance {
135
136     /* Settings passed from the simulator */
137     unsigned            Addr;           /* Address of the chip */
138     unsigned            Range;          /* Memory range */
139
140     /* X variables */
141     Display*            ScreenDisplay;
142     Window              ScreenWindow;
143     int                 Screen;
144     GC                  ScreenGC;
145
146     /* Windows rows and columns */
147     unsigned            Rows;
148     unsigned            Cols;
149
150     /* Window dimensions, 384*288 (PAL) */
151     unsigned            XTotal;
152     unsigned            YTotal;
153
154     /* Usable area within the window */
155     unsigned            XSize;
156     unsigned            YSize;
157
158     /* Offset of the usable area */
159     unsigned            XOffs;
160     unsigned            YOffs;
161
162     /* Character height */
163     unsigned            CharHeight;
164
165     /* Fore- and background color */
166     XColor              FgColor;
167     XColor              BgColor;
168
169     /* A list of 4 rectangles used to draw the border */
170     XRectangle          Border[4];
171
172     /* The virtual screen we are writing to. */
173     unsigned            MemSize;
174     unsigned char*      Mem;
175
176     /* The font data */
177     unsigned            FontDataSize;
178     unsigned char*      FontData;
179
180 };
181
182 /* If we have a video ram window, place it's instance data here */
183 static ScreenInstance* VScreen = 0;
184
185
186
187 /*****************************************************************************/
188 /*                               Exported function                           */
189 /*****************************************************************************/
190
191
192
193 int GetChipData (const ChipData** Data, unsigned* Count)
194 {
195     /* Pass the control structure to the caller */
196     *Data  = CData;
197     *Count = sizeof (CData) / sizeof (CData[0]);
198
199     /* Call was successful */
200     return 0;
201 }
202
203
204
205 /*****************************************************************************/
206 /*                             Helper functions                              */
207 /*****************************************************************************/
208
209
210
211 static long CfgGetNum (void* CfgInfo, const char* AttrName, long Min, long Max, long Def)
212 /* Read a number from the attributes. Check against Min/Max. Return the
213  * number or Def if it doesn't exist.
214  */
215 {
216     long Val;
217
218     /* Read the attribute if it does exist */
219     if (Sim->GetCfgNum (CfgInfo, AttrName, &Val)) {
220         /* Check it */
221         if (Val < Min || Val > Max) {
222             Sim->Error ("Range error for attribute `%s'", AttrName);
223         }
224
225         /* Return it */
226         return Val;
227
228     } else {
229
230         /* Return the default */
231         return Def;
232
233     }
234 }
235
236
237
238 /*****************************************************************************/
239 /*                              Console screen                               */
240 /*****************************************************************************/
241
242
243
244 static int ScreenInitChip (const struct SimData* Data)
245 /* Initialize the chip, return an error code */
246 {
247     /* Remember the pointer */
248     Sim = Data;
249
250     /* Always successful */
251     return 0;
252 }
253
254
255
256 static void* ScreenCreateInstance (unsigned Addr, unsigned Range, void* CfgInfo)
257 /* Create a new chip instance */
258 {
259     char*       Name;
260     FILE*       F;
261     unsigned    ColorDepth;
262     Colormap    CM;
263     XSizeHints  SizeHints;
264     XWMHints    WMHints;
265     Cursor      C;
266
267     /* Allocate the instance data */
268     ScreenInstance* V = VScreen = Sim->Malloc (sizeof (ScreenInstance));
269
270     /* Remember a few settings */
271     V->Addr  = Addr;
272     V->Range = Range;
273
274     /* Character height is 8 or given as attribute */
275     V->CharHeight = (unsigned) CfgGetNum (CfgInfo, "charheight", 8, 16, 8);
276
277     /* Allocate memory for the font */
278     V->FontDataSize = V->CharHeight * 256;
279     V->FontData = Sim->Malloc (V->FontDataSize);
280
281     /* We must have a "fontdata" attribute. Get it. */
282     if (Sim->GetCfgStr (CfgInfo, "fontdata", &Name) == 0) {
283         /* Attribute not found */
284         Sim->Error ("Attribute `fontdata' missing");        /* ### */
285     }
286
287     /* Open the file with the given name */
288     F = fopen (Name, "rb");
289     if (F == 0) {
290         Sim->Error ("Cannot open `%s': %s", Name, strerror (errno));
291     }
292
293     /* Read the file into the memory */
294     if (fread (V->FontData, 1, V->FontDataSize, F) != V->FontDataSize) {
295         Sim->Warning ("Font data file `%s' seems to be corrupt", Name);
296     }
297
298     /* Close the file */
299     fclose (F);
300
301     /* Free the file name */
302     Sim->Free (Name);
303
304     /* Read screen rows and columns */
305     V->Rows = (unsigned) CfgGetNum (CfgInfo, "rows", 15, 75, 25);
306     V->Cols = (unsigned) CfgGetNum (CfgInfo, "cols", 32, 132, 80);
307
308     /* Allocate screen memory and clear it */
309     V->MemSize = V->Rows * V->Cols;
310     V->Mem = Sim->Malloc (V->MemSize);
311     memset (V->Mem, ' ', V->MemSize);
312
313     /* Setup the window geometry */
314     V->XSize  = V->Cols * 8;
315     V->YSize  = V->Rows * V->CharHeight;
316     V->XTotal = V->XSize + 20;
317     V->YTotal = V->YSize + 20;
318     V->XOffs  = (V->XTotal - V->XSize) / 2;
319     V->YOffs  = (V->YTotal - V->YSize) / 2;
320
321     /* Setup the rectanges used to draw the exterior */
322     V->Border[0].x      = 0;
323     V->Border[0].y      = 0;
324     V->Border[0].width  = V->XTotal;
325     V->Border[0].height = V->YOffs;
326     V->Border[1].x      = 0;
327     V->Border[1].y      = V->YOffs + V->YSize;
328     V->Border[1].width  = V->XTotal;
329     V->Border[1].height = V->YOffs;
330     V->Border[2].x      = 0;
331     V->Border[2].y      = V->YOffs;
332     V->Border[2].width  = V->XOffs;
333     V->Border[2].height = V->YSize;
334     V->Border[3].x      = V->XOffs + V->XSize;
335     V->Border[3].y      = V->YOffs;
336     V->Border[3].width  = V->XOffs;
337     V->Border[3].height = V->YSize;
338
339     /* Open the X display. */
340     V->ScreenDisplay = XOpenDisplay ("");
341     if (V->ScreenDisplay == NULL) {
342         Sim->Error ("Screen: Cannot open X display");
343     }
344
345     /* Get a screen */
346     V->Screen = DefaultScreen (V->ScreenDisplay);
347
348     /* Check the available colors. For now, we expect direct colors, so we
349      * will check for a color depth of at least 16.
350      */
351     ColorDepth = XDefaultDepth (V->ScreenDisplay, V->Screen);
352     if (ColorDepth < 16) {
353         /* OOPS */
354         Sim->Error ("Screen: Need color display");
355     }
356
357     /* Get all needed colors */
358     V->FgColor = FgColor;
359     V->BgColor = BgColor;
360     CM = DefaultColormap (V->ScreenDisplay, V->Screen);
361     if (XAllocColor (V->ScreenDisplay, CM, &V->FgColor) == 0) {
362         Sim->Error ("Screen: Cannot allocate foreground color");
363     }
364     if (XAllocColor (V->ScreenDisplay, CM, &V->BgColor) == 0) {
365         Sim->Error ("Screen: Cannot allocate background color");
366     }
367
368     /* Set up the size hints structure */
369     SizeHints.x          = 0;
370     SizeHints.y          = 0;
371     SizeHints.flags      = PPosition | PSize | PMinSize | PMaxSize | PResizeInc;
372     SizeHints.width      = V->XTotal;
373     SizeHints.height     = V->YTotal;
374     SizeHints.min_width  = V->XTotal;
375     SizeHints.min_height = V->YTotal;
376     SizeHints.max_width  = V->XTotal;
377     SizeHints.max_height = V->YTotal;
378     SizeHints.width_inc  = 0;
379     SizeHints.height_inc = 0;
380     WMHints.flags        = InputHint;
381     WMHints.input        = True;
382
383     /* Create the window */
384     V->ScreenWindow = XCreateSimpleWindow (V->ScreenDisplay,
385                                            DefaultRootWindow (V->ScreenDisplay),
386                                            SizeHints.x,
387                                            SizeHints.y,
388                                            SizeHints.width,
389                                            SizeHints.height,
390                                            5,
391                                            V->FgColor.pixel,
392                                            V->BgColor.pixel);
393
394     /* Set the standard window properties */
395     XSetStandardProperties (V->ScreenDisplay,           /* Display */
396                             V->ScreenWindow,            /* Window */
397                             "sim65 console screen",     /* Window name */
398                             "sim65 console screen",     /* Icon name */
399                             None,                       /* Icon Pixmap */
400                             0,                          /* argv */
401                             0,                          /* argc */
402                             &SizeHints);                /* Hints */
403     XSetWMHints (V->ScreenDisplay, V->ScreenWindow, &WMHints);
404
405     /* GC creation and initialization */
406     V->ScreenGC = XCreateGC (V->ScreenDisplay, V->ScreenWindow, 0, 0);
407
408     /* Set the cursor to show over the console window */
409     C = XCreateFontCursor (V->ScreenDisplay, XC_pirate);
410     XDefineCursor (V->ScreenDisplay, V->ScreenWindow, C);
411
412     /* Select input events */
413     XSelectInput (V->ScreenDisplay, V->ScreenWindow, ExposureMask | StructureNotifyMask);
414
415     /* Show the window */
416     XMapRaised (V->ScreenDisplay, V->ScreenWindow);
417
418     /* Handle events */
419     ScreenEventLoop ();
420
421     /* Return the instance data */
422     return V;
423 }
424
425
426
427 static void ScreenDestroyInstance (void* Data)
428 /* Destroy a chip instance */
429 {
430     /* Cast the data pointer */
431     ScreenInstance* V = Data;
432
433     /* Free X resources */
434     XUndefineCursor (V->ScreenDisplay, V->ScreenWindow);
435     XFreeGC (V->ScreenDisplay, V->ScreenGC);
436     XDestroyWindow (V->ScreenDisplay, V->ScreenWindow);
437     XCloseDisplay (V->ScreenDisplay);
438
439     /* Clear the global pointer */
440     VScreen = 0;
441
442     /* Free the instance data */
443     Sim->Free (V->FontData);
444     Sim->Free (V->Mem);
445     Sim->Free (V);
446 }
447
448
449
450 static void ScreenWrite (void* Data, unsigned Offs, unsigned char Val)
451 /* Write user data */
452 {
453     /* Cast the data pointer */
454     ScreenInstance* V = Data;
455
456     /* Check the offset */
457     if (Offs >= V->MemSize) {
458         Sim->Break ("Screen: Accessing invalid memory at $%06X", V->Addr + Offs);
459         return;
460     }
461
462     /* Write the value */
463     V->Mem[Offs] = Val;
464
465     /* Schedule a redraw */
466     ScreenDrawChar (Offs);
467
468     /* Call the event loop */
469     ScreenEventLoop ();
470 }
471
472
473
474 static unsigned char ScreenRead (void* Data, unsigned Offs)
475 /* Read user data */
476 {
477     /* Cast the data pointer */
478     ScreenInstance* V = Data;
479
480     /* Check the offset */
481     if (Offs >= sizeof (V->Mem)) {
482         Sim->Break ("Screen: Accessing invalid memory at $%06X", V->Addr + Offs);
483         return 0xFF;
484     } else {
485         return V->Mem[Offs];
486     }
487 }
488
489
490
491 static void ScreenDrawBorder (void)
492 /* Draw the complete border */
493 {
494     if (VScreen) {
495         /* Set the border color */
496         XSetForeground (VScreen->ScreenDisplay, VScreen->ScreenGC, VScreen->BgColor.pixel);
497
498         /* Fill all rectangles that make the border */
499         XFillRectangles (VScreen->ScreenDisplay, VScreen->ScreenWindow, VScreen->ScreenGC,
500                          VScreen->Border, sizeof (VScreen->Border) / sizeof (VScreen->Border[0]));
501     }
502 }
503
504
505
506 static void ScreenDrawChar (unsigned Offs)
507 /* Draw one character at the given position */
508 {
509     unsigned    Row, Col;
510     XPoint      Points[128];
511     unsigned    PCount;
512
513     /* Get the character from the video RAM */
514     unsigned char C = VScreen->Mem[Offs];
515
516     /* Calculate the offset for the character data in the character ROM */
517     unsigned char* D = VScreen->FontData + (C * VScreen->CharHeight);
518
519     /* Calculate the coords for the output */
520     unsigned X = VScreen->XOffs + (Offs % VScreen->Cols) * 8;
521     unsigned Y = VScreen->YOffs + (Offs / VScreen->Cols) * VScreen->CharHeight;
522
523     /* Clear the character area with the background color */
524     XSetForeground (VScreen->ScreenDisplay, VScreen->ScreenGC, VScreen->BgColor.pixel);
525     XFillRectangle (VScreen->ScreenDisplay, VScreen->ScreenWindow, VScreen->ScreenGC, X, Y, 8, VScreen->CharHeight);
526
527     /* Prepare the foreground pixels */
528     PCount = 0;
529     for (Row = 0; Row < VScreen->CharHeight; ++Row) {
530
531         /* Get next byte from char rom */
532         unsigned Data = *D++;
533
534         /* Make pixels from this byte */
535         for (Col = 0; Col < 8; ++Col) {
536             if (Data & 0x80) {
537                 /* Foreground pixel */
538                 Points[PCount].x = X + Col;
539                 Points[PCount].y = Y + Row;
540                 ++PCount;
541             }
542             Data <<= 1;
543         }
544     }
545     if (PCount) {
546         /* Set the character color */
547         XSetForeground (VScreen->ScreenDisplay, VScreen->ScreenGC, VScreen->FgColor.pixel);
548
549         /* Draw the pixels */
550         XDrawPoints (VScreen->ScreenDisplay, VScreen->ScreenWindow, VScreen->ScreenGC,
551                      Points, PCount, CoordModeOrigin);
552     }
553 }
554
555
556
557 static void ScreenDrawArea (unsigned X1, unsigned Y1, unsigned X2, unsigned Y2)
558 /* Update an area of the interior screen */
559 {
560     unsigned X, Y;
561
562     /* Check if we have to draw anything */
563     if (X2 < VScreen->XOffs || Y2 < VScreen->YOffs ||
564         X1 >= VScreen->XOffs + VScreen->XSize ||
565         Y1 >= VScreen->YOffs + VScreen->YSize) {
566         /* Completely outside */
567         return;
568     }
569
570     /* Make the coordinates relative to the interior */
571     X1 -= VScreen->XOffs;
572     Y1 -= VScreen->YOffs;
573     X2 -= VScreen->XOffs;
574     Y2 -= VScreen->YOffs;
575
576     /* Loop updating characters */
577     for (Y = Y1; Y <= Y2; Y += 8) {
578         for (X = X1; X <= X2; X += 8) {
579             ScreenDrawChar ((Y / 8) * 40 + (X / 8));
580         }
581     }
582 }
583
584
585
586 static void ScreenDrawAllChars (void)
587 /* Redraw the complete interior screen */
588 {
589     unsigned I;
590     for (I = 0; I < 25*40; ++I) {
591         ScreenDrawChar (I);
592     }
593 }
594
595
596
597 static void ScreenEventLoop (void)
598 /* Get all waiting events and handle them */
599 {
600     unsigned X1, Y1, X2, Y2;
601
602     /* Read input events */
603     while (XEventsQueued (VScreen->ScreenDisplay, QueuedAfterFlush) != 0) {
604
605         /* Read an event */
606         XEvent Event;
607         XNextEvent (VScreen->ScreenDisplay, &Event);
608
609         switch (Event.type) {
610
611             case Expose:
612                 /* Calculate the area to redraw, then update the screen */
613                 X1 = Event.xexpose.x;
614                 Y1 = Event.xexpose.y;
615                 X2 = Event.xexpose.x + Event.xexpose.width - 1;
616                 Y2 = Event.xexpose.y + Event.xexpose.height - 1;
617                 if (X1 < VScreen->XOffs || X2 > VScreen->XOffs + VScreen->XSize ||
618                     Y1 < VScreen->YOffs || Y2 > VScreen->YOffs + VScreen->YSize) {
619                     /* Update the border */
620                     ScreenDrawBorder ();
621                 }
622                 ScreenDrawArea (X1, Y1, X2, Y2);
623                 break;
624
625             case MappingNotify:
626                 XRefreshKeyboardMapping (&Event.xmapping);
627                 break;
628
629         }
630     }
631
632     /* Flush the outgoing event queue */
633     XFlush (VScreen->ScreenDisplay);
634 }
635
636
637