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 FgColor = {
119 0, 32*256, 141*256, 32*256, 0, 0 /* green */
121 static const XColor BgColor = {
122 0, 0*256, 0*256, 0*256, 0, 0 /* black */
126 /*****************************************************************************/
128 /*****************************************************************************/
132 /* Screen instance data */
133 typedef struct ScreenInstance ScreenInstance;
134 struct ScreenInstance {
136 /* Settings passed from the simulator */
137 unsigned Addr; /* Address of the chip */
138 unsigned Range; /* Memory range */
141 Display* ScreenDisplay;
146 /* Windows rows and columns */
150 /* Window dimensions, 384*288 (PAL) */
154 /* Usable area within the window */
158 /* Offset of the usable area */
162 /* Character height */
165 /* Fore- and background color */
169 /* A list of 4 rectangles used to draw the border */
170 XRectangle Border[4];
172 /* The virtual screen we are writing to. */
177 unsigned FontDataSize;
178 unsigned char* FontData;
182 /* If we have a video ram window, place it's instance data here */
183 static ScreenInstance* VScreen = 0;
187 /*****************************************************************************/
188 /* Exported function */
189 /*****************************************************************************/
193 int GetChipData (const ChipData** Data, unsigned* Count)
195 /* Pass the control structure to the caller */
197 *Count = sizeof (CData) / sizeof (CData[0]);
199 /* Call was successful */
205 /*****************************************************************************/
206 /* Helper functions */
207 /*****************************************************************************/
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.
218 /* Read the attribute if it does exist */
219 if (Sim->GetCfgNum (CfgInfo, AttrName, &Val)) {
221 if (Val < Min || Val > Max) {
222 Sim->Error ("Range error for attribute `%s'", AttrName);
230 /* Return the default */
238 /*****************************************************************************/
240 /*****************************************************************************/
244 static int ScreenInitChip (const struct SimData* Data)
245 /* Initialize the chip, return an error code */
247 /* Remember the pointer */
250 /* Always successful */
256 static void* ScreenCreateInstance (unsigned Addr, unsigned Range, void* CfgInfo)
257 /* Create a new chip instance */
263 XSizeHints SizeHints;
267 /* Allocate the instance data */
268 ScreenInstance* V = VScreen = Sim->Malloc (sizeof (ScreenInstance));
270 /* Remember a few settings */
274 /* Character height is 8 or given as attribute */
275 V->CharHeight = (unsigned) CfgGetNum (CfgInfo, "charheight", 8, 16, 8);
277 /* Allocate memory for the font */
278 V->FontDataSize = V->CharHeight * 256;
279 V->FontData = Sim->Malloc (V->FontDataSize);
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"); /* ### */
287 /* Open the file with the given name */
288 F = fopen (Name, "rb");
290 Sim->Error ("Cannot open `%s': %s", Name, strerror (errno));
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);
301 /* Free the file name */
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);
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);
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;
321 /* Setup the rectanges used to draw the exterior */
324 V->Border[0].width = V->XTotal;
325 V->Border[0].height = V->YOffs;
327 V->Border[1].y = V->YOffs + V->YSize;
328 V->Border[1].width = V->XTotal;
329 V->Border[1].height = V->YOffs;
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;
339 /* Open the X display. */
340 V->ScreenDisplay = XOpenDisplay ("");
341 if (V->ScreenDisplay == NULL) {
342 Sim->Error ("Screen: Cannot open X display");
346 V->Screen = DefaultScreen (V->ScreenDisplay);
348 /* Check the available colors. For now, we expect direct colors, so we
349 * will check for a color depth of at least 16.
351 ColorDepth = XDefaultDepth (V->ScreenDisplay, V->Screen);
352 if (ColorDepth < 16) {
354 Sim->Error ("Screen: Need color display");
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");
364 if (XAllocColor (V->ScreenDisplay, CM, &V->BgColor) == 0) {
365 Sim->Error ("Screen: Cannot allocate background color");
368 /* Set up the size hints structure */
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;
383 /* Create the window */
384 V->ScreenWindow = XCreateSimpleWindow (V->ScreenDisplay,
385 DefaultRootWindow (V->ScreenDisplay),
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 */
402 &SizeHints); /* Hints */
403 XSetWMHints (V->ScreenDisplay, V->ScreenWindow, &WMHints);
405 /* GC creation and initialization */
406 V->ScreenGC = XCreateGC (V->ScreenDisplay, V->ScreenWindow, 0, 0);
408 /* Set the cursor to show over the console window */
409 C = XCreateFontCursor (V->ScreenDisplay, XC_pirate);
410 XDefineCursor (V->ScreenDisplay, V->ScreenWindow, C);
412 /* Select input events */
413 XSelectInput (V->ScreenDisplay, V->ScreenWindow, ExposureMask | StructureNotifyMask);
415 /* Show the window */
416 XMapRaised (V->ScreenDisplay, V->ScreenWindow);
421 /* Return the instance data */
427 static void ScreenDestroyInstance (void* Data)
428 /* Destroy a chip instance */
430 /* Cast the data pointer */
431 ScreenInstance* V = Data;
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);
439 /* Clear the global pointer */
442 /* Free the instance data */
443 Sim->Free (V->FontData);
450 static void ScreenWrite (void* Data, unsigned Offs, unsigned char Val)
451 /* Write user data */
453 /* Cast the data pointer */
454 ScreenInstance* V = Data;
456 /* Check the offset */
457 if (Offs >= V->MemSize) {
458 Sim->Break ("Screen: Accessing invalid memory at $%06X", V->Addr + Offs);
462 /* Write the value */
465 /* Schedule a redraw */
466 ScreenDrawChar (Offs);
468 /* Call the event loop */
474 static unsigned char ScreenRead (void* Data, unsigned Offs)
477 /* Cast the data pointer */
478 ScreenInstance* V = Data;
480 /* Check the offset */
481 if (Offs >= sizeof (V->Mem)) {
482 Sim->Break ("Screen: Accessing invalid memory at $%06X", V->Addr + Offs);
491 static void ScreenDrawBorder (void)
492 /* Draw the complete border */
495 /* Set the border color */
496 XSetForeground (VScreen->ScreenDisplay, VScreen->ScreenGC, VScreen->BgColor.pixel);
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]));
506 static void ScreenDrawChar (unsigned Offs)
507 /* Draw one character at the given position */
513 /* Get the character from the video RAM */
514 unsigned char C = VScreen->Mem[Offs];
516 /* Calculate the offset for the character data in the character ROM */
517 unsigned char* D = VScreen->FontData + (C * VScreen->CharHeight);
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;
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);
527 /* Prepare the foreground pixels */
529 for (Row = 0; Row < VScreen->CharHeight; ++Row) {
531 /* Get next byte from char rom */
532 unsigned Data = *D++;
534 /* Make pixels from this byte */
535 for (Col = 0; Col < 8; ++Col) {
537 /* Foreground pixel */
538 Points[PCount].x = X + Col;
539 Points[PCount].y = Y + Row;
546 /* Set the character color */
547 XSetForeground (VScreen->ScreenDisplay, VScreen->ScreenGC, VScreen->FgColor.pixel);
549 /* Draw the pixels */
550 XDrawPoints (VScreen->ScreenDisplay, VScreen->ScreenWindow, VScreen->ScreenGC,
551 Points, PCount, CoordModeOrigin);
557 static void ScreenDrawArea (unsigned X1, unsigned Y1, unsigned X2, unsigned Y2)
558 /* Update an area of the interior screen */
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 */
570 /* Make the coordinates relative to the interior */
571 X1 -= VScreen->XOffs;
572 Y1 -= VScreen->YOffs;
573 X2 -= VScreen->XOffs;
574 Y2 -= VScreen->YOffs;
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));
586 static void ScreenDrawAllChars (void)
587 /* Redraw the complete interior screen */
590 for (I = 0; I < 25*40; ++I) {
597 static void ScreenEventLoop (void)
598 /* Get all waiting events and handle them */
600 unsigned X1, Y1, X2, Y2;
602 /* Read input events */
603 while (XEventsQueued (VScreen->ScreenDisplay, QueuedAfterFlush) != 0) {
607 XNextEvent (VScreen->ScreenDisplay, &Event);
609 switch (Event.type) {
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 */
622 ScreenDrawArea (X1, Y1, X2, Y2);
626 XRefreshKeyboardMapping (&Event.xmapping);
632 /* Flush the outgoing event queue */
633 XFlush (VScreen->ScreenDisplay);