From f2d1ea10d9c9b90da9ef8bc6079947ecffe71e5c Mon Sep 17 00:00:00 2001 From: uz Date: Thu, 8 Oct 2009 18:33:50 +0000 Subject: [PATCH] Added a simulated console (memory mapped screen device, inpout device will follow). git-svn-id: svn://svn.cc65.org/cc65/trunk@4351 b7a2c559-68d2-44c3-8de9-860c34a00d81 --- src/sim65/chips/console.c | 637 +++++++++++++++++++++++++++++++++++ src/sim65/chips/make/gcc.mak | 3 +- 2 files changed, 639 insertions(+), 1 deletion(-) create mode 100644 src/sim65/chips/console.c diff --git a/src/sim65/chips/console.c b/src/sim65/chips/console.c new file mode 100644 index 000000000..ad520c5d4 --- /dev/null +++ b/src/sim65/chips/console.c @@ -0,0 +1,637 @@ +/*****************************************************************************/ +/* */ +/* 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); +} + + + diff --git a/src/sim65/chips/make/gcc.mak b/src/sim65/chips/make/gcc.mak index d0da77692..82a5e152e 100644 --- a/src/sim65/chips/make/gcc.mak +++ b/src/sim65/chips/make/gcc.mak @@ -14,7 +14,8 @@ LDFLAGS = #LIBS = $(COMMON)/common.a -CHIPS = ram.so \ +CHIPS = console.so \ + ram.so \ rom.so \ stdio.so \ vic2.so -- 2.39.5