OSDN Git Service

[VM][DEVICE] Enable to build with new state framework.
[csp-qt/common_source_project-fm7.git] / source / src / vm / z80pio.cpp
1 /*
2         Skelton for retropc emulator
3
4         Author : Takeda.Toshiya
5         Date   : 2006.09.17 -
6
7         [ Z80PIO ]
8 */
9
10 #include "z80pio.h"
11
12 #define MODE_OUTPUT     0x00
13 #define MODE_INPUT      0x40
14 #define MODE_BIDIRECT   0x80
15 #define MODE_CONTROL    0xc0
16
17 void Z80PIO::reset()
18 {
19         for(int ch = 0; ch < 2; ch++) {
20                 port[ch].wreg = 0xffffff00;     // force output the first written data
21                 port[ch].mode = MODE_INPUT;
22                 port[ch].ctrl1 = 0;
23                 port[ch].ctrl2 = 0;
24                 port[ch].dir = 0xff;
25                 port[ch].mask = 0xff;
26                 port[ch].vector = 0;
27                 port[ch].set_dir = false;
28                 port[ch].set_mask = false;
29                 // status
30                 port[ch].ready_signal = -1;
31                 port[ch].input_empty = false;
32                 port[ch].output_ready = false;
33                 // interrupt
34                 port[ch].enb_intr = false;
35                 port[ch].req_intr = false;
36                 port[ch].in_service = false;
37         }
38         iei = oei = true;
39         update_ready();
40 }
41
42 /*
43         AD0 is to C/~D, AD1 is to B/~A:
44         
45         0       port a data
46         1       port a control
47         2       port b data
48         3       port b control
49 */
50
51 void Z80PIO::write_io8(uint32_t addr, uint32_t data)
52 {
53         int ch = (addr >> 1) & 1;
54         bool mode_changed = false;
55         data &= 0xff;
56         
57         switch(addr & 3) {
58         case 0:
59         case 2:
60                 // data
61                 if(port[ch].wreg != data) {
62                         write_signals(&port[ch].outputs_data, data);
63                         port[ch].wreg = data;
64                 }
65                 if(port[ch].output_ready) {
66                         port[ch].output_ready = false;
67                         update_ready();
68                 }
69                 port[ch].output_ready = true;
70                 update_ready();
71                 if(port[ch].mode == MODE_OUTPUT || port[ch].mode == MODE_BIDIRECT) {
72                         if(!port[ch].hand_shake) {
73                                 // the peripheral reads the data and sets the strobe signal immediately
74                                 if(!(ch == 1 && port[0].mode == MODE_BIDIRECT)) {
75                                         write_signal(SIG_Z80PIO_STROBE_A + ch, 1, 1);
76                                 }
77                         }
78                 } else if(port[ch].mode == MODE_CONTROL) {
79                         check_mode3_intr(ch);
80                 }
81                 break;
82         case 1:
83         case 3:
84                 // control
85                 if(port[ch].set_dir) {
86                         port[ch].dir = data;
87                         port[ch].set_dir = false;
88                 } else if(port[ch].set_mask) {
89                         port[ch].mask = data;
90                         port[ch].set_mask = false;
91                         port[ch].enb_intr = port[ch].enb_intr_tmp;
92                         update_intr();
93                 } else if(!(data & 0x01)) {
94                         port[ch].vector = data;
95                 } else if((data & 0x0f) == 0x03) {
96                         port[ch].enb_intr = ((data & 0x80) != 0);
97                         port[ch].ctrl2 = data;
98                         update_intr();
99                 } else if((data & 0x0f) == 0x07) {
100                         port[ch].enb_intr = ((data & 0x80) != 0);
101                         port[ch].ctrl1 = data;
102                         if(data & 0x10) {
103                                 port[ch].set_mask = true;
104                                 // canel pending interrup ???
105                                 port[ch].req_intr = false;
106                                 // disable interrupt until the mask register is written
107                                 port[ch].enb_intr_tmp = port[ch].enb_intr;
108                                 port[ch].enb_intr = false;
109                         }
110                         update_intr();
111                 } else if((data & 0x0f) == 0x0f) {
112                         // port[].dir 0=output, 1=input
113                         if((data & 0xc0) == MODE_OUTPUT) {
114                                 port[ch].dir = 0x00;
115                         } else if((data & 0xc0) == MODE_INPUT || (data & 0xc0) == MODE_BIDIRECT) {
116                                 port[ch].dir = 0xff;
117                         } else if((data & 0xc0) == MODE_CONTROL) {
118                                 port[ch].set_dir = true;
119                         }
120                         mode_changed = (port[ch].mode != (data & 0xc0));
121                         port[ch].mode = data & 0xc0;
122                 }
123                 if(port[ch].mode == MODE_BIDIRECT) {
124                         check_mode3_intr(ch);
125                 }
126                 if(mode_changed) {
127                         port[ch].input_empty = false;
128                         port[ch].output_ready = false;
129                         update_ready();
130                 }
131                 break;
132         }
133 }
134
135 uint32_t Z80PIO::read_io8(uint32_t addr)
136 {
137         int ch = (addr >> 1) & 1;
138         
139         switch(addr & 3) {
140         case 0:
141         case 2:
142                 // data
143                 if(!port[ch].input_empty) {
144                         port[ch].input_empty = true;
145                         update_ready();
146                 }
147                 return (port[ch].rreg & port[ch].dir) | (port[ch].wreg & ~port[ch].dir);break;
148         case 1:
149         case 3:
150                 // status (sharp z-80pio special function)
151                 return port[0].mode | (port[1].mode >> 4);
152         }
153         return 0xff;
154 }
155
156 void Z80PIO::write_signal(int id, uint32_t data, uint32_t mask)
157 {
158         // port[].dir 0=output, 1=input
159         int ch = 1;
160         
161         switch(id) {
162         case SIG_Z80PIO_PORT_A:
163                 ch = 0;
164         case SIG_Z80PIO_PORT_B:
165                 port[ch].rreg = (port[ch].rreg & ~mask) | (data & mask);
166                 if(port[ch].input_empty) {
167                         port[ch].input_empty = false;
168                         update_ready();
169                 }
170                 if(port[ch].mode == MODE_INPUT || port[ch].mode == MODE_BIDIRECT) {
171                         if(!port[ch].hand_shake) {
172                                 // the peripheral sets the strobe signal immediately after it writes the data
173                                 if(ch == 0 && port[0].mode == MODE_BIDIRECT) {
174                                         write_signal(SIG_Z80PIO_STROBE_B, 1, 1);
175                                 } else if(!(ch == 1 && port[0].mode == MODE_BIDIRECT)) {
176                                         write_signal(SIG_Z80PIO_STROBE_A + ch, 1, 1);
177                                 }
178                         }
179                 } else if(port[ch].mode == MODE_CONTROL) {
180                         check_mode3_intr(ch);
181                 }
182                 break;
183         case SIG_Z80PIO_STROBE_A:
184                 ch = 0;
185         case SIG_Z80PIO_STROBE_B:
186                 if(data & mask) {
187                         if(port[ch].output_ready) {
188                                 port[ch].output_ready = false;
189                                 update_ready();
190                         }
191                         port[ch].req_intr = true;
192                         update_intr();
193                 }
194                 break;
195         }
196 }
197
198 void Z80PIO::update_ready()
199 {
200         for(int ch = 0; ch < 2; ch++) {
201                 int next_signal = 0;
202                 if(ch == 1 && port[0].mode == MODE_BIDIRECT) {
203                         next_signal = (port[0].input_empty == true);
204                 } else {
205                         if(port[ch].mode == MODE_OUTPUT || port[ch].mode == MODE_BIDIRECT) {
206                                 next_signal = (port[ch].output_ready == true);
207                         } else if(port[ch].mode == MODE_INPUT) {
208                                 next_signal = (port[ch].input_empty == true);
209                         }
210                 }
211                 if(port[ch].ready_signal != next_signal) {
212                         write_signals(&port[ch].outputs_ready, (next_signal != 0) ? 0xffffffff : 0);
213                         port[ch].ready_signal = next_signal;
214                 }
215         }
216 }
217
218 void Z80PIO::check_mode3_intr(int ch)
219 {
220         // check mode3 interrupt status
221         uint8_t mask = ~port[ch].mask;
222         uint8_t val = (port[ch].rreg & port[ch].dir) | (port[ch].wreg & ~port[ch].dir);
223         val &= mask;
224         
225         if((port[ch].ctrl1 & 0x60) == 0x00 && val != mask) {
226                 port[ch].req_intr = true;
227         } else if((port[ch].ctrl1 & 0x60) == 0x20 && val != 0) {
228                 port[ch].req_intr = true;
229         } else if((port[ch].ctrl1 & 0x60) == 0x40 && val == 0) {
230                 port[ch].req_intr = true;
231         } else if((port[ch].ctrl1 & 0x60) == 0x60 && val == mask) {
232                 port[ch].req_intr = true;
233         } else {
234                 port[ch].req_intr = false;
235         }
236         update_intr();
237 }
238
239 void Z80PIO::set_intr_iei(bool val)
240 {
241         if(iei != val) {
242                 iei = val;
243                 update_intr();
244         }
245 }
246
247 #define set_intr_oei(val) { \
248         if(oei != val) { \
249                 oei = val; \
250                 if(d_child) { \
251                         d_child->set_intr_iei(oei); \
252                 } \
253         } \
254 }
255
256 void Z80PIO::update_intr()
257 {
258         bool next;
259         
260         // set oei signal
261         if((next = iei) == true) {
262                 for(int ch = 0; ch < 2; ch++) {
263                         if(port[ch].in_service) {
264                                 next = false;
265                                 break;
266                         }
267                 }
268         }
269         set_intr_oei(next);
270         
271         // set int signal
272         if((next = iei) == true) {
273                 next = false;
274                 for(int ch = 0; ch < 2; ch++) {
275                         if(port[ch].in_service) {
276                                 break;
277                         }
278                         if(port[ch].enb_intr && port[ch].req_intr) {
279                                 next = true;
280                                 break;
281                         }
282                 }
283         }
284         if(d_cpu) {
285                 d_cpu->set_intr_line(next, true, intr_bit);
286         }
287 }
288
289 uint32_t Z80PIO::get_intr_ack()
290 {
291         // ack (M1=IORQ=L)
292         for(int ch = 0; ch < 2; ch++) {
293                 if(port[ch].in_service) {
294                         // invalid interrupt status
295                         return 0xff;
296                 }
297                 if(port[ch].enb_intr && port[ch].req_intr) {
298                         port[ch].req_intr = false;
299                         port[ch].in_service = true;
300                         update_intr();
301                         return port[ch].vector;
302                 }
303         }
304         if(d_child) {
305                 return d_child->get_intr_ack();
306         }
307         return 0xff;
308 }
309
310 void Z80PIO::notify_intr_reti()
311 {
312         // detect RETI
313         for(int ch = 0; ch < 2; ch++) {
314                 if(port[ch].in_service) {
315                         port[ch].in_service = false;
316                         update_intr();
317                         return;
318                 }
319         }
320         if(d_child) {
321                 d_child->notify_intr_reti();
322         }
323 }
324
325 #define STATE_VERSION   1
326
327 bool Z80PIO::process_state(FILEIO* state_fio, bool loading)
328 {
329         if(!state_fio->StateCheckUint32(STATE_VERSION)) {
330                 return false;
331         }
332         if(!state_fio->StateCheckInt32(this_device_id)) {
333                 return false;
334         }
335         for(int i = 0; i < 2; i++) {
336                 state_fio->StateValue(port[i].wreg);
337                 state_fio->StateValue(port[i].rreg);
338                 state_fio->StateValue(port[i].mode);
339                 state_fio->StateValue(port[i].ctrl1);
340                 state_fio->StateValue(port[i].ctrl2);
341                 state_fio->StateValue(port[i].dir);
342                 state_fio->StateValue(port[i].mask);
343                 state_fio->StateValue(port[i].vector);
344                 state_fio->StateValue(port[i].set_dir);
345                 state_fio->StateValue(port[i].set_mask);
346                 state_fio->StateValue(port[i].hand_shake);
347                 state_fio->StateValue(port[i].ready_signal);
348                 state_fio->StateValue(port[i].input_empty);
349                 state_fio->StateValue(port[i].output_ready);
350                 state_fio->StateValue(port[i].enb_intr);
351                 state_fio->StateValue(port[i].enb_intr_tmp);
352                 state_fio->StateValue(port[i].req_intr);
353                 state_fio->StateValue(port[i].in_service);
354         }
355         state_fio->StateValue(iei);
356         state_fio->StateValue(oei);
357         state_fio->StateValue(intr_bit);
358         return true;
359 }
360