-[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:
1 parent
c819ab5f16
commit
fb5097733d
29 files changed
+2286
-65
No files matched your search
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
namespace m6502
|
||||
{
|
||||
void reset();
|
||||
void interrupt(uint8_t type);
|
||||
bool tick();
|
||||
|
||||
uint16_t getPC();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
namespace atari2600
|
||||
{
|
||||
void init();
|
||||
void quit();
|
||||
void step();
|
||||
void pause();
|
||||
void resume();
|
||||
bool is_paused();
|
||||
bool is_quitting();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user