commit 421826d4b60ce1b928d9467d1b5bfca6b6853302 Author: Raimon @ quifisraimon Date: Wed Sep 9 10:02:13 2026 +0200 - First commit: - [NEW] Afegit el mòdul "acabat" del 6502 - [NEW] Afegit el módul "atari2600" director - [NEW] Afeggits stubs de display, speaker, mem, tia i pia diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f2ae84 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +nes diff --git a/6502m.cpp b/6502m.cpp new file mode 100644 index 0000000..b520539 --- /dev/null +++ b/6502m.cpp @@ -0,0 +1,618 @@ +#include "6502m.h" +#include "mem.h" + + // P register Flags + #define fC 0b00000001 // Carry + #define fZ 0b00000010 // Zero + #define fI 0b00000100 // Interrupt disable + #define fD 0b00001000 // Decimal + #define fB 0b00010000 // Interrupt type? + #define f1 0b00100000 // -Unused- + #define fV 0b01000000 // Overflow + #define fN 0b10000000 // Negative + +namespace m6502 +{ + uint8_t interrupt_vector = 0xfe; + + uint8_t rA; + uint8_t rX; + uint8_t rY; + uint8_t rS; + uint8_t rP; + uint8_t rPC_lo; + uint8_t rPC_hi; + //#define rPC (*_rPC) + + uint8_t rB; + uint8_t rT; + uint8_t rI; + uint8_t rAD_lo; + uint8_t rAD_hi; + //#define rAD (*_rAD) + uint8_t rTEMP_lo; + uint8_t rTEMP_hi; + //#define rTEMP (*_rTEMP) + + uint16_t exec_PC; + uint16_t getPC() { return exec_PC; } + + // Explicit 16-bit accessors prevent UB and stay host-endian safe + inline uint16_t get_PC() { return (uint16_t)rPC_lo | ((uint16_t)rPC_hi << 8); } + inline void set_PC(uint16_t val) { rPC_lo = val & 0xFF; rPC_hi = val >> 8; } + inline uint16_t inc_PC() { if (++rPC_lo == 0) rPC_hi++; return get_PC(); } // Natural 16-bit bump + + inline uint16_t get_AD() { return (uint16_t)rAD_lo | ((uint16_t)rAD_hi << 8); } + inline void set_AD(uint16_t val) { rAD_lo = val & 0xFF; rAD_hi = val >> 8; } + inline void inc_AD() { if (++rAD_lo == 0) rAD_hi++; } // Natural 16-bit bump + + inline uint16_t get_TEMP() { return (uint16_t)rTEMP_lo | ((uint16_t)rTEMP_hi << 8); } + inline void set_TEMP(uint16_t val) { rTEMP_lo = val & 0xFF; rTEMP_hi = val >> 8; } + inline void inc_TEMP() { if (++rTEMP_lo == 0) rTEMP_hi++; } // Natural 16-bit bump + + uint8_t microcode[256]; + uint8_t microcode_pos = 0; + uint8_t microcode_last = 0; + + enum microInstructions { + miFetchOpcode, + miFakeFetchOperand, + miFetchOperandLo, + miFetchOperandHi, + miFetchOperandHiAndIndexX, + miFetchOperandHiAndIndexY, + miPushPCHi, + miPushPCLo, + miPushP, + miPCLoInt, + miPCHiInt, + miIndexX, + miFetchAddressLo, + miFetchAddressHi, + miFetchAddressHiAndIndex, + miReadAddress, + miReadAddressAndSkip, + miWriteRegister, + miWriteAddress, + miPushA, + miFetchOperandAndCheckBranch, + miCheckIfPageCrossed, + miPullPCHi, + miPullPCLo, + miPullP, + miPullA, + miIncrementPC, + miFetchAddressHiToPC, + miIndexY, + miNumMicroinstructions + }; + + + uint8_t instructions[256][7] = { + /* 0x00 BRK */ { miFetchOperandLo, miPushPCHi, miPushPCLo, miPushP, miPCLoInt, miPCHiInt, miFetchOpcode }, + /* 0x01 ORA X,ind */ { miFetchOperandLo, miIndexX, miFetchAddressLo, miFetchAddressHi, miReadAddress, miFetchOpcode }, + /* 0x02 --- */ { miFetchOpcode }, + /* 0x03 --- */ { miFetchOpcode }, + /* 0x04 --- */ { miFetchOpcode }, + /* 0x05 ORA zpg */ { miFetchOperandLo, miReadAddress, miFetchOpcode }, + /* 0x06 ASL zpg */ { miFetchOperandLo, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0x07 --- */ { miFetchOpcode }, + /* 0x08 PHP */ { miFetchOperandLo, miPushP, miFetchOpcode }, + /* 0x09 ORA imm */ { miFetchOperandLo, miFetchOpcode }, + /* 0x0A ASL A */ { miFakeFetchOperand, miFetchOpcode }, + /* 0x0B --- */ { miFetchOpcode }, + /* 0x0C --- */ { miFetchOpcode }, + /* 0x0D ORA abs */ { miFetchOperandLo, miFetchOperandHi, miReadAddress, miFetchOpcode }, + /* 0x0E ASL abs */ { miFetchOperandLo, miFetchOperandHi, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0x0F --- */ { miFetchOpcode }, + + /* 0x10 BPL */ { miFetchOperandAndCheckBranch, miCheckIfPageCrossed, miFakeFetchOperand, miFetchOpcode }, + /* 0x11 ORA ind,Y */ { miFetchOperandLo, miFetchAddressLo, miFetchAddressHiAndIndex, miReadAddressAndSkip, miReadAddress, miFetchOpcode }, + /* 0x12 --- */ { miFetchOpcode }, + /* 0x13 --- */ { miFetchOpcode }, + /* 0x14 --- */ { miFetchOpcode }, + /* 0x15 ORA zpg,X */ { miFetchOperandLo, miIndexX, miReadAddress, miFetchOpcode }, + /* 0x16 ASL zpg,X */ { miFetchOperandLo, miIndexX, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0x17 --- */ { miFetchOpcode }, + /* 0x18 CLC */ { miFetchOperandLo, miFetchOpcode }, + /* 0x19 ORA abs,Y */ { miFetchOperandLo, miFetchOperandHiAndIndexY, miReadAddressAndSkip, miReadAddress, miFetchOpcode }, + /* 0x1A --- */ { miFetchOpcode }, + /* 0x1B --- */ { miFetchOpcode }, + /* 0x1C --- */ { miFetchOpcode }, + /* 0x1D ORA abs,X */ { miFetchOperandLo, miFetchOperandHiAndIndexX, miReadAddressAndSkip, miReadAddress, miFetchOpcode }, + /* 0x1E ASL abs,X */ { miFetchOperandLo, miFetchOperandHiAndIndexX, miReadAddress, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0x1F --- */ { miFetchOpcode }, + + /* 0x20 JSR abs */ { miFetchAddressLo, miFakeFetchOperand, miPushPCHi, miPushPCLo, miFetchAddressHi, miFetchOpcode }, + /* 0x21 AND X,ind */ { miFetchOperandLo, miIndexX, miFetchAddressLo, miFetchAddressHi, miReadAddress, miFetchOpcode }, + /* 0x22 --- */ { miFetchOpcode }, + /* 0x23 --- */ { miFetchOpcode }, + /* 0x24 BIT zpg */ { miFetchOperandLo, miReadAddress, miFetchOpcode }, + /* 0x25 AND zpg */ { miFetchOperandLo, miReadAddress, miFetchOpcode }, + /* 0x26 ROL zpg */ { miFetchOperandLo, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0x27 --- */ { miFetchOpcode }, + /* 0x28 PLP */ { miFetchOperandLo, miPullP, miFetchOpcode }, + /* 0x29 AND imm */ { miFetchOperandLo, miFetchOpcode }, + /* 0x2A ROL A */ { miFakeFetchOperand, miFetchOpcode }, + /* 0x2B --- */ { miFetchOpcode }, + /* 0x2C BIT abs */ { miFetchOperandLo, miFetchOperandHi, miReadAddress, miFetchOpcode }, + /* 0x2D AND abs */ { miFetchOperandLo, miFetchOperandHi, miReadAddress, miFetchOpcode }, + /* 0x2E ROL abs */ { miFetchOperandLo, miFetchOperandHi, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0x2F --- */ { miFetchOpcode }, + + /* 0x30 BMI */ { miFetchOperandAndCheckBranch, miCheckIfPageCrossed, miFakeFetchOperand, miFetchOpcode }, + /* 0x31 AND ind,Y */ { miFetchOperandLo, miFetchAddressLo, miFetchAddressHiAndIndex, miReadAddressAndSkip, miReadAddress, miFetchOpcode }, + /* 0x32 --- */ { miFetchOpcode }, + /* 0x33 --- */ { miFetchOpcode }, + /* 0x34 --- */ { miFetchOpcode }, + /* 0x35 AND zpg,X */ { miFetchOperandLo, miIndexX, miReadAddress, miFetchOpcode }, + /* 0x36 ROL zpg,X */ { miFetchOperandLo, miIndexX, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0x37 --- */ { miFetchOpcode }, + /* 0x38 SEC */ { miFetchOperandLo, miFetchOpcode }, + /* 0x39 AND abs,Y */ { miFetchOperandLo, miFetchOperandHiAndIndexY, miReadAddressAndSkip, miReadAddress, miFetchOpcode }, + /* 0x3A --- */ { miFetchOpcode }, + /* 0x3B --- */ { miFetchOpcode }, + /* 0x3C --- */ { miFetchOpcode }, + /* 0x3D AND abs,X */ { miFetchOperandLo, miFetchOperandHiAndIndexX, miReadAddressAndSkip, miReadAddress, miFetchOpcode }, + /* 0x3E ROL abs,X */ { miFetchOperandLo, miFetchOperandHiAndIndexX, miReadAddress, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0x3F --- */ { miFetchOpcode }, + + /* 0x40 RTI */ { miFetchOperandLo, miFakeFetchOperand, miPullP, miPullPCLo, miPullPCHi, miFetchOpcode }, + /* 0x41 EOR X,ind */ { miFetchOperandLo, miIndexX, miFetchAddressLo, miFetchAddressHi, miReadAddress, miFetchOpcode }, + /* 0x42 --- */ { miFetchOpcode }, + /* 0x43 --- */ { miFetchOpcode }, + /* 0x44 --- */ { miFetchOpcode }, + /* 0x45 EOR zpg */ { miFetchOperandLo, miReadAddress, miFetchOpcode }, + /* 0x46 LSR zpg */ { miFetchOperandLo, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0x47 --- */ { miFetchOpcode }, + /* 0x48 PHA */ { miFetchOperandLo, miPushA, miFetchOpcode }, + /* 0x49 EOR imm */ { miFetchOperandLo, miFetchOpcode }, + /* 0x4A LSR A */ { miFakeFetchOperand, miFetchOpcode }, + /* 0x4B --- */ { miFetchOpcode }, + /* 0x4C JMP abs */ { miFetchAddressLo, miFetchAddressHi, miFetchOpcode }, + /* 0x4D EOR abs */ { miFetchOperandLo, miFetchOperandHi, miReadAddress, miFetchOpcode }, + /* 0x4E LSR abs */ { miFetchOperandLo, miFetchOperandHi, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0x4F --- */ { miFetchOpcode }, + + /* 0x50 BVC */ { miFetchOperandAndCheckBranch, miCheckIfPageCrossed, miFakeFetchOperand, miFetchOpcode }, + /* 0x51 EOR ind,Y */ { miFetchOperandLo, miFetchAddressLo, miFetchAddressHiAndIndex, miReadAddressAndSkip, miReadAddress, miFetchOpcode }, + /* 0x52 --- */ { miFetchOpcode }, + /* 0x53 --- */ { miFetchOpcode }, + /* 0x54 --- */ { miFetchOpcode }, + /* 0x55 EOR zpg,X */ { miFetchOperandLo, miIndexX, miReadAddress, miFetchOpcode }, + /* 0x56 LSR zpg,X */ { miFetchOperandLo, miIndexX, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0x57 --- */ { miFetchOpcode }, + /* 0x58 CLI */ { miFetchOperandLo, miFetchOpcode }, + /* 0x59 EOR abs,Y */ { miFetchOperandLo, miFetchOperandHiAndIndexY, miReadAddressAndSkip, miReadAddress, miFetchOpcode }, + /* 0x5A --- */ { miFetchOpcode }, + /* 0x5B --- */ { miFetchOpcode }, + /* 0x5C --- */ { miFetchOpcode }, + /* 0x5D EOR abs,X */ { miFetchOperandLo, miFetchOperandHiAndIndexX, miReadAddressAndSkip, miReadAddress, miFetchOpcode }, + /* 0x5E LSR abs,X */ { miFetchOperandLo, miFetchOperandHiAndIndexX, miReadAddress, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0x5F --- */ { miFetchOpcode }, + + /* 0x60 RTS */ { miFetchOperandLo, miFakeFetchOperand, miPullPCLo, miPullPCHi, miIncrementPC, miFetchOpcode }, + /* 0x61 ADC X,ind */ { miFetchOperandLo, miIndexX, miFetchAddressLo, miFetchAddressHi, miReadAddress, miFetchOpcode }, + /* 0x62 --- */ { miFetchOpcode }, + /* 0x63 --- */ { miFetchOpcode }, + /* 0x64 --- */ { miFetchOpcode }, + /* 0x65 ADC zpg */ { miFetchOperandLo, miReadAddress, miFetchOpcode }, + /* 0x66 ROR zpg */ { miFetchOperandLo, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0x67 --- */ { miFetchOpcode }, + /* 0x68 PLA */ { miFetchOperandLo, miFakeFetchOperand, miPullA, miFetchOpcode }, + /* 0x69 ADC imm */ { miFetchOperandLo, miFetchOpcode }, + /* 0x6A ROR A */ { miFakeFetchOperand, miFetchOpcode }, + /* 0x6B --- */ { miFetchOpcode }, + /* 0x6C JMP ind */ { miFetchOperandLo, miFetchOperandHi, miFetchAddressLo, miFetchAddressHiToPC, miFetchOpcode }, + /* 0x6D ADC abs */ { miFetchOperandLo, miFetchOperandHi, miReadAddress, miFetchOpcode }, + /* 0x6E ROR abs */ { miFetchOperandLo, miFetchOperandHi, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0x6F --- */ { miFetchOpcode }, + + /* 0x70 BVS */ { miFetchOperandAndCheckBranch, miCheckIfPageCrossed, miFakeFetchOperand, miFetchOpcode }, + /* 0x71 ADC ind,Y */ { miFetchOperandLo, miFetchAddressLo, miFetchAddressHiAndIndex, miReadAddressAndSkip, miReadAddress, miFetchOpcode }, + /* 0x72 --- */ { miFetchOpcode }, + /* 0x73 --- */ { miFetchOpcode }, + /* 0x74 --- */ { miFetchOpcode }, + /* 0x75 ADC zpg,X */ { miFetchOperandLo, miIndexX, miReadAddress, miFetchOpcode }, + /* 0x76 ROR zpg,X */ { miFetchOperandLo, miIndexX, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0x77 --- */ { miFetchOpcode }, + /* 0x78 SEI */ { miFetchOperandLo, miFetchOpcode }, + /* 0x79 ADC abs,Y */ { miFetchOperandLo, miFetchOperandHiAndIndexY, miReadAddressAndSkip, miReadAddress, miFetchOpcode }, + /* 0x7A --- */ { miFetchOpcode }, + /* 0x7B --- */ { miFetchOpcode }, + /* 0x7C --- */ { miFetchOpcode }, + /* 0x7D ADC abs,X */ { miFetchOperandLo, miFetchOperandHiAndIndexX, miReadAddressAndSkip, miReadAddress, miFetchOpcode }, + /* 0x7E ROR abs,X */ { miFetchOperandLo, miFetchOperandHiAndIndexX, miReadAddress, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0x7F --- */ { miFetchOpcode }, + + /* 0x80 --- */ { miFetchOpcode }, + /* 0x81 STA X,ind */ { miFetchOperandLo, miIndexX, miFetchAddressLo, miFetchAddressHi, miWriteRegister, miFetchOpcode }, + /* 0x82 --- */ { miFetchOpcode }, + /* 0x83 --- */ { miFetchOpcode }, + /* 0x84 STY zpg */ { miFetchOperandLo, miWriteRegister, miFetchOpcode }, + /* 0x85 STA zpg */ { miFetchOperandLo, miWriteRegister, miFetchOpcode }, + /* 0x86 STX zpg */ { miFetchOperandLo, miWriteRegister, miFetchOpcode }, + /* 0x87 --- */ { miFetchOpcode }, + /* 0x88 DEY */ { miFetchOperandLo, miFetchOpcode }, + /* 0x89 --- */ { miFetchOpcode }, + /* 0x8A TXA */ { miFetchOperandLo, miFetchOpcode }, + /* 0x8B --- */ { miFetchOpcode }, + /* 0x8C STY abs */ { miFetchOperandLo, miFetchOperandHi, miWriteRegister, miFetchOpcode }, + /* 0x8D STA abs */ { miFetchOperandLo, miFetchOperandHi, miWriteRegister, miFetchOpcode }, + /* 0x8E STX abs */ { miFetchOperandLo, miFetchOperandHi, miWriteRegister, miFetchOpcode }, + /* 0x8F --- */ { miFetchOpcode }, + + /* 0x90 BCC */ { miFetchOperandAndCheckBranch, miCheckIfPageCrossed, miFakeFetchOperand, miFetchOpcode }, + /* 0x91 STA ind,Y */ { miFetchOperandLo, miFetchAddressLo, miFetchAddressHiAndIndex, miReadAddress, miWriteRegister, miFetchOpcode }, + /* 0x92 --- */ { miFetchOpcode }, + /* 0x93 --- */ { miFetchOpcode }, + /* 0x94 STY zpg,X */ { miFetchOperandLo, miIndexX, miWriteRegister, miFetchOpcode }, + /* 0x95 STA zpg,X */ { miFetchOperandLo, miIndexX, miWriteRegister, miFetchOpcode }, + /* 0x96 STX zpg,X */ { miFetchOperandLo, miIndexX, miWriteRegister, miFetchOpcode }, + /* 0x97 --- */ { miFetchOpcode }, + /* 0x98 TYA */ { miFetchOperandLo, miFetchOpcode }, + /* 0x99 STA abs,Y */ { miFetchOperandLo, miFetchOperandHiAndIndexY, miReadAddress, miWriteRegister, miFetchOpcode }, + /* 0x9A TXS */ { miFetchOperandLo, miFetchOpcode }, + /* 0x9B --- */ { miFetchOpcode }, + /* 0x9C --- */ { miFetchOpcode }, + /* 0x9D STA abs,X */ { miFetchOperandLo, miFetchOperandHiAndIndexX, miReadAddress, miWriteRegister, miFetchOpcode }, + /* 0x9E --- */ { miFetchOpcode }, + /* 0x9F --- */ { miFetchOpcode }, + + /* 0xA0 LDY imm */ { miFetchOperandLo, miFetchOpcode }, + /* 0xA1 LDA X,ind */ { miFetchOperandLo, miIndexX, miFetchAddressLo, miFetchAddressHi, miReadAddress, miFetchOpcode }, + /* 0xA2 LDX imm */ { miFetchOperandLo, miFetchOpcode }, + /* 0xA3 --- */ { miFetchOpcode }, + /* 0xA4 LDY zpg */ { miFetchOperandLo, miReadAddress, miFetchOpcode }, + /* 0xA5 LDA zpg */ { miFetchOperandLo, miReadAddress, miFetchOpcode }, + /* 0xA6 LDX zpg */ { miFetchOperandLo, miReadAddress, miFetchOpcode }, + /* 0xA7 --- */ { miFetchOpcode }, + /* 0xA8 TAY */ { miFetchOperandLo, miFetchOpcode }, + /* 0xA9 LDA imm */ { miFetchOperandLo, miFetchOpcode }, + /* 0xAA TAX */ { miFetchOperandLo, miFetchOpcode }, + /* 0xAB --- */ { miFetchOpcode }, + /* 0xAC LDY abs */ { miFetchOperandLo, miFetchOperandHi, miReadAddress, miFetchOpcode }, + /* 0xAD LDA abs */ { miFetchOperandLo, miFetchOperandHi, miReadAddress, miFetchOpcode }, + /* 0xAE LDX abs */ { miFetchOperandLo, miFetchOperandHi, miReadAddress, miFetchOpcode }, + /* 0xAF --- */ { miFetchOpcode }, + + /* 0xB0 BCS */ { miFetchOperandAndCheckBranch, miCheckIfPageCrossed, miFakeFetchOperand, miFetchOpcode }, + /* 0xB1 LDA ind,Y */ { miFetchOperandLo, miFetchAddressLo, miFetchAddressHiAndIndex, miReadAddressAndSkip, miReadAddress, miFetchOpcode }, + /* 0xB2 --- */ { miFetchOpcode }, + /* 0xB3 --- */ { miFetchOpcode }, + /* 0xB4 LDY zpg,X */ { miFetchOperandLo, miIndexX, miReadAddress, miFetchOpcode }, + /* 0xB5 LDA zpg,X */ { miFetchOperandLo, miIndexX, miReadAddress, miFetchOpcode }, + /* 0xB6 LDX zpg,Y */ { miFetchOperandLo, miIndexY, miReadAddress, miFetchOpcode }, + /* 0xB7 --- */ { miFetchOpcode }, + /* 0xB8 CLV */ { miFetchOperandLo, miFetchOpcode }, + /* 0xB9 LDA abs,Y */ { miFetchOperandLo, miFetchOperandHiAndIndexY, miReadAddressAndSkip, miReadAddress, miFetchOpcode }, + /* 0xBA TSX */ { miFetchOperandLo, miFetchOpcode }, + /* 0xBB --- */ { miFetchOpcode }, + /* 0xBC LDY abs,X */ { miFetchOperandLo, miFetchOperandHiAndIndexX, miReadAddressAndSkip, miReadAddress, miFetchOpcode }, + /* 0xBD LDA abs,X */ { miFetchOperandLo, miFetchOperandHiAndIndexX, miReadAddressAndSkip, miReadAddress, miFetchOpcode }, + /* 0xBE LDX abs,Y */ { miFetchOperandLo, miFetchOperandHiAndIndexY, miReadAddressAndSkip, miReadAddress, miFetchOpcode }, + /* 0xBF --- */ { miFetchOpcode }, + + /* 0xC0 CPY, imm */ { miFetchOperandLo, miFetchOpcode }, + /* 0xC1 CMP X,ind */ { miFetchOperandLo, miIndexX, miFetchAddressLo, miFetchAddressHi, miReadAddress, miFetchOpcode }, + /* 0xC2 --- */ { miFetchOpcode }, + /* 0xC3 --- */ { miFetchOpcode }, + /* 0xC4 CPY zpg */ { miFetchOperandLo, miReadAddress, miFetchOpcode }, + /* 0xC5 CMP zpg */ { miFetchOperandLo, miReadAddress, miFetchOpcode }, + /* 0xC6 DEC zpg */ { miFetchOperandLo, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0xC7 --- */ { miFetchOpcode }, + /* 0xC8 INY */ { miFetchOperandLo, miFetchOpcode }, + /* 0xC9 CMP imm */ { miFetchOperandLo, miFetchOpcode }, + /* 0xCA DEX */ { miFetchOperandLo, miFetchOpcode }, + /* 0xCB --- */ { miFetchOpcode }, + /* 0xCC CPY abs */ { miFetchOperandLo, miFetchOperandHi, miReadAddress, miFetchOpcode }, + /* 0xCD CMP abs */ { miFetchOperandLo, miFetchOperandHi, miReadAddress, miFetchOpcode }, + /* 0xCE DEC abs */ { miFetchOperandLo, miFetchOperandHi, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0xCF --- */ { miFetchOpcode }, + + /* 0xD0 BNE */ { miFetchOperandAndCheckBranch, miCheckIfPageCrossed, miFakeFetchOperand, miFetchOpcode }, + /* 0xD1 CMP ind,Y */ { miFetchOperandLo, miFetchAddressLo, miFetchAddressHiAndIndex, miReadAddressAndSkip, miReadAddress, miFetchOpcode }, + /* 0xD2 --- */ { miFetchOpcode }, + /* 0xD3 --- */ { miFetchOpcode }, + /* 0xD4 --- */ { miFetchOpcode }, + /* 0xD5 CMP zpg,X */ { miFetchOperandLo, miIndexX, miReadAddress, miFetchOpcode }, + /* 0xD6 DEC zpg,X */ { miFetchOperandLo, miIndexX, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0xD7 --- */ { miFetchOpcode }, + /* 0xD8 CLD */ { miFetchOperandLo, miFetchOpcode }, + /* 0xD9 CMP abs,Y */ { miFetchOperandLo, miFetchOperandHiAndIndexY, miReadAddressAndSkip, miReadAddress, miFetchOpcode }, + /* 0xDA --- */ { miFetchOpcode }, + /* 0xDB --- */ { miFetchOpcode }, + /* 0xDC --- */ { miFetchOpcode }, + /* 0xDD CMP abs,X */ { miFetchOperandLo, miFetchOperandHiAndIndexX, miReadAddressAndSkip, miReadAddress, miFetchOpcode }, + /* 0xDE DEC abs,X */ { miFetchOperandLo, miFetchOperandHiAndIndexX, miReadAddress, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0xDF --- */ { miFetchOpcode }, + + /* 0xE0 CPX, imm */ { miFetchOperandLo, miFetchOpcode }, + /* 0xE1 SBC X,ind */ { miFetchOperandLo, miIndexX, miFetchAddressLo, miFetchAddressHi, miReadAddress, miFetchOpcode }, + /* 0xE2 --- */ { miFetchOpcode }, + /* 0xE3 --- */ { miFetchOpcode }, + /* 0xE4 CPX zpg */ { miFetchOperandLo, miReadAddress, miFetchOpcode }, + /* 0xE5 SBC zpg */ { miFetchOperandLo, miReadAddress, miFetchOpcode }, + /* 0xE6 INC zpg */ { miFetchOperandLo, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0xE7 --- */ { miFetchOpcode }, + /* 0xE8 INX */ { miFetchOperandLo, miFetchOpcode }, + /* 0xE9 SBC imm */ { miFetchOperandLo, miFetchOpcode }, + /* 0xEA NOP */ { miFetchOpcode }, + /* 0xEB --- */ { miFetchOpcode }, + /* 0xEC CPX abs */ { miFetchOperandLo, miFetchOperandHi, miReadAddress, miFetchOpcode }, + /* 0xED SBC abs */ { miFetchOperandLo, miFetchOperandHi, miReadAddress, miFetchOpcode }, + /* 0xEE INC abs */ { miFetchOperandLo, miFetchOperandHi, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0xEF --- */ { miFetchOpcode }, + + /* 0xF0 BEQ */ { miFetchOperandAndCheckBranch, miCheckIfPageCrossed, miFakeFetchOperand, miFetchOpcode }, + /* 0xF1 SBC ind,Y */ { miFetchOperandLo, miFetchAddressLo, miFetchAddressHiAndIndex, miReadAddressAndSkip, miReadAddress, miFetchOpcode }, + /* 0xF2 --- */ { miFetchOpcode }, + /* 0xF3 --- */ { miFetchOpcode }, + /* 0xF4 --- */ { miFetchOpcode }, + /* 0xF5 SBC zpg,X */ { miFetchOperandLo, miIndexX, miReadAddress, miFetchOpcode }, + /* 0xF6 INC zpg,X */ { miFetchOperandLo, miIndexX, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0xF7 --- */ { miFetchOpcode }, + /* 0xF8 SED */ { miFetchOperandLo, miFetchOpcode }, + /* 0xF9 SBC abs,Y */ { miFetchOperandLo, miFetchOperandHiAndIndexY, miReadAddressAndSkip, miReadAddress, miFetchOpcode }, + /* 0xFA --- */ { miFetchOpcode }, + /* 0xFB --- */ { miFetchOpcode }, + /* 0xFC --- */ { miFetchOpcode }, + /* 0xFD SBC abs,X */ { miFetchOperandLo, miFetchOperandHiAndIndexX, miReadAddressAndSkip, miReadAddress, miFetchOpcode }, + /* 0xFE INC abs,X */ { miFetchOperandLo, miFetchOperandHiAndIndexX, miReadAddress, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode }, + /* 0xFF --- */ { miFetchOpcode }, + }; + + void SetFlags(uint8_t flags) + { + if (flags&fC) rP = ( rP & ~fC ) | ( rTEMP_hi & fC ); + if (flags&fN) rP = ( rP & ~fN ) | ( rTEMP_lo & fN ); + if (flags&fZ) rP = ( rTEMP_lo ? rP & ~fZ : rP | fZ ); + if (flags&fV) rP = ( (rTEMP_lo ^ rA) & (rTEMP_lo ^ rB) & 0x80 ) ? rP | fV : rP & ~fV; + } + + void DoDecimalADC() + { + uint8_t c_in = (rP & fC) ? 1 : 0; + uint8_t low = (rA & 0x0F) + (rB & 0x0F) + c_in; + if (low > 9) low += 6; + uint8_t high = (rA >> 4) + (rB >> 4) + (low > 0x0F ? 1 : 0); + + // Set Zero and Negative flags based on BINARY addition result (NMOS 6502 accurate) + uint16_t bin_sum = (uint16_t)rA + rB + c_in; + rTEMP_lo = bin_sum & 0xFF; + if (~(rA ^ rB) & (rA ^ rTEMP_lo) & 0x80) rP |= fV; else rP &= ~fV; + SetFlags(fN | fZ); + + if (high > 9) high += 6; + rP = (high > 15) ? (rP | fC) : (rP & ~fC); + rA = ((high << 4) | (low & 0x0F)) & 0xFF; + } + + void DoDecimalSBC() + { + uint8_t c_in = (rP & fC) ? 1 : 0; + uint16_t bin_diff = (uint16_t)rA - rB - (1 - c_in); + + int low = (rA & 0x0F) - (rB & 0x0F) - (1 - c_in); + int high = (rA >> 4) - (rB >> 4); + if (low < 0) { low -= 6; high--; } + if (high < 0) high -= 6; + + rTEMP_lo = bin_diff & 0xFF; + if ((rA ^ rB) & (rA ^ rTEMP_lo) & 0x80) rP |= fV; else rP &= ~fV; + if (!(bin_diff & 0x100)) rP |= fC; else rP &= ~fC; + SetFlags(fN | fZ); + + rA = ((high << 4) | (low & 0x0F)) & 0xFF; + } + + void DoBinaryADC() + { + uint8_t c_in = (rP & fC) ? 1 : 0; + uint16_t sum = (uint16_t)rA + rB + c_in; + rTEMP_lo = sum & 0xFF; + if (sum > 0xFF) rP |= fC; else rP &= ~fC; + if (~(rA ^ rB) & (rA ^ rTEMP_lo) & 0x80) rP |= fV; else rP &= ~fV; + SetFlags(fN | fZ); + rA = rTEMP_lo; + } + + void DoBinarySBC() + { + uint8_t tmp = rB; + rB ^= 0xFF; + DoBinaryADC(); + rB = tmp; + } + + void DoOpcodeWork() + { + if (rI==0xCA) { rX--; rB = rX; SetFlags(fN|fZ); return; } /* DEX */ + if (rI==0x88) { rY--; rB = rY; SetFlags(fN|fZ); return; } /* DEY */ + if (rI==0xE8) { rX++; rB = rX; SetFlags(fN|fZ); return; } /* INX */ + if (rI==0xC8) { rY++; rB = rY; SetFlags(fN|fZ); return; } /* INY */ + + if (rI==0xA8) { rY = rA; rB = rY; SetFlags(fN|fZ); return; } /* TAY */ + if (rI==0x98) { rA = rY; rB = rA; SetFlags(fN|fZ); return; } /* TYA */ + if (rI==0xAA) { rX = rA; rB = rX; SetFlags(fN|fZ); return; } /* TAX */ + if (rI==0x8A) { rA = rX; rB = rA; SetFlags(fN|fZ); return; } /* TXA */ + if (rI==0xBA) { rX = rS; rB = rX; SetFlags(fN|fZ); return; } /* TSX */ + if (rI==0x9A) { rS = rX; rB = rS; SetFlags(fN|fZ); return; } /* TXS */ + + switch (rI&0x03) { + case 0: /* Clear/set flags*/ + if ( (rI&0x1c)==0x18 ) { + switch (rI>>5) { + case 0: rP &= ~fC; break; + case 1: rP |= fC; break; + case 2: rP &= ~fI; break; + case 3: rP |= fI; break; + case 5: rP &= ~fV; break; + case 6: rP &= ~fD; break; + case 7: rP |= fD; break; + } + } else if ( ((rI>>5)==5) && (rI!=0xB0) ) { + /* LDY */ rTEMP_lo = rB; SetFlags(fN|fZ); rY = rTEMP_lo; break; + } else if ( ((rI&0xD3)==0xC0) && (((rI>>2)&3)!=2) ) { + if ( (rI&0xf0)==0xC0 ) { + /* CPY */ set_TEMP(rY + ~rB); SetFlags(fN|fZ|fC); break; + } else { + /* CPX */ set_TEMP(rX + ~rB); SetFlags(fN|fZ|fC); break; + } + } + break; + case 1: /* ALU */ + + switch ((rI>>5)&0x07) { + case 0: /* ORA */ rTEMP_lo = rA | rB; SetFlags(fN|fZ); rA = rTEMP_lo; break; + case 1: /* AND */ rTEMP_lo = rA & rB; SetFlags(fN|fZ); rA = rTEMP_lo; break; + case 2: /* EOR */ rTEMP_lo = rA ^ rB; SetFlags(fN|fZ); rA = rTEMP_lo; break; + case 7: /* SBC */ if (rP & fD) DoDecimalSBC(); else DoBinarySBC(); break; + case 3: /* ADC */ if (rP & fD) DoDecimalADC(); else DoBinaryADC(); break; + case 5: /* LDA */ rTEMP_lo = rB; SetFlags(fN|fZ); rA = rTEMP_lo; break; + case 6: /* CMP */ + uint16_t diff = (uint16_t)rA - rB; + rP = (diff < 0x100) ? (rP | fC) : (rP & ~fC); + rTEMP_lo = diff & 0xFF; + SetFlags(fN|fZ); + break; + }; + if ( (rI==0x24) || (rI==0x2C) ) /* BIT */ { rTEMP_lo = rA & rB; rP = (rP&~(fN|fV)) | (rB&(fN|fV)); SetFlags(fZ); } + break; + case 2: + if ( ((rI>>5)==5) ) { + /* LDX */ rTEMP_lo = rB; SetFlags(fN|fZ); rX = rTEMP_lo; break; + } + break; + + }; + } + + void DoRMW() + { + switch (rI&0x03) { + case 2: /* RMW */ + if ((rI & 0xF)==0xA) rB = rA; + switch ((rI>>5)&0x07) { + case 0: /* ASL */ rP = ( rP & ~fC ) | ( (rB & 0x80)>>7 ); rB = rB << 1; SetFlags(fN|fZ); break; + case 1: /* ROL */ rT = rP & fC; rP = ( rP & ~fC ) | ( (rB & 0x80)>>7 ); rB = (rB << 1) | rT; SetFlags(fN|fZ); break; + case 2: /* LSR */ rP = ( rP & ~fC ) | ( (rB & fC) ); rB = rB >> 1; SetFlags(fN|fZ); break; + case 3: /* ROR */ rT = (rP & fC) << 7; rP = ( rP & ~fC ) | ( (rB & fC) ); rB = (rB >> 1) | rT; SetFlags(fN|fZ); break; + case 6: /* DEC */ rB--; SetFlags(fN|fZ); break; + case 7: /* INC */ rB++; SetFlags(fN|fZ); break; + }; + if ((rI & 0xF)==0xA) rA = rB; + break; + }; + } + + bool BranchCondition() + { + const uint8_t flags[4] = {fN, fV, fC, fZ}; + const uint8_t flag = flags[rI>>6]; + const bool condition = (rI>>5)&1; + return (rP & flag) == condition; + } + + void InsertFetchOpcode() + { + microcode[microcode_pos] = miFetchOpcode; + microcode_last = microcode_pos+1; + } + + void FetchOpcode() + { + DoOpcodeWork(); + + exec_PC = get_PC(); + rI = mem::read(exec_PC); + inc_PC(); + int i=0; do { microcode[microcode_last++] = instructions[rI][i++]; } while (instructions[rI][i-1] != miFetchOpcode); + } + + void FakeFetchOperand() { rAD_lo = mem::read(get_PC()); } + void FetchOperandLo() { rAD_lo = rB = mem::read(get_PC()); inc_PC(); set_TEMP(rB); } + void FetchOperandHi() { rAD_hi = mem::read(get_PC()); inc_PC(); } + void FetchOperandHiAndIndexX() { rAD_hi = mem::read(get_PC()); inc_PC(); set_TEMP(rAD_lo + rX); rAD_lo = rTEMP_lo; } + void FetchOperandHiAndIndexY() { rAD_hi = mem::read(get_PC()); inc_PC(); set_TEMP(rAD_lo + rY); rAD_lo = rTEMP_lo; } + + void PushPCHi() { mem::write(0x0100+(rS--), rPC_hi); } + void PushPCLo() { mem::write(0x0100+(rS--), rPC_lo); } + void PushP() { mem::write(0x0100+(rS--), rP|f1|fB); rP |= fI; } + void PCLoInt() { rPC_lo = mem::read(0xff00+interrupt_vector); } + void PCHiInt() { rPC_hi = mem::read(0xff00+interrupt_vector+1); } + + void IndexX() { rB = mem::read(get_AD()); rAD_lo += rX; } + void FetchAddressLo() { rT = mem::read(get_AD()); inc_AD(); } + void FetchAddressHi() { rAD_hi = mem::read(get_AD()); rAD_lo = rT; } + void FetchAddressHiAndIndex() { rAD_hi = mem::read(get_AD()); set_TEMP(rAD_lo + rY); rAD_lo = rTEMP_lo; } + void ReadAddress() { rB = mem::read(get_AD()); rAD_hi += rTEMP_hi; } + + void ReadAddressAndSkip() { rB = mem::read(get_AD()); rAD_hi += rTEMP_hi; if (rTEMP_hi) InsertFetchOpcode(); } + void WriteRegister() { mem::write(get_AD(), (rI&3)==1 ? rA : (rI&3)==2 ? rX : rY ); } + void WriteAddress() { mem::write(get_AD(), rB); DoRMW(); } + void PushA() { mem::write(0x0100+(rS--), rA); } + void FetchOperandAndCheckBranch() { + int8_t offset = (int8_t)mem::read(get_PC()); inc_PC(); + rB = (uint8_t)offset; + + if (!BranchCondition()) { + InsertFetchOpcode(); + return; + } + set_TEMP(rPC_lo + offset); + rT = rPC_hi; + rPC_lo = rTEMP_lo; + } + + void CheckIfPageCrossed() { + FakeFetchOperand(); + + int8_t offset = (int8_t)rB; + + if (offset >= 0 && rTEMP_hi != 0) { + rPC_hi++; + } else if (offset < 0 && rTEMP_hi == 0) { + rPC_hi--; + } else { + InsertFetchOpcode(); + } + } + + void PullPCHi() { rPC_hi = mem::read(0x0100+(++rS)); } + void PullPCLo() { rPC_lo = mem::read(0x0100+(++rS)); } + void PullP() { rP = mem::read(0x0100+(++rS)); } + void PullA() { rA = mem::read(0x0100+(++rS)); } + + void IncrementPC() { inc_PC(); } + void FetchAddressHiToPC() { uint16_t high_addr = (get_AD() & 0xFF00) | ((get_AD() + 1) & 0x00FF); rPC_hi = mem::read(high_addr); rPC_lo = rT; } + void IndexY() { rB = mem::read(get_AD()); rAD_lo += rY; } + + void (*microinstructions[miNumMicroinstructions])(void) { + FetchOpcode, FakeFetchOperand, FetchOperandLo, FetchOperandHi, FetchOperandHiAndIndexX, FetchOperandHiAndIndexY, + PushPCHi, PushPCLo, PushP, PCLoInt, PCHiInt, + IndexX, FetchAddressLo, FetchAddressHi, FetchAddressHiAndIndex, ReadAddress, + ReadAddressAndSkip, WriteRegister, WriteAddress, PushA, FetchOperandAndCheckBranch, + CheckIfPageCrossed, PullPCHi, PullPCLo, PullP, PullA, + IncrementPC, FetchAddressHiToPC, IndexY }; + + void reset() + { + + } + + void interrupt(uint8_t type) + { + interrupt_vector = type; + } + + bool tick() + { + uint8_t m_inst = microcode[microcode_pos++]; + microinstructions[m_inst](); + + // tornem true si anem a començar una nova instrucció i, per tant, el step ha acabat + return (m_inst == miFetchOpcode); + } + +} diff --git a/6502m.h b/6502m.h new file mode 100644 index 0000000..b0c2715 --- /dev/null +++ b/6502m.h @@ -0,0 +1,11 @@ +#pragma once +#include + +namespace m6502 +{ + void reset(); + void interrupt(uint8_t type); + bool tick(); + + uint16_t getPC(); +} diff --git a/atari2600.cpp b/atari2600.cpp new file mode 100644 index 0000000..22010c6 --- /dev/null +++ b/atari2600.cpp @@ -0,0 +1,81 @@ +#include "atari2600.h" +#include "6502m.h" +#include "mem.h" +#include "tia.h" +#include "pia.h" +#include "display.h" +#include "speaker.h" +//#include "keyboard.h" +//#include "z80debugger.h" + +namespace atari2600 +{ + static bool should_exit = false; + static bool paused = false; + + void init() + { + paused = false; + m6502::reset(); + mem::reset(); + tia::reset(); + pia::reset(); + + display::init(); + speaker::init(); + //speaker::register_source(psg::get_sample); + //speaker::register_source(keyboard::get_sample); + } + + void quit() + { + should_exit = true; + } + + void step() + { + uint8_t t_states; + uint16_t pc = m6502::getPC(); + +// if (z80debugger::isbreak(pc, 9)) { +// msx::pause(); +// z80debugger::show(); +// return; +// } else { + bool instruction_done = false; + while (!instruction_done) { + instruction_done = m6502::tick(); + tia::tick(); + tia::tick(); + tia::tick(); + pia::tick(); + t_states++; + } + //speaker::update(t_states); +// } + } + + void pause() + { + display::setTitle(" (paused)"); + paused = true; + display::redraw(); + } + + void resume() + { + display::setTitle(""); + paused = false; + } + + bool is_paused() + { + return paused; + } + + bool is_quitting() + { + return should_exit; + } + +} diff --git a/atari2600.h b/atari2600.h new file mode 100644 index 0000000..8f64a1e --- /dev/null +++ b/atari2600.h @@ -0,0 +1,12 @@ +#pragma once + +namespace atari2600 +{ + void init(); + void quit(); + void step(); + void pause(); + void resume(); + bool is_paused(); + bool is_quitting(); +} diff --git a/display.h b/display.h new file mode 100644 index 0000000..566ae08 --- /dev/null +++ b/display.h @@ -0,0 +1,15 @@ +#pragma once + +namespace display +{ + void init(); + void focus(); + void redraw(const bool present=true); + void present(); + void setTitle(const char* title); + + void incZoom(); + void decZoom(); + void toggleFullscreen(); + const bool getFullscreen(); +} diff --git a/lagueirtofile b/lagueirtofile new file mode 100644 index 0000000..589df46 --- /dev/null +++ b/lagueirtofile @@ -0,0 +1,43 @@ +[linux] +cppflags = -Wall -Os -ffunction-sections -fdata-sections -std=c++20 -Isource +libs = -Wl,--gc-sections -lSDL3 +executable = atari2600 +sourcepath = source+ +buildpath = build + +[linux_debug] default +cppflags = -D DEBUG -g -Wall -std=c++20 -Isource +libs = -lSDL3 -lGL +executable = atari2600_debug +sourcepath = source+ +buildpath = build + +[windows] +cppflags = -Wall -Os -ffunction-sections -fdata-sections -std=c++20 -Isource +libs = icon.res -Wl,--gc-sections -lmingw32 -lSDL3 -lopengl32 -mwindows -static-libstdc++ -static-libgcc -lpthread -mwindows +executable = atari2600.exe +sourcepath = source+ +buildpath = build + +[windows_debug] +cppflags = -D DEBUG -g -Wall -std=c++20 -Isource +libs = -lmingw32 -lSDL3 +executable = atari2600_debug.exe +sourcepath = source+ +buildpath = build + +[macos] +compiler = clang++ +cppflags = -Wall -Os -Wno-deprecated -ffunction-sections -fdata-sections -std=c++20 -Isource +libs = -lSDL3 +executable = atari2600 +sourcepath = source+ +buildpath = build + +[macos_debug] +compiler = clang++ +cppflags = -D DEBUG -g -Wall -Wno-deprecated -std=c++20 -Isource +libs = -lSDL3 +executable = atari2600_debug +sourcepath = source+ +buildpath = build diff --git a/mem.h b/mem.h new file mode 100644 index 0000000..77d3238 --- /dev/null +++ b/mem.h @@ -0,0 +1,8 @@ +#pragma once + +namespace mem +{ + void reset(); + uint8_t read(uint16_t address); + void write(uint16_t address, uint8_t value); +} \ No newline at end of file diff --git a/pia.h b/pia.h new file mode 100644 index 0000000..4e28253 --- /dev/null +++ b/pia.h @@ -0,0 +1,7 @@ +#pragma once + +namespace pia +{ + void reset(); + void tick(); +} diff --git a/speaker.h b/speaker.h new file mode 100644 index 0000000..973e8ee --- /dev/null +++ b/speaker.h @@ -0,0 +1,12 @@ +#pragma once +#include + +namespace speaker +{ + void init(); + void quit(); + void enable(); + void disable(); + void register_source(uint8_t(*callback)()); + void update(const uint32_t dt); +} diff --git a/tia.h b/tia.h new file mode 100644 index 0000000..b98b1a0 --- /dev/null +++ b/tia.h @@ -0,0 +1,7 @@ +#pragma once + +namespace tia +{ + void reset(); + void tick(); +}