ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/CPUC64.cpp
Revision: 1.2
Committed: 2003-07-01T17:51:17Z (20 years, 9 months ago) by cebix
Branch: MAIN
Changes since 1.1: +1 -1 lines
Log Message:
updated copyright date

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * CPUC64.cpp - 6510 (C64) emulation (line based)
3     *
4 cebix 1.2 * Frodo (C) 1994-1997,2002-2003 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     void MOS6510::jump(uint16 adr)
483     {
484     if (adr < 0xa000) {
485     pc = ram + adr;
486     pc_base = ram;
487     } else
488     switch (adr >> 12) {
489     case 0xa:
490     case 0xb:
491     if (basic_in) {
492     pc = basic_rom + (adr & 0x1fff);
493     pc_base = basic_rom - 0xa000;
494     } else {
495     pc = ram + adr;
496     pc_base = ram;
497     }
498     break;
499     case 0xc:
500     pc = ram + adr;
501     pc_base = ram;
502     break;
503     case 0xd:
504     if (io_in)
505     illegal_jump(pc-pc_base, adr);
506     else if (char_in) {
507     pc = char_rom + (adr & 0x0fff);
508     pc_base = char_rom - 0xd000;
509     } else {
510     pc = ram + adr;
511     pc_base = ram;
512     }
513     break;
514     case 0xe:
515     case 0xf:
516     if (kernal_in) {
517     pc = kernal_rom + (adr & 0x1fff);
518     pc_base = kernal_rom - 0xe000;
519     } else {
520     pc = ram + adr;
521     pc_base = ram;
522     }
523     break;
524     }
525     }
526     #else
527     inline void MOS6510::jump(uint16 adr)
528     {
529     pc = adr;
530     }
531     #endif
532    
533    
534     /*
535     * Adc instruction
536     */
537    
538     void MOS6510::do_adc(uint8 byte)
539     {
540     if (!d_flag) {
541     uint16 tmp;
542    
543     // Binary mode
544     tmp = a + byte + (c_flag ? 1 : 0);
545     c_flag = tmp > 0xff;
546     v_flag = !((a ^ byte) & 0x80) && ((a ^ tmp) & 0x80);
547     z_flag = n_flag = a = tmp;
548    
549     } else {
550     uint16 al, ah;
551    
552     // Decimal mode
553     al = (a & 0x0f) + (byte & 0x0f) + (c_flag ? 1 : 0); // Calculate lower nybble
554     if (al > 9) al += 6; // BCD fixup for lower nybble
555    
556     ah = (a >> 4) + (byte >> 4); // Calculate upper nybble
557     if (al > 0x0f) ah++;
558    
559     z_flag = a + byte + (c_flag ? 1 : 0); // Set flags
560     n_flag = ah << 4; // Only highest bit used
561     v_flag = (((ah << 4) ^ a) & 0x80) && !((a ^ byte) & 0x80);
562    
563     if (ah > 9) ah += 6; // BCD fixup for upper nybble
564     c_flag = ah > 0x0f; // Set carry flag
565     a = (ah << 4) | (al & 0x0f); // Compose result
566     }
567     }
568    
569    
570     /*
571     * Sbc instruction
572     */
573    
574     void MOS6510::do_sbc(uint8 byte)
575     {
576     uint16 tmp = a - byte - (c_flag ? 0 : 1);
577    
578     if (!d_flag) {
579    
580     // Binary mode
581     c_flag = tmp < 0x100;
582     v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80);
583     z_flag = n_flag = a = tmp;
584    
585     } else {
586     uint16 al, ah;
587    
588     // Decimal mode
589     al = (a & 0x0f) - (byte & 0x0f) - (c_flag ? 0 : 1); // Calculate lower nybble
590     ah = (a >> 4) - (byte >> 4); // Calculate upper nybble
591     if (al & 0x10) {
592     al -= 6; // BCD fixup for lower nybble
593     ah--;
594     }
595     if (ah & 0x10) ah -= 6; // BCD fixup for upper nybble
596    
597     c_flag = tmp < 0x100; // Set flags
598     v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80);
599     z_flag = n_flag = tmp;
600    
601     a = (ah << 4) | (al & 0x0f); // Compose result
602     }
603     }
604    
605    
606     /*
607     * Get 6510 register state
608     */
609    
610     void MOS6510::GetState(MOS6510State *s)
611     {
612     s->a = a;
613     s->x = x;
614     s->y = y;
615    
616     s->p = 0x20 | (n_flag & 0x80);
617     if (v_flag) s->p |= 0x40;
618     if (d_flag) s->p |= 0x08;
619     if (i_flag) s->p |= 0x04;
620     if (!z_flag) s->p |= 0x02;
621     if (c_flag) s->p |= 0x01;
622    
623     s->ddr = ram[0];
624     s->pr = ram[1] & 0x3f;
625    
626     #if PC_IS_POINTER
627     s->pc = pc - pc_base;
628     #else
629     s->pc = pc;
630     #endif
631     s->sp = sp | 0x0100;
632    
633     s->intr[INT_VICIRQ] = interrupt.intr[INT_VICIRQ];
634     s->intr[INT_CIAIRQ] = interrupt.intr[INT_CIAIRQ];
635     s->intr[INT_NMI] = interrupt.intr[INT_NMI];
636     s->intr[INT_RESET] = interrupt.intr[INT_RESET];
637     s->nmi_state = nmi_state;
638     s->dfff_byte = dfff_byte;
639     s->instruction_complete = true;
640     }
641    
642    
643     /*
644     * Restore 6510 state
645     */
646    
647     void MOS6510::SetState(MOS6510State *s)
648     {
649     a = s->a;
650     x = s->x;
651     y = s->y;
652    
653     n_flag = s->p;
654     v_flag = s->p & 0x40;
655     d_flag = s->p & 0x08;
656     i_flag = s->p & 0x04;
657     z_flag = !(s->p & 0x02);
658     c_flag = s->p & 0x01;
659    
660     ram[0] = s->ddr;
661     ram[1] = s->pr;
662     new_config();
663    
664     jump(s->pc);
665     sp = s->sp & 0xff;
666    
667     interrupt.intr[INT_VICIRQ] = s->intr[INT_VICIRQ];
668     interrupt.intr[INT_CIAIRQ] = s->intr[INT_CIAIRQ];
669     interrupt.intr[INT_NMI] = s->intr[INT_NMI];
670     interrupt.intr[INT_RESET] = s->intr[INT_RESET];
671     nmi_state = s->nmi_state;
672     dfff_byte = s->dfff_byte;
673     }
674    
675    
676     /*
677     * Reset CPU
678     */
679    
680     void MOS6510::Reset(void)
681     {
682     // Delete 'CBM80' if present
683     if (ram[0x8004] == 0xc3 && ram[0x8005] == 0xc2 && ram[0x8006] == 0xcd
684     && ram[0x8007] == 0x38 && ram[0x8008] == 0x30)
685     ram[0x8004] = 0;
686    
687     // Initialize extra 6510 registers and memory configuration
688     ram[0] = ram[1] = 0;
689     new_config();
690    
691     // Clear all interrupt lines
692     interrupt.intr_any = 0;
693     nmi_state = false;
694    
695     // Read reset vector
696     jump(read_word(0xfffc));
697     }
698    
699    
700     /*
701     * Illegal opcode encountered
702     */
703    
704     void MOS6510::illegal_op(uint8 op, uint16 at)
705     {
706     char illop_msg[80];
707    
708     sprintf(illop_msg, "Illegal opcode %02x at %04x.", op, at);
709     ShowRequester(illop_msg, "Reset");
710     the_c64->Reset();
711     Reset();
712     }
713    
714    
715     /*
716     * Jump to illegal address space (PC_IS_POINTER only)
717     */
718    
719     void MOS6510::illegal_jump(uint16 at, uint16 to)
720     {
721     char illop_msg[80];
722    
723     sprintf(illop_msg, "Jump to I/O space at %04x to %04x.", at, to);
724     ShowRequester(illop_msg, "Reset");
725     the_c64->Reset();
726     Reset();
727     }
728    
729    
730     /*
731     * Stack macros
732     */
733    
734     // Pop a byte from the stack
735     #define pop_byte() ram[(++sp) | 0x0100]
736    
737     // Push a byte onto the stack
738     #define push_byte(byte) (ram[(sp--) & 0xff | 0x0100] = (byte))
739    
740     // Pop processor flags from the stack
741     #define pop_flags() \
742     n_flag = tmp = pop_byte(); \
743     v_flag = tmp & 0x40; \
744     d_flag = tmp & 0x08; \
745     i_flag = tmp & 0x04; \
746     z_flag = !(tmp & 0x02); \
747     c_flag = tmp & 0x01;
748    
749     // Push processor flags onto the stack
750     #define push_flags(b_flag) \
751     tmp = 0x20 | (n_flag & 0x80); \
752     if (v_flag) tmp |= 0x40; \
753     if (b_flag) tmp |= 0x10; \
754     if (d_flag) tmp |= 0x08; \
755     if (i_flag) tmp |= 0x04; \
756     if (!z_flag) tmp |= 0x02; \
757     if (c_flag) tmp |= 0x01; \
758     push_byte(tmp);
759    
760    
761     /*
762     * Emulate cycles_left worth of 6510 instructions
763     * Returns number of cycles of last instruction
764     */
765    
766     int MOS6510::EmulateLine(int cycles_left)
767     {
768     uint8 tmp, tmp2;
769     uint16 adr; // Used by read_adr_abs()!
770     int last_cycles = 0;
771    
772     // Any pending interrupts?
773     if (interrupt.intr_any) {
774     handle_int:
775     if (interrupt.intr[INT_RESET])
776     Reset();
777    
778     else if (interrupt.intr[INT_NMI]) {
779     interrupt.intr[INT_NMI] = false; // Simulate an edge-triggered input
780     #if PC_IS_POINTER
781     push_byte((pc-pc_base) >> 8); push_byte(pc-pc_base);
782     #else
783     push_byte(pc >> 8); push_byte(pc);
784     #endif
785     push_flags(false);
786     i_flag = true;
787     jump(read_word(0xfffa));
788     last_cycles = 7;
789    
790     } else if ((interrupt.intr[INT_VICIRQ] || interrupt.intr[INT_CIAIRQ]) && !i_flag) {
791     #if PC_IS_POINTER
792     push_byte((pc-pc_base) >> 8); push_byte(pc-pc_base);
793     #else
794     push_byte(pc >> 8); push_byte(pc);
795     #endif
796     push_flags(false);
797     i_flag = true;
798     jump(read_word(0xfffe));
799     last_cycles = 7;
800     }
801     }
802    
803     #include "CPU_emulline.h"
804    
805     // Extension opcode
806     case 0xf2:
807     #if PC_IS_POINTER
808     if ((pc-pc_base) < 0xe000) {
809     illegal_op(0xf2, pc-pc_base-1);
810     #else
811     if (pc < 0xe000) {
812     illegal_op(0xf2, pc-1);
813     #endif
814     break;
815     }
816     switch (read_byte_imm()) {
817     case 0x00:
818     ram[0x90] |= TheIEC->Out(ram[0x95], ram[0xa3] & 0x80);
819     c_flag = false;
820     jump(0xedac);
821     break;
822     case 0x01:
823     ram[0x90] |= TheIEC->OutATN(ram[0x95]);
824     c_flag = false;
825     jump(0xedac);
826     break;
827     case 0x02:
828     ram[0x90] |= TheIEC->OutSec(ram[0x95]);
829     c_flag = false;
830     jump(0xedac);
831     break;
832     case 0x03:
833     ram[0x90] |= TheIEC->In(&a);
834     set_nz(a);
835     c_flag = false;
836     jump(0xedac);
837     break;
838     case 0x04:
839     TheIEC->SetATN();
840     jump(0xedfb);
841     break;
842     case 0x05:
843     TheIEC->RelATN();
844     jump(0xedac);
845     break;
846     case 0x06:
847     TheIEC->Turnaround();
848     jump(0xedac);
849     break;
850     case 0x07:
851     TheIEC->Release();
852     jump(0xedac);
853     break;
854     default:
855     #if PC_IS_POINTER
856     illegal_op(0xf2, pc-pc_base-1);
857     #else
858     illegal_op(0xf2, pc-1);
859     #endif
860     break;
861     }
862     break;
863     }
864     }
865     return last_cycles;
866     }