OSDN Git Service

[VM][DEVICE][WIP] Updating State functions.Still cause FTBFS.
[csp-qt/common_source_project-fm7.git] / source / src / vm / i8155.cpp
1 /*
2         Skelton for retropc emulator
3
4         Author : Takeda.Toshiya
5         Date   : 2009.01.05-
6
7         [ i8155 ]
8 */
9
10 #include "i8155.h"
11
12 // status and portc
13 #define STA_INTR_A      1
14 #define STA_BF_A        2
15 #define STA_STB_A       4
16 #define STA_INTE_A      4
17 #define STA_INTR_B      8
18 #define STA_BF_B        0x10
19 #define STA_STB_B       0x20
20 #define STA_INTE_B      0x20
21 #define STA_INTR_T      0x40
22
23 // command
24 #define CMD_INTE_A      0x10
25 #define CMD_INTE_B      0x20
26
27 // mode
28 #define PIO_MODE_3      ((cmdreg & 0xc) == 4)
29 #define PIO_MODE_4      ((cmdreg & 0xc) == 8)
30
31 void I8155::initialize()
32 {
33         DEVICE::initialize();
34         // initialize timer
35         count = countreg = 0x3fff;
36         prev_out = true;
37         prev_in = false;
38         now_count = stop_tc = false;
39         half = true;
40         
41         // clear ram
42         memset(ram, 0, sizeof(ram));
43 }
44
45 void I8155::reset()
46 {
47         // reset pio
48         for(int i = 0; i < 3; i++) {
49                 pio[i].rmask = 0xff;
50                 pio[i].wreg = 0;
51                 pio[i].rreg = 0;
52                 pio[i].first = true;
53         }
54         statreg = cmdreg = 0;
55         register_id = -1;
56         
57         // stop count but don't reset timer
58         stop_count();
59 }
60
61 void I8155::write_data8(uint32_t addr, uint32_t data)
62 {
63         ram[addr & 0xff] = data;
64 }
65
66 uint32_t I8155::read_data8(uint32_t addr)
67 {
68         return ram[addr & 0xff];
69 }
70
71 void I8155::write_io8(uint32_t addr, uint32_t data)
72 {
73         switch(addr & 7) {
74         case 0:
75                 pio[0].rmask = (data & 1) ? 0 : 0xff;
76                 pio[1].rmask = (data & 2) ? 0 : 0xff;
77                 pio[2].rmask = (data & 0xc) ? 0 : 0xff;
78                 statreg &= ~(STA_INTE_A | STA_INTE_B);
79                 if(data & CMD_INTE_A) {
80                         statreg |= STA_INTE_A;
81                 }
82                 if(data & CMD_INTE_B) {
83                         statreg |= STA_INTE_B;
84                 }
85                 // timer operation
86                 switch(data & 0xc0) {
87                 case 0x40:
88                         stop_count();
89                         break;
90                 case 0x80:
91                         stop_tc = true;
92                         break;
93                 case 0xc0:
94                         start_count();
95                         break;
96                 }
97                 cmdreg = data;
98                 break;
99         case 1:
100                 set_pio(0, data);
101                 break;
102         case 2:
103                 set_pio(1, data);
104                 break;
105         case 3:
106                 if(PIO_MODE_3) {
107                         data = (data & ~7) | (pio[2].wreg & 7);
108                 }
109                 if(!PIO_MODE_4) {
110                         set_pio(2, data & 0x3f);
111                 }
112                 break;
113         case 4:
114                 countreg = (countreg & 0xff00) | data;
115                 break;
116         case 5:
117                 countreg = (countreg & 0xff) | (data << 8);
118                 break;
119         }
120 }
121
122 uint32_t I8155::read_io8(uint32_t addr)
123 {
124         switch(addr & 7) {
125         case 0:
126                 if(statreg & STA_INTR_T) {
127                         statreg &= ~STA_INTR_T;
128                         return statreg | STA_INTR_T;
129                 }
130                 return statreg;
131         case 1:
132                 if(PIO_MODE_3 || PIO_MODE_4) {
133                         statreg &= ~(STA_INTR_A | STA_BF_A);
134                         set_pio(2, pio[2].wreg & ~(STA_INTR_A | STA_BF_A));
135                 }
136                 return (pio[0].rreg & pio[0].rmask) | (pio[0].wreg & ~pio[0].rmask);
137         case 2:
138                 if(PIO_MODE_4) {
139                         statreg &= ~(STA_INTR_B | STA_BF_B);
140                         set_pio(2, pio[2].wreg & ~(STA_INTR_B | STA_BF_B));
141                 }
142                 return (pio[1].rreg & pio[1].rmask) | (pio[1].wreg & ~pio[1].rmask);
143         case 3:
144                 return (pio[2].rreg & pio[2].rmask) | (pio[2].wreg & ~pio[2].rmask);
145         case 4:
146                 update_count();
147                 return count & 0xff;
148         case 5:
149                 update_count();
150                 return ((count >> 8) & 0x3f) | ((countreg >> 8) & 0xc0);
151         }
152         return 0xff;
153 }
154
155 void I8155::write_signal(int id, uint32_t data, uint32_t mask)
156 {
157         switch(id) {
158         case SIG_I8155_PORT_A:
159                 if(PIO_MODE_3 || PIO_MODE_4) {
160                         // note: strobe signal must be checked
161                         uint32_t val = pio[2].wreg | STA_BF_A;
162                         statreg |= STA_BF_A;
163                         if(cmdreg & CMD_INTE_A) {
164                                 val |= STA_INTR_A;
165                                 statreg |= STA_INTR_A;
166                         }
167                         set_pio(2, val);
168                 }
169                 pio[0].rreg = (pio[0].rreg & ~mask) | (data & mask);
170                 break;
171         case SIG_I8155_PORT_B:
172                 if(PIO_MODE_4) {
173                         // note: strobe signal must be checked
174                         uint32_t val = pio[2].wreg | STA_BF_B;
175                         statreg |= STA_BF_B;
176                         if(cmdreg & CMD_INTE_B) {
177                                 val |= STA_INTR_B;
178                                 statreg |= STA_INTR_B;
179                         }
180                         set_pio(2, val);
181                 }
182                 pio[1].rreg = (pio[1].rreg & ~mask) | (data & mask);
183                 break;
184         case SIG_I8155_PORT_C:
185                 pio[2].rreg = (pio[2].rreg & ~mask) | (data & mask);
186                 break;
187         case SIG_I8155_CLOCK:
188                 if(prev_in && !(data & mask)) {
189                         input_clock(1);
190                 }
191                 prev_in = ((data & mask) != 0);
192                 break;
193         }
194 }
195
196 #define COUNT_VALUE ((countreg & 0x3fff) > 2 ? (countreg & 0x3fff) : 2)
197
198 void I8155::event_callback(int event_id, int err)
199 {
200         register_id = -1;
201         input_clock(input_clk);
202         
203         // register next event
204         if(freq && now_count) {
205                 input_clk = get_next_clock();
206                 period = (int)(cpu_clocks * input_clk / freq) + err;
207                 prev_clk = get_current_clock() + err;
208                 register_event_by_clock(this, 0, period, false, &register_id);
209         }
210 }
211
212 void I8155::input_clock(int clock)
213 {
214         if(!(now_count && clock)) {
215                 return;
216         }
217         
218         // update counter
219         count -= clock;
220         int32_t tmp = COUNT_VALUE;
221 loop:
222         if(half) {
223                 set_signal(count > (tmp >> 1));
224         } else {
225                 set_signal(count > 1);
226         }
227         if(count <= 0) {
228                 statreg |= STA_INTR_T;
229                 if(!stop_tc) {
230                         set_signal(true);
231                         count += tmp;
232                         goto loop;
233                 } else {
234                         now_count = false;
235                 }
236         }
237 }
238
239 void I8155::start_count()
240 {
241         // set timer mode
242         stop_tc = ((countreg & 0x4000) == 0);
243         half = ((countreg & 0x8000) == 0);
244         
245         if(!now_count) {
246                 count = COUNT_VALUE;
247                 now_count = true;
248                 
249                 // register event
250                 if(freq && register_id == -1) {
251                         input_clk = get_next_clock();
252                         period = (int)(cpu_clocks * input_clk / freq);
253                         prev_clk = get_current_clock();
254                         register_event_by_clock(this, 0, period, false, &register_id);
255                 }
256         }
257 }
258
259 void I8155::stop_count()
260 {
261         if(register_id != -1) {
262                 cancel_event(this, register_id);
263         }
264         register_id = -1;
265         now_count = false;
266 }
267
268 void I8155::update_count()
269 {
270         if(register_id != -1) {
271                 // update counter
272                 int passed = get_passed_clock(prev_clk);
273                 uint32_t input = (uint32_t)(freq * passed / cpu_clocks);
274                 if(input_clk <= input) {
275                         input = input_clk - 1;
276                 }
277                 if(input > 0) {
278                         input_clock(input);
279                         // cancel and re-register event
280                         cancel_event(this, register_id);
281                         input_clk -= input;
282                         period -= passed;
283                         prev_clk = get_current_clock();
284                         register_event_by_clock(this, 0, period, false, &register_id);
285                 }
286         }
287 }
288
289 int I8155::get_next_clock()
290 {
291         if(half) {
292                 int32_t tmp = COUNT_VALUE >> 1;
293                 return (count > tmp) ? count - tmp : count;
294         }
295         return (count > 1) ? count - 1 : 1;
296 }
297
298 void I8155::set_signal(bool signal)
299 {
300         if(prev_out && !signal) {
301                 // H->L
302                 write_signals(&outputs_timer, 0);
303         } else if(!prev_out && signal) {
304                 // L->H
305                 write_signals(&outputs_timer, 0xffffffff);
306         }
307         prev_out = signal;
308 }
309
310 void I8155::set_pio(int ch, uint8_t data)
311 {
312         if(pio[ch].wreg != data || pio[ch].first) {
313                 write_signals(&pio[ch].outputs, data);
314                 pio[ch].wreg = data;
315                 pio[ch].first = false;
316         }
317 }
318
319 #define STATE_VERSION   1
320
321 bool I8155::process_state(FILEIO* state_fio, bool loading)
322 {
323         if(!state_fio->StateCheckUint32(STATE_VERSION)) {
324                 return false;
325         }
326         if(!state_fio->StateCheckInt32(this_device_id)) {
327                 return false;
328         }
329         state_fio->StateValue(count);
330         state_fio->StateValue(countreg);
331         state_fio->StateValue(now_count);
332         state_fio->StateValue(stop_tc);
333         state_fio->StateValue(half);
334         state_fio->StateValue(prev_out);
335         state_fio->StateValue(prev_in);
336         state_fio->StateValue(freq);
337         state_fio->StateValue(register_id);
338         state_fio->StateValue(input_clk);
339         state_fio->StateValue(prev_clk);
340         state_fio->StateValue(period);
341         state_fio->StateValue(cpu_clocks);
342         for(int i = 0; i < 3; i++) {
343                 state_fio->StateValue(pio[i].wreg);
344                 state_fio->StateValue(pio[i].rreg);
345                 state_fio->StateValue(pio[i].rmask);
346                 state_fio->StateValue(pio[i].mode);
347                 state_fio->StateValue(pio[i].first);
348         }
349         state_fio->StateValue(cmdreg);
350         state_fio->StateValue(statreg);
351         state_fio->StateArray(ram, sizeof(ram), 1);
352         return true;
353 }
354
355