Skip to content

Commit 51e70d7

Browse files
author
Tom Seddon
committed
Fix 65c02 timing. See #597.
* add 6502 timing tests * fix CMOS read (zp),Y, as per #597 * fix CMOS write (zp),Y, mostly as per #597 (tweak required) * fix CMOS RMW abs,X
1 parent ccaa16e commit 51e70d7

File tree

6 files changed

+190
-3
lines changed

6 files changed

+190
-3
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,6 @@
8282
[submodule "submodules/shared_lib"]
8383
path = submodules/shared_lib
8484
url = https://github.com/tom-seddon/shared_lib
85+
[submodule "submodules/beeb_6502_timing_tests"]
86+
path = submodules/beeb_6502_timing_tests
87+
url = https://github.com/tom-seddon/beeb_6502_timing_tests

src/6502/6502_gen.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -374,8 +374,8 @@ class Instr {
374374
// set differently.
375375
*ifun += CMOS_FUNCTION_SUFFIX;
376376
} else if ((type == InstrType_RMW) ||
377-
(type == InstrType_W && (m == Mode_Abx || m == Mode_Aby)) ||
378-
(type == InstrType_R && (m == Mode_Abx || m == Mode_Aby))) {
377+
(type == InstrType_W && (m == Mode_Abx || m == Mode_Aby || m == Mode_Iny)) ||
378+
(type == InstrType_R && (m == Mode_Abx || m == Mode_Aby || m == Mode_Iny))) {
379379
*tfun = CMOS_FUNCTION_SUFFIX;
380380
}
381381
}

src/6502/c/6502.c

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1386,12 +1386,12 @@ static void Cycle5_RMW_ABX2_CMOS_AC0(M6502 *s) {
13861386
}
13871387

13881388
static void Cycle3_RMW_ABX2_CMOS_AC1(M6502 *s) {
1389+
s->abus.w = s->ad.w + s->x;
13891390
s->read = M6502ReadType_Uninteresting;
13901391
s->tfn = &Cycle4_RMW_ABX2_CMOS_AC1;
13911392
}
13921393

