ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/CPUC64.cpp
Revision: 1.6
Committed: 2004-01-12T15:13:20Z (20 years, 10 months ago) by cebix
Branch: MAIN
Changes since 1.5: +1 -1 lines
Log Message:
Happy New Year!

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * CPUC64.cpp - 6510 (C64) emulation (line based)
3     *
4 cebix 1.6 * Frodo (C) 1994-1997,2002-2004 Christian Bauer
5 cebix 1.1 *
6     * This program is free software; you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation; either version 2 of the License, or
9     * (at your option) any later version.
10     *
11     * This program is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with this program; if not, write to the Free Software
18     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19     */
20    
21     /*
22     * Notes:
23     * ------
24     *
25     * - The EmulateLine() function is called for every emulated
26     * raster line. It has a cycle counter that is decremented
27     * by every executed opcode and if the counter goes below
28     * zero, the function returns.
29     * - Memory configurations:
30     * $01 $a000-$bfff $d000-$dfff $e000-$ffff
31     * -----------------------------------------------
32     * 0 RAM RAM RAM
33     * 1 RAM Char ROM RAM
34     * 2 RAM Char ROM Kernal ROM
35     * 3 Basic ROM Char ROM Kernal ROM
36     * 4 RAM RAM RAM
37     * 5 RAM I/O RAM
38     * 6 RAM I/O Kernal ROM
39     * 7 Basic ROM I/O Kernal ROM
40     * - All memory accesses are done with the read_byte() and
41     * write_byte() functions which also do the memory address
42     * decoding. The read_zp() and write_zp() functions allow
43     * faster access to the zero page, the pop_byte() and
44     * push_byte() macros for the stack.
45     * - If a write occurs to addresses 0 or 1, new_config is
46     * called to check whether the memory configuration has
47     * changed
48     * - The PC is either emulated with a 16 bit address or a
49     * direct memory pointer (for faster access), depending on
50     * the PC_IS_POINTER #define. In the latter case, a second
51     * pointer, pc_base, is kept to allow recalculating the
52     * 16 bit 6510 PC if it has to be pushed on the stack.
53     * - The possible interrupt sources are:
54     * INT_VICIRQ: I flag is checked, jump to ($fffe)
55     * INT_CIAIRQ: I flag is checked, jump to ($fffe)
56     * INT_NMI: Jump to ($fffa)
57     * INT_RESET: Jump to ($fffc)
58     * - Interrupts are not checked before every opcode but only
59     * at certain times:
60     * On entering EmulateLine()
61     * On CLI
62     * On PLP if the I flag was cleared
63     * On RTI if the I flag was cleared
64     * - The z_flag variable has the inverse meaning of the
65     * 6510 Z flag
66     * - Only the highest bit of the n_flag variable is used
67     * - The $f2 opcode that would normally crash the 6510 is
68     * used to implement emulator-specific functions, mainly
69     * those for the IEC routines
70     *
71     * Incompatibilities:
72     * ------------------
73     *
74     * - If PC_IS_POINTER is set, neither branches accross memory
75     * areas nor jumps to I/O space are possible
76     * - Extra cycles for crossing page boundaries are not
77     * accounted for
78     * - The cassette sense line is always closed
79     */
80    
81     #include "sysdeps.h"
82    
83     #include "CPUC64.h"
84     #include "C64.h"
85     #include "VIC.h"
86     #include "SID.h"
87     #include "CIA.h"
88     #include "REU.h"
89     #include "IEC.h"
90     #include "Display.h"
91     #include "Version.h"
92    
93    
94     enum {
95     INT_RESET = 3
96     };
97    
98    
99     /*
100     * 6510 constructor: Initialize registers
101     */
102    
103     MOS6510::MOS6510(C64 *c64, uint8 *Ram, uint8 *Basic, uint8 *Kernal, uint8 *Char, uint8 *Color)
104     : the_c64(c64), ram(Ram), basic_rom(Basic), kernal_rom(Kernal), char_rom(Char), color_ram(Color)
105     {
106     a = x = y = 0;
107     sp = 0xff;
108     n_flag = z_flag = 0;
109     v_flag = d_flag = c_flag = false;
110     i_flag = true;
111     dfff_byte = 0x55;
112     borrowed_cycles = 0;
113     }
114    
115    
116     /*
117     * Reset CPU asynchronously
118     */
119    
120     void MOS6510::AsyncReset(void)
121     {
122     interrupt.intr[INT_RESET] = true;
123     }
124    
125    
126     /*
127     * Raise NMI asynchronously (Restore key)
128     */
129    
130     void MOS6510::AsyncNMI(void)
131     {
132     if (!nmi_state)
133     interrupt.intr[INT_NMI] = true;
134     }
135    
136    
137     /*
138     * Memory configuration has probably changed
139     */
140    
141     void MOS6510::new_config(void)
142     {
143     uint8 port = ~ram[0] | ram[1];
144    
145     basic_in = (port & 3) == 3;
146     kernal_in = port & 2;
147     char_in = (port & 3) && !(port & 4);
148     io_in = (port & 3) && (port & 4);
149     }
150    
151    
152     /*
153     * Read a byte from I/O / ROM space
154     */
155    
156     inline uint8 MOS6510::read_byte_io(uint16 adr)
157     {
158     switch (adr >> 12) {
159     case 0xa:
160     case 0xb:
161     if (basic_in)
162     return basic_rom[adr & 0x1fff];
163     else
164     return ram[adr];
165     case 0xc:
166     return ram[adr];
167     case 0xd:
168     if (io_in)
169     switch ((adr >> 8) & 0x0f) {
170     case 0x0: // VIC
171     case 0x1:
172     case 0x2:
173     case 0x3:
174     return TheVIC->ReadRegister(adr & 0x3f);
175     case 0x4: // SID
176     case 0x5:
177     case 0x6:
178     case 0x7:
179     return TheSID->ReadRegister(adr & 0x1f);
180     case 0x8: // Color RAM
181     case 0x9:
182     case 0xa:
183     case 0xb:
184     return color_ram[adr & 0x03ff] | rand() & 0xf0;
185     case 0xc: // CIA 1
186     return TheCIA1->ReadRegister(adr & 0x0f);
187     case 0xd: // CIA 2
188     return TheCIA2->ReadRegister(adr & 0x0f);
189     case 0xe: // REU/Open I/O
190     case 0xf:
191     if ((adr & 0xfff0) == 0xdf00)
192     return TheREU->ReadRegister(adr & 0x0f);
193     else if (adr < 0xdfa0)
194     return rand();
195     else
196     return read_emulator_id(adr & 0x7f);
197     }
198     else if (char_in)
199     return char_rom[adr & 0x0fff];
200     else
201     return ram[adr];
202     case 0xe:
203     case 0xf:
204     if (kernal_in)
205     return kernal_rom[adr & 0x1fff];
206     else
207     return ram[adr];
208     default: // Can't happen
209     return 0;
210     }
211     }
212    
213    
214     /*
215     * Read a byte from the CPU's address space
216     */
217    
218     uint8 MOS6510::read_byte(uint16 adr)
219     {
220     if (adr < 0xa000)
221     return ram[adr];
222     else
223     return read_byte_io(adr);
224     }
225    
226    
227     /*
228     * $dfa0-$dfff: Emulator identification
229     */
230    
231     const char frodo_id[0x5c] = "FRODO\r(C) 1994-1997 CHRISTIAN BAUER";
232    
233     uint8 MOS6510::read_emulator_id(uint16 adr)
234     {
235     switch (adr) {
236     case 0x7c: // $dffc: revision
237     return FRODO_REVISION << 4;
238     case 0x7d: // $dffd: version
239     return FRODO_VERSION;
240     case 0x7e: // $dffe returns 'F' (Frodo ID)
241     return 'F';
242     case 0x7f: // $dfff alternates between $55 and $aa
243     dfff_byte = ~dfff_byte;
244     return dfff_byte;
245     default:
246     return frodo_id[adr - 0x20];
247     }
248     }
249    
250    
251     /*
252     * Read a word (little-endian) from the CPU's address space
253     */
254    
255     #if LITTLE_ENDIAN_UNALIGNED
256    
257     inline uint16 MOS6510::read_word(uint16 adr)
258     {
259     switch (adr >> 12) {
260     case 0x0:
261     case 0x1:
262     case 0x2:
263     case 0x3:
264     case 0x4:
265     case 0x5:
266     case 0x6:
267     case 0x7:
268     case 0x8:
269     case 0x9:
270     return *(uint16*)&ram[adr];
271     break;
272     case 0xa:
273     case 0xb:
274     if (basic_in)
275     return *(uint16*)&basic_rom[adr & 0x1fff];
276     else
277     return *(uint16*)&ram[adr];
278     case 0xc:
279     return *(uint16*)&ram[adr];
280     case 0xd:
281     if (io_in)
282     return read_byte(adr) | (read_byte(adr+1) << 8);
283     else if (char_in)
284     return *(uint16*)&char_rom[adr & 0x0fff];
285     else
286     return *(uint16*)&ram[adr];
287     case 0xe:
288     case 0xf:
289     if (kernal_in)
290     return *(uint16*)&kernal_rom[adr & 0x1fff];
291     else
292     return *(uint16*)&ram[adr];
293     default: // Can't happen
294     return 0;
295     }
296     }
297    
298     #else
299    
300     inline uint16 MOS6510::read_word(uint16 adr)
301     {
302     return read_byte(adr) | (read_byte(adr+1) << 8);
303     }
304    
305     #endif
306    
307    
308     /*
309     * Write byte to I/O space
310     */
311    
312     void MOS6510::write_byte_io(uint16 adr, uint8 byte)
313     {
314     if (adr >= 0xe000) {
315     ram[adr] = byte;
316     if (adr == 0xff00)
317     TheREU->FF00Trigger();
318     } else if (io_in)
319     switch ((adr >> 8) & 0x0f) {
320     case 0x0: // VIC
321     case 0x1:
322     case 0x2:
323     case 0x3:
324     TheVIC->WriteRegister(adr & 0x3f, byte);
325     return;
326     case 0x4: // SID
327     case 0x5:
328     case 0x6:
329     case 0x7:
330     TheSID->WriteRegister(adr & 0x1f, byte);
331     return;
332     case 0x8: // Color RAM
333     case 0x9:
334     case 0xa:
335     case 0xb:
336     color_ram[adr & 0x03ff] = byte & 0x0f;
337     return;
338     case 0xc: // CIA 1
339     TheCIA1->WriteRegister(adr & 0x0f, byte);
340     return;
341     case 0xd: // CIA 2
342     TheCIA2->WriteRegister(adr & 0x0f, byte);
343     return;
344     case 0xe: // REU/Open I/O
345     case 0xf:
346     if ((adr & 0xfff0) == 0xdf00)
347     TheREU->WriteRegister(adr & 0x0f, byte);
348     return;
349     }
350     else
351     ram[adr] = byte;
352     }
353    
354    
355     /*
356     * Write a byte to the CPU's address space
357     */
358    
359     inline void MOS6510::write_byte(uint16 adr, uint8 byte)
360     {
361     if (adr < 0xd000) {
362     ram[adr] = byte;
363     if (adr < 2)
364     new_config();
365     } else
366     write_byte_io(adr, byte);
367     }
368    
369    
370     /*
371     * Read a byte from the zeropage
372     */
373    
374     inline uint8 MOS6510::read_zp(uint16 adr)
375     {
376     return ram[adr];
377     }
378    
379    
380     /*
381     * Read a word (little-endian) from the zeropage
382     */
383    
384     inline uint16 MOS6510::read_zp_word(uint16 adr)
385     {
386     // !! zeropage word addressing wraps around !!
387     #if LITTLE_ENDIAN_UNALIGNED
388     return *(uint16 *)&ram[adr & 0xff];
389     #else
390     return ram[adr & 0xff] | (ram[(adr+1) & 0xff] << 8);
391     #endif
392     }
393    
394    
395     /*
396     * Write a byte to the zeropage
397     */
398    
399     inline void MOS6510::write_zp(uint16 adr, uint8 byte)
400     {
401     ram[adr] = byte;
402    
403     // Check if memory configuration may have changed.
404     if (adr < 2)
405     new_config();
406     }
407    
408    
409     /*
410     * Read byte from 6510 address space with special memory config (used by SAM)
411     */
412    
413     uint8 MOS6510::ExtReadByte(uint16 adr)
414     {
415     // Save old memory configuration
416     bool bi = basic_in, ki = kernal_in, ci = char_in, ii = io_in;
417    
418     // Set new configuration
419     basic_in = (ExtConfig & 3) == 3;
420     kernal_in = ExtConfig & 2;
421     char_in = (ExtConfig & 3) && ~(ExtConfig & 4);
422     io_in = (ExtConfig & 3) && (ExtConfig & 4);
423    
424     // Read byte
425     uint8 byte = read_byte(adr);
426    
427     // Restore old configuration
428     basic_in = bi; kernal_in = ki; char_in = ci; io_in = ii;
429    
430     return byte;
431     }
432    
433    
434     /*
435     * Write byte to 6510 address space with special memory config (used by SAM)
436     */
437    
438     void MOS6510::ExtWriteByte(uint16 adr, uint8 byte)
439     {
440     // Save old memory configuration
441     bool bi = basic_in, ki = kernal_in, ci = char_in, ii = io_in;
442    
443     // Set new configuration
444     basic_in = (ExtConfig & 3) == 3;
445     kernal_in = ExtConfig & 2;
446     char_in = (ExtConfig & 3) && ~(ExtConfig & 4);
447     io_in = (ExtConfig & 3) && (ExtConfig & 4);
448    
449     // Write byte
450     write_byte(adr, byte);
451    
452     // Restore old configuration
453     basic_in = bi; kernal_in = ki; char_in = ci; io_in = ii;
454     }
455    
456    
457     /*
458     * Read byte from 6510 address space with current memory config (used by REU)
459     */
460    
461     uint8 MOS6510::REUReadByte(uint16 adr)
462     {
463     return read_byte(adr);
464     }
465    
466    
467     /*
468     * Write byte to 6510 address space with current memory config (used by REU)
469     */
470    
471     void MOS6510::REUWriteByte(uint16 adr, uint8 byte)
472     {
473     write_byte(adr, byte);
474     }
475    
476    
477     /*
478     * Jump to address
479     */
480    
481     #if PC_IS_POINTER
482 cebix 1.3 #define jump(adr) \
483     if ((adr) < 0xa000) { \
484     pc = ram + (adr); \
485     pc_base = ram; \
486     } else { \
487     switch ((adr) >> 12) { \
488     case 0xa: \
489     case 0xb: \
490     if (basic_in) { \
491     pc = basic_rom + ((adr) & 0x1fff); \
492     pc_base = basic_rom - 0xa000; \
493     } else { \
494     pc = ram + (adr); \
495     pc_base = ram; \
496     } \
497     break; \
498     case 0xc: \
499     pc = ram + (adr); \
500     pc_base = ram; \
501     break; \
502     case 0xd: \
503     if (io_in) { \
504     illegal_jump(pc-pc_base, (adr)); \
505     } else if (char_in) { \
506     pc = char_rom + ((adr) & 0x0fff); \
507     pc_base = char_rom - 0xd000; \
508     } else { \
509     pc = ram + (adr); \
510     pc_base = ram; \
511     } \
512     break; \
513     case 0xe: \
514     case 0xf: \
515     if (kernal_in) { \
516     pc = kernal_rom + ((adr) & 0x1fff); \
517     pc_base = kernal_rom - 0xe000; \
518     } else { \
519     pc = ram + (adr); \
520     pc_base = ram; \
521     } \
522     break; \
523     } \
524     }
525 cebix 1.1 #else
526 cebix 1.3 #define jump(adr) pc = (adr)
527 cebix 1.1 #endif
528    
529    
530     /*
531     * Adc instruction
532     */
533    
534     void MOS6510::do_adc(uint8 byte)
535     {
536     if (!d_flag) {
537 cebix 1.3 uint16 tmp = a + (byte) + (c_flag ? 1 : 0);
538 cebix 1.1 c_flag = tmp > 0xff;
539 cebix 1.3 v_flag = !((a ^ (byte)) & 0x80) && ((a ^ tmp) & 0x80);
540 cebix 1.1 z_flag = n_flag = a = tmp;
541     } else {
542     uint16 al, ah;
543 cebix 1.3 al = (a & 0x0f) + ((byte) & 0x0f) + (c_flag ? 1 : 0);
544     if (al > 9) al += 6;
545     ah = (a >> 4) + ((byte) >> 4);
546 cebix 1.1 if (al > 0x0f) ah++;
547 cebix 1.3 z_flag = a + (byte) + (c_flag ? 1 : 0);
548     n_flag = ah << 4;
549     v_flag = (((ah << 4) ^ a) & 0x80) && !((a ^ (byte)) & 0x80);
550     if (ah > 9) ah += 6;
551     c_flag = ah > 0x0f;
552     a = (ah << 4) | (al & 0x0f);
553 cebix 1.1 }
554     }
555    
556    
557     /*
558     * Sbc instruction
559     */
560    
561     void MOS6510::do_sbc(uint8 byte)
562     {
563 cebix 1.3 uint16 tmp = a - (byte) - (c_flag ? 0 : 1);
564 cebix 1.1 if (!d_flag) {
565     c_flag = tmp < 0x100;
566 cebix 1.3 v_flag = ((a ^ tmp) & 0x80) && ((a ^ (byte)) & 0x80);
567 cebix 1.1 z_flag = n_flag = a = tmp;
568     } else {
569     uint16 al, ah;
570 cebix 1.3 al = (a & 0x0f) - ((byte) & 0x0f) - (c_flag ? 0 : 1);
571     ah = (a >> 4) - ((byte) >> 4);
572 cebix 1.1 if (al & 0x10) {
573 cebix 1.3 al -= 6;
574 cebix 1.1 ah--;
575     }
576 cebix 1.3 if (ah & 0x10) ah -= 6;
577     c_flag = tmp < 0x100;
578     v_flag = ((a ^ tmp) & 0x80) && ((a ^ (byte)) & 0x80);
579 cebix 1.1 z_flag = n_flag = tmp;
580 cebix 1.3 a = (ah << 4) | (al & 0x0f);
581 cebix 1.1 }
582     }
583    
584    
585     /*
586     * Get 6510 register state
587     */
588    
589     void MOS6510::GetState(MOS6510State *s)
590     {
591     s->a = a;
592     s->x = x;
593     s->y = y;
594    
595     s->p = 0x20 | (n_flag & 0x80);
596     if (v_flag) s->p |= 0x40;
597     if (d_flag) s->p |= 0x08;
598     if (i_flag) s->p |= 0x04;
599     if (!z_flag) s->p |= 0x02;
600     if (c_flag) s->p |= 0x01;
601 cebix 1.3
602 cebix 1.1 s->ddr = ram[0];
603     s->pr = ram[1] & 0x3f;
604    
605     #if PC_IS_POINTER
606     s->pc = pc - pc_base;
607     #else
608     s->pc = pc;
609     #endif
610     s->sp = sp | 0x0100;
611    
612     s->intr[INT_VICIRQ] = interrupt.intr[INT_VICIRQ];
613     s->intr[INT_CIAIRQ] = interrupt.intr[INT_CIAIRQ];
614     s->intr[INT_NMI] = interrupt.intr[INT_NMI];
615     s->intr[INT_RESET] = interrupt.intr[INT_RESET];
616     s->nmi_state = nmi_state;
617     s->dfff_byte = dfff_byte;
618     s->instruction_complete = true;
619     }
620    
621    
622     /*
623     * Restore 6510 state
624     */
625    
626     void MOS6510::SetState(MOS6510State *s)
627     {
628     a = s->a;
629     x = s->x;
630     y = s->y;
631    
632     n_flag = s->p;
633     v_flag = s->p & 0x40;
634     d_flag = s->p & 0x08;
635     i_flag = s->p & 0x04;
636     z_flag = !(s->p & 0x02);
637     c_flag = s->p & 0x01;
638    
639     ram[0] = s->ddr;
640     ram[1] = s->pr;
641     new_config();
642    
643     jump(s->pc);
644     sp = s->sp & 0xff;
645    
646     interrupt.intr[INT_VICIRQ] = s->intr[INT_VICIRQ];
647     interrupt.intr[INT_CIAIRQ] = s->intr[INT_CIAIRQ];
648     interrupt.intr[INT_NMI] = s->intr[INT_NMI];
649     interrupt.intr[INT_RESET] = s->intr[INT_RESET];
650     nmi_state = s->nmi_state;
651     dfff_byte = s->dfff_byte;
652     }
653    
654    
655     /*
656     * Reset CPU
657     */
658    
659     void MOS6510::Reset(void)
660     {
661     // Delete 'CBM80' if present
662     if (ram[0x8004] == 0xc3 && ram[0x8005] == 0xc2 && ram[0x8006] == 0xcd
663     && ram[0x8007] == 0x38 && ram[0x8008] == 0x30)
664     ram[0x8004] = 0;
665    
666     // Initialize extra 6510 registers and memory configuration
667     ram[0] = ram[1] = 0;
668     new_config();
669    
670     // Clear all interrupt lines
671     interrupt.intr_any = 0;
672     nmi_state = false;
673    
674     // Read reset vector
675     jump(read_word(0xfffc));
676     }
677    
678    
679     /*
680     * Illegal opcode encountered
681     */
682    
683     void MOS6510::illegal_op(uint8 op, uint16 at)
684     {
685     char illop_msg[80];
686    
687     sprintf(illop_msg, "Illegal opcode %02x at %04x.", op, at);
688     ShowRequester(illop_msg, "Reset");
689     the_c64->Reset();
690     Reset();
691     }
692    
693    
694     /*
695     * Jump to illegal address space (PC_IS_POINTER only)
696     */
697    
698     void MOS6510::illegal_jump(uint16 at, uint16 to)
699     {
700     char illop_msg[80];
701    
702     sprintf(illop_msg, "Jump to I/O space at %04x to %04x.", at, to);
703     ShowRequester(illop_msg, "Reset");
704     the_c64->Reset();
705     Reset();
706     }
707    
708    
709     /*
710     * Stack macros
711     */
712    
713     // Pop a byte from the stack
714     #define pop_byte() ram[(++sp) | 0x0100]
715    
716     // Push a byte onto the stack
717     #define push_byte(byte) (ram[(sp--) & 0xff | 0x0100] = (byte))
718    
719     // Pop processor flags from the stack
720     #define pop_flags() \
721     n_flag = tmp = pop_byte(); \
722     v_flag = tmp & 0x40; \
723     d_flag = tmp & 0x08; \
724     i_flag = tmp & 0x04; \
725     z_flag = !(tmp & 0x02); \
726     c_flag = tmp & 0x01;
727    
728     // Push processor flags onto the stack
729     #define push_flags(b_flag) \
730     tmp = 0x20 | (n_flag & 0x80); \
731     if (v_flag) tmp |= 0x40; \
732     if (b_flag) tmp |= 0x10; \
733     if (d_flag) tmp |= 0x08; \
734     if (i_flag) tmp |= 0x04; \
735     if (!z_flag) tmp |= 0x02; \
736     if (c_flag) tmp |= 0x01; \
737     push_byte(tmp);
738    
739    
740     /*
741     * Emulate cycles_left worth of 6510 instructions
742     * Returns number of cycles of last instruction
743     */
744    
745     int MOS6510::EmulateLine(int cycles_left)
746     {
747     uint8 tmp, tmp2;
748     uint16 adr; // Used by read_adr_abs()!
749     int last_cycles = 0;
750    
751     // Any pending interrupts?
752     if (interrupt.intr_any) {
753     handle_int:
754 cebix 1.3 if (interrupt.intr[INT_RESET]) {
755 cebix 1.1 Reset();
756    
757 cebix 1.3 } else if (interrupt.intr[INT_NMI]) {
758 cebix 1.1 interrupt.intr[INT_NMI] = false; // Simulate an edge-triggered input
759     #if PC_IS_POINTER
760     push_byte((pc-pc_base) >> 8); push_byte(pc-pc_base);
761     #else
762     push_byte(pc >> 8); push_byte(pc);
763     #endif
764     push_flags(false);
765     i_flag = true;
766 cebix 1.3 adr = read_word(0xfffa);
767     jump(adr);
768 cebix 1.1 last_cycles = 7;
769    
770     } else if ((interrupt.intr[INT_VICIRQ] || interrupt.intr[INT_CIAIRQ]) && !i_flag) {
771     #if PC_IS_POINTER
772     push_byte((pc-pc_base) >> 8); push_byte(pc-pc_base);
773     #else
774     push_byte(pc >> 8); push_byte(pc);
775     #endif
776     push_flags(false);
777     i_flag = true;
778 cebix 1.3 adr = read_word(0xfffe);
779     jump(adr);
780 cebix 1.1 last_cycles = 7;
781     }
782     }
783    
784 cebix 1.4 #include "CPU_emulline.h"
785 cebix 1.1
786     // Extension opcode
787     case 0xf2:
788     #if PC_IS_POINTER
789     if ((pc-pc_base) < 0xe000) {
790     illegal_op(0xf2, pc-pc_base-1);
791     #else
792     if (pc < 0xe000) {
793     illegal_op(0xf2, pc-1);
794     #endif
795     break;
796     }
797     switch (read_byte_imm()) {
798     case 0x00:
799     ram[0x90] |= TheIEC->Out(ram[0x95], ram[0xa3] & 0x80);
800     c_flag = false;
801     jump(0xedac);
802     break;
803     case 0x01:
804     ram[0x90] |= TheIEC->OutATN(ram[0x95]);
805     c_flag = false;
806     jump(0xedac);
807     break;
808     case 0x02:
809     ram[0x90] |= TheIEC->OutSec(ram[0x95]);
810     c_flag = false;
811     jump(0xedac);
812     break;
813     case 0x03:
814 cebix 1.5 ram[0x90] |= TheIEC->In(a);
815 cebix 1.1 set_nz(a);
816     c_flag = false;
817     jump(0xedac);
818     break;
819     case 0x04:
820     TheIEC->SetATN();
821     jump(0xedfb);
822     break;
823     case 0x05:
824     TheIEC->RelATN();
825     jump(0xedac);
826     break;
827     case 0x06:
828     TheIEC->Turnaround();
829     jump(0xedac);
830     break;
831     case 0x07:
832     TheIEC->Release();
833     jump(0xedac);
834     break;
835     default:
836     #if PC_IS_POINTER
837     illegal_op(0xf2, pc-pc_base-1);
838     #else
839     illegal_op(0xf2, pc-1);
840     #endif
841     break;
842     }
843     break;
844     }
845     }
846     return last_cycles;
847     }