1 /*****************************************************************************/
5 /* Console plugin for the sim65 simulator */
9 /* (C) 2003-2009, Ullrich von Bassewitz */
10 /* Roemerstrasse 52 */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
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. */
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: */
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 */
32 /*****************************************************************************/
42 #include <X11/Xutil.h>
43 #include <X11/Xatom.h>
44 #include <X11/cursorfont.h>
54 /*****************************************************************************/
56 /*****************************************************************************/
60 static int ScreenInitChip (const struct SimData* Data);
61 /* Initialize the chip, return an error code */
63 static void* ScreenCreateInstance (unsigned Addr, unsigned Range, void* CfgInfo);
64 /* Create a new chip instance */
66 static void ScreenDestroyInstance (void* Data);
67 /* Destroy a chip instance */
69 static void ScreenWrite (void* Data, unsigned Offs, unsigned char Val);
72 static unsigned char ScreenRead (void* Data, unsigned Offs);
75 static void ScreenDrawBorder (void);
76 /* Draw the complete border */
78 static void ScreenDrawChar (unsigned Offs);
79 /* Draw one character at the given position */
81 static void ScreenDrawAllChars (void);
82 /* Redraw the complete interior screen */
84 static void ScreenEventLoop (void);
85 /* Get all waiting events and handle them */
89 /*****************************************************************************/
91 /*****************************************************************************/
95 /* The SimData pointer we get when InitChip is called */
96 static const SimData* Sim;
98 /* Control data passed to the main program */
99 static const struct ChipData CData[] = {
101 "VIDEOSCREEN", /* Name of the chip */
102 CHIPDATA_TYPE_CHIP, /* Type of the chip */
103 CHIPDATA_VER_MAJOR, /* Version information */
106 /* -- Exported functions -- */
108 ScreenCreateInstance,
109 ScreenDestroyInstance,
117 /* Defines for console screen */
118 static const XColor GreenColor = {
119 0, 32*256, 141*256, 32*256, 0, 0 /* green */
121 static const XColor AmberColor = {
122 0, 255*256, 204*256, 51*256, 0, 0 /* amber */
124 static const XColor WhiteColor = {
125 0, 224*256, 224*256, 224*256, 0, 0 /* white */
127 static const XColor BgColor = {
128 0, 0*256, 0*256, 0*256, 0, 0 /* black */
132 /*****************************************************************************/
134 /*****************************************************************************/
138 /* Screen instance data */
139 typedef struct ScreenInstance ScreenInstance;
140 struct ScreenInstance {
142 /* Settings passed from the simulator */
143 unsigned Addr; /* Address of the chip */
144 unsigned Range; /* Memory range */
147 Display* ScreenDisplay;
152 /* Windows rows and columns */
156 /* Window dimensions, determined by char resolution and char set */
160 /* Usable area within the window */
164 /* Offset of the usable area */
168 /* Character height */
171 /* Fore- and background color */
175 /* A list of 4 rectangles used to draw the border */
176 XRectangle Border[4];
178 /* The virtual screen we are writing to. */
183 unsigned FontDataSize;
184 unsigned char* FontData;
188 /* If we have a video ram window, place it's instance data here */
189 static ScreenInstance* VScreen = 0;
193 /*****************************************************************************/
194 /* Exported function */
195 /*****************************************************************************/
199 int GetChipData (const ChipData** Data, unsigned* Count)
201 /* Pass the control structure to the caller */
203 *Count = sizeof (CData) / sizeof (CData[0]);
205 /* Call was successful */
211 /*****************************************************************************/
212 /* Helper functions */
213 /*****************************************************************************/
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.
224 /* Read the attribute if it does exist */
225 if (Sim->GetCfgNum (CfgInfo, AttrName, &Val)) {
227 if (Val < Min || Val > Max) {
228 Sim->Error ("Range error for attribute `%s'", AttrName);
236 /* Return the default */
244 /*****************************************************************************/
246 /*****************************************************************************/
250 static int ScreenInitChip (const struct SimData* Data)
251 /* Initialize the chip, return an error code */
253 /* Remember the pointer */
256 /* Always successful */
262 static void* ScreenCreateInstance (unsigned Addr, unsigned Range, void* CfgInfo)
263 /* Create a new chip instance */
269 XSizeHints SizeHints;
275 /* Allocate the instance data */
276 ScreenInstance* V = VScreen = Sim->Malloc (sizeof (ScreenInstance));
278 /* Remember a few settings */
282 /* Character height is 8 or given as attribute */
283 V->CharHeight = (unsigned) CfgGetNum (CfgInfo, "charheight", 8, 16, 8);
285 /* Allocate memory for the font */
286 V->FontDataSize = V->CharHeight * 256;
287 V->FontData = Sim->Malloc (V->FontDataSize);
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"); /* ### */
295 /* Open the file with the given name */
296 F = fopen (Name, "rb");
298 Sim->Error ("Cannot open `%s': %s", Name, strerror (errno));
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);
309 /* Free the file name */
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);
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);
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;
329 /* Setup the rectanges used to draw the exterior */
332 V->Border[0].width = V->XTotal;
333 V->Border[0].height = V->YOffs;
335 V->Border[1].y = V->YOffs + V->YSize;
336 V->Border[1].width = V->XTotal;
337 V->Border[1].height = V->YOffs;
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;
347 /* Open the X display. */
348 V->ScreenDisplay = XOpenDisplay ("");
349 if (V->ScreenDisplay == NULL) {
350 Sim->Error ("Screen: Cannot open X display");
354 V->Screen = DefaultScreen (V->ScreenDisplay);
356 /* Check the available colors. For now, we expect direct colors, so we
357 * will check for a color depth of at least 16.
359 ColorDepth = XDefaultDepth (V->ScreenDisplay, V->Screen);
360 if (ColorDepth < 16) {
362 Sim->Error ("Screen: Need color display");
365 /* Determine the character color */
366 CharColor = (unsigned) CfgGetNum (CfgInfo, "charcolor", 0, 2, 0);
368 /* Get all needed colors */
370 case 1: V->FgColor = AmberColor; break;
371 case 2: V->FgColor = WhiteColor; break;
372 default: V->FgColor = GreenColor; break;
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");
379 if (XAllocColor (V->ScreenDisplay, CM, &V->BgColor) == 0) {
380 Sim->Error ("Screen: Cannot allocate background color");
383 /* Set up the size hints structure */
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;
398 /* Create the window */
399 V->ScreenWindow = XCreateSimpleWindow (V->ScreenDisplay,
400 DefaultRootWindow (V->ScreenDisplay),
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 */
417 &SizeHints); /* Hints */
418 XSetWMHints (V->ScreenDisplay, V->ScreenWindow, &WMHints);
420 /* GC creation and initialization */
421 V->ScreenGC = XCreateGC (V->ScreenDisplay, V->ScreenWindow, 0, 0);
423 /* Set the cursor to show over the console window */
424 C = XCreateFontCursor (V->ScreenDisplay, XC_pirate);
425 XDefineCursor (V->ScreenDisplay, V->ScreenWindow, C);
427 /* Select input events */
428 XSelectInput (V->ScreenDisplay, V->ScreenWindow, ExposureMask | StructureNotifyMask | KeyPressMask);
430 /* Show the window */
431 XMapRaised (V->ScreenDisplay, V->ScreenWindow);
436 /* Return the instance data */
442 static void ScreenDestroyInstance (void* Data)
443 /* Destroy a chip instance */
445 /* Cast the data pointer */
446 ScreenInstance* V = Data;
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);
454 /* Clear the global pointer */
457 /* Free the instance data */
458 Sim->Free (V->FontData);
465 static void ScreenWrite (void* Data, unsigned Offs, unsigned char Val)
466 /* Write user data */
468 /* Cast the data pointer */
469 ScreenInstance* V = Data;
471 /* Check the offset */
472 if (Offs >= V->MemSize) {
473 Sim->Break ("Screen: Accessing invalid memory at $%06X", V->Addr + Offs);
477 /* Write the value */
480 /* Schedule a redraw */
481 ScreenDrawChar (Offs);
483 /* Call the event loop */
489 static unsigned char ScreenRead (void* Data, unsigned Offs)
492 /* Cast the data pointer */
493 ScreenInstance* V = Data;
495 /* Check the offset */
496 if (Offs >= sizeof (V->Mem)) {
497 Sim->Break ("Screen: Accessing invalid memory at $%06X", V->Addr + Offs);
506 static void ScreenDrawBorder (void)
507 /* Draw the complete border */
510 /* Set the border color */
511 XSetForeground (VScreen->ScreenDisplay, VScreen->ScreenGC, VScreen->BgColor.pixel);
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]));
521 static void ScreenDrawChar (unsigned Offs)
522 /* Draw one character at the given position */
528 /* Get the character from the video RAM */
529 unsigned char C = VScreen->Mem[Offs];
531 /* Calculate the offset for the character data in the character ROM */
532 unsigned char* D = VScreen->FontData + (C * VScreen->CharHeight);
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;
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);
542 /* Prepare the foreground pixels */
544 for (Row = 0; Row < VScreen->CharHeight; ++Row) {
546 /* Get next byte from char rom */
547 unsigned Data = *D++;
549 /* Make pixels from this byte */
550 for (Col = 0; Col < 8; ++Col) {
552 /* Foreground pixel */
553 Points[PCount].x = X + Col;
554 Points[PCount].y = Y + Row;
561 /* Set the character color */
562 XSetForeground (VScreen->ScreenDisplay, VScreen->ScreenGC, VScreen->FgColor.pixel);
564 /* Draw the pixels */
565 XDrawPoints (VScreen->ScreenDisplay, VScreen->ScreenWindow, VScreen->ScreenGC,
566 Points, PCount, CoordModeOrigin);
572 static void ScreenDrawArea (unsigned X1, unsigned Y1, unsigned X2, unsigned Y2)
573 /* Update an area of the interior screen */
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 */
585 /* Make the coordinates relative to the interior */
586 X1 -= VScreen->XOffs;
587 Y1 -= VScreen->YOffs;
588 X2 -= VScreen->XOffs;
589 Y2 -= VScreen->YOffs;
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));
601 static void ScreenDrawAllChars (void)
602 /* Redraw the complete interior screen */
605 for (I = 0; I < 25*40; ++I) {
612 static void ScreenEventLoop (void)
613 /* Get all waiting events and handle them */
615 unsigned X1, Y1, X2, Y2;
617 /* Read input events */
618 while (XEventsQueued (VScreen->ScreenDisplay, QueuedAfterFlush) != 0) {
622 XNextEvent (VScreen->ScreenDisplay, &Event);
624 switch (Event.type) {
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 */
637 ScreenDrawArea (X1, Y1, X2, Y2);
641 XRefreshKeyboardMapping (&Event.xmapping);
648 /* Ignore anything else */
654 /* Flush the outgoing event queue */
655 XFlush (VScreen->ScreenDisplay);