13931394
static void Cycle4_RMW_ABX2_CMOS_AC1(M6502 *s) {
1394-
s->abus.w = s->ad.w + s->x;
13951395
s->read = M6502ReadType_Data;
13961396
s->tfn = &Cycle5_RMW_ABX2_CMOS_AC1;
13971397
}
@@ -2144,6 +2144,75 @@ static void Cycle5_R_ABY_BCD_CMOS_D1_AC1(M6502 *s) {
21442144
//////////////////////////////////////////////////////////////////////////
21452145
//////////////////////////////////////////////////////////////////////////
21462146

2147+
static void Cycle1_R_INY_CMOS(M6502 *);
2148+
static void Cycle2_R_INY_CMOS(M6502 *);
2149+
static void Cycle3_R_INY_CMOS(M6502 *);
2150+
static void Cycle4_R_INY_CMOS_AC0(M6502 *);
2151+
static void Cycle4_R_INY_CMOS_AC1(M6502 *);
2152+
static void Cycle5_R_INY_CMOS_AC1(M6502 *);
2153+
2154+
static void Cycle0_R_INY_CMOS(M6502 *s) {
2155+
s->abus.w = s->pc.w++;
2156+
s->read = M6502ReadType_Instruction;
2157+
s->tfn = &Cycle1_R_INY_CMOS;
2158+
}
2159+
2160+
static void Cycle1_R_INY_CMOS(M6502 *s) {
2161+
s->ia.b.l = s->dbus;
2162+
s->abus.w = s->ia.b.l;
2163+
s->read = M6502ReadType_Address;
2164+
s->tfn = &Cycle2_R_INY_CMOS;
2165+
}
2166+
2167+
static void Cycle2_R_INY_CMOS(M6502 *s) {
2168+
s->ad.b.l = s->dbus;
2169+
s->abus.w = (uint8_t)(s->ia.b.l + 1);
2170+
s->read = M6502ReadType_Address;
2171+
s->tfn = &Cycle3_R_INY_CMOS;
2172+
}
2173+
2174+
static void Cycle3_R_INY_CMOS(M6502 *s) {
2175+
s->ad.b.h = s->dbus;
2176+
M6502Word ffa = {s->ad.w + s->y};
2177+
if (ffa.b.h != s->ad.b.h) {
2178+
s->read = M6502ReadType_Uninteresting;
2179+
s->tfn = &Cycle4_R_INY_CMOS_AC1;
2180+
} else {
2181+
CheckForInterrupts(s);
2182+
s->abus = ffa;
2183+
s->read = M6502ReadType_Data;
2184+
s->tfn = &Cycle4_R_INY_CMOS_AC0;
2185+
}
2186+
}
2187+
2188+
static void Cycle4_R_INY_CMOS_AC0(M6502 *s) {
2189+
s->data = s->dbus;
2190+
(*s->ifn)(s);
2191+
#ifdef _DEBUG
2192+
s->ifn = NULL;
2193+
#endif
2194+
M6502_NextInstruction(s);
2195+
}
2196+
2197+
static void Cycle4_R_INY_CMOS_AC1(M6502 *s) {
2198+
s->abus.w = s->ad.w + s->y;
2199+
CheckForInterrupts(s);
2200+
s->read = M6502ReadType_Data;
2201+
s->tfn = &Cycle5_R_INY_CMOS_AC1;
2202+
}
2203+
2204+
static void Cycle5_R_INY_CMOS_AC1(M6502 *s) {
2205+
s->data = s->dbus;
2206+
(*s->ifn)(s);
2207+
#ifdef _DEBUG
2208+
s->ifn = NULL;
2209+
#endif
2210+
M6502_NextInstruction(s);
2211+
}
2212+
2213+
//////////////////////////////////////////////////////////////////////////
2214+
//////////////////////////////////////////////////////////////////////////
2215+
21472216
static void Cycle1_R_INY_BCD_CMOS(M6502 *);
21482217
static void Cycle2_R_INY_BCD_CMOS(M6502 *);
21492218
static void Cycle3_R_INY_BCD_CMOS_D1(M6502 *);
@@ -2287,6 +2356,54 @@ static void Cycle6_R_INY_BCD_CMOS_D1_AC1(M6502 *s) {
22872356
//////////////////////////////////////////////////////////////////////////
22882357
//////////////////////////////////////////////////////////////////////////
22892358

2359+
static void Cycle1_W_INY_CMOS(M6502 *);
2360+
static void Cycle2_W_INY_CMOS(M6502 *);
2361+
static void Cycle3_W_INY_CMOS(M6502 *);
2362+
static void Cycle4_W_INY_CMOS(M6502 *);
2363+
static void Cycle5_W_INY_CMOS(M6502 *);
2364+
2365+
static void Cycle0_W_INY_CMOS(M6502 *s) {
2366+
s->abus.w = s->pc.w++;
2367+
s->read = M6502ReadType_Instruction;
2368+
s->tfn = &Cycle1_W_INY_CMOS;
2369+
}
2370+
2371+
static void Cycle1_W_INY_CMOS(M6502 *s) {
2372+
s->ia.b.l = s->dbus;
2373+
s->abus.w = s->ia.b.l;
2374+
s->read = M6502ReadType_Address;
2375+
s->tfn = &Cycle2_W_INY_CMOS;
2376+
}
2377+
2378+
static void Cycle2_W_INY_CMOS(M6502 *s) {
2379+
s->ad.b.l = s->dbus;
2380+
s->abus.w = (uint8_t)(s->ia.b.l + 1);
2381+
s->read = M6502ReadType_Address;
2382+
s->tfn = &Cycle3_W_INY_CMOS;
2383+
}
2384+
2385+
static void Cycle3_W_INY_CMOS(M6502 *s) {
2386+
s->ad.b.h = s->dbus;
2387+
s->read = M6502ReadType_Uninteresting;
2388+
s->tfn = &Cycle4_W_INY_CMOS;
2389+
}
2390+
2391+
static void Cycle4_W_INY_CMOS(M6502 *s) {
2392+
s->abus.w = s->ad.w + s->y;
2393+
(*s->ifn)(s);
2394+
s->read = 0;
2395+
s->dbus = s->data;
2396+
s->tfn = &Cycle5_W_INY_CMOS;
2397+
CheckForInterrupts(s);
2398+
}
2399+
2400+
static void Cycle5_W_INY_CMOS(M6502 *s) {
2401+
M6502_NextInstruction(s);
2402+
}
2403+
2404+
//////////////////////////////////////////////////////////////////////////
2405+
//////////////////////////////////////////////////////////////////////////
2406+
22902407
// Broken variant that doesn't handle page crossing properly. Make the dp111
22912408
// timing test pass.
22922409

src/beeb/tests/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,7 @@ foreach(ACCCON RANGE 3)
142142
endforeach()
143143

144144
add_test_beeb_wildcard_category(disk.hard.master)
145+
146+
foreach(CPUTYPE nmos cmos)
147+
add_test_beeb_literal_category(beeb_6502_timings.timings.${CPUTYPE})
148+
endforeach()

src/beeb/tests/test_beeb.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,7 @@ class TestBBCMicro : public BBCMicro {
537537
std::string oswrch_output;
538538
std::string spool_output;
539539
std::string spool_output_name;
540+
bool ever_hit_brk = false;
540541

541542
#if BBCMICRO_DEBUGGER
542543
class Writer {
@@ -1023,6 +1024,10 @@ uint32_t TestBBCMicro::Update1() {
10231024
const M6502 *cpu = this->GetM6502();
10241025

10251026
if (M6502_IsAboutToExecute(cpu)) {
1027+
if (cpu->dbus == 0x00) {
1028+
this->ever_hit_brk = true;
1029+
}
1030+
10261031
const uint8_t *ram = this->GetRAM();
10271032

10281033
if (cpu->abus.b.l == ram[WRCHV + 0] && cpu->abus.b.h == ram[WRCHV + 1]) {
@@ -1561,6 +1566,52 @@ class StandardTest : public Test {
15611566
//////////////////////////////////////////////////////////////////////////
15621567
//////////////////////////////////////////////////////////////////////////
15631568

1569+
// run BASIC program that issues an error via BRK on failure.
1570+
//
1571+
// The test is run until control returns to the BASIC prompt (one way or another). The test succeeds if no BRK was ever executed.
1572+
class BasicTest : public Test {
1573+
public:
1574+
BasicTest(std::string name, const TestBBCType type, std::string file_name)
1575+
: m_name(std::move(name))
1576+
, m_type(std::move(type))
1577+
, m_file_name(std::move(file_name)) {
1578+
}
1579+
1580+
std::string GetFullName() const override {
1581+
return m_name;
1582+
}
1583+
1584+
void Run() override {
1585+
TestBBCMicro bbc(m_type);
1586+
1587+
bbc.StartCaptureOSWRCH();
1588+
bbc.RunUntilOSWORD0(10.0);
1589+
1590+
// TODO: seems I can't make up my mind how I want to specify the file names
1591+
bbc.LoadFile(m_file_name, 0x1900);
1592+
bbc.Paste("PAGE=&1900\rOLD\rRUN\r");
1593+
bbc.RunUntilOSWORD0(30.0);
1594+
1595+
{
1596+
LOGF(BBC_OUTPUT, "All Output: ");
1597+
LOGI(BBC_OUTPUT);
1598+
LOG_STR(BBC_OUTPUT, GetPrintable(bbc.oswrch_output).c_str());
1599+
LOG(BBC_OUTPUT).EnsureBOL();
1600+
}
1601+
1602+
TEST_FALSE(bbc.ever_hit_brk);
1603+
}
1604+
1605+
protected:
1606+
private:
1607+
std::string m_name;
1608+
TestBBCType m_type;
1609+
std::string m_file_name;
1610+
};
1611+
1612+
//////////////////////////////////////////////////////////////////////////
1613+
//////////////////////////////////////////////////////////////////////////
1614+
15641615
class KevinEdwardsTest : public Test {
15651616
public:
15661617
KevinEdwardsTest(const std::string &name, std::string pre_paste_text)
@@ -2925,6 +2976,17 @@ int main(int argc, char *argv[]) {
29252976
all_tests.push_back(std::make_unique<MMFSDiskAccessTest>(strprintf("disk.mmfs.compact.%d.mos511i", io_flags), GetMasterCompactMOS511iType(), io_flags));
29262977
}
29272978

2979+
{
2980+
std::string path = PathJoined(b2_SOURCE_DIR, "submodules/beeb_6502_timing_tests/beeb/beeb_6502_timing_tests/0/$.TIMINGS");
2981+
2982+
all_tests.push_back(std::make_unique<BasicTest>("beeb_6502_timings.timings.nmos", GetBTapeType(), path));
2983+
all_tests.push_back(std::make_unique<BasicTest>("beeb_6502_timings.timings.cmos", GetMasterMOS320Type(), path));
2984+
}
2985+
2986+
//
2987+
// all tests must be added by this point!
2988+
//
2989+
29282990
std::set<std::string> names;
29292991
for (const std::unique_ptr<Test> &test : all_tests) {
29302992
names.insert(test->GetFullName());

submodules/beeb_6502_timing_tests

Submodule beeb_6502_timing_tests added at f3dec4f

0 commit comments

Comments
 (0)