1 /*****************************************************************************/
5 /* VIC II plugin for the sim65 6502 simulator */
9 /* (C) 2003 Ullrich von Bassewitz */
10 /* Römerstrasse 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 VicInitChip (const struct SimData* Data);
61 /* Initialize the chip, return an error code */
63 static void* VicCreateInstance (unsigned Addr, unsigned Range, void* CfgInfo);
64 /* Create a new chip instance */
66 static void VicDestroyInstance (void* Data);
67 /* Destroy a chip instance */
69 static void VicWrite (void* Data, unsigned Offs, unsigned char Val);
72 static unsigned char VicRead (void* Data, unsigned Offs);
75 static int VRamInitChip (const struct SimData* Data);
76 /* Initialize the chip, return an error code */
78 static void* VRamCreateInstance (unsigned Addr, unsigned Range, void* CfgInfo);
79 /* Create a new chip instance */
81 static void VRamDestroyInstance (void* Data);
82 /* Destroy a chip instance */
84 static void VRamWrite (void* Data, unsigned Offs, unsigned char Val);
87 static unsigned char VRamRead (void* Data, unsigned Offs);
90 static void VRamDrawBorder (void);
91 /* Draw the complete border */
93 static void VRamDrawChar (unsigned Offs);
94 /* Draw one character at the given position */
96 static void VRamDrawAllChars (void);
97 /* Redraw the complete interior screen */
99 static void VRamEventLoop (void);
100 /* Get all waiting events and handle them */
102 static int CRamInitChip (const struct SimData* Data);
103 /* Initialize the chip, return an error code */
105 static void* CRamCreateInstance (unsigned Addr, unsigned Range, void* CfgInfo);
106 /* Create a new chip instance */
108 static void CRamDestroyInstance (void* Data);
109 /* Destroy a chip instance */
111 static void CRamWrite (void* Data, unsigned Offs, unsigned char Val);
112 /* Write user data */
114 static unsigned char CRamRead (void* Data, unsigned Offs);
119 /*****************************************************************************/
121 /*****************************************************************************/
125 /* The SimData pointer we get when InitChip is called */
126 static const SimData* Sim;
128 /* Control data passed to the main program */
129 static const struct ChipData CData[] = {
131 "VIC2", /* Name of the chip */
132 CHIPDATA_TYPE_CHIP, /* Type of the chip */
133 CHIPDATA_VER_MAJOR, /* Version information */
136 /* -- Exported functions -- */
146 "VIC2-VIDEORAM", /* Name of the chip */
147 CHIPDATA_TYPE_CHIP, /* Type of the chip */
148 CHIPDATA_VER_MAJOR, /* Version information */
151 /* -- Exported functions -- */
161 "VIC2-COLORRAM", /* Name of the chip */
162 CHIPDATA_TYPE_CHIP, /* Type of the chip */
163 CHIPDATA_VER_MAJOR, /* Version information */
166 /* -- Exported functions -- */
177 /* Defines for the VIC chip */
178 #define VIC_COLOR_COUNT 16
182 /* The application color map. VIC II color values are taken from
183 * http://www.pepto.de/projects/colorvic/ (Philip "Pepto" Timmermann)
185 static XColor VicColors [VIC_COLOR_COUNT] = {
186 { 0, 0*256, 0*256, 0*256, 0, 0 }, /* black */
187 { 0, 255*256, 255*256, 255*256, 0, 0 }, /* white */
188 { 0, 104*256, 55*256, 43*256, 0, 0 }, /* red */
189 { 0, 112*256, 163*256, 178*256, 0, 0 }, /* cyan */
190 { 0, 111*256, 61*256, 134*256, 0, 0 }, /* purple */
191 { 0, 88*256, 141*256, 67*256, 0, 0 }, /* green */
192 { 0, 53*256, 40*256, 121*256, 0, 0 }, /* blue */
193 { 0, 184*256, 199*256, 111*256, 0, 0 }, /* yellow */
194 { 0, 111*256, 79*256, 37*256, 0, 0 }, /* orange */
195 { 0, 67*256, 57*256, 0*256, 0, 0 }, /* brown */
196 { 0, 154*256, 103*256, 89*256, 0, 0 }, /* light red */
197 { 0, 68*256, 68*256, 68*256, 0, 0 }, /* dark grey */
198 { 0, 108*256, 108*256, 108*256, 0, 0 }, /* grey */
199 { 0, 154*256, 210*256, 132*256, 0, 0 }, /* light green */
200 { 0, 108*256, 94*256, 181*256, 0, 0 }, /* light blue */
201 { 0, 149*256, 149*256, 149*256, 0, 0 } /* light grey */
205 /*****************************************************************************/
207 /*****************************************************************************/
211 /* VIC II instance data */
212 typedef struct VicInstance VicInstance;
214 unsigned Addr; /* Address of the chip */
215 unsigned Range; /* Memory range */
216 unsigned char Regs[47]; /* VIC registers */
219 /* Video RAM instance data */
220 typedef struct VRamInstance VRamInstance;
221 struct VRamInstance {
223 /* Settings passed from the simulator */
224 unsigned Addr; /* Address of the chip */
225 unsigned Range; /* Memory range */
233 /* Window dimensions, 384*288 (PAL) */
237 /* Usable area within the window */
241 /* Offset of the usable area */
245 /* The window color map. */
246 XColor Colors [VIC_COLOR_COUNT];
248 /* A list of 4 rectangles used to draw the border */
249 XRectangle Border[4];
251 /* The virtual screen we are writing to. */
252 unsigned char Mem[0x400];
254 /* The character ROM data */
255 unsigned char CharRom[0x1000];
259 typedef struct CRamInstance CRamInstance;
260 struct CRamInstance {
262 /* Settings passed from the simulator */
263 unsigned Addr; /* Address of the chip */
264 unsigned Range; /* Memory range */
266 /* The memory we are writing to. */
267 unsigned char Mem[0x400];
270 /* If we have a video ram window, place it's instance data here */
271 static VicInstance* Vic = 0;
272 static VRamInstance* VRam = 0;
273 static CRamInstance* CRam = 0;
277 /*****************************************************************************/
278 /* Exported function */
279 /*****************************************************************************/
283 int GetChipData (const ChipData** Data, unsigned* Count)
285 /* Pass the control structure to the caller */
287 *Count = sizeof (CData) / sizeof (CData[0]);
289 /* Call was successful */
295 /*****************************************************************************/
297 /*****************************************************************************/
301 static int VicInitChip (const struct SimData* Data)
302 /* Initialize the chip, return an error code */
304 /* Remember the pointer */
307 /* Always successful */
313 static void* VicCreateInstance (unsigned Addr, unsigned Range,
314 void* CfgInfo attribute ((unused)))
315 /* Initialize a new chip instance */
317 /* Allocate a new instance structure */
318 VicInstance* V = Vic = Sim->Malloc (sizeof (VicInstance));
320 /* Initialize the structure, allocate RAM and attribute memory */
323 memset (V->Regs, 0, sizeof (V->Regs));
325 /* Done, return the instance data */
331 static void VicDestroyInstance (void* Data)
332 /* Destroy a chip instance */
334 /* Cast the data pointer */
335 VicInstance* V = Data;
337 /* Free the instance data */
343 static void VicWrite (void* Data, unsigned Offs, unsigned char Val)
344 /* Write user data */
346 /* Cast the data pointer */
347 VicInstance* V = Data;
349 /* Check for a write outside our range */
350 if (Offs >= sizeof (V->Regs)) {
351 Sim->Break ("Writing to invalid VIC register at $%04X", V->Addr+Offs);
357 /* Handle special registers */
366 /* Background color #0 */
374 /* Handle the event queue */
383 static unsigned char VicRead (void* Data, unsigned Offs)
386 /* Cast the data pointer */
387 VicInstance* V = Data;
389 /* Simulate the rasterline register */
390 if (V->Regs[17] & 0x80) {
391 if (++V->Regs[18] == (312 & 0xFF)) {
396 if (++V->Regs[18] == 0) {
401 /* Check for a read outside our range */
402 if (Offs >= sizeof (V->Regs)) {
404 Sim->Break ("Reading invalid VIC register at $%04X", V->Addr+Offs);
410 return V->Regs[Offs];
417 /*****************************************************************************/
419 /*****************************************************************************/
423 static int VRamInitChip (const struct SimData* Data)
424 /* Initialize the chip, return an error code */
426 /* Remember the pointer */
429 /* Always successful */
435 static void* VRamCreateInstance (unsigned Addr, unsigned Range, void* CfgInfo)
436 /* Create a new chip instance */
443 XSizeHints SizeHints;
447 /* Allocate the instance data */
448 VRamInstance* V = VRam = Sim->Malloc (sizeof (VRamInstance));
450 /* Remember a few settings */
454 /* Setup the window geometry */
455 V->XTotal = 384; /* PAL */
459 V->XOffs = (V->XTotal - V->XSize) / 2;
460 V->YOffs = (V->YTotal - V->YSize) / 2;
462 /* Setup the rectanges used to draw the exterior */
465 V->Border[0].width = V->XTotal;
466 V->Border[0].height = V->YOffs;
468 V->Border[1].y = V->YOffs + V->YSize;
469 V->Border[1].width = V->XTotal;
470 V->Border[1].height = V->YOffs;
472 V->Border[2].y = V->YOffs;
473 V->Border[2].width = V->XOffs;
474 V->Border[2].height = V->YSize;
475 V->Border[3].x = V->XOffs + V->XSize;
476 V->Border[3].y = V->YOffs;
477 V->Border[3].width = V->XOffs;
478 V->Border[3].height = V->YSize;
480 /* We must have a "file" attribute. Get it. */
481 if (Sim->GetCfgStr (CfgInfo, "file", &Name) == 0) {
482 /* Attribute not found */
483 Sim->Error ("Attribute `file' missing"); /* ### */
486 /* Open the file with the given name */
487 F = fopen (Name, "rb");
489 Sim->Error ("Cannot open `%s': %s", Name, strerror (errno));
492 /* Read the file into the memory */
493 if (fread (V->CharRom, 1, sizeof (V->CharRom), F) != sizeof (V->CharRom)) {
494 Sim->Warning ("Char ROM `%s' seems to be corrupt", Name);
500 /* Free the file name */
503 /* Open the X display. */
504 V->VicDisplay = XOpenDisplay ("");
505 if (V->VicDisplay == NULL) {
506 Sim->Error ("VRAM: Cannot open X display");
510 V->VicScreen = DefaultScreen (V->VicDisplay);
512 /* Check the available colors. For now, we expect direct colors, so we
513 * will check for a color depth of at least 16.
515 ColorDepth = XDefaultDepth (V->VicDisplay, V->VicScreen);
516 if (ColorDepth < 16) {
518 Sim->Error ("VRAM: Need color display");
521 /* Get all needed colors */
522 memcpy (V->Colors, VicColors, sizeof (V->Colors));
523 CM = DefaultColormap (V->VicDisplay, V->VicScreen);
524 for (CIdx = 0; CIdx < VIC_COLOR_COUNT; CIdx++) {
525 if (XAllocColor (V->VicDisplay, CM, &V->Colors [CIdx]) == 0) {
526 Sim->Error ("VRAM: Cannot allocate color");
530 /* Set up the size hints structure */
533 SizeHints.flags = PPosition | PSize | PMinSize | PMaxSize | PResizeInc;
534 SizeHints.width = V->XTotal;
535 SizeHints.height = V->YTotal;
536 SizeHints.min_width = V->XTotal;
537 SizeHints.min_height = V->YTotal;
538 SizeHints.max_width = V->XTotal;
539 SizeHints.max_height = V->YTotal;
540 SizeHints.width_inc = 0;
541 SizeHints.height_inc = 0;
542 WMHints.flags = InputHint;
543 WMHints.input = True;
545 /* Create the window */
546 V->VicWindow = XCreateSimpleWindow (V->VicDisplay,
547 DefaultRootWindow (V->VicDisplay),
553 V->Colors [VIC_WHITE].pixel,
554 V->Colors [VIC_BLACK].pixel);
556 /* Set the standard window properties */
557 XSetStandardProperties (V->VicDisplay, /* Display */
558 V->VicWindow, /* Window */
559 "sim65 VIC screen", /* Window name */
560 "sim65 VIC screen", /* Icon name */
561 None, /* Icon Pixmap */
564 &SizeHints); /* Hints */
565 XSetWMHints (V->VicDisplay, V->VicWindow, &WMHints);
567 /* GC creation and initialization */
568 V->VicGC = XCreateGC (V->VicDisplay, V->VicWindow, 0, 0);
570 /* Set the cursor to show over the Vic window */
571 C = XCreateFontCursor (V->VicDisplay, XC_pirate);
572 XDefineCursor (V->VicDisplay, V->VicWindow, C);
574 /* Select input events */
575 XSelectInput (V->VicDisplay, V->VicWindow, ExposureMask | StructureNotifyMask);
577 /* Show the window */
578 XMapRaised (V->VicDisplay, V->VicWindow);
583 /* Return the instance data */
589 static void VRamDestroyInstance (void* Data)
590 /* Destroy a chip instance */
592 /* Cast the data pointer */
593 VRamInstance* V = Data;
595 /* Free X resources */
596 XUndefineCursor (V->VicDisplay, V->VicWindow);
597 XFreeGC (V->VicDisplay, V->VicGC);
598 XDestroyWindow (V->VicDisplay, V->VicWindow);
599 XCloseDisplay (V->VicDisplay);
601 /* Clear the global pointer */
604 /* Free the instance data */
610 static void VRamWrite (void* Data, unsigned Offs, unsigned char Val)
611 /* Write user data */
613 /* Cast the data pointer */
614 VRamInstance* V = Data;
616 /* Check the offset */
617 if (Offs >= sizeof (V->Mem)) {
618 Sim->Break ("VRAM: Accessing invalid memory at $%06X", V->Addr + Offs);
622 /* Write the value */
625 /* If this changes the visible part of the screen, schedule a redraw */
628 /* Schedule a redraw */
631 /* Call the event loop */
638 static unsigned char VRamRead (void* Data, unsigned Offs)
641 /* Cast the data pointer */
642 VRamInstance* V = Data;
644 /* Check the offset */
645 if (Offs >= sizeof (V->Mem)) {
646 Sim->Break ("VRAM: Accessing invalid memory at $%06X", V->Addr + Offs);
655 static void VRamDrawBorder (void)
656 /* Draw the complete border */
659 /* Set the border color */
660 XSetForeground (VRam->VicDisplay, VRam->VicGC, VRam->Colors[Vic->Regs[32]].pixel);
662 /* Fill all rectangles that make the border */
663 XFillRectangles (VRam->VicDisplay, VRam->VicWindow, VRam->VicGC,
664 VRam->Border, sizeof (VRam->Border) / sizeof (VRam->Border[0]));
670 static void VRamDrawChar (unsigned Offs)
671 /* Draw one character at the given position */
678 /* Get the character from the video RAM */
679 unsigned char C = VRam->Mem[Offs];
681 /* Calculate the offset for the character data in the character ROM */
682 unsigned char* D = VRam->CharRom + (C * 8);
684 /* Calculate the coords for the output */
685 unsigned X = VRam->XOffs + (Offs % 40) * 8;
686 unsigned Y = VRam->YOffs + (Offs / 40) * 8;
688 /* Clear the character area with the background color */
689 XSetForeground (VRam->VicDisplay, VRam->VicGC, VRam->Colors[Vic->Regs[33]].pixel);
690 XFillRectangle (VRam->VicDisplay, VRam->VicWindow, VRam->VicGC, X, Y, 8, 8);
692 /* Set the character color */
693 Color = CRam? CRam->Mem[Offs] & 0x0F : VIC_WHITE;
694 XSetForeground (VRam->VicDisplay, VRam->VicGC, VRam->Colors[Color].pixel);
696 /* Draw the foreground pixels */
698 for (Row = 0; Row < 8; ++Row) {
700 /* Get next byte from char rom */
701 unsigned Data = *D++;
703 /* Make pixels from this byte */
704 for (Col = 0; Col < 8; ++Col) {
706 /* Foreground pixel */
707 Points[PCount].x = X + Col;
708 Points[PCount].y = Y + Row;
715 XDrawPoints (VRam->VicDisplay, VRam->VicWindow, VRam->VicGC,
716 Points, PCount, CoordModeOrigin);
722 static void VRamDrawArea (unsigned X1, unsigned Y1, unsigned X2, unsigned Y2)
723 /* Update an area of the interior screen */
727 /* Check if we have to draw anything */
728 if (X2 < VRam->XOffs || Y2 < VRam->YOffs ||
729 X1 >= VRam->XOffs + VRam->XSize ||
730 Y1 >= VRam->YOffs + VRam->YSize) {
731 /* Completely outside */
735 /* Make the coordinates relative to the interior */
741 /* Loop updating characters */
742 for (Y = Y1; Y <= Y2; Y += 8) {
743 for (X = X1; X <= X2; X += 8) {
744 VRamDrawChar ((Y / 8) * 40 + (X / 8));
751 static void VRamDrawAllChars (void)
752 /* Redraw the complete interior screen */
755 for (I = 0; I < 25*40; ++I) {
762 static void VRamEventLoop (void)
763 /* Get all waiting events and handle them */
765 unsigned X1, Y1, X2, Y2;
767 /* Read input events */
768 while (XEventsQueued (VRam->VicDisplay, QueuedAfterFlush) != 0) {
772 XNextEvent (VRam->VicDisplay, &Event);
774 switch (Event.type) {
777 /* Calculate the area to redraw, then update the screen */
778 X1 = Event.xexpose.x;
779 Y1 = Event.xexpose.y;
780 X2 = Event.xexpose.x + Event.xexpose.width - 1;
781 Y2 = Event.xexpose.y + Event.xexpose.height - 1;
782 if (X1 < VRam->XOffs || X2 > VRam->XOffs + VRam->XSize ||
783 Y1 < VRam->YOffs || Y2 > VRam->YOffs + VRam->YSize) {
784 /* Update the border */
787 VRamDrawArea (X1, Y1, X2, Y2);
791 XRefreshKeyboardMapping (&Event.xmapping);
797 /* Flush the outgoing event queue */
798 XFlush (VRam->VicDisplay);
803 /*****************************************************************************/
805 /*****************************************************************************/
809 static int CRamInitChip (const struct SimData* Data)
810 /* Initialize the chip, return an error code */
812 /* Remember the pointer */
815 /* Always successful */
821 static void* CRamCreateInstance (unsigned Addr, unsigned Range,
822 void* CfgInfo attribute ((unused)))
823 /* Create a new chip instance */
825 /* Allocate the instance data */
826 CRamInstance* C = CRam = Sim->Malloc (sizeof (CRamInstance));
828 /* Remember a few settings */
832 /* Clear the color RAM memory */
833 memset (C->Mem, 0x00, sizeof (C->Mem));
835 /* Return the instance data */
841 static void CRamDestroyInstance (void* Data)
842 /* Destroy a chip instance */
844 /* Clear the global pointer */
847 /* Free the instance data */
853 static void CRamWrite (void* Data, unsigned Offs, unsigned char Val)
854 /* Write user data */
856 /* Cast the data pointer */
857 CRamInstance* C = Data;
859 /* Check the offset */
860 if (Offs >= sizeof (C->Mem)) {
861 Sim->Break ("CRAM: Accessing invalid memory at $%06X", C->Addr + Offs);
865 /* Write the value */
866 C->Mem[Offs] = Val & 0x0F;
868 /* If this changes the visible part of the screen, schedule a redraw */
871 /* Schedule a redraw */
874 /* Call the event loop */
881 static unsigned char CRamRead (void* Data, unsigned Offs)
884 /* Cast the data pointer */
885 CRamInstance* C = Data;
887 /* Check the offset */
888 if (Offs >= sizeof (C->Mem)) {
889 Sim->Break ("CRAM: Accessing invalid memory at $%06X", C->Addr + Offs);
892 return C->Mem[Offs] | 0xF0;