1 /*
2  * viciivsid-irq.c - IRQ related functions for the MOS 6569 (VIC-II) emulation.
3  *
4  * Written by
5  *  Andreas Boose <viceteam@t-online.de>
6  *  Ettore Perazzoli <ettore@comm2000.it>
7  *
8  * This file is part of VICE, the Versatile Commodore Emulator.
9  * See README for copyright notice.
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24  *  02111-1307  USA.
25  *
26  */
27 
28 #include "vice.h"
29 
30 #include "alarm.h"
31 #include "interrupt.h"
32 #include "maincpu.h"
33 #include "types.h"
34 #include "vicii-irq.h"
35 #include "viciitypes.h"
36 
vicii_irq_set_line(void)37 void vicii_irq_set_line(void)
38 {
39     if (vicii.irq_status & vicii.regs[0x1a]) {
40         vicii.irq_status |= 0x80;
41         maincpu_set_irq(vicii.int_num, 1);
42     } else {
43         vicii.irq_status &= 0x7f;
44         maincpu_set_irq(vicii.int_num, 0);
45     }
46 }
47 
vicii_irq_set_line_clk(CLOCK mclk)48 static inline void vicii_irq_set_line_clk(CLOCK mclk)
49 {
50     if (vicii.irq_status & vicii.regs[0x1a]) {
51         vicii.irq_status |= 0x80;
52         maincpu_set_irq_clk(vicii.int_num, 1, mclk);
53     } else {
54         vicii.irq_status &= 0x7f;
55         maincpu_set_irq_clk(vicii.int_num, 0, mclk);
56     }
57 }
58 
vicii_irq_raster_set(CLOCK mclk)59 void vicii_irq_raster_set(CLOCK mclk)
60 {
61     vicii.irq_status |= 0x1;
62     vicii_irq_set_line_clk(mclk);
63 }
64 
vicii_irq_raster_clear(CLOCK mclk)65 void vicii_irq_raster_clear(CLOCK mclk)
66 {
67     vicii.irq_status &= 0xfe;
68     vicii_irq_set_line_clk(mclk);
69 }
70 
vicii_irq_sbcoll_set(void)71 void vicii_irq_sbcoll_set(void)
72 {
73     vicii.irq_status |= 0x2;
74     vicii_irq_set_line();
75 }
76 
vicii_irq_sbcoll_clear(void)77 void vicii_irq_sbcoll_clear(void)
78 {
79     vicii.irq_status &= 0xfd;
80     vicii_irq_set_line();
81 }
82 
vicii_irq_sscoll_set(void)83 void vicii_irq_sscoll_set(void)
84 {
85     vicii.irq_status |= 0x4;
86     vicii_irq_set_line();
87 }
88 
vicii_irq_sscoll_clear(void)89 void vicii_irq_sscoll_clear(void)
90 {
91     vicii.irq_status &= 0xfb;
92     vicii_irq_set_line();
93 }
94 
vicii_irq_lightpen_set(CLOCK mclk)95 void vicii_irq_lightpen_set(CLOCK mclk)
96 {
97     vicii.irq_status |= 0x8;
98     vicii_irq_set_line_clk(mclk);
99 }
100 
vicii_irq_lightpen_clear(CLOCK mclk)101 void vicii_irq_lightpen_clear(CLOCK mclk)
102 {
103     vicii.irq_status &= 0xf7;
104     vicii_irq_set_line_clk(mclk);
105 }
106 
vicii_irq_set_raster_line(unsigned int line)107 void vicii_irq_set_raster_line(unsigned int line)
108 {
109     if (line == vicii.raster_irq_line && vicii.raster_irq_clk != CLOCK_MAX) {
110         return;
111     }
112 
113     if (line < (unsigned int)vicii.screen_height) {
114         unsigned int current_line = VICII_RASTER_Y(maincpu_clk);
115 
116         vicii.raster_irq_clk = (VICII_LINE_START_CLK(maincpu_clk)
117                                 + VICII_RASTER_IRQ_DELAY - INTERRUPT_DELAY
118                                 + (vicii.cycles_per_line
119                                    * (line - current_line)));
120         /* Raster interrupts on line 0 are delayed by 1 cycle.  */
121         if (line == 0) {
122             vicii.raster_irq_clk++;
123         }
124 
125         if (line <= current_line) {
126             vicii.raster_irq_clk += (vicii.screen_height * vicii.cycles_per_line);
127         }
128         alarm_set(vicii.raster_irq_alarm, vicii.raster_irq_clk);
129     } else {
130         VICII_DEBUG_RASTER(("update_raster_irq(): "
131                             "raster compare out of range ($%04X)!", line));
132         vicii.raster_irq_clk = CLOCK_MAX;
133         alarm_unset(vicii.raster_irq_alarm);
134     }
135 
136     VICII_DEBUG_RASTER(("update_raster_irq(): "
137                         "vicii.raster_irq_clk = %ul, "
138                         "line = $%04X, "
139                         "vicii.regs[0x1a] & 1 = %d",
140                         vicii.raster_irq_clk, line, vicii.regs[0x1a] & 1));
141 
142     vicii.raster_irq_line = line;
143 }
144 
vicii_irq_check_state(uint8_t value,unsigned int high)145 void vicii_irq_check_state(uint8_t value, unsigned int high)
146 {
147     unsigned int irq_line, line;
148     unsigned int old_raster_irq_line;
149     CLOCK old_raster_irq_clk = vicii.raster_irq_clk;
150 
151     if (high) {
152         irq_line = (vicii.raster_irq_line & 0xff) | ((value & 0x80) << 1);
153     } else {
154         irq_line = (vicii.raster_irq_line & 0x100) | value;
155     }
156 
157     if (irq_line == vicii.raster_irq_line) {
158         return;
159     }
160 
161     line = VICII_RASTER_Y(maincpu_clk);
162 
163     old_raster_irq_line = vicii.raster_irq_line;
164     vicii_irq_set_raster_line(irq_line);
165 
166     if (vicii.regs[0x1a] & 0x1) {
167         int trigger_irq;
168 
169         trigger_irq = 0;
170 
171         if (old_raster_irq_clk == VICII_LINE_START_CLK(maincpu_clk) + (line == 0 ? 1 : 0)) {
172             trigger_irq = 2;
173         }
174 
175         if (maincpu_rmw_flag) {
176             if (high) {
177                 if (VICII_RASTER_CYCLE(maincpu_clk) == 0
178                     && (line & 0xff) == 0) {
179                     unsigned int previous_line = VICII_PREVIOUS_LINE(line);
180 
181                     if (previous_line != old_raster_irq_line
182                         && ((old_raster_irq_line & 0xff)
183                             == (previous_line & 0xff))) {
184                         trigger_irq = 1;
185                     }
186                 } else {
187                     if (line != old_raster_irq_line
188                         && (old_raster_irq_line & 0xff) == (line & 0xff)) {
189                         trigger_irq = 1;
190                     }
191                 }
192             } else {
193                 if (VICII_RASTER_CYCLE(maincpu_clk) == 0) {
194                     unsigned int previous_line = VICII_PREVIOUS_LINE(line);
195 
196                     if (previous_line != old_raster_irq_line
197                         && ((old_raster_irq_line & 0x100)
198                             == (previous_line & 0x100))) {
199                         trigger_irq = 1;
200                     }
201                 } else {
202                     if (line != old_raster_irq_line
203                         && (old_raster_irq_line & 0x100) == (line & 0x100)) {
204                         trigger_irq = 1;
205                     }
206                 }
207             }
208         }
209 
210         if (vicii.raster_irq_line == line && line != old_raster_irq_line) {
211             trigger_irq = 1;
212         }
213 
214         if (trigger_irq == 1) {
215             vicii_irq_raster_set(maincpu_clk);
216         }
217 
218         if (trigger_irq == 2) {
219             vicii_irq_raster_set(old_raster_irq_clk);
220         }
221     }
222 }
223 
vicii_irq_next_frame(void)224 void vicii_irq_next_frame(void)
225 {
226     vicii.raster_irq_clk += vicii.screen_height * vicii.cycles_per_line;
227     alarm_set(vicii.raster_irq_alarm, vicii.raster_irq_clk);
228 }
229 
230 /* If necessary, emulate a raster compare IRQ. This is called when the raster
231    line counter matches the value stored in the raster line register.  */
vicii_irq_alarm_handler(CLOCK offset,void * data)232 void vicii_irq_alarm_handler(CLOCK offset, void *data)
233 {
234     vicii_irq_raster_set(vicii.raster_irq_clk);
235     vicii_irq_next_frame();
236 }
237 
vicii_irq_init(void)238 void vicii_irq_init(void)
239 {
240     vicii.int_num = interrupt_cpu_status_int_new(maincpu_int_status, "VICII");
241 
242     vicii.raster_irq_alarm = alarm_new(maincpu_alarm_context, "VicIIRasterIrq",
243                                        vicii_irq_alarm_handler, NULL);
244 }
245