Main Page | Class Hierarchy | Compound List | File List | Compound Members | File Members | Related Pages

CPUC64_SC.cpp

Go to the documentation of this file.
00001 /*
00002  *  CPUC64_SC.cpp - Single-cycle 6510 (C64) emulation
00003  *
00004  *  Frodo (C) 1994-1997 Christian Bauer
00005  *
00006 
00007  *
00008  * Notes:
00009  * ------
00010  *
00011  * Opcode execution:
00012  *  - All opcodes are resolved into single clock cycles. There is one
00013  *    switch case for each cycle.
00014  *  - The "state" variable specifies the routine to be executed in the
00015  *    next cycle. Its upper 8 bits contain the current opcode, its lower
00016  *    8 bits contain the cycle number (0..7) within the opcode.
00017  *  - Opcodes are fetched in cycle 0 (state = 0)
00018  *  - The states 0x0010..0x0027 are used for interrupts
00019  *  - There is exactly one memory access in each clock cycle
00020  *
00021  * Memory configurations:
00022  *
00023  * $01  $a000-$bfff  $d000-$dfff  $e000-$ffff
00024  * -----------------------------------------------
00025  *  0       RAM          RAM          RAM
00026  *  1       RAM       Char ROM        RAM
00027  *  2       RAM       Char ROM    Kernal ROM
00028  *  3    Basic ROM    Char ROM    Kernal ROM
00029  *  4       RAM          RAM          RAM
00030  *  5       RAM          I/O          RAM
00031  *  6       RAM          I/O      Kernal ROM
00032  *  7    Basic ROM       I/O      Kernal ROM
00033  *
00034  *  - All memory accesses are done with the read_byte() and
00035  *    write_byte() functions which also do the memory address
00036  *    decoding.
00037  *  - If a write occurs to addresses 0 or 1, new_config is
00038  *    called to check whether the memory configuration has
00039  *    changed
00040  *  - The possible interrupt sources are:
00041  *      INT_VICIRQ: I flag is checked, jump to ($fffe)
00042  *      INT_CIAIRQ: I flag is checked, jump to ($fffe)
00043  *      INT_NMI: Jump to ($fffa)
00044  *      INT_RESET: Jump to ($fffc)
00045  *  - The z_flag variable has the inverse meaning of the
00046  *    6510 Z flag
00047  *  - Only the highest bit of the n_flag variable is used
00048  *  - The $f2 opcode that would normally crash the 6510 is
00049  *    used to implement emulator-specific functions, mainly
00050  *    those for the IEC routines
00051  *
00052  * Incompatibilities:
00053  * ------------------
00054  *
00055  *  - If BA is low and AEC is high, read accesses should occur
00056  */
00057 
00058 #include "sysdeps.h"
00059 
00060 #include "CPUC64.h"
00061 #include "CPU_common.h"
00062 #include "C64.h"
00063 #include "VIC.h"
00064 #include "SID.h"
00065 #include "CIA.h"
00066 #include "REU.h"
00067 #include "IEC.h"
00068 #include "Display.h"
00069 #include "Version.h"
00070 
00071 
00072 enum {
00073         INT_RESET = 3
00074 };
00075 
00076 
00077 /*
00078  *  6510 constructor: Initialize registers
00079  */
00080 
00081 MOS6510::MOS6510(C64 *c64, uint8 *Ram, uint8 *Basic, uint8 *Kernal, uint8 *Char, uint8 *Color)
00082  : the_c64(c64), ram(Ram), basic_rom(Basic), kernal_rom(Kernal), char_rom(Char), color_ram(Color)
00083 {
00084         a = x = y = 0;
00085         sp = 0xff;
00086         n_flag = z_flag = 0;
00087         v_flag = d_flag = c_flag = false;
00088         i_flag = true;
00089         dfff_byte = 0x55;
00090         BALow = false;
00091         first_irq_cycle = first_nmi_cycle = 0;
00092 }
00093 
00094 
00095 /*
00096  *  Reset CPU asynchronously
00097  */
00098 
00099 void MOS6510::AsyncReset(void)
00100 {
00101         interrupt.intr[INT_RESET] = true;
00102 }
00103 
00104 
00105 /*
00106  *  Raise NMI asynchronously (Restore key)
00107  */
00108 
00109 void MOS6510::AsyncNMI(void)
00110 {
00111         if (!nmi_state)
00112                 interrupt.intr[INT_NMI] = true;
00113 }
00114 
00115 
00116 /*
00117  *  Get 6510 register state
00118  */
00119 
00120 void MOS6510::GetState(MOS6510State *s)
00121 {
00122         s->a = a;
00123         s->x = x;
00124         s->y = y;
00125 
00126         s->p = 0x20 | (n_flag & 0x80);
00127         if (v_flag) s->p |= 0x40;
00128         if (d_flag) s->p |= 0x08;
00129         if (i_flag) s->p |= 0x04;
00130         if (!z_flag) s->p |= 0x02;
00131         if (c_flag) s->p |= 0x01;
00132         
00133         s->ddr = ddr;
00134         s->pr = pr;
00135 
00136         s->pc = pc;
00137         s->sp = sp | 0x0100;
00138 
00139         s->intr[INT_VICIRQ] = interrupt.intr[INT_VICIRQ];
00140         s->intr[INT_CIAIRQ] = interrupt.intr[INT_CIAIRQ];
00141         s->intr[INT_NMI] = interrupt.intr[INT_NMI];
00142         s->intr[INT_RESET] = interrupt.intr[INT_RESET];
00143         s->nmi_state = nmi_state;
00144         s->dfff_byte = dfff_byte;
00145         s->instruction_complete = (state == 0);
00146 }
00147 
00148 
00149 /*
00150  *  Restore 6510 state
00151  */
00152 
00153 void MOS6510::SetState(MOS6510State *s)
00154 {
00155         a = s->a;
00156         x = s->x;
00157         y = s->y;
00158 
00159         n_flag = s->p;
00160         v_flag = s->p & 0x40;
00161         d_flag = s->p & 0x08;
00162         i_flag = s->p & 0x04;
00163         z_flag = !(s->p & 0x02);
00164         c_flag = s->p & 0x01;
00165 
00166         ddr = s->ddr;
00167         pr = s->pr;
00168         new_config();
00169 
00170         pc = s->pc;
00171         sp = s->sp & 0xff;
00172 
00173         interrupt.intr[INT_VICIRQ] = s->intr[INT_VICIRQ];
00174         interrupt.intr[INT_CIAIRQ] = s->intr[INT_CIAIRQ];
00175         interrupt.intr[INT_NMI] = s->intr[INT_NMI];
00176         interrupt.intr[INT_RESET] = s->intr[INT_RESET];
00177         nmi_state = s->nmi_state;
00178         dfff_byte = s->dfff_byte;
00179         if (s->instruction_complete)
00180                 state = 0;
00181 }
00182 
00183 
00184 /*
00185  *  Memory configuration has probably changed
00186  */
00187 
00188 void MOS6510::new_config(void)
00189 {
00190         uint8 port = ~ddr | pr;
00191 
00192         basic_in = (port & 3) == 3;
00193         kernal_in = port & 2;
00194         char_in = (port & 3) && !(port & 4);
00195         io_in = (port & 3) && (port & 4);
00196 }
00197 
00198 
00199 /*
00200  *  Read a byte from I/O / ROM space
00201  */
00202 
00203 inline uint8 MOS6510::read_byte_io(uint16 adr)
00204 {
00205         switch (adr >> 12) {
00206                 case 0xa:
00207                 case 0xb:
00208                         if (basic_in)
00209                                 return basic_rom[adr & 0x1fff];
00210                         else
00211                                 return ram[adr];
00212                 case 0xc:
00213                         return ram[adr];
00214                 case 0xd:
00215                         if (io_in)
00216                                 switch ((adr >> 8) & 0x0f) {
00217                                         case 0x0:       // VIC
00218                                         case 0x1:
00219                                         case 0x2:
00220                                         case 0x3:
00221                                                 return TheVIC->ReadRegister(adr & 0x3f);
00222                                         case 0x4:       // SID
00223                                         case 0x5:
00224                                         case 0x6:
00225                                         case 0x7:
00226                                                 return TheSID->ReadRegister(adr & 0x1f);
00227                                         case 0x8:       // Color RAM
00228                                         case 0x9:
00229                                         case 0xa:
00230                                         case 0xb:
00231                                                 return color_ram[adr & 0x03ff] & 0x0f | TheVIC->LastVICByte & 0xf0;
00232                                         case 0xc:       // CIA 1
00233                                                 return TheCIA1->ReadRegister(adr & 0x0f);
00234                                         case 0xd:       // CIA 2
00235                                                 return TheCIA2->ReadRegister(adr & 0x0f);
00236                                         case 0xe:       // REU/Open I/O
00237                                         case 0xf:
00238                                                 if ((adr & 0xfff0) == 0xdf00)
00239                                                         return TheREU->ReadRegister(adr & 0x0f);
00240                                                 else if (adr < 0xdfa0)
00241                                                         return TheVIC->LastVICByte;
00242                                                 else
00243                                                         return read_emulator_id(adr & 0x7f);
00244                                 }
00245                         else if (char_in)
00246                                 return char_rom[adr & 0x0fff];
00247                         else
00248                                 return ram[adr];
00249                 case 0xe:
00250                 case 0xf:
00251                         if (kernal_in)
00252                                 return kernal_rom[adr & 0x1fff];
00253                         else
00254                                 return ram[adr];
00255                 default:        // Can't happen
00256                         return 0;
00257         }
00258 }
00259 
00260 
00261 /*
00262  *  Read a byte from the CPU's address space
00263  */
00264 
00265 #ifdef __i386
00266 inline
00267 #endif
00268 uint8 MOS6510::read_byte(uint16 adr)
00269 {
00270         if (adr < 0xa000) {
00271                 if (adr >= 2)
00272                         return ram[adr];
00273                 else if (adr == 0)
00274                         return ddr;
00275                 else
00276                         return (ddr & pr) | (~ddr & 0x17);
00277         } else
00278                 return read_byte_io(adr);
00279 }
00280 
00281 
00282 /*
00283  *  $dfa0-$dfff: Emulator identification
00284  */
00285 
00286 const char frodo_id[0x5c] = "FRODO\r(C) 1994-1997 CHRISTIAN BAUER";
00287 
00288 uint8 MOS6510::read_emulator_id(uint16 adr)
00289 {
00290         switch (adr) {
00291                 case 0x7c:      // $dffc: revision
00292                         return FRODO_REVISION << 4;
00293                 case 0x7d:      // $dffd: version
00294                         return FRODO_VERSION;
00295                 case 0x7e:      // $dffe returns 'F' (Frodo ID)
00296                         return 'F';
00297                 case 0x7f:      // $dfff alternates between $55 and $aa
00298                         dfff_byte = ~dfff_byte;
00299                         return dfff_byte;
00300                 default:
00301                         return frodo_id[adr - 0x20];
00302         }
00303 }
00304 
00305 
00306 /*
00307  *  Read a word (little-endian) from the CPU's address space
00308  */
00309 
00310 inline uint16 MOS6510::read_word(uint16 adr)
00311 {
00312         return read_byte(adr) | (read_byte(adr+1) << 8);
00313 }
00314 
00315 
00316 /*
00317  *  Write a byte to I/O space
00318  */
00319 
00320 inline void MOS6510::write_byte_io(uint16 adr, uint8 byte)
00321 {
00322         if (adr >= 0xe000) {
00323                 ram[adr] = byte;
00324                 if (adr == 0xff00)
00325                         TheREU->FF00Trigger();
00326         } else if (io_in)
00327                 switch ((adr >> 8) & 0x0f) {
00328                         case 0x0:       // VIC
00329                         case 0x1:
00330                         case 0x2:
00331                         case 0x3:
00332                                 TheVIC->WriteRegister(adr & 0x3f, byte);
00333                                 return;
00334                         case 0x4:       // SID
00335                         case 0x5:
00336                         case 0x6:
00337                         case 0x7:
00338                                 TheSID->WriteRegister(adr & 0x1f, byte);
00339                                 return;
00340                         case 0x8:       // Color RAM
00341                         case 0x9:
00342                         case 0xa:
00343                         case 0xb:
00344                                 color_ram[adr & 0x03ff] = byte & 0x0f;
00345                                 return;
00346                         case 0xc:       // CIA 1
00347                                 TheCIA1->WriteRegister(adr & 0x0f, byte);
00348                                 return;
00349                         case 0xd:       // CIA 2
00350                                 TheCIA2->WriteRegister(adr & 0x0f, byte);
00351                                 return;
00352                         case 0xe:       // REU/Open I/O
00353                         case 0xf:
00354                                 if ((adr & 0xfff0) == 0xdf00)
00355                                         TheREU->WriteRegister(adr & 0x0f, byte);
00356                                 return;
00357                 }
00358         else
00359                 ram[adr] = byte;        
00360 }
00361 
00362 
00363 /*
00364  *  Write a byte to the CPU's address space
00365  */
00366 
00367 void MOS6510::write_byte(uint16 adr, uint8 byte)
00368 {
00369         if (adr < 0xd000) {
00370                 if (adr >= 2)
00371                         ram[adr] = byte;
00372                 else if (adr == 0) {
00373                         ddr = byte;
00374                         ram[0] = TheVIC->LastVICByte;
00375                         new_config();
00376                 } else {
00377                         pr = byte;
00378                         ram[1] = TheVIC->LastVICByte;
00379                         new_config();
00380                 }
00381         } else
00382                 write_byte_io(adr, byte);
00383 }
00384 
00385 
00386 /*
00387  *  Read byte from 6510 address space with special memory config (used by SAM)
00388  */
00389 
00390 uint8 MOS6510::ExtReadByte(uint16 adr)
00391 {
00392         // Save old memory configuration
00393         bool bi = basic_in, ki = kernal_in, ci = char_in, ii = io_in;
00394 
00395         // Set new configuration
00396         basic_in = (ExtConfig & 3) == 3;
00397         kernal_in = ExtConfig & 2;
00398         char_in = (ExtConfig & 3) && ~(ExtConfig & 4);
00399         io_in = (ExtConfig & 3) && (ExtConfig & 4);
00400 
00401         // Read byte
00402         uint8 byte = read_byte(adr);
00403 
00404         // Restore old configuration
00405         basic_in = bi; kernal_in = ki; char_in = ci; io_in = ii;
00406 
00407         return byte;
00408 }
00409 
00410 
00411 /*
00412  *  Write byte to 6510 address space with special memory config (used by SAM)
00413  */
00414 
00415 void MOS6510::ExtWriteByte(uint16 adr, uint8 byte)
00416 {
00417         // Save old memory configuration
00418         bool bi = basic_in, ki = kernal_in, ci = char_in, ii = io_in;
00419 
00420         // Set new configuration
00421         basic_in = (ExtConfig & 3) == 3;
00422         kernal_in = ExtConfig & 2;
00423         char_in = (ExtConfig & 3) && ~(ExtConfig & 4);
00424         io_in = (ExtConfig & 3) && (ExtConfig & 4);
00425 
00426         // Write byte
00427         write_byte(adr, byte);
00428 
00429         // Restore old configuration
00430         basic_in = bi; kernal_in = ki; char_in = ci; io_in = ii;
00431 }
00432 
00433 
00434 /*
00435  *  Read byte from 6510 address space with current memory config (used by REU)
00436  */
00437 
00438 uint8 MOS6510::REUReadByte(uint16 adr)
00439 {
00440         return read_byte(adr);
00441 }
00442 
00443 
00444 /*
00445  *  Write byte to 6510 address space with current memory config (used by REU)
00446  */
00447 
00448 void MOS6510::REUWriteByte(uint16 adr, uint8 byte)
00449 {
00450         write_byte(adr, byte);
00451 }
00452 
00453 
00454 /*
00455  *  Adc instruction
00456  */
00457 
00458 inline void MOS6510::do_adc(uint8 byte)
00459 {
00460         if (!d_flag) {
00461                 uint16 tmp;
00462 
00463                 // Binary mode
00464                 tmp = a + byte + (c_flag ? 1 : 0);
00465                 c_flag = tmp > 0xff;
00466                 v_flag = !((a ^ byte) & 0x80) && ((a ^ tmp) & 0x80);
00467                 z_flag = n_flag = a = tmp;
00468 
00469         } else {
00470                 uint16 al, ah;
00471 
00472                 // Decimal mode
00473                 al = (a & 0x0f) + (byte & 0x0f) + (c_flag ? 1 : 0);             // Calculate lower nybble
00474                 if (al > 9) al += 6;                                                                    // BCD fixup for lower nybble
00475 
00476                 ah = (a >> 4) + (byte >> 4);                                                    // Calculate upper nybble
00477                 if (al > 0x0f) ah++;
00478 
00479                 z_flag = a + byte + (c_flag ? 1 : 0);                                   // Set flags
00480                 n_flag = ah << 4;       // Only highest bit used
00481                 v_flag = (((ah << 4) ^ a) & 0x80) && !((a ^ byte) & 0x80);
00482 
00483                 if (ah > 9) ah += 6;                                                                    // BCD fixup for upper nybble
00484                 c_flag = ah > 0x0f;                                                                             // Set carry flag
00485                 a = (ah << 4) | (al & 0x0f);                                                    // Compose result
00486         }
00487 }
00488 
00489 
00490 /*
00491  * Sbc instruction
00492  */
00493 
00494 inline void MOS6510::do_sbc(uint8 byte)
00495 {
00496         uint16 tmp = a - byte - (c_flag ? 0 : 1);
00497 
00498         if (!d_flag) {
00499 
00500                 // Binary mode
00501                 c_flag = tmp < 0x100;
00502                 v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80);
00503                 z_flag = n_flag = a = tmp;
00504 
00505         } else {
00506                 uint16 al, ah;
00507 
00508                 // Decimal mode
00509                 al = (a & 0x0f) - (byte & 0x0f) - (c_flag ? 0 : 1);     // Calculate lower nybble
00510                 ah = (a >> 4) - (byte >> 4);                                                    // Calculate upper nybble
00511                 if (al & 0x10) {
00512                         al -= 6;                                                                                        // BCD fixup for lower nybble
00513                         ah--;
00514                 }
00515                 if (ah & 0x10) ah -= 6;                                                                 // BCD fixup for upper nybble
00516 
00517                 c_flag = tmp < 0x100;                                                                   // Set flags
00518                 v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80);
00519                 z_flag = n_flag = tmp;
00520 
00521                 a = (ah << 4) | (al & 0x0f);                                                    // Compose result
00522         }
00523 }
00524 
00525 
00526 /*
00527  *  Reset CPU
00528  */
00529 
00530 void MOS6510::Reset(void)
00531 {
00532         // Delete 'CBM80' if present
00533         if (ram[0x8004] == 0xc3 && ram[0x8005] == 0xc2 && ram[0x8006] == 0xcd
00534          && ram[0x8007] == 0x38 && ram[0x8008] == 0x30)
00535                 ram[0x8004] = 0;
00536 
00537         // Initialize extra 6510 registers and memory configuration
00538         ddr = pr = 0;
00539         new_config();
00540 
00541         // Clear all interrupt lines
00542         interrupt.intr_any = 0;
00543         nmi_state = false;
00544 
00545         // Read reset vector
00546         pc = read_word(0xfffc);
00547         state = 0;
00548 }
00549 
00550 
00551 /*
00552  *  Illegal opcode encountered
00553  */
00554 
00555 void MOS6510::illegal_op(uint8 op, uint16 at)
00556 {
00557         char illop_msg[80];
00558 
00559         sprintf(illop_msg, "Illegal opcode %02x at %04x.", op, at);
00560         ShowRequester(illop_msg, "Reset");
00561         the_c64->Reset();
00562         Reset();
00563 }
00564 
00565 
00566 /*
00567  *  Emulate one 6510 clock cycle
00568  */
00569 
00570 // Read byte from memory
00571 #define read_to(adr, to) \
00572         if (BALow) \
00573                 return; \
00574         to = read_byte(adr);
00575 
00576 // Read byte from memory, throw away result
00577 #define read_idle(adr) \
00578         if (BALow) \
00579                 return; \
00580         read_byte(adr);
00581 
00582 void MOS6510::EmulateCycle(void)
00583 {
00584         uint8 data, tmp;
00585 
00586         // Any pending interrupts in state 0 (opcode fetch)?
00587         if (!state && interrupt.intr_any) {
00588                 if (interrupt.intr[INT_RESET])
00589                         Reset();
00590                 else if (interrupt.intr[INT_NMI] && (the_c64->CycleCounter-first_nmi_cycle >= 2)) {
00591                         interrupt.intr[INT_NMI] = false;        // Simulate an edge-triggered input
00592                         state = 0x0010;
00593                 } else if ((interrupt.intr[INT_VICIRQ] || interrupt.intr[INT_CIAIRQ]) && (the_c64->CycleCounter-first_irq_cycle >= 2) && !i_flag)
00594                         state = 0x0008;
00595         }
00596 
00597 #include "CPU_emulcycle.i"
00598 
00599                 // Extension opcode
00600                 case O_EXT:
00601                         if (pc < 0xe000) {
00602                                 illegal_op(0xf2, pc-1);
00603                                 break;
00604                         }
00605                         switch (read_byte(pc++)) {
00606                                 case 0x00:
00607                                         ram[0x90] |= TheIEC->Out(ram[0x95], ram[0xa3] & 0x80);
00608                                         c_flag = false;
00609                                         pc = 0xedac;
00610                                         Last;
00611                                 case 0x01:
00612                                         ram[0x90] |= TheIEC->OutATN(ram[0x95]);
00613                                         c_flag = false;
00614                                         pc = 0xedac;
00615                                         Last;
00616                                 case 0x02:
00617                                         ram[0x90] |= TheIEC->OutSec(ram[0x95]);
00618                                         c_flag = false;
00619                                         pc = 0xedac;
00620                                         Last;
00621                                 case 0x03:
00622                                         ram[0x90] |= TheIEC->In(&a);
00623                                         set_nz(a);
00624                                         c_flag = false;
00625                                         pc = 0xedac;
00626                                         Last;
00627                                 case 0x04:
00628                                         TheIEC->SetATN();
00629                                         pc = 0xedfb;
00630                                         Last;
00631                                 case 0x05:
00632                                         TheIEC->RelATN();
00633                                         pc = 0xedac;
00634                                         Last;
00635                                 case 0x06:
00636                                         TheIEC->Turnaround();
00637                                         pc = 0xedac;
00638                                         Last;
00639                                 case 0x07:
00640                                         TheIEC->Release();
00641                                         pc = 0xedac;
00642                                         Last;
00643                                 default:
00644                                         illegal_op(0xf2, pc-1);
00645                                         break;
00646                         }
00647                         break;
00648 
00649                 default:
00650                         illegal_op(op, pc-1);
00651                         break;
00652         }
00653 }

Generated on Tue Feb 8 04:07:43 2005 for E32frodo by doxygen 1.3.3