fixed timing issues

This commit is contained in:
ArthurFerreira2 2021-06-25 00:08:30 +02:00 committed by GitHub
parent a5c58da8d6
commit a531330d0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 237 additions and 54 deletions

View File

@ -1,9 +1,14 @@
/*
puce6502 - MOS 6502 cpu emulator
Last modified 1st of August 2020
Last modified 21st of June 2021
Copyright (c) 2018 Arthur Ferreira (arthur.ferreira2@gmail.com)
This version has been modified for reinette II plus, a french Apple II plus
emulator using SDL2 (https://github.com/ArthurFerreira2/reinette-II-plus).
Please download the latest version from
https://github.com/ArthurFerreira2/puce6502
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
@ -21,14 +26,39 @@
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// set to zero for 'normal' use
// or to 1 if you want to run the functionnal tests
#define _FUNCTIONNAL_TESTS 1
#include "puce6502.h"
// function to be provided by user to handle read and writes to locations not
// in ROM or in RAM : Soft Switches, extension cards ROMs, PIA, VIA, ACIA etc...
extern uint8_t softSwitches(uint16_t address, uint8_t value);
#define CARRY 0x01
#define ZERO 0x02
#define INTR 0x04
#define DECIM 0x08
#define BREAK 0x10
#define UNDEF 0x20
#define OFLOW 0x40
#define SIGN 0x80
#if _FUNCTIONNAL_TESTS
// for functionnal tests, see main()
uint8_t RAM[65536];
inline uint8_t readMem(uint16_t address) { return RAM[address]; }
inline void writeMem(uint16_t address, uint8_t value) { RAM[address] = value; }
#else
// user provided functions
extern uint8_t readMem(uint16_t address);
extern void writeMem(uint16_t address, uint8_t value);
#endif
#define CARRY 0x01
@ -40,27 +70,23 @@ extern uint8_t softSwitches(uint16_t address, uint8_t value);
#define OFLOW 0x40
#define SIGN 0x80
struct Operand {
static struct Operand {
uint8_t code;
bool setAcc;
uint8_t value;
uint16_t address;
bool setAcc;
} ope;
struct Register {
static struct Register {
uint8_t A,X,Y,SR,SP;
uint16_t PC;
} reg;
// instruction timing :
// http://nparker.llx.com/a2/opcodes.html
// http://wouter.bbcmicro.net/general/6502/6502_opcodes.html
// NOT IMPLEMENTED :
// Absolute-X, absolute-Y, and Zpage-Y addressing modes need an extra cycle
// if indexing crosses a page boundary, or if the instruction writes to memory.
unsigned long long int ticks; // exportable to user modules
static int cycles[256] = { // cycle count per instruction
static const int cycles[256] = { // cycles per instruction
7,6,0,0,0,3,5,0,3,2,2,0,0,4,6,0,3,5,0,0,0,4,6,0,2,4,0,0,0,4,7,0,
6,6,0,0,3,3,5,0,4,2,2,0,4,4,6,0,3,5,0,0,0,4,6,0,2,4,0,0,0,4,7,0,
6,6,0,0,0,3,5,0,3,2,2,0,3,4,6,0,3,5,0,0,0,4,6,0,2,4,0,0,0,4,7,0,
@ -72,20 +98,6 @@ static int cycles[256] = { // cycle count per instruction
};
//=============================================================== MEMORY AND I/O
inline static uint8_t readMem(uint16_t address){
if (address < RAMSIZE) return(ram[address]);
if (address >= ROMSTART) return(rom[address - ROMSTART]);
return softSwitches(address, 0); // MEMORY MAPPED I/O
}
inline static void writeMem(uint16_t address, uint8_t value){
if (address < RAMSIZE) ram[address] = value;
else if (address < ROMSTART) softSwitches(address, value);
}
//=============================================== STACK, SIGN AND OTHER ROUTINES
inline static void push(uint8_t value){
@ -96,7 +108,7 @@ inline static uint8_t pull(){
return(readMem(0x100 + ++reg.SP));
}
inline static void setSZ(uint8_t value){ // update both the Sign & Zero FLAGS
inline static void setSZ(uint8_t value){ // updates both the Sign & Zero FLAGS
if (value & 0x00FF) reg.SR &= ~ZERO;
else reg.SR |= ZERO;
if (value & 0x80) reg.SR |= SIGN;
@ -109,7 +121,7 @@ inline static void branch(){ // used by the 8 branch instructions
reg.PC += ope.address;
}
inline static void makeUpdates(uint8_t val){ // used by ASL, LSR, ROL and ROR
inline static void makeUpdates(uint8_t val){ // used by ASL, LSR, ROL and ROR
if (ope.setAcc){
reg.A = val;
ope.setAcc = false;
@ -145,6 +157,7 @@ static void ZPX(){ // Zero Page,X
}
static void ZPY(){ // Zero Page,Y
if (readMem(reg.PC) + reg.Y > 0xFF) ticks++;
ope.address = (readMem(reg.PC++) + reg.Y) & 0xFF;
ope.value = readMem(ope.address);
}
@ -161,12 +174,14 @@ static void ABS(){ // ABSolute
}
static void ABX(){ // ABsolute,X
if (readMem(reg.PC) + reg.X > 0xFF) ticks++;
ope.address = (readMem(reg.PC) | (readMem(reg.PC + 1) << 8)) + reg.X;
ope.value = readMem(ope.address);
reg.PC += 2;
}
static void ABY(){ // ABsolute,Y
if (readMem(reg.PC) + reg.Y > 0xFF) ticks++;
ope.address = (readMem(reg.PC) | (readMem(reg.PC + 1) << 8)) + reg.Y;
ope.value = readMem(ope.address);
reg.PC += 2;
@ -204,7 +219,7 @@ void BRK(){ // BReaK
push(reg.PC & 0xFF);
push(reg.SR | BREAK);
reg.SR |= INTR;
reg.PC = readMem(0xFFFE) | (readMem(0xFFFF) << 8);
reg.PC = readMem(0xFFFE) | ((readMem(0xFFFF) << 8)); // IRQ/BRK vect @FFFE/FF
}
static void CLD(){ // CLear Decimal
@ -482,7 +497,7 @@ static void UND(){ // UNDefined (not a valid or supported 6502 opcode)
//================================================================== JUMP TABLES
static void (*instruction[])(void) = {
static void (*instruction[256])(void) = {
BRK, ORA, UND, UND, UND, ORA, ASL, UND, PHP, ORA, ASL, UND, UND, ORA, ASL, UND,
BPL, ORA, UND, UND, UND, ORA, ASL, UND, CLC, ORA, UND, UND, UND, ORA, ASL, UND,
JSR, AND, UND, UND, BIT, AND, ROL, UND, PLP, AND, ROL, UND, BIT, AND, ROL, UND,
@ -501,7 +516,7 @@ static void (*instruction[])(void) = {
BEQ, SBC, UND, UND, UND, SBC, INC, UND, SED, SBC, UND, UND, UND, SBC, INC, UND
};
static void (*addressing[])(void) = {
static void (*addressing[256])(void) = {
IMP, IDX, IMP, IMP, IMP, ZPG, ZPG, IMP, IMP, IMM, ACC, IMP, IMP, ABS, ABS, IMP,
REL, IDY, IMP, IMP, IMP, ZPX, ZPX, IMP, IMP, ABY, IMP, IMP, IMP, ABX, ABX, IMP,
ABS, IDX, IMP, IMP, ZPG, ZPG, ZPG, IMP, IMP, IMM, ACC, IMP, ABS, ABS, ABS, IMP,
@ -523,15 +538,8 @@ static void (*addressing[])(void) = {
//========================================================= USER INTERFACE (API)
void puce6502Reset(){
reg.PC = readMem(0xFFFC) | (readMem(0xFFFD) << 8);
reg.SP = 0xFF;
reg.SR = (reg.SR | INTR) & ~DECIM;
ope.setAcc = false;
ticks += 7;
}
void puce6502Exec(long long int cycleCount){
uint16_t puce6502Exec(unsigned long long int cycleCount){
cycleCount += ticks; // cycleCount becomes the target ticks value
while (ticks < cycleCount) {
ope.code = readMem(reg.PC++); // FETCH and increment the Program Counter
@ -539,4 +547,171 @@ void puce6502Exec(long long int cycleCount){
instruction[ope.code](); // EXECUTE the instruction
ticks += cycles[ope.code]; // update ticks count
}
return reg.PC;
}
void puce6502RST() {
reg.PC = readMem(0xFFFC) | (readMem(0xFFFD) << 8);
reg.SP = 0xFF;
reg.SR = (reg.SR | INTR | BREAK | UNDEF) & ~DECIM;
ticks += 7;
}
void puce6502NMI() {
writeMem(0x100 + reg.SP--, ((reg.PC) >> 8) & 0xFF);
writeMem(0x100 + reg.SP--, reg.PC & 0xFF);
writeMem(0x100 + reg.SP--, reg.SR);
reg.PC = readMem(0xFFFA) | (readMem(0xFFFB) << 8);
ticks += 7;
}
void puce6502IRQ() {
if (!(reg.SR & INTR)) return;
writeMem(0x100 + reg.SP--, ((reg.PC) >> 8) & 0xFF);
writeMem(0x100 + reg.SP--, reg.PC & 0xFF);
writeMem(0x100 + reg.SP--, reg.SR);
reg.PC = readMem(0xFFFE) | (readMem(0xFFFF) << 8);
ticks += 7;
}
// ALL the code down below was used during developpment for test and debug
// and is not required for normal operation
#include <stdio.h>
static const char* mn[256] = {
"BRK","ORA","UND","UND","UND","ORA","ASL","UND","PHP","ORA","ASL","UND","UND","ORA","ASL","UND",
"BPL","ORA","UND","UND","UND","ORA","ASL","UND","CLC","ORA","UND","UND","UND","ORA","ASL","UND",
"JSR","AND","UND","UND","BIT","AND","ROL","UND","PLP","AND","ROL","UND","BIT","AND","ROL","UND",
"BMI","AND","UND","UND","UND","AND","ROL","UND","SEC","AND","UND","UND","UND","AND","ROL","UND",
"RTI","EOR","UND","UND","UND","EOR","LSR","UND","PHA","EOR","LSR","UND","JMP","EOR","LSR","UND",
"BVC","EOR","UND","UND","UND","EOR","LSR","UND","CLI","EOR","UND","UND","UND","EOR","LSR","UND",
"RTS","ADC","UND","UND","UND","ADC","ROR","UND","PLA","ADC","ROR","UND","JMP","ADC","ROR","UND",
"BVS","ADC","UND","UND","UND","ADC","ROR","UND","SEI","ADC","UND","UND","UND","ADC","ROR","UND",
"UND","STA","UND","UND","STY","STA","STX","UND","DEY","UND","TXA","UND","STY","STA","STX","UND",
"BCC","STA","UND","UND","STY","STA","STX","UND","TYA","STA","TXS","UND","UND","STA","UND","UND",
"LDY","LDA","LDX","UND","LDY","LDA","LDX","UND","TAY","LDA","TAX","UND","LDY","LDA","LDX","UND",
"BCS","LDA","UND","UND","LDY","LDA","LDX","UND","CLV","LDA","TSX","UND","LDY","LDA","LDX","UND",
"CPY","CMP","UND","UND","CPY","CMP","DEC","UND","INY","CMP","DEX","UND","CPY","CMP","DEC","UND",
"BNE","CMP","UND","UND","UND","CMP","DEC","UND","CLD","CMP","UND","UND","UND","CMP","DEC","UND",
"CPX","SBC","UND","UND","CPX","SBC","INC","UND","INX","SBC","NOP","UND","CPX","SBC","INC","UND",
"BEQ","SBC","UND","UND","UND","SBC","INC","UND","SED","SBC","UND","UND","UND","SBC","INC","UND"
};
static const int am[256] = {
0x0 , 0xC , 0x0 , 0x0 , 0x0 , 0x3 , 0x3 , 0x0 , 0x0 , 0x2 , 0x1 , 0x0 , 0x0 , 0x7 , 0x7 , 0x0 ,
0x6 , 0xD , 0x0 , 0x0 , 0x0 , 0x4 , 0x4 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x0 , 0x8 , 0x8 , 0x0 ,
0x7 , 0xC , 0x0 , 0x0 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x2 , 0x1 , 0x0 , 0x7 , 0x7 , 0x7 , 0x0 ,
0x6 , 0xD , 0x0 , 0x0 , 0x0 , 0x4 , 0x4 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x0 , 0x8 , 0x8 , 0x0 ,
0x0 , 0xC , 0x0 , 0x0 , 0x0 , 0x3 , 0x3 , 0x0 , 0x0 , 0x2 , 0x1 , 0x0 , 0x7 , 0x7 , 0x7 , 0x0 ,
0x6 , 0xD , 0x0 , 0x0 , 0x0 , 0x4 , 0x4 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x0 , 0x8 , 0x8 , 0x0 ,
0x0 , 0xC , 0x0 , 0x0 , 0x0 , 0x3 , 0x3 , 0x0 , 0x0 , 0x2 , 0x1 , 0x0 , 0xA , 0x7 , 0x7 , 0x0 ,
0x6 , 0xD , 0x0 , 0x0 , 0x0 , 0x4 , 0x4 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x0 , 0x8 , 0x8 , 0x0 ,
0x0 , 0xC , 0x0 , 0x0 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x7 , 0x7 , 0x7 , 0x0 ,
0x6 , 0xD , 0x0 , 0x0 , 0x4 , 0x4 , 0x5 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x0 , 0x8 , 0x0 , 0x0 ,
0x2 , 0xC , 0x2 , 0x0 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x2 , 0x0 , 0x0 , 0x7 , 0x7 , 0x7 , 0x0 ,
0x6 , 0xD , 0x0 , 0x0 , 0x4 , 0x4 , 0x5 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x8 , 0x8 , 0x9 , 0x0 ,
0x2 , 0xC , 0x0 , 0x0 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x2 , 0x0 , 0x0 , 0x7 , 0x7 , 0x7 , 0x0 ,
0x6 , 0xD , 0x0 , 0x0 , 0x0 , 0x4 , 0x4 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x0 , 0x8 , 0x8 , 0x0 ,
0x2 , 0xC , 0x0 , 0x0 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x2 , 0x0 , 0x0 , 0x7 , 0x7 , 0x7 , 0x0 ,
0x6 , 0xD , 0x0 , 0x0 , 0x0 , 0x4 , 0x4 , 0x0 , 0x0 , 0x9 , 0x0 , 0x0 , 0x0 , 0x8 , 0x8 , 0x0
};
void dasm(uint16_t address) {
uint8_t op = readMem(address);
uint8_t b1 = readMem((address + 1) & 0xFFFF);
uint8_t b2 = readMem((address + 2) & 0xFFFF);
printf("%04X %02X ", address, op);
switch(am[op]) {
case 0x0: printf(" %s ", mn[op] ); break; // implied
case 0x1: printf(" %s A ", mn[op] ); break; // accumulator
case 0x2: printf("%02X %s #$%02X ", b1, mn[op],b1 ); break; // immediate
case 0x3: printf("%02X %s $%02X ", b1, mn[op],b1 ); break; // zero page
case 0x4: printf("%02X %s $%02X,X ", b1, mn[op],b1 ); break; // zero page, X indexed
case 0x5: printf("%02X %s $%02X,Y ", b1, mn[op],b1 ); break; // zero page, Y indexed
case 0x6: printf("%02X %s $%02X ", b1, mn[op],b1 ); break; // relative
case 0xC: printf("%02X %s ($%02X,X) ", b1, mn[op],b1 ); break; // X indexed, indirect
case 0xD: printf("%02X %s ($%02X),Y ", b1, mn[op],b1 ); break; // indirect, Y indexed
case 0x7: printf("%02X%02X %s $%02X%02X ",b1,b2,mn[op],b2,b1); break; // absolute
case 0x8: printf("%02X%02X %s $%02X%02X,X ",b1,b2,mn[op],b2,b1); break; // absolute, X indexed
case 0x9: printf("%02X%02X %s $%02X%02X,Y ",b1,b2,mn[op],b2,b1); break; // absolute, Y indexed
case 0xA: printf("%02X%02X %s ($%02X%02X) ",b1,b2,mn[op],b2,b1); break; // indirect
}
}
void printRegs() {
printf("A=%02X X=%02X Y=%02X S=%02X *S=%02X %c%c%c%c%c%c%c%c", \
reg.A, reg.X, reg.Y, reg.SP, readMem(0x100 + reg.SP), \
reg.SR&SIGN?'N':'-', reg.SR&OFLOW?'V':'-',reg.SR&UNDEF?'U':'.',reg.SR&BREAK?'B':'-', \
reg.SR&DECIM?'D':'-',reg.SR&INTR?'I':'-', reg.SR&ZERO?'Z':'-', reg.SR&CARRY?'C':'-');
}
void setPC(uint16_t address) {
reg.PC = address;
}
uint16_t getPC(){
return reg.PC;
}
#if _FUNCTIONNAL_TESTS
// 6502 functonnal tests
// using Klaus Dormann's functonnal tests published at :
// https://github.com/Klaus2m5/6502_65C02_functional_tests
int main(int argc, char* argv[]){
char *filename = "6502_functional_test.bin";
FILE *f = fopen(filename, "rb");
if (!f || fread(RAM, 1, 65536, f) != 65536) {
printf("ERROR : can't load %s\n", filename);
return(0);
}
fclose(f);
puce6502RST(); // reset the CPU
reg.PC = 0x0400; // set Program Counter to start of code
unsigned long long int oldticks = ticks;
uint16_t oldPC = 0x0400, newPC = 0x0400; // to detect the BNE $FE when an error occurs
while(1) {
dasm(newPC);
printf(" ");
newPC = puce6502Exec(1);
printRegs();
printf(" Cycles: %llu Total: %llu\n", ticks - oldticks, ticks);
oldticks = ticks;
if (newPC == 0x3469){ // 6502_functional_test SUCCESS
printf("\nReached end of 6502_functional_test @ %04X : SUCCESS !\n", newPC);
break;
}
if (newPC == oldPC ) {
printf("\n\nLoop detected @ %04X - Press ENTER to proceed with next test or CTRL<C> to stop\n\n", newPC);
return(-1);
getchar();
reg.PC = newPC + 2;
}
oldPC = newPC;
}
return(0);
}
#endif

View File

@ -3,6 +3,12 @@
Last modified 1st of August 2020
Copyright (c) 2018 Arthur Ferreira (arthur.ferreira2@gmail.com)
This version has been modified for reinette II plus, a french Apple II plus
emulator using SDL2 (https://github.com/ArthurFerreira2/reinette-II-plus).
Please download the latest version from
https://github.com/ArthurFerreira2/puce6502
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
@ -22,22 +28,24 @@
THE SOFTWARE.
*/
#ifndef _CPU_H
#define _CPU_H
#ifndef _PUCE6502_H
#define _PUCE6502_H
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef enum {false, true} bool;
typedef enum { false, true } bool;
#define ROMSTART 0xD000
#define ROMSIZE 0x3000
#define RAMSIZE 0xC000
extern unsigned long long int ticks;
uint8_t rom[ROMSIZE];
uint8_t ram[RAMSIZE];
uint16_t puce6502Exec(unsigned long long int cycleCount);
void puce6502RST();
void puce6502IRQ();
void puce6502NMI();
long long int ticks;
// void printRegs();
// void dasm(uint16_t address);
// void setPC(uint16_t address);
// uint16_t getPC();
void puce6502Reset();
void puce6502Exec(long long int cycleCount);
#endif