-[NEW] reorganització de códi

-[NEW] mòdul display operatiu
-[NEW] mòdul tia pràcticament acabat (falta el reset)
-[NEW] glue code para poder compilar. Primera compilació
-[NEW] Afegides classes de UI
-[NEW] Afegides classes de debug desensamblador del z80, a convertir a 6502
This commit is contained in:
raimon@prometeo committed 2026-09-17 10:43:59 +02:00
1 parent c819ab5f16
commit fb5097733d
29 files changed
+2286 -65

No files matched your search

+618
View File
@@ -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);
}
}
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <stdint.h>
namespace m6502
{
void reset();
void interrupt(uint8_t type);
bool tick();
uint16_t getPC();
}
+91
View File
@@ -0,0 +1,91 @@
#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"
#define NTSC_CLOCK 1193182.0f
#define PAL_CLOCK 1182298.0f
#define SECAM_CLOCK 1187500.0f
#define PAL_M_CLOCK 1191870.0f
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(NTSC_CLOCK);
speaker::register_source(tia::audio::get_sample0);
speaker::register_source(tia::audio::get_sample1);
//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) {
if (!tia::is_rdy_low()) {
instruction_done = m6502::tick();
} else {
instruction_done = true;
}
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;
}
}
+12
View File
@@ -0,0 +1,12 @@
#pragma once
namespace atari2600
{
void init();
void quit();
void step();
void pause();
void resume();
bool is_paused();
bool is_quitting();
}
+251
View File
@@ -0,0 +1,251 @@
#include "display.h"
#include <SDL3/SDL.h>
#include "tia.h"
#include "atari2600.h"
#include "ui_window.h"
//#include "z80debugger.h"
#include "ui.h"
namespace display
{
constexpr int screen_width = 160;
constexpr int screen_height = 192;
// Standard Stella NTSC Palette (ARGB format: 0xFFRRGGBB)
// Indexed by: (COLUx >> 1) & 0x7F
const uint32_t palette[128] = {
// Hue $0 (Grayscale / Monochromatic)
0xFF000000, 0xFF282828, 0xFF444444, 0xFF5C5C5C, 0xFF707070, 0xFF848484, 0xFF989898, 0xFFACACAC,
// Hue $1 (Gold / Yellow-Orange)
0xFF4C2C00, 0xFF684400, 0xFF845C00, 0xFF9C7400, 0xFFB48C00, 0xFFCCA414, 0xFFE4BC34, 0xFFFCD454,
// Hue $2 (Orange / Red-Orange)
0xFF541800, 0xFF742C00, 0xFF904400, 0xFFA85C00, 0xFFC07400, 0xFFD88C00, 0xFFF0A420, 0xFFFFBC40,
// Hue $3 (Red-Orange)
0xFF580C00, 0xFF781C00, 0xFF943000, 0xFFB04800, 0xFFC86000, 0xFFE07808, 0xFFF89028, 0xFFFFA848,
// Hue $4 (Magenta / Red-Violet)
0xFF540420, 0xFF74103C, 0xFF902058, 0xFFAC3474, 0xFFC4488C, 0xFFDC5CA8, 0xFFF474C0, 0xFFFF8CD8,
// Hue $5 (Purple / Violet)
0xFF440454, 0xFF601074, 0xFF7C2090, 0xFF9434AC, 0xFFAC48C4, 0xFFC45CDC, 0xFFDC74F4, 0xFFF48CFF,
// Hue $6 (Blue-Violet / Indigo)
0xFF2C0874, 0xFF441494, 0xFF5C24B0, 0xFF7438CC, 0xFF8C4CE4, 0xFFA460FC, 0xFFBC78FF, 0xFFD490FF,
// Hue $7 (Blue)
0xFF101480, 0xFF2424A0, 0xFF3C38BC, 0xFF504CD4, 0xFF6860EC, 0xFF8078FF, 0xFF9890FF, 0xFFB0A8FF,
// Hue $8 (Blue-Cyan)
0xFF002074, 0xFF003494, 0xFF0C48B0, 0xFF2060CC, 0xFF3474E4, 0xFF4C8CFC, 0xFF64A4FF, 0xFF7CBCFF,
// Hue $9 (Cyan / Blue-Green)
0xFF002854, 0xFF004074, 0xFF005490, 0xFF006CAC, 0xFF1880C4, 0xFF3098DC, 0xFF48B0F4, 0xFF60C8FF,
// Hue $A (Teal / Green-Cyan)
0xFF00302C, 0xFF004844, 0xFF00605C, 0xFF007874, 0xFF00908C, 0xFF1CA8A4, 0xFF38C0BC, 0xFF54D8D4,
// Hue $B (Green)
0xFF003408, 0xFF004C18, 0xFF00642C, 0xFF007C40, 0xFF009454, 0xFF14AC6C, 0xFF30C484, 0xFF4CDC9C,
// Hue $C (Yellow-Green)
0xFF0C3400, 0xFF204C00, 0xFF346400, 0xFF487C00, 0xFF5C9400, 0xFF74AC10, 0xFF8CC428, 0xFFA4DC44,
// Hue $D (Olive Green)
0xFF243000, 0xFF3C4800, 0xFF546000, 0xFF6C7800, 0xFF849000, 0xFF9CA800, 0xFFB4C018, 0xFFCCC434,
// Hue $E (Brown / Olive-Yellow)
0xFF3C2C00, 0xFF584000, 0xFF745800, 0xFF8C6C00, 0xFFA48400, 0xFFBC9C00, 0xFFD4B420, 0xFFECC83C,
// Hue $F (Orange-Brown)
0xFF482400, 0xFF643800, 0xFF805000, 0xFF986400, 0xFFB07C00, 0xFFC89400, 0xFFE0AC28, 0xFFF8C444
};
SDL_Window *win = nullptr;
SDL_Renderer *ren = nullptr;
SDL_Texture *tex = nullptr;
SDL_Texture *uitex = nullptr;
uint8_t zoom = 1;
bool fullscreen = false;
int fullscreen_scale = 1;
SDL_FRect dest_rect;
bool eventHandler(SDL_Event *e)
{
if (e->type==SDL_EVENT_WINDOW_CLOSE_REQUESTED) {
return false;
} else if ((e->type==SDL_EVENT_WINDOW_SHOWN) || (e->type==SDL_EVENT_WINDOW_EXPOSED)) {
redraw();
}
if (e->type == SDL_EVENT_KEY_DOWN) {
const SDL_Scancode &key = e->key.scancode;
switch (key) {
case SDL_SCANCODE_F8: atari2600::pause(); /*z80debugger::show();*/ break;
case SDL_SCANCODE_F12: if (atari2600::is_paused()) atari2600::resume(); else atari2600::pause(); break;
case SDL_SCANCODE_Q: if (e->key.mod & SDL_KMOD_CTRL) { return false; break; }
default: break;
}
if (atari2600::is_paused()) {
switch (key) {
case SDL_SCANCODE_F2: display::decZoom(); break;
case SDL_SCANCODE_F3: display::incZoom(); break;
case SDL_SCANCODE_F4: display::toggleFullscreen(); break;
default: break;
}
}
}
if (e->type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
if (atari2600::is_paused())
atari2600::resume();
else
atari2600::pause();
}
if (e->type == SDL_EVENT_MOUSE_MOTION) {
SDL_ShowCursor();
}
return true;
}
void init()
{
if (win) ui::window::unregisterWindow(SDL_GetWindowID(win));
if (tex) SDL_DestroyTexture(tex);
if (uitex) SDL_DestroyTexture(uitex);
if (ren) SDL_DestroyRenderer(ren);
if (win) SDL_DestroyWindow(win);
const int z = fullscreen ? 1 : zoom;
win = SDL_CreateWindow("Atari 2600 Screen", screen_width*2*z, screen_height*z, 0);
SDL_SetWindowFullscreen(win, fullscreen);
ren = SDL_CreateRenderer(win, NULL);
tex = SDL_CreateTexture(ren, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, screen_width, screen_height);
SDL_SetTextureScaleMode(tex, SDL_SCALEMODE_NEAREST);
uitex = ui::createtexture(ren);
ui::window::registerWindow(SDL_GetWindowID(win), eventHandler);
if (fullscreen)
{
int w, h;
SDL_GetWindowSize(win, &w, &h);
fullscreen_scale = h/screen_height;
dest_rect.w = screen_width*2 * fullscreen_scale;
dest_rect.h = screen_height * fullscreen_scale;
dest_rect.x = (w - dest_rect.w)/2;
dest_rect.y = (h - dest_rect.h)/2;
}
else
{
dest_rect.x = dest_rect.y = 0;
dest_rect.w = screen_width*2 * zoom;
dest_rect.h = screen_height * zoom;
}
focus();
}
void focus()
{
if (win)
{
SDL_RaiseWindow(win);
redraw();
}
}
void redraw(const bool present)
{
//if (zx_tape::getplaying() && zx_tape::getOption(ZXTAPE_OPTION_FAST_LOAD)) return;
//ui::setrenderer(ren, uitex);
const uint8_t *framebuffer = tia::get_framebuffer();
//framebuffer += screen_width*26;
Uint32* pixels;
int pitch;
SDL_LockTexture(tex, NULL, (void**)&pixels, &pitch);
for (int i=0; i<screen_width*screen_height;++i) *(pixels++) = palette[framebuffer[i]];
SDL_UnlockTexture(tex);
if (fullscreen)
{
SDL_SetRenderDrawColor(ren, 0, 0, 0, 255);
SDL_RenderClear(ren);
}
// Pintem la textura a pantalla
SDL_RenderTexture(ren, tex, NULL, &dest_rect);
// Pintem les ralles dels pixels
//if (zoom>2 || fullscreen) {
// SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_BLEND);
// SDL_SetRenderDrawColor(ren, 160, 170, 5, 96);
// const float scale = fullscreen ? fullscreen_scale : zoom;
// for (int i=0;i<144;++i) SDL_RenderDrawLine(ren, dest_rect.x, dest_rect.y+i*scale, dest_rect.x+159*scale, dest_rect.y+i*scale);
// for (int i=0;i<160;++i) SDL_RenderDrawLine(ren, dest_rect.x+i*scale, dest_rect.y, dest_rect.x+i*scale, dest_rect.y+143*scale);
//}
//if (present)
// SDL_RenderPresent(ren);
//else
//{
// SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_BLEND);
// SDL_SetRenderDrawColor(ren, 0, 0, 0, 128);
// int w = screen_width*zoom;
// int h = screen_height*zoom;
// if (fullscreen) SDL_GetWindowSize(win, &w, &h);
// SDL_FRect rect {0,0,float(w),float(h)};
// SDL_RenderFillRect(ren, &rect);
//}
if (atari2600::is_paused()) {
SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(ren, 0, 0, 0, 128);
int w = screen_width*2*zoom;
int h = screen_height*zoom;
if (fullscreen) SDL_GetWindowSize(win, &w, &h);
SDL_FRect rect {0,0,float(w),float(h)};
SDL_RenderFillRect(ren, &rect);
}
SDL_RenderPresent(ren);
}
void present()
{
SDL_RenderPresent(ren);
}
void setTitle(const char* title)
{
char tmp[256];
strcpy(tmp, "MSX Screen");
strcat(tmp, title);
SDL_SetWindowTitle(win, tmp);
}
void setZoom(const int value)
{
if (fullscreen) return;
if (value < 1) return;
const SDL_DisplayMode *dm = SDL_GetCurrentDisplayMode(SDL_GetDisplayForWindow(win));
if (screen_width*2*value > dm->w) return;
if (screen_height*value > dm->h) return;
zoom = value;
init();
}
void incZoom()
{
if (fullscreen) return;
setZoom(zoom+1);
}
void decZoom()
{
if (fullscreen) return;
setZoom(zoom-1);
}
void toggleFullscreen()
{
fullscreen = !fullscreen;
init();
}
const bool getFullscreen()
{
return fullscreen;
}
}
+15
View File
@@ -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();
}
+48
View File
@@ -0,0 +1,48 @@
#include <SDL3/SDL.h>
#include "atari2600.h"
#include "rom.h"
#include "ui.h"
#include "ui_window.h"
//#include "z80debugger.h"
int main(int argc, char *argv[]) {
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
atari2600::init();
if (argc > 1) {
rom::load(argv[1]);
} else {
rom::load("combat.bin");
}
int wait = 16;
SDL_Event e;
while (!atari2600::is_quitting()) {
wait--;
if (wait==0) {
wait = 16;
if (atari2600::is_paused()) SDL_WaitEventTimeout(NULL, 1000);
while (SDL_PollEvent(&e)) {
bool result = true;
if (e.type == SDL_EVENT_QUIT) { atari2600::quit(); break; }
if (e.type == SDL_EVENT_KEY_DOWN) {
if (atari2600::is_paused()) {
switch (e.key.scancode) {
case SDL_SCANCODE_F10: atari2600::step(); /*z80debugger::refresh();*/ break;
}
}
}
SDL_Window* window = SDL_GetWindowFromEvent(&e);
if (!ui::window::sendEvent(SDL_GetWindowID(window), &e)) { atari2600::quit(); break; }
}
}
if (!atari2600::is_paused()) atari2600::step();
ui::setClicked(false);
}
SDL_Quit();
return 0;
}
+31
View File
@@ -0,0 +1,31 @@
#include "mem.h"
#include "rom.h"
#include "tia.h"
#include "pia.h"
namespace mem
{
void reset()
{
}
uint8_t read(uint16_t address)
{
address &= 0x1fff;
if (address & 0x1000) return rom::read(address);
if (address & 0x0080) return pia::read(address);
return tia::read(address);
}
void write(uint16_t address, uint8_t value)
{
address &= 0x1fff;
if (address & 0x1000) { rom::write(address, value); return; }
if (address & 0x0080) { pia::write(address, value); return; }
tia::write(address, value);
}
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include <stdint.h>
namespace mem
{
void reset();
uint8_t read(uint16_t address);
void write(uint16_t address, uint8_t value);
}
+88
View File
@@ -0,0 +1,88 @@
#include "pia.h"
namespace pia
{
uint8_t ram[128];
uint8_t INTIM;
uint8_t TIMINT;
void reset()
{
}
void tick()
{
}
uint8_t read(uint16_t address)
{
if (address & 0x200) {
if ( (address & 0x04) ) {
// Timer functions
if (address & 0x01) {
uint8_t flags = TIMINT;
TIMINT &= ~0x80; // resetejem el flag
return flags;
} else {
return INTIM;
}
} else {
// I/O Ports
switch( address & 0x03) {
case 0x00: // [TODO] SWCHA
return 0x00;
case 0x01: // [TODO] DDRA
return 0x00;
case 0x02: // [TODO] SWCHB
return 0x00;
case 0x03: // [TODO] DDRB
return 0x00;
default:
return 0x00;
}
}
} else {
return ram[address & 0x7f];
}
}
void write(uint16_t address, uint8_t value)
{
if (address & 0x200) {
if ( (address & 0x04) ) {
if ( (address & 0x10) ) {
// Timer functions
switch( address & 0x03) {
case 0x00: // [TODO] TIM1T
break;
case 0x01: // [TODO] TIM8T
break;
case 0x02: // [TODO] TIM64T
break;
case 0x03: // [TODO] TIM1024T
break;
}
} else {
// Funcionalitat relacionada amb les interrupcions. Ignorar per a la Atari 2600
}
} else {
// I/O Ports
switch( address & 0x03) {
case 0x00: // [TODO] SWCHA
break;
case 0x01: // [TODO] DDRA
break;
case 0x02: // [TODO] SWCHB
break;
case 0x03: // [TODO] DDRB
break;
}
}
} else {
ram[address & 0x7f] = value;
}
}
}
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include <stdint.h>
namespace pia
{
void reset();
void tick();
uint8_t read(uint16_t address);
void write(uint16_t address, uint8_t value);
}
+40
View File
@@ -0,0 +1,40 @@
#include "rom.h"
#include <stdio.h>
#include <stdlib.h>
namespace rom
{
uint8_t *data = NULL;
int size = 0;
uint16_t mask = 0xffff;
void load(const char* filename)
{
if (data) free(data);
size = 0;
FILE* f = fopen(filename, "rb");
if (f) {
fseek(f, 0, SEEK_END);
size = ftell(f);
fseek(f, 0, SEEK_SET);
data = (uint8_t*)malloc(size);
fread(data, size, 1, f);
fclose(f);
if (size <= 4096) mask = size-1;
// [TODO] Gestionar ROMs mes grans de 4K
}
}
uint8_t read(uint16_t address)
{
return data[address & mask];
}
void write(uint16_t address, uint8_t value)
{
data[address & mask] = value;
}
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include <stdint.h>
namespace rom
{
void load(const char* filename);
uint8_t read(uint16_t address);
void write(uint16_t address, uint8_t value);
}
+88
View File
@@ -0,0 +1,88 @@
#include "speaker.h"
#include <SDL3/SDL.h>
#include <vector>
#include <stdlib.h>
namespace speaker
{
int sampling_freq = 44100;
uint16_t audio_buffer_size = 1024;
SDL_AudioStream* sdlAudioStream = nullptr;
uint8_t *sound_buffer = nullptr;
uint16_t sound_pos=0;
float t_sound=0.0f;
float cycles_per_sample = 0.0f;
std::vector<uint8_t(*)()> sources;
void init(float clock_frequency)
{
if (sound_buffer) quit();
SDL_AudioSpec audioSpec{SDL_AUDIO_U8, 1, sampling_freq};
sdlAudioStream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &audioSpec, nullptr, nullptr);
cycles_per_sample = clock_frequency / (float)sampling_freq;
sound_buffer = (uint8_t*)malloc(audio_buffer_size);
sound_pos = 0;
t_sound = 0.0f;
enable();
}
void quit()
{
disable();
sources.clear();
if (sound_buffer) {
free(sound_buffer);
sound_buffer = nullptr;
}
if (sdlAudioStream) {
SDL_DestroyAudioStream(sdlAudioStream);
sdlAudioStream = nullptr;
}
}
void enable()
{
SDL_ResumeAudioStreamDevice(sdlAudioStream);
}
void disable()
{
SDL_PauseAudioStreamDevice(sdlAudioStream);
}
void register_source(uint8_t(*callback)())
{
sources.push_back(callback);
}
void update(const uint32_t dt)
{
t_sound += (float)dt;
if (t_sound >= cycles_per_sample)
{
t_sound -= cycles_per_sample;
uint32_t sample = 0;
for (auto callback : sources) sample += callback();
sample /= sources.size();
sound_buffer[sound_pos++] = (uint8_t)sample;
}
if (sound_pos >= audio_buffer_size)
{
SDL_PutAudioStreamData(sdlAudioStream, sound_buffer, sound_pos);
sound_pos = 0;
while (SDL_GetAudioStreamQueued(sdlAudioStream) > 2048)
{
SDL_Delay(1);
}
}
}
}
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include <stdint.h>
namespace speaker
{
void init(float clock_frequency);
void quit();
void enable();
void disable();
void register_source(uint8_t(*callback)());
void update(const uint32_t dt);
}
+188
View File
@@ -0,0 +1,188 @@
#include "tia-audio.h"
namespace tia
{
namespace audio
{
static const uint16_t BIT_PATTERN_15 = 0x7556;
static const uint32_t BIT_PATTERN_31 = 0x74567254;
static const uint8_t LFSR511_TABLE[511] = {
1,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,1,0,0,0,1,1,1,0,0,1,0,1,
0,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,1,1,1,0,0,0,0,0,1,0,1,0,1,0,0,
1,0,0,1,0,0,0,1,0,0,1,1,0,0,0,1,0,1,1,1,0,0,1,1,1,0,1,0,1,1,0,
1,0,0,0,1,1,0,1,1,0,0,0,1,1,0,0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,
0,0,1,1,0,1,1,1,1,0,1,0,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,1,0,0,0,
1,0,0,0,0,1,0,0,1,0,1,0,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,0,1,
1,1,0,1,0,0,0,1,0,1,0,0,1,1,1,0,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,
1,0,0,1,1,1,1,1,1,0,1,1,0,1,1,1,0,0,1,0,0,0,0,1,1,1,0,1,1,0,1,
1,1,0,0,1,0,1,1,0,1,1,0,0,1,1,1,1,1,0,0,1,1,0,1,0,1,0,1,0,0,0,
1,0,1,1,1,1,0,0,1,0,1,0,0,0,1,0,1,1,0,1,0,1,1,1,0,1,1,0,0,0,0,
1,0,1,1,0,0,1,0,1,0,1,1,1,1,0,1,1,0,1,0,0,1,0,1,0,0,1,0,1,1,0,
0,0,0,0,1,0,0,0,1,1,1,1,1,1,0,1,0,0,0,0,0,1,1,1,0,0,0,1,0,1,0,
1,1,0,1,1,0,1,0,1,0,0,1,0,0,1,1,0,1,0,0,0,0,0,0,0,1,1,0,1,1,1,
0,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,0,1,1,1,1,0,1,1,1,1,0,0,1,1,1,
1,0,1,0,0,1,1,1,0,0,1,0,0,0,0,0,1,0,1,1,0,1,0,0,1,0,0,0,1,1,0,
1,0,1,1,0,1,1,1,1,1,1,0,1,0,1,0,1,0,1,1,0,0,1,1,0,1,1,1,0,1,0,
1,0,0,0,0,0,1,1
};
uint8_t get_lfsr511_bit(uint32_t step)
{
return LFSR511_TABLE[step % 511];
}
uint8_t get_poly_mode_bit(uint8_t audc, uint32_t step)
{
switch (audc & 0x0F) {
case 0:
case 11:
return 1; // Direct volume (PCM output)
case 1:
// 4-bit Poly (15 steps)
return (BIT_PATTERN_15 >> (step % 15)) & 1;
case 2:
// 5-bit Poly -> 4-bit Poly divisor (31 steps)
return (BIT_PATTERN_31 >> (step % 31)) & 1;
case 3: {
// 5-bit Poly XOR 4-bit Poly (465 steps)
uint8_t b5 = (BIT_PATTERN_31 >> (step % 31)) & 1;
uint8_t b4 = (BIT_PATTERN_15 >> (step % 15)) & 1;
return b5 ^ b4;
}
case 4:
case 5:
// Pure Tone: Divide frequency clock by 2
return (step & 1);
case 6:
case 10:
// Divide by 31 then divide by 2
return ((step / 31) & 1);
case 7:
case 9:
// 5-bit Poly output variant
return ((BIT_PATTERN_31 >> ((step / 2) % 31)) & 1);
case 8:
// White Noise (9-bit Poly / 511 steps)
return get_lfsr511_bit(step);
case 12:
case 13:
// Pure Tone: Divide frequency clock by 6
return ((step / 3) & 1);
case 14:
// Divide by 93 (31 * 3) then divide by 2
return ((step / 93) & 1);
case 15:
// 5-bit Poly clocked down by 6
return ((BIT_PATTERN_31 >> ((step / 6) % 31)) & 1);
default:
return 0;
}
}
struct channel
{
uint8_t control = 0;
uint8_t frequency = 0;
uint8_t volume = 0;
uint8_t freq_counter = 0;
uint32_t poly_counter = 0;
};
channel channels[2];
void tick()
{
for (int i=0; i<2; ++i) {
if (channels[i].freq_counter == 0) {
channels[i].freq_counter = channels[i].frequency + 1;
channels[i].poly_counter++;
} else {
channels[i].freq_counter--;
}
}
}
void write(uint16_t address, uint8_t value)
{
switch (address) {
case 0x15: // AUDC0
channels[0].control = value & 0x0f; break;
case 0x16: // AUDC1
channels[1].control = value & 0x0f; break;
case 0x17: // AUDF0
channels[0].frequency = value & 0x1f; break;
case 0x18: // AUDF1
channels[1].frequency = value & 0x1f; break;
case 0x19: // AUDV0
channels[0].volume = value & 0x0f; break;
case 0x1A: // AUDV1
channels[1].volume = value & 0x0f; break;
}
}
uint8_t get_sample0() { return get_sample(0); }
uint8_t get_sample1() { return get_sample(1); }
uint8_t get_sample(uint8_t channel)
{
if (channels[channel].volume == 0) return 0;
bool bit_out = false;
switch (channels[channel].control) {
case 0:
case 11:
// Set to 1: Direct volume output (PCM mode)
bit_out = true;
break;
case 1:
// 4-bit Poly (15-step repeating sequence)
bit_out = (BIT_PATTERN_15 >> (channels[channel].poly_counter % 15)) & 1;
break;
case 2:
// 5-bit Poly (31-step repeating sequence)
bit_out = (BIT_PATTERN_31 >> (channels[channel].poly_counter % 31)) & 1;
break;
case 4:
case 5:
// Pure tone (Divide by 2 square wave)
bit_out = (channels[channel].poly_counter & 1);
break;
case 8:
// White noise (9-bit Poly / 511-step sequence)
bit_out = get_lfsr511_bit(channels[channel].poly_counter % 511);
break;
case 12:
case 13:
// Pure tone (Divide by 6 square wave)
bit_out = ((channels[channel].poly_counter / 3) & 1);
break;
default:
// Handle remaining poly modes (3, 6, 7, 9, 10, 14, 15) via lookup table
bit_out = get_poly_mode_bit(channels[channel].control, channels[channel].poly_counter);
break;
}
return bit_out ? channels[channel].volume * 17 : 0;
}
}
}
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#include <stdint.h>
namespace tia
{
namespace audio
{
void tick();
void write(uint16_t address, uint8_t value);
uint8_t get_sample(uint8_t channel);
}
}
+464
View File
@@ -0,0 +1,464 @@
#include "tia.h"
#include "tia-audio.h"
#include "display.h"
namespace tia
{
bool collision_missile0_player1 = false;
bool collision_missile0_player0 = false;
bool collision_missile1_player0 = false;
bool collision_missile1_player1 = false;
bool collision_player0_playfield = false;
bool collision_player0_ball = false;
bool collision_player1_playfield = false;
bool collision_player1_ball = false;
bool collision_missile0_playfield = false;
bool collision_missile0_ball = false;
bool collision_missile1_playfield = false;
bool collision_missile1_ball = false;
bool collision_ball_playfield = false;
bool collision_player0_player1 = false;
bool collision_missile0_missile1 = false;
float paddle_pos[4] { 0.5f, 0.5f, 0.5f, 0.5f };
uint32_t paddle_charge_cycles[4] { 0, 0, 0, 0 };
bool joy0_button_pressed = false;
bool joy1_button_pressed = false;
uint8_t player0_size = 1;
uint8_t player0_num_copies = 1;
uint8_t player0_copies_distance = 0;
uint8_t player0_color = 0;
bool player0_reflect = false;
uint8_t player0_pos = 0;
uint8_t player0_bits = 0;
int8_t player0_hmotion = 0;
bool player0_vert_delay = false;
uint8_t player0_pixel_counter = 0;
uint8_t player0_pixel_position = 0;
uint8_t player1_size = 1;
uint8_t player1_num_copies = 1;
uint8_t player1_copies_distance = 0;
uint8_t player1_color = 0;
bool player1_reflect = false;
uint8_t player1_pos = 0;
uint8_t player1_bits = 0;
int8_t player1_hmotion = 0;
bool player1_vert_delay = false;
uint8_t player1_pixel_counter = 0;
uint8_t player1_pixel_position = 0;
bool missile0_enabled = false;
uint8_t missile0_size = 1;
uint8_t missile0_pos = 0;
int8_t missile0_hmotion = 0;
bool missile0_reset = false;
uint8_t missile0_pixel_counter = 0;
bool missile1_enabled = false;
uint8_t missile1_size = 1;
uint8_t missile1_pos = 0;
int8_t missile1_hmotion = 0;
bool missile1_reset = false;
uint8_t missile1_pixel_counter = 0;
bool ball_enabled = false;
uint8_t ball_size = 1;
uint8_t ball_pos = 0;
int8_t ball_hmotion = 0;
bool ball_vert_delay = false;
uint8_t ball_pixel_counter = 0;
uint8_t playfield_color = 0;
bool playfield_reflected = false;
bool playfield_score = false;
bool playfield_priority = false;
uint32_t playfield_bits = 0x00000000;
uint8_t playfield_shifts[2][40] = {
// Repeat
{16,17,18,19, 15,14,13,12,11,10,9,8, 0,1,2,3,4,5,6,7, 16,17,18,19, 15,14,13,12,11,10,9,8, 0,1,2,3,4,5,6,7},
// Reflect
{16,17,18,19, 15,14,13,12,11,10,9,8, 0,1,2,3,4,5,6,7, 7,6,5,4,3,2,1,0, 8,9,10,11,12,13,14,15, 19,18,17,16}
};
uint8_t background_color = 0;
uint32_t current_frame_cycles = 0;
bool hmove = false;
bool vsync_set = false;
bool vblank_set = false;
bool vblank_latch = false;
bool vblank_dump = false;
uint16_t color_clock = 0;
uint16_t current_scanline = 0;
bool rdy_low = false;
uint8_t framebuffer[160][192];
void reset()
{
}
const uint8_t *get_framebuffer()
{
return (const uint8_t*)framebuffer;
}
void tick()
{
if (!vblank_dump) current_frame_cycles++;
color_clock = (color_clock + 1) % 228;
if (color_clock == 0) {
rdy_low = false;
hmove = false;
current_scanline++;
}
if (missile0_reset) missile0_pos = player0_pos + (4*player0_size);
if (missile1_reset) missile1_pos = player1_pos + (4*player1_size);
audio::tick();
// [TODO] Pintar pixels
if ( (!vsync_set) && (!vblank_set) && (color_clock >= 68) && (current_scanline >= 37) && (current_scanline < 229) && (!hmove || color_clock >= 92) ) {
const uint8_t pixel_clock = color_clock - 68;
if (player0_pos == pixel_clock) {
player0_pixel_counter = player0_size;
player0_pixel_position = 7;
}
if (player1_pos == pixel_clock) {
player1_pixel_counter = player1_size;
player1_pixel_position = 7;
}
if ( (missile0_enabled) && (missile0_pos == pixel_clock) ) {
missile0_pixel_counter = missile0_size;
}
if ( (missile1_enabled) && (missile1_pos == pixel_clock) ) {
missile1_pixel_counter = missile1_size;
}
if ( (ball_enabled) && (ball_pos == pixel_clock) ) {
ball_pixel_counter = ball_size;
}
bool player0_visible = false;
bool player1_visible = false;
bool missile0_visible = false;
bool missile1_visible = false;
bool ball_visible = false;
bool playfield_visible = false;
if (player0_pixel_counter>0) {
if ((player0_bits>>player0_pixel_position)&0x01) player0_visible = true;
player0_pixel_counter--;
if ( (player0_pixel_counter==0) && (player0_pixel_position>0) ) {
player0_pixel_counter = player0_size;
player0_pixel_position--;
}
}
if (player1_pixel_counter>0) {
if ((player1_bits>>player1_pixel_position)&0x01) player1_visible = true;
player1_pixel_counter--;
if ( (player1_pixel_counter==0) && (player1_pixel_position>0) ) {
player1_pixel_counter = player1_size;
player1_pixel_position--;
}
}
if (missile0_pixel_counter>0) {
missile0_visible = true;
missile0_pixel_counter--;
}
if (missile1_pixel_counter>0) {
missile1_visible = true;
missile1_pixel_counter--;
}
if (ball_pixel_counter>0) {
ball_visible = true;
ball_pixel_counter--;
}
const uint8_t playfield_clock = pixel_clock >> 2;
const uint8_t playfield_shift = playfield_shifts[playfield_reflected?1:0][playfield_clock];
if ( (playfield_bits >> playfield_shift) & 0x01) {
playfield_visible = true;
}
if (missile0_visible && player1_visible) collision_missile0_player1 = true;
if (missile0_visible && player0_visible) collision_missile0_player0 = true;
if (missile1_visible && player0_visible) collision_missile1_player0 = true;
if (missile1_visible && player1_visible) collision_missile1_player1 = true;
if (player0_visible && playfield_visible) collision_player0_playfield = true;
if (player0_visible && ball_visible) collision_player0_ball = true;
if (player1_visible && playfield_visible) collision_player1_playfield = true;
if (player1_visible && ball_visible) collision_player1_ball = true;
if (missile0_visible && playfield_visible) collision_missile0_playfield = true;
if (missile0_visible && ball_visible) collision_missile0_ball = true;
if (missile1_visible && playfield_visible) collision_missile1_playfield = true;
if (missile1_visible && ball_visible) collision_missile1_ball = true;
if (ball_visible && playfield_visible) collision_ball_playfield = true;
if (player0_visible && player1_visible) collision_player0_player1 = true;
if (missile0_visible && missile1_visible) collision_missile0_missile1 = true;
const uint8_t x = pixel_clock;
const uint8_t y = current_scanline-37;
if (playfield_priority) {
if (ball_visible) {
framebuffer[y][x] = playfield_color;
} else if (playfield_visible) {
framebuffer[y][x] = playfield_score ? (x<80 ? player0_color : player1_color) : playfield_color;
} else if (player0_visible || missile0_visible) {
framebuffer[y][x] = player0_color;
} else if (player1_visible || missile1_visible) {
framebuffer[y][x] = player1_color;
} else {
framebuffer[y][x] = background_color;
}
} else {
if (player0_visible || missile0_visible) {
framebuffer[y][x] = player0_color;
} else if (player1_visible || missile1_visible) {
framebuffer[y][x] = player1_color;
} else if (ball_visible) {
framebuffer[y][x] = playfield_color;
} else if (playfield_visible) {
framebuffer[y][x] = playfield_score ? (x<80 ? player0_color : player1_color) : playfield_color;
} else {
framebuffer[y][x] = background_color;
}
}
}
}
uint8_t get_paddle_state(int index)
{
if (vblank_dump) return 0x00;
return (current_frame_cycles >= paddle_charge_cycles[index]) ? 0x80 : 0x00;
}
uint8_t read(uint16_t address)
{
switch (address & 0x0f) {
case 0x00: return (collision_missile0_player1?0x80:0x00) | (collision_missile0_player0?0x40:0x00);
case 0x01: return (collision_missile1_player0?0x80:0x00) | (collision_missile1_player1?0x40:0x00);
case 0x02: return (collision_player0_playfield?0x80:0x00) | (collision_player0_ball?0x40:0x00);
case 0x03: return (collision_player1_playfield?0x80:0x00) | (collision_player1_ball?0x40:0x00);
case 0x04: return (collision_missile0_playfield?0x80:0x00) | (collision_missile0_ball?0x40:0x00);
case 0x05: return (collision_missile1_playfield?0x80:0x00) | (collision_missile1_ball?0x40:0x00);
case 0x06: return (collision_ball_playfield?0x80:0x00);
case 0x07: return (collision_player0_player1?0x80:0x00) | (collision_missile0_missile1?0x40:0x00);
case 0x08: return get_paddle_state(0);
case 0x09: return get_paddle_state(1);
case 0x0A: return get_paddle_state(2);
case 0x0B: return get_paddle_state(3);
case 0x0C: return joy0_button_pressed ? 0x00 : 0x80;
case 0x0D: return joy1_button_pressed ? 0x00 : 0x80;
default: return 0x00;
}
}
void write(uint16_t address, uint8_t value)
{
address &= 0x3f;
if (address >= 0x2d) address -= 0x20;
// [TODO] Procesar segons l'adreça resultant
switch (address) {
case 0x00: // VSYNC
{
const bool old_vsync = vsync_set;
vsync_set = value & 0x20;
if (old_vsync && !vsync_set) { // Resetejem el frame
current_scanline = 0;
display::redraw();
}
break;
}
case 0x01: // VBLANK
{
bool prev_dump = vblank_dump;
vblank_set = value & 0x20;
vblank_latch = value & 0x40;
vblank_dump = value & 0x80;
if (prev_dump && !vblank_dump) {
current_frame_cycles = 0;
for (int i = 0; i < 4; ++i) paddle_charge_cycles[i] = static_cast<uint32_t>(paddle_pos[i] * 2400.0f);
}
break;
}
case 0x02: // WSYNC
rdy_low = true;
break;
case 0x03: // RSYNC
color_clock = 0;
rdy_low = false;
break;
case 0x04: // NUSIZ0
missile0_size = 1 << ((value >> 4) & 0x03);
switch (value & 0x07) {
case 0: player0_num_copies = 1; player0_copies_distance = 0; player0_size = 1; break;
case 1: player0_num_copies = 2; player0_copies_distance = 2; player0_size = 1; break;
case 2: player0_num_copies = 2; player0_copies_distance = 4; player0_size = 1; break;
case 3: player0_num_copies = 3; player0_copies_distance = 2; player0_size = 1; break;
case 4: player0_num_copies = 2; player0_copies_distance = 8; player0_size = 1; break;
case 5: player0_num_copies = 1; player0_copies_distance = 0; player0_size = 2; break;
case 6: player0_num_copies = 3; player0_copies_distance = 4; player0_size = 1; break;
case 7: player0_num_copies = 1; player0_copies_distance = 0; player0_size = 4; break;
}
break;
case 0x05: // NUSIZ1
missile1_size = 1 << ((value >> 4) & 0x03);
switch (value & 0x07) {
case 0: player1_num_copies = 1; player1_copies_distance = 0; player1_size = 1; break;
case 1: player1_num_copies = 2; player1_copies_distance = 2; player1_size = 1; break;
case 2: player1_num_copies = 2; player1_copies_distance = 4; player1_size = 1; break;
case 3: player1_num_copies = 3; player1_copies_distance = 2; player1_size = 1; break;
case 4: player1_num_copies = 2; player1_copies_distance = 8; player1_size = 1; break;
case 5: player1_num_copies = 1; player1_copies_distance = 0; player1_size = 2; break;
case 6: player1_num_copies = 3; player1_copies_distance = 4; player1_size = 1; break;
case 7: player1_num_copies = 1; player1_copies_distance = 0; player1_size = 4; break;
}
break;
case 0x06: // COLUP0
player0_color = (value >> 1) & 0x7F; break;
case 0x07: // COLUP1
player1_color = (value >> 1) & 0x7F; break;
case 0x08: // COLUPF
playfield_color = (value >> 1) & 0x7F; break;
case 0x09: // COLUBK
background_color = (value >> 1) & 0x7F; break;
case 0x0A: // CTRLPF
playfield_reflected = value & 0x01;
playfield_score = value & 0x02;
playfield_priority = value & 0x04;
ball_size = 1 << ((value >> 4) & 0x03);
break;
case 0x0B: // REFP0
player0_reflect = value & 0x08; break;
case 0x0C: // REFP1
player1_reflect = value & 0x08; break;
case 0x0D: // PF0
playfield_bits = (playfield_bits & 0x0000ffff) | (uint32_t(value>>4)<<16); break;
case 0x0E: // PF1
playfield_bits = (playfield_bits & 0x000f00ff) | (uint32_t(value)<<8); break;
case 0x0F: // PF2
playfield_bits = (playfield_bits & 0x000fff00) | (uint32_t(value)); break;
case 0x10: // RESP0
player0_pos = color_clock < 68 ? 0 : color_clock-68; break;
case 0x11: // RESP1
player1_pos = color_clock < 68 ? 0 : color_clock-68; break;
case 0x12: // RESM0
missile0_pos = color_clock < 68 ? 0 : color_clock-68; break;
case 0x13: // RESM1
missile1_pos = color_clock < 68 ? 0 : color_clock-68; break;
case 0x14: // RESBL
ball_pos = color_clock < 68 ? 0 : color_clock-68; break;
case 0x15: // AUDC0
case 0x16: // AUDC1
case 0x17: // AUDF0
case 0x18: // AUDF1
case 0x19: // AUDV0
case 0x1A: // AUDV1
audio::write(address, value);
break;
case 0x1B: //GRP0
player0_bits = value; break;
case 0x1C: //GRP1
player1_bits = value; break;
case 0x1D: //ENAM0
missile0_enabled = value & 0x02; break;
case 0x1E: //ENAM1
missile0_enabled = value & 0x02; break;
case 0x1F: //ENABL
missile0_enabled = value & 0x02; break;
case 0x20: //HMP0
player0_hmotion = int8_t(value) >> 4; break;
case 0x21: //HMP1
player1_hmotion = int8_t(value) >> 4; break;
case 0x22: //HMM0
missile0_hmotion = int8_t(value) >> 4; break;
case 0x23: //HMM1
missile1_hmotion = int8_t(value) >> 4; break;
case 0x24: //HMBL
ball_hmotion = int8_t(value) >> 4; break;
case 0x25: //VDELP0
player0_vert_delay = value & 0x01; break;
case 0x26: //VDELP1
player1_vert_delay = value & 0x01; break;
case 0x27: //VDELBL
ball_vert_delay = value & 0x01; break;
case 0x28: // RESMP0
missile0_reset = value & 0x02; break;
case 0x29: // RESMP1
missile1_reset = value & 0x02; break;
case 0x2A: // HMOVE
// [TODO] Implementació bàsica, reemplazar per una que use els 15 color_clocks en el futur
player0_pos += player0_hmotion;
player1_pos += player1_hmotion;
missile0_pos += missile0_hmotion;
missile1_pos += missile1_hmotion;
ball_pos += ball_hmotion;
hmove = true;
break;
case 0x2B: // HMCLR
player0_hmotion = player1_hmotion = missile0_hmotion = missile1_hmotion = ball_hmotion = 0;
break;
case 0x2C: // CXCLR
collision_missile0_player1 = false;
collision_missile0_player0 = false;
collision_missile1_player0 = false;
collision_missile1_player1 = false;
collision_player0_playfield = false;
collision_player0_ball = false;
collision_player1_playfield = false;
collision_player1_ball = false;
collision_missile0_playfield = false;
collision_missile0_ball = false;
collision_missile1_playfield = false;
collision_missile1_ball = false;
collision_ball_playfield = false;
collision_player0_player1 = false;
collision_missile0_missile1 = false;
break;
}
}
bool is_rdy_low()
{
return rdy_low;
}
}
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include <stdint.h>
namespace tia
{
void reset();
void tick();
uint8_t read(uint16_t address);
void write(uint16_t address, uint8_t value);
bool is_rdy_low();
const uint8_t *get_framebuffer();
namespace audio
{
uint8_t get_sample0();
uint8_t get_sample1();
}
}
+172
View File
@@ -0,0 +1,172 @@
#include "ui.h"
namespace ui
{
uint8_t colors[16][3] = {
{0,0,0},
{0,0,128},
{0,128,0},
{0,128,128},
{128,0,0},
{128,0,128},
{255,128,0},
{128,128,128},
{30,30,30},
{0,0,255},
{0,255,0},
{0,255,255},
{255,0,0},
{255,0,255},
{255,255,0},
{255,255,255},
};
SDL_Renderer *ren = nullptr;
SDL_Texture *tex = nullptr;
uint8_t offset_x = 0;
uint8_t offset_y = 0;
bool clicked = false;
void (*dialog)(void) = nullptr;
SDL_Texture * createtexture(SDL_Renderer *renderer)
{
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, SDL_LoadBMP("font.bmp"));
SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_NEAREST);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
return texture;
}
void setrenderer(SDL_Renderer *renderer, SDL_Texture *texture)
{
ren = renderer;
tex = texture;
offset_x = offset_y = 0;
}
void setoffset(uint8_t x, uint8_t y)
{
offset_x = x;
offset_y = y;
}
void incoffset(uint8_t x, uint8_t y)
{
offset_x += x;
offset_y += y;
}
uint8_t getOffsetX()
{
return offset_x;
}
uint8_t getOffsetY()
{
return offset_y;
}
bool mouseInside(int x, int y, int w, int h)
{
float mxf, myf;
SDL_GetMouseState(&mxf, &myf);
int mx = int(mxf)/CHR_W;
int my = int(myf)/CHR_H;
mx -= offset_x;
my -= offset_y;
return (mx >= x) && (mx < x+w) && (my >= y) && (my < y+h);
}
void panel(int x, int y, int w, int h, const char *title)
{
ui::setoffset(x, y);
ui::box(0,0,w,h,COLOR_WHITE);
ui::printrect(2,0, strlen(title)+2,1, COLOR_DARK);
ui::printtxt(3,0, title, COLOR_WHITE);
ui::incoffset(1, 1);
}
void box(int x, int y, int w, int h, uint8_t color)
{
SDL_FRect rect {float((offset_x+x)*CHR_W)+3, float((offset_y+y)*CHR_H)+6, float(w*CHR_W-6), float(h*CHR_H-13)};
SDL_SetRenderDrawColor(ren, colors[color][0], colors[color][1], colors[color][2], 255);
SDL_RenderRect(ren, &rect);
}
void printrect(int x, int y, int w, int h, uint8_t color)
{
SDL_FRect rect {float(offset_x+x)*CHR_W, float(offset_y+y)*CHR_H, float(w*CHR_W), float(h*CHR_H)};
SDL_SetRenderDrawColor(ren, colors[color][0], colors[color][1], colors[color][2], 255);
SDL_RenderFillRect(ren, &rect);
}
void printvoidrect(int x, int y, int w, int h, uint8_t color)
{
SDL_FRect rect {float(offset_x+x)*CHR_W, float(offset_y+y)*CHR_H, float(w*CHR_W), float(h*CHR_H)};
SDL_SetRenderDrawColor(ren, colors[color][0], colors[color][1], colors[color][2], 255);
SDL_RenderRect(ren, &rect);
}
void printchar(int x, int y, char chr, uint8_t color)
{
if (color != 255) SDL_SetRenderDrawColor(ren, colors[color][0], colors[color][1], colors[color][2], 255);
if (chr==32) return;
if (chr<32 || chr>127) chr = '.';
SDL_FRect src {float((chr-32)&0xf)*CHR_W, float((chr-32)>>4)*CHR_H, float(CHR_W), float(CHR_H)};
SDL_FRect dst {float(offset_x+x)*CHR_W, float(offset_y+y)*CHR_H, float(CHR_W), float(CHR_H)};
SDL_RenderTexture(ren, tex, &src, &dst);
}
void printtxt(int x, int y, const char *text, uint8_t color)
{
SDL_SetTextureColorMod(tex, colors[color][0], colors[color][1], colors[color][2]);
for (int i=0; i<strlen(text);++i) if (text[i]!=32) printchar(x+i, y, text[i]);
}
void placechar(int x, int y, char chr, uint8_t color)
{
if (color != 255) SDL_SetRenderDrawColor(ren, colors[color][0], colors[color][1], colors[color][2], 255);
if (chr==32) return;
if (chr<32 || chr>127) chr = '.';
SDL_FRect src {float((chr-32)&0xf)*CHR_W, float((chr-32)>>4)*CHR_H, float(CHR_W), float(CHR_H)};
SDL_FRect dst {float(x), float(y), float(CHR_W*2), float(CHR_H*2)};
SDL_RenderTexture(ren, tex, &src, &dst);
}
void placetxt(int x, int y, const char *text, uint8_t color)
{
SDL_SetTextureColorMod(tex, colors[color][0], colors[color][1], colors[color][2]);
for (int i=0; i<strlen(text);++i) if (text[i]!=32) placechar(x+i*CHR_W*2, y, text[i]);
}
void setClicked(const bool value)
{
clicked = value;
}
const bool getClicked()
{
return clicked;
}
void setDialog(void(*new_dialog)(void))
{
ui:dialog = new_dialog;
}
bool hasDialog()
{
return dialog != nullptr;
}
void callDialog()
{
if (dialog) dialog();
}
}
+50
View File
@@ -0,0 +1,50 @@
#pragma once
#include <SDL3/SDL.h>
namespace ui
{
#define CHR_W 6
#define CHR_H 13
#define COLOR_BLACK 0
#define COLOR_DARK_BLUE 1
#define COLOR_GREEN 2
#define COLOR_TEAL 3
#define COLOR_BROWN 4
#define COLOR_PURPLE 5
#define COLOR_ORANGE 6
#define COLOR_GRAY 7
#define COLOR_DARK 8
#define COLOR_BLUE 9
#define COLOR_LIME 10
#define COLOR_CYAN 11
#define COLOR_RED 12
#define COLOR_MAGENTA 13
#define COLOR_YELLOW 14
#define COLOR_WHITE 15
SDL_Texture * createtexture(SDL_Renderer *renderer);
void setrenderer(SDL_Renderer *renderer, SDL_Texture *texture);
void setoffset(uint8_t x, uint8_t y);
void incoffset(uint8_t x, uint8_t y);
uint8_t getOffsetX();
uint8_t getOffsetY();
bool mouseInside(int x, int y, int w, int h);
void panel(int x, int y, int w, int h, const char *title);
void box(int x, int y, int w, int h, uint8_t color);
void printrect(int x, int y, int w, int h, uint8_t color);
void printvoidrect(int x, int y, int w, int h, uint8_t color);
void printchar(int x, int y, char chr, uint8_t color=255);
void printtxt(int x, int y, const char *text, uint8_t color);
void placechar(int x, int y, char chr, uint8_t color=255);
void placetxt(int x, int y, const char *text, uint8_t color);
void setClicked(const bool value);
const bool getClicked();
void setDialog(void(*new_dialog)(void));
bool hasDialog();
void callDialog();
}
+33
View File
@@ -0,0 +1,33 @@
#include "ui_window.h"
#include <vector>
namespace ui
{
namespace window
{
struct window_t
{
Uint32 window;
bool (*callback)(SDL_Event*);
};
std::vector<window_t> windows;
void registerWindow(Uint32 window, bool(*callback)(SDL_Event*))
{
for (auto win : windows) if (win.window == window) return;
windows.push_back((window_t){window, callback});
}
void unregisterWindow(Uint32 window)
{
for (auto win = windows.begin(); win != windows.end(); win++) if ((*win).window == window) { windows.erase(win); return; }
}
bool sendEvent(Uint32 window, SDL_Event *e)
{
for (auto win : windows) if (win.window == window) return win.callback(e);
return true;
}
}
}
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include <SDL3/SDL.h>
namespace ui
{
namespace window
{
void registerWindow(Uint32 window, bool(*callback)(SDL_Event *e));
void unregisterWindow(Uint32 window);
bool sendEvent(Uint32 window, SDL_Event *e);
}
}