Merge pull request #1749 from TomHarte/6809Timing

Fix 6809 LEAx timing.
This commit is contained in:
Thomas Harte authored and GitHub committed 2026-04-06 15:05:33 -04:00
commit 001e1ffab0
5 files changed
+31 -120

No files matched your search

+1 -91
View File
@@ -11,97 +11,7 @@
using namespace Thomson;
CD90_640::CD90_640() : WD::WD1770(P1770) {
// 325 is a peculiar RPM, but seems to match a spin-up test in the disk ROM that polls the WD1770's status register
// index hole bit and counts time. Furthermore there are other machines with unusual RPMs. Could definitely
// still just imply an issue elsewhere in the emulator though.
emplace_drives(2, 8'000'000, 325, 2);
//
// On "325 RPM":
//
// Faulty reasoning was:
// ---------------------
//
// The DOS ROM verifies that a disk is present and spinning before proceeding.
// Part of that test is of spin speed.
//
// Specifically, with X = 0xa7d0 it performs:
//
// ;
// ; Wait for index hole set.
// ;
// a29b LDA ,X ; 4 µs
// a29d ANDA #$02 ; 2 µs
// a29f BEQ ZA29B ; 3 µs
//
// ;
// ; i.e. start of track falls about here
// ;
//
// ;
// ; Fixed delay...
// ;
// a2a1 LDY #M09C4 ; 4 µs
// a2a5 DEY ; 5 µs
// a2a7 BNE ZA2A5 ; 3 µs = net delay of 4 + 9C4*7 = 17,504 µs
//
// a2a9 PSHS CC ; 6 µs
// a2ab SEIF ; 3 µs
//
// ;
// ; Count time until index hole is set again
// ;
// a2ad LDA ,X ; 4 µs, read WD status
// a2af INY ; 5 µs
// a2b1 ANDA #$02 ; 2 µs
// a2b3 BEQ ZA2AD ; 3 µs
//
// a2b5 PULS CC
//
// ;
// ; Check whether rotation speed was within bounds
// ;
// a2b7 CMPY #M311B
// a2bb BMI ZA2C7
// a2bd CMPY #M3357
// a2c1 BPL ZA2C7
//
// [test passed code here, test failed code from a2c7]
//
// If I've disassembled that correctly, then the process is:
//
// (1) wait for index hole, with up to 9 µs of latency;
// (2) spin in a loop for 17,504 µs;
// (3) do minor stack/flag work for 9 µs;
// (4) count time from here until next index hole, ending with Y = time/13;
// (5) test that that is within the bounds 0x311b, 0x3357.
//
// Ignoring minor potential latencies in loop exits, that's:
//
// (1) wait for index hole;
// (2) spend 17,513 µs doing something else;
// (3) get Y = [µs from there to next index hole] / 13.
//
// i.e. it's a test that the rotation it samples takes n µs, where:
//
// 14 * 0x311b < n - 17,513 < 14 * 0x3357
// 14 * 12,571 + 20,009 < n < 14 * 13,143 + 20,009
// 196,003 < n < 204,011
//
// i.e.
//
// ~331 >= RPM >= ~319 RPM.
//
// So 325 RPM is a really weird number but I can't currently say why it should be
// wrong rather than merely unexpected. So here it is.
//
// Correct reasoning is:
// ---------------------
//
// The LEAYs that constitute INY and DEY above should be _five_ cycles long, not four.
// My 6809 timing is adrift.
// If loops were properly timed then 300 RPM looks appropriate.
// Cf. https://forum.system-cfg.com/viewtopic.php?f=1&t=17137
emplace_drives(2, 8'000'000, 300, 2);
}
uint8_t CD90_640::control() {
+21 -20
View File
@@ -824,7 +824,8 @@ struct M6809Traits {
sequence(0xbf); // STY
}
// Omitted entirely: LEAS, LEAU, LEAX, LEAY that come only in indexed mode. Maybe use those as the indexed test?
// Omitted entirely: LEAS, LEAU, LEAX, LEAY that come only in indexed mode. LEAS is the subject of the indexed
// timing tests, and I _assume_ the rest conform.
// MARK: - JMP
@@ -1087,35 +1088,35 @@ struct M6809Traits {
test(
{0x32, NoOffset | RegX}, // ,R
{
RW::Read, RW::Read, RW::NoData
RW::Read, RW::Read, RW::NoData, RW::NoData,
}
);
test(
{0x32, NoOffset | RegX | Indirect}, // [,R]
{
RW::Read, RW::Read, RW::NoData, RW::Read, RW::Read, RW::NoData
RW::Read, RW::Read, RW::NoData, RW::Read, RW::Read, RW::NoData, RW::NoData,
}
);
test(
{0x32, Offset5bit | RegX}, // [5n,R]
{
RW::Read, RW::Read, RW::NoData, RW::NoData
RW::Read, RW::Read, RW::NoData, RW::NoData, RW::NoData,
}
);
test(
{0x32, Offset8bit | RegX, 0x00}, // 8n,R
{
RW::Read, RW::Read, RW::Read, RW::NoData
RW::Read, RW::Read, RW::Read, RW::NoData, RW::NoData,
}
);
test(
{0x32, Offset8bit | RegX | Indirect, 0x00}, // [8n,R]
{
RW::Read, RW::Read, RW::Read, RW::NoData, RW::Read, RW::Read, RW::NoData,
RW::Read, RW::Read, RW::Read, RW::NoData, RW::Read, RW::Read, RW::NoData, RW::NoData,
}
);
@@ -1124,7 +1125,7 @@ struct M6809Traits {
{
RW::Read, RW::Read,
RW::Read, RW::Read,
RW::NoData, RW::NoData, RW::NoData,
RW::NoData, RW::NoData, RW::NoData, RW::NoData,
}
);
@@ -1132,7 +1133,7 @@ struct M6809Traits {
{0x32, ARegisterOffset | RegX}, // A,R
{
RW::Read, RW::Read,
RW::NoData, RW::NoData,
RW::NoData, RW::NoData, RW::NoData,
}
);
@@ -1141,7 +1142,7 @@ struct M6809Traits {
{
RW::Read, RW::Read,
RW::NoData, RW::NoData,
RW::Read, RW::Read, RW::NoData,
RW::Read, RW::Read, RW::NoData, RW::NoData,
}
);
@@ -1152,7 +1153,7 @@ struct M6809Traits {
{0x32, DRegisterOffset | RegX}, // D,R
{
RW::Read, RW::Read,
RW::NoData, RW::NoData, RW::NoData, RW::NoData, RW::NoData,
RW::NoData, RW::NoData, RW::NoData, RW::NoData, RW::NoData, RW::NoData,
}
);
@@ -1161,7 +1162,7 @@ struct M6809Traits {
{
RW::Read, RW::Read,
RW::NoData, RW::NoData, RW::NoData, RW::NoData, RW::NoData,
RW::Read, RW::Read, RW::NoData,
RW::Read, RW::Read, RW::NoData, RW::NoData,
}
);
@@ -1169,7 +1170,7 @@ struct M6809Traits {
{0x32, PostInc1 | RegX}, // R+
{
RW::Read, RW::Read,
RW::NoData, RW::NoData, RW::NoData,
RW::NoData, RW::NoData, RW::NoData, RW::NoData,
}
);
@@ -1177,7 +1178,7 @@ struct M6809Traits {
{0x32, PreDec1 | RegX}, // -R
{
RW::Read, RW::Read,
RW::NoData, RW::NoData, RW::NoData,
RW::NoData, RW::NoData, RW::NoData, RW::NoData,
}
);
@@ -1185,7 +1186,7 @@ struct M6809Traits {
{0x32, PostInc2 | RegX}, // R++
{
RW::Read, RW::Read,
RW::NoData, RW::NoData, RW::NoData, RW::NoData,
RW::NoData, RW::NoData, RW::NoData, RW::NoData, RW::NoData,
}
);
@@ -1194,14 +1195,14 @@ struct M6809Traits {
{
RW::Read, RW::Read,
RW::NoData, RW::NoData, RW::NoData, RW::NoData,
RW::Read, RW::Read, RW::NoData,
RW::Read, RW::Read, RW::NoData, RW::NoData,
}
);
test(
{0x32, PCOffset8bit | RegX, 0x00}, // 8n, PC
{
RW::Read, RW::Read, RW::Read, RW::NoData,
RW::Read, RW::Read, RW::Read, RW::NoData, RW::NoData,
}
);
@@ -1209,7 +1210,7 @@ struct M6809Traits {
{0x32, PCOffset8bit | RegX | Indirect, 0x00, 0x00, 0x00}, // [8n, PC]
{
RW::Read, RW::Read, RW::Read, RW::NoData,
RW::Read, RW::Read, RW::NoData,
RW::Read, RW::Read, RW::NoData, RW::NoData,
}
);
@@ -1217,7 +1218,7 @@ struct M6809Traits {
{0x32, PCOffset16bit | RegX, 0x00, 0x00}, // 16n, PC
{
RW::Read, RW::Read, RW::Read, RW::Read,
RW::NoData, RW::NoData, RW::NoData, RW::NoData,
RW::NoData, RW::NoData, RW::NoData, RW::NoData, RW::NoData,
}
);
@@ -1226,7 +1227,7 @@ struct M6809Traits {
{
RW::Read, RW::Read, RW::Read, RW::Read,
RW::NoData, RW::NoData, RW::NoData, RW::NoData,
RW::Read, RW::Read, RW::NoData,
RW::Read, RW::Read, RW::NoData, RW::NoData,
}
);
@@ -1235,7 +1236,7 @@ struct M6809Traits {
{
RW::Read, RW::Read,
RW::Read, RW::Read, RW::NoData,
RW::Read, RW::Read, RW::NoData,
RW::Read, RW::Read, RW::NoData, RW::NoData,
}
);
}
+3 -3
View File
@@ -666,7 +666,7 @@ struct Processor {
);
address_.halves.high = registers_.reg<R8::DP>();
if(operation_.type == AccessType::LEA) {
if(operation_.operation == Operation::JMP) {
goto internal_lic_break;
}
internal_cycle(LIC::Inactive);
@@ -694,7 +694,7 @@ struct Processor {
++registers_.pc.full
);
if(operation_.type == AccessType::LEA) {
if(operation_.operation == Operation::JMP) {
goto internal_lic_break;
}
internal_cycle(LIC::Inactive);
@@ -766,7 +766,7 @@ struct Processor {
read(BusState::Normal, LIC::Inactive, Literal(address_.full), operand_.halves.low, ++address_.full);
address_ = operand_;
if(operation_.type == AccessType::LEA) {
if(operation_.operation == Operation::JMP) {
goto internal_lic_break;
}
internal_cycle(LIC::Inactive);
+5 -6
View File
@@ -545,10 +545,10 @@ Cycles perform(
case STY: st<R16::Y>(registers, word); return 0;
case STS: st<R16::S>(registers, word); return 0;
case LEAU: lea<R16::U>(registers, word); return 0;
case LEAX: lea<R16::X>(registers, word); return 0;
case LEAY: lea<R16::Y>(registers, word); return 0;
case LEAS: lea<R16::S>(registers, word); return 0;
case LEAU: lea<R16::U>(registers, word); return 1;
case LEAX: lea<R16::X>(registers, word); return 1;
case LEAY: lea<R16::Y>(registers, word); return 1;
case LEAS: lea<R16::S>(registers, word); return 1;
case MUL: mul(registers); return 9; // Per 6809cyc.txt; might need more research.
@@ -606,8 +606,7 @@ inline bool is_zero_costed(const InstructionSet::M6809::Operation operation) {
case CLR: case LDA: case LDB: case STA: case STB:
case LDD: case LDU: case LDX: case LDY: case LDS:
case STD: case STU: case STX: case STY: case STS:
case LEAU: case LEAX: case LEAY: case LEAS: case SEX:
case TSTA: case TSTB: case JMP:
case SEX: case TSTA: case TSTB: case JMP:
return true;
}
}
+1
View File
@@ -9,6 +9,7 @@
#pragma once
#include <cassert>
#include <concepts>
#include <cstdint>
#include "Numeric/RegisterSizes.hpp"