1 /*
2  * This file is part of libsidplayfp, a SID player engine.
3  *
4  * Copyright 2011-2020 Leandro Nini <drfiemost@users.sourceforge.net>
5  * Copyright 2009-2014 VICE Project
6  * Copyright 2007-2010 Antti Lankila
7  * Copyright 2001 Simon White
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
22  */
23 
24 #ifndef LIGHTPEN_H
25 #define LIGHTPEN_H
26 
27 namespace libsidplayfp
28 {
29 
30 /**
31  * Lightpen emulation.
32  * Does not reflect model differences.
33  */
34 class Lightpen
35 {
36 private:
37     /// Last VIC raster line
38     unsigned int lastLine;
39 
40     /// VIC cycles per line
41     unsigned int cyclesPerLine;
42 
43     /// X coordinate
44     unsigned int lpx;
45 
46     /// Y coordinate
47     unsigned int lpy;
48 
49     /// Has light pen IRQ been triggered in this frame already?
50     bool isTriggered;
51 
52 private:
53     /**
54      * Transform line cycle into x coordinate.
55      *
56      * @param lineCycle
57      * @return x position divided by two
58      */
getXpos(unsigned int lineCycle)59     uint8_t getXpos(unsigned int lineCycle) const
60     {
61         if (lineCycle < 12)
62             lineCycle += cyclesPerLine-1;
63 
64         lineCycle -= 12;
65 
66         return lineCycle << 2;
67     }
68 
69 public:
70     /**
71      * Set VIC screen size.
72      *
73      * @param height number of raster lines
74      * @param width number of cycles per line
75      */
setScreenSize(unsigned int height,unsigned int width)76     void setScreenSize(unsigned int height, unsigned int width)
77     {
78         lastLine = height - 1;
79         cyclesPerLine = width;
80     }
81 
82     /**
83      * Reset the lightpen.
84      */
reset()85     void reset()
86     {
87         lpx = 0;
88         lpy = 0;
89         isTriggered = false;
90     }
91 
92     /**
93      * Return the low byte of x coordinate.
94      */
getX()95     uint8_t getX() const { return lpx; }
96 
97     /**
98      * Return the low byte of y coordinate.
99      */
getY()100     uint8_t getY() const { return lpy; }
101 
102     /**
103      * Retrigger lightpen on vertical blank.
104      *
105      * @param lineCycle current line cycle
106      * @param rasterY current y raster position
107      * @return true if an IRQ should be triggered
108      */
retrigger(unsigned int lineCycle,unsigned int rasterY)109     bool retrigger(unsigned int lineCycle, unsigned int rasterY)
110     {
111         const bool triggered = trigger(lineCycle, rasterY);
112         switch (cyclesPerLine)
113         {
114         case 63:
115         default:
116             lpx = 0xd1;
117             break;
118         case 65:
119             lpx = 0xd5;
120             break;
121         }
122         return triggered;
123     }
124 
125     /**
126      * Trigger lightpen from CIA.
127      *
128      * @param lineCycle current line cycle
129      * @param rasterY current y raster position
130      * @return true if an IRQ should be triggered
131      */
trigger(unsigned int lineCycle,unsigned int rasterY)132     bool trigger(unsigned int lineCycle, unsigned int rasterY)
133     {
134         if (isTriggered)
135             return false;
136 
137         isTriggered = true;
138 
139         // don't latch on the last line, except on the first cycle
140         if ((rasterY != lastLine) || (lineCycle == 0))
141         {
142             // Latch current coordinates
143             lpx = getXpos(lineCycle) + 2;
144             lpy = rasterY;
145         }
146 
147         return true;
148     }
149 
150     /**
151      * Untrigger lightpen from CIA.
152      */
untrigger()153     void untrigger() { isTriggered = false; }
154 };
155 
156 }
157 
158 #endif
159