/*****************************************************************************/ /* */ /* console.c */ /* */ /* Console plugin for the sim65 simulator */ /* */ /* */ /* */ /* (C) 2003-2009, Ullrich von Bassewitz */ /* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ /* warranty. In no event will the authors be held liable for any damages */ /* arising from the use of this software. */ /* */ /* Permission is granted to anyone to use this software for any purpose, */ /* including commercial applications, and to alter it and redistribute it */ /* freely, subject to the following restrictions: */ /* */ /* 1. The origin of this software must not be misrepresented; you must not */ /* claim that you wrote the original software. If you use this software */ /* in a product, an acknowledgment in the product documentation would be */ /* appreciated but is not required. */ /* 2. Altered source versions must be plainly marked as such, and must not */ /* be misrepresented as being the original software. */ /* 3. This notice may not be removed or altered from any source */ /* distribution. */ /* */ /*****************************************************************************/ #include #include #include #include #include #include #include #include /* common */ #include "attrib.h" /* sim65 */ #include "chipif.h" /*****************************************************************************/ /* Forwards */ /*****************************************************************************/ static int ScreenInitChip (const struct SimData* Data); /* Initialize the chip, return an error code */ static void* ScreenCreateInstance (unsigned Addr, unsigned Range, void* CfgInfo); /* Create a new chip instance */ static void ScreenDestroyInstance (void* Data); /* Destroy a chip instance */ static void ScreenWrite (void* Data, unsigned Offs, unsigned char Val); /* Write user data */ static unsigned char ScreenRead (void* Data, unsigned Offs); /* Read user data */ static void ScreenDrawBorder (void); /* Draw the complete border */ static void ScreenDrawChar (unsigned Offs); /* Draw one character at the given position */ static void ScreenDrawAllChars (void); /* Redraw the complete interior screen */ static void ScreenEventLoop (void); /* Get all waiting events and handle them */ /*****************************************************************************/ /* Global data */ /*****************************************************************************/ /* The SimData pointer we get when InitChip is called */ static const SimData* Sim; /* Control data passed to the main program */ static const struct ChipData CData[] = { { "VIDEOSCREEN", /* Name of the chip */ CHIPDATA_TYPE_CHIP, /* Type of the chip */ CHIPDATA_VER_MAJOR, /* Version information */ CHIPDATA_VER_MINOR, /* -- Exported functions -- */ ScreenInitChip, ScreenCreateInstance, ScreenDestroyInstance, ScreenWrite, ScreenWrite, ScreenRead, ScreenRead }, }; /* Defines for console screen */ static const XColor FgColor = { 0, 32*256, 141*256, 32*256, 0, 0 /* green */ }; static const XColor BgColor = { 0, 0*256, 0*256, 0*256, 0, 0 /* black */ }; /*****************************************************************************/ /* Data */ /*****************************************************************************/ /* Screen instance data */ typedef struct ScreenInstance ScreenInstance; struct ScreenInstance { /* Settings passed from the simulator */ unsigned Addr; /* Address of the chip */ unsigned Range; /* Memory range */ /* X variables */ Display* ScreenDisplay; Window ScreenWindow; int Screen; GC ScreenGC; /* Windows rows and columns */ unsigned Rows; unsigned Cols; /* Window dimensions, 384*288 (PAL) */ unsigned XTotal; unsigned YTotal; /* Usable area within the window */ unsigned XSize; unsigned YSize; /* Offset of the usable area */ unsigned XOffs; unsigned YOffs; /* Character height */ unsigned CharHeight; /* Fore- and background color */ XColor FgColor; XColor BgColor; /* A list of 4 rectangles used to draw the border */ XRectangle Border[4]; /* The virtual screen we are writing to. */ unsigned MemSize; unsigned char* Mem; /* The font data */ unsigned FontDataSize; unsigned char* FontData; }; /* If we have a video ram window, place it's instance data here */ static ScreenInstance* VScreen = 0; /*****************************************************************************/ /* Exported function */ /*****************************************************************************/ int GetChipData (const ChipData** Data, unsigned* Count) { /* Pass the control structure to the caller */ *Data = CData; *Count = sizeof (CData) / sizeof (CData[0]); /* Call was successful */ return 0; } /*****************************************************************************/ /* Helper functions */ /*****************************************************************************/ static long CfgGetNum (void* CfgInfo, const char* AttrName, long Min, long Max, long Def) /* Read a number from the attributes. Check against Min/Max. Return the * number or Def if it doesn't exist. */ { long Val; /* Read the attribute if it does exist */ if (Sim->GetCfgNum (CfgInfo, AttrName, &Val)) { /* Check it */ if (Val < Min || Val > Max) { Sim->Error ("Range error for attribute `%s'", AttrName); } /* Return it */ return Val; } else { /* Return the default */ return Def; } } /*****************************************************************************/ /* Console screen */ /*****************************************************************************/ static int ScreenInitChip (const struct SimData* Data) /* Initialize the chip, return an error code */ { /* Remember the pointer */ Sim = Data; /* Always successful */ return 0; } static void* ScreenCreateInstance (unsigned Addr, unsigned Range, void* CfgInfo) /* Create a new chip instance */ { char* Name; FILE* F; unsigned ColorDepth; Colormap CM; XSizeHints SizeHints; XWMHints WMHints; Cursor C; /* Allocate the instance data */ ScreenInstance* V = VScreen = Sim->Malloc (sizeof (ScreenInstance)); /* Remember a few settings */ V->Addr = Addr; V->Range = Range; /* Character height is 8 or given as attribute */ V->CharHeight = (unsigned) CfgGetNum (CfgInfo, "charheight", 8, 16, 8); /* Allocate memory for the font */ V->FontDataSize = V->CharHeight * 256; V->FontData = Sim->Malloc (V->FontDataSize); /* We must have a "fontdata" attribute. Get it. */ if (Sim->GetCfgStr (CfgInfo, "fontdata", &Name) == 0) { /* Attribute not found */ Sim->Error ("Attribute `fontdata' missing"); /* ### */ } /* Open the file with the given name */ F = fopen (Name, "rb"); if (F == 0) { Sim->Error ("Cannot open `%s': %s", Name, strerror (errno)); } /* Read the file into the memory */ if (fread (V->FontData, 1, V->FontDataSize, F) != V->FontDataSize) { Sim->Warning ("Font data file `%s' seems to be corrupt", Name); } /* Close the file */ fclose (F); /* Free the file name */ Sim->Free (Name); /* Read screen rows and columns */ V->Rows = (unsigned) CfgGetNum (CfgInfo, "rows", 15, 75, 25); V->Cols = (unsigned) CfgGetNum (CfgInfo, "cols", 32, 132, 80); /* Allocate screen memory and clear it */ V->MemSize = V->Rows * V->Cols; V->Mem = Sim->Malloc (V->MemSize); memset (V->Mem, ' ', V->MemSize); /* Setup the window geometry */ V->XSize = V->Cols * 8; V->YSize = V->Rows * V->CharHeight; V->XTotal = V->XSize + 20; V->YTotal = V->YSize + 20; V->XOffs = (V->XTotal - V->XSize) / 2; V->YOffs = (V->YTotal - V->YSize) / 2; /* Setup the rectanges used to draw the exterior */ V->Border[0].x = 0; V->Border[0].y = 0; V->Border[0].width = V->XTotal; V->Border[0].height = V->YOffs; V->Border[1].x = 0; V->Border[1].y = V->YOffs + V->YSize; V->Border[1].width = V->XTotal; V->Border[1].height = V->YOffs; V->Border[2].x = 0; V->Border[2].y = V->YOffs; V->Border[2].width = V->XOffs; V->Border[2].height = V->YSize; V->Border[3].x = V->XOffs + V->XSize; V->Border[3].y = V->YOffs; V->Border[3].width = V->XOffs; V->Border[3].height = V->YSize; /* Open the X display. */ V->ScreenDisplay = XOpenDisplay (""); if (V->ScreenDisplay == NULL) { Sim->Error ("Screen: Cannot open X display"); } /* Get a screen */ V->Screen = DefaultScreen (V->ScreenDisplay); /* Check the available colors. For now, we expect direct colors, so we * will check for a color depth of at least 16. */ ColorDepth = XDefaultDepth (V->ScreenDisplay, V->Screen); if (ColorDepth < 16) { /* OOPS */ Sim->Error ("Screen: Need color display"); } /* Get all needed colors */ V->FgColor = FgColor; V->BgColor = BgColor; CM = DefaultColormap (V->ScreenDisplay, V->Screen); if (XAllocColor (V->ScreenDisplay, CM, &V->FgColor) == 0) { Sim->Error ("Screen: Cannot allocate foreground color"); } if (XAllocColor (V->ScreenDisplay, CM, &V->BgColor) == 0) { Sim->Error ("Screen: Cannot allocate background color"); } /* Set up the size hints structure */ SizeHints.x = 0; SizeHints.y = 0; SizeHints.flags = PPosition | PSize | PMinSize | PMaxSize | PResizeInc; SizeHints.width = V->XTotal; SizeHints.height = V->YTotal; SizeHints.min_width = V->XTotal; SizeHints.min_height = V->YTotal; SizeHints.max_width = V->XTotal; SizeHints.max_height = V->YTotal; SizeHints.width_inc = 0; SizeHints.height_inc = 0; WMHints.flags = InputHint; WMHints.input = True; /* Create the window */ V->ScreenWindow = XCreateSimpleWindow (V->ScreenDisplay, DefaultRootWindow (V->ScreenDisplay), SizeHints.x, SizeHints.y, SizeHints.width, SizeHints.height, 5, V->FgColor.pixel, V->BgColor.pixel); /* Set the standard window properties */ XSetStandardProperties (V->ScreenDisplay, /* Display */ V->ScreenWindow, /* Window */ "sim65 console screen", /* Window name */ "sim65 console screen", /* Icon name */ None, /* Icon Pixmap */ 0, /* argv */ 0, /* argc */ &SizeHints); /* Hints */ XSetWMHints (V->ScreenDisplay, V->ScreenWindow, &WMHints); /* GC creation and initialization */ V->ScreenGC = XCreateGC (V->ScreenDisplay, V->ScreenWindow, 0, 0); /* Set the cursor to show over the console window */ C = XCreateFontCursor (V->ScreenDisplay, XC_pirate); XDefineCursor (V->ScreenDisplay, V->ScreenWindow, C); /* Select input events */ XSelectInput (V->ScreenDisplay, V->ScreenWindow, ExposureMask | StructureNotifyMask); /* Show the window */ XMapRaised (V->ScreenDisplay, V->ScreenWindow); /* Handle events */ ScreenEventLoop (); /* Return the instance data */ return V; } static void ScreenDestroyInstance (void* Data) /* Destroy a chip instance */ { /* Cast the data pointer */ ScreenInstance* V = Data; /* Free X resources */ XUndefineCursor (V->ScreenDisplay, V->ScreenWindow); XFreeGC (V->ScreenDisplay, V->ScreenGC); XDestroyWindow (V->ScreenDisplay, V->ScreenWindow); XCloseDisplay (V->ScreenDisplay); /* Clear the global pointer */ VScreen = 0; /* Free the instance data */ Sim->Free (V->FontData); Sim->Free (V->Mem); Sim->Free (V); } static void ScreenWrite (void* Data, unsigned Offs, unsigned char Val) /* Write user data */ { /* Cast the data pointer */ ScreenInstance* V = Data; /* Check the offset */ if (Offs >= V->MemSize) { Sim->Break ("Screen: Accessing invalid memory at $%06X", V->Addr + Offs); return; } /* Write the value */ V->Mem[Offs] = Val; /* Schedule a redraw */ ScreenDrawChar (Offs); /* Call the event loop */ ScreenEventLoop (); } static unsigned char ScreenRead (void* Data, unsigned Offs) /* Read user data */ { /* Cast the data pointer */ ScreenInstance* V = Data; /* Check the offset */ if (Offs >= sizeof (V->Mem)) { Sim->Break ("Screen: Accessing invalid memory at $%06X", V->Addr + Offs); return 0xFF; } else { return V->Mem[Offs]; } } static void ScreenDrawBorder (void) /* Draw the complete border */ { if (VScreen) { /* Set the border color */ XSetForeground (VScreen->ScreenDisplay, VScreen->ScreenGC, VScreen->BgColor.pixel); /* Fill all rectangles that make the border */ XFillRectangles (VScreen->ScreenDisplay, VScreen->ScreenWindow, VScreen->ScreenGC, VScreen->Border, sizeof (VScreen->Border) / sizeof (VScreen->Border[0])); } } static void ScreenDrawChar (unsigned Offs) /* Draw one character at the given position */ { unsigned Row, Col; XPoint Points[128]; unsigned PCount; /* Get the character from the video RAM */ unsigned char C = VScreen->Mem[Offs]; /* Calculate the offset for the character data in the character ROM */ unsigned char* D = VScreen->FontData + (C * VScreen->CharHeight); /* Calculate the coords for the output */ unsigned X = VScreen->XOffs + (Offs % VScreen->Cols) * 8; unsigned Y = VScreen->YOffs + (Offs / VScreen->Cols) * VScreen->CharHeight; /* Clear the character area with the background color */ XSetForeground (VScreen->ScreenDisplay, VScreen->ScreenGC, VScreen->BgColor.pixel); XFillRectangle (VScreen->ScreenDisplay, VScreen->ScreenWindow, VScreen->ScreenGC, X, Y, 8, VScreen->CharHeight); /* Prepare the foreground pixels */ PCount = 0; for (Row = 0; Row < VScreen->CharHeight; ++Row) { /* Get next byte from char rom */ unsigned Data = *D++; /* Make pixels from this byte */ for (Col = 0; Col < 8; ++Col) { if (Data & 0x80) { /* Foreground pixel */ Points[PCount].x = X + Col; Points[PCount].y = Y + Row; ++PCount; } Data <<= 1; } } if (PCount) { /* Set the character color */ XSetForeground (VScreen->ScreenDisplay, VScreen->ScreenGC, VScreen->FgColor.pixel); /* Draw the pixels */ XDrawPoints (VScreen->ScreenDisplay, VScreen->ScreenWindow, VScreen->ScreenGC, Points, PCount, CoordModeOrigin); } } static void ScreenDrawArea (unsigned X1, unsigned Y1, unsigned X2, unsigned Y2) /* Update an area of the interior screen */ { unsigned X, Y; /* Check if we have to draw anything */ if (X2 < VScreen->XOffs || Y2 < VScreen->YOffs || X1 >= VScreen->XOffs + VScreen->XSize || Y1 >= VScreen->YOffs + VScreen->YSize) { /* Completely outside */ return; } /* Make the coordinates relative to the interior */ X1 -= VScreen->XOffs; Y1 -= VScreen->YOffs; X2 -= VScreen->XOffs; Y2 -= VScreen->YOffs; /* Loop updating characters */ for (Y = Y1; Y <= Y2; Y += 8) { for (X = X1; X <= X2; X += 8) { ScreenDrawChar ((Y / 8) * 40 + (X / 8)); } } } static void ScreenDrawAllChars (void) /* Redraw the complete interior screen */ { unsigned I; for (I = 0; I < 25*40; ++I) { ScreenDrawChar (I); } } static void ScreenEventLoop (void) /* Get all waiting events and handle them */ { unsigned X1, Y1, X2, Y2; /* Read input events */ while (XEventsQueued (VScreen->ScreenDisplay, QueuedAfterFlush) != 0) { /* Read an event */ XEvent Event; XNextEvent (VScreen->ScreenDisplay, &Event); switch (Event.type) { case Expose: /* Calculate the area to redraw, then update the screen */ X1 = Event.xexpose.x; Y1 = Event.xexpose.y; X2 = Event.xexpose.x + Event.xexpose.width - 1; Y2 = Event.xexpose.y + Event.xexpose.height - 1; if (X1 < VScreen->XOffs || X2 > VScreen->XOffs + VScreen->XSize || Y1 < VScreen->YOffs || Y2 > VScreen->YOffs + VScreen->YSize) { /* Update the border */ ScreenDrawBorder (); } ScreenDrawArea (X1, Y1, X2, Y2); break; case MappingNotify: XRefreshKeyboardMapping (&Event.xmapping); break; } } /* Flush the outgoing event queue */ XFlush (VScreen->ScreenDisplay); }