Skip to content

Commit

Permalink
feat: implement ZX Spectrum +2/3 floating bus (#752)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dotneteer committed Apr 13, 2024
1 parent fe5e3d3 commit 926547c
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 24 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -29,7 +29,7 @@ Klive IDE intends to support retro computers with the Z80 family of CPUs. Klive

- **ZX Spectrum 48K**
- **ZX Spectrum 128K**
- **ZX Spectrum +2E/+3E** (*in progress*)
- **ZX Spectrum +2E/+3E**
- **Cambridge Z88** (*in progress*)
- **ZX Spectrum Next** (*in progress*)
- ZX 80/81 (*in the future*)
Expand Down
Binary file added _input/A Yankee in Iraq v1.3.3.tap
Binary file not shown.
25 changes: 16 additions & 9 deletions src/emu/machines/zxSpectrumP3e/ZxSpectrumP3eFloatingBusDevice.ts
@@ -1,6 +1,7 @@
import { IFloatingBusDevice } from "@emu/abstractions/IFloatingBusDevice";
import { IZxSpectrumMachine } from "@renderer/abstractions/IZxSpectrumMachine";
import { RenderingPhase } from "@renderer/abstractions/RenderingPhase";
import { ZxSpectrumP3EMachine } from "./ZxSpectrumP3eMachine";

/**
* This class implements the ZX Spectrum 128 floating bus device.
Expand Down Expand Up @@ -31,22 +32,28 @@ export class ZxSpectrumP3eFloatingBusDevice implements IFloatingBusDevice {
* @returns The value read from the floating bus
*/
public readFloatingBus (): number {
const machine = (this.machine as ZxSpectrumP3EMachine);
const screen = this.machine.screenDevice;
const currentTactIndex =
(this.machine.currentFrameTact - 3 + this.machine.tactsInFrame) %
this.machine.tactsInFrame;
const renderingTact = screen.renderingTactTable[currentTactIndex];
switch (renderingTact?.phase) {
case RenderingPhase.BorderFetchPixel:
case RenderingPhase.DisplayB1FetchB2:
case RenderingPhase.DisplayB2FetchB1:
return this.machine.readScreenMemory(renderingTact.pixelAddress);
case RenderingPhase.BorderFetchAttr:
case RenderingPhase.DisplayB1FetchA2:
case RenderingPhase.DisplayB2FetchA1:
return this.machine.readScreenMemory(renderingTact.attributeAddress);
case RenderingPhase.Border:
case RenderingPhase.None:
case RenderingPhase.DisplayB1:
case RenderingPhase.DisplayB2:
return machine.lastContendedValue | 0x01;
default:
return 0xff;
return machine.lastUlaReadValue;
}
}
}

/**
* Floating bus ports for the ZX Spectrum +2/+3 machine
*/
export const zxSpectrumP32FloatingBusPorts: Record<string, boolean> = {};
for (let n = 0; n < 0x1000; n++) {
zxSpectrumP32FloatingBusPorts[4 * n + 1] = true;
}
59 changes: 45 additions & 14 deletions src/emu/machines/zxSpectrumP3e/ZxSpectrumP3eMachine.ts
Expand Up @@ -22,7 +22,10 @@ import { TapeDevice, TapeSaver } from "../tape/TapeDevice";
import { zxSpectrum128SysVars } from "../zxSpectrum128/ZxSpectrum128Machine";
import { ZxSpectrum128PsgDevice } from "../zxSpectrum128/ZxSpectrum128PsgDevice";
import { zxSpectrum48SysVars } from "../zxSpectrum48/ZxSpectrum48Machine";
import { ZxSpectrumP3eFloatingBusDevice } from "./ZxSpectrumP3eFloatingBusDevice";
import {
ZxSpectrumP3eFloatingBusDevice,
zxSpectrumP32FloatingBusPorts
} from "./ZxSpectrumP3eFloatingBusDevice";
import { Store } from "@common/state/redux-light";
import { AppState } from "@common/state/AppState";
import { PagedMemory } from "../memory/PagedMemory";
Expand All @@ -32,7 +35,6 @@ import { SpectrumKeyCode } from "@emu/machines/zxSpectrum/SpectrumKeyCode";
import { MachineModel } from "@common/machines/info-types";
import { MC_DISK_SUPPORT } from "@common/machines/constants";
import { IFloppyControllerDevice } from "@emu/abstractions/IFloppyControllerDevice";
import { setMediaAction } from "@common/state/actions";
import { MEDIA_DISK_A, MEDIA_DISK_B } from "@common/structs/project-const";

/**
Expand All @@ -54,12 +56,19 @@ export class ZxSpectrumP3EMachine extends ZxSpectrumBase {
private screenStartOffset = 0;
selectedRom = 0;
selectedBank = 0;

// --- We need this value for the floating bus device
lastContendedValue = 0xff;
lastUlaReadValue = 0xff;

// --- Paging-related fields
pagingEnabled = true;
useShadowScreen = false;
inSpecialPagingMode = false;
specialConfigMode = 0;
diskMotorOn = false;


/**
* Represents the PSG device of ZX Spectrum +3E
*/
Expand Down Expand Up @@ -228,7 +237,7 @@ export class ZxSpectrumP3EMachine extends ZxSpectrumBase {
// --- Reset media
this.setMachineProperty(MEDIA_DISK_A);
this.setMachineProperty(MEDIA_DISK_B);
}
}

/**
* Indicates if the currently selected ROM is the ZX Spectrum 48 ROM
Expand All @@ -243,7 +252,9 @@ export class ZxSpectrumP3EMachine extends ZxSpectrumBase {
* @returns The byte at the specified screen memory location
*/
readScreenMemory (offset: number): number {
return this.memory.memory[this.screenStartOffset + (offset & 0x3fff)];
const value = this.memory.memory[this.screenStartOffset + (offset & 0x3fff)];
this.lastUlaReadValue = value;
return value;
}

/**
Expand Down Expand Up @@ -312,7 +323,11 @@ export class ZxSpectrumP3EMachine extends ZxSpectrumBase {
* @returns The byte read from the memory
*/
doReadMemory (address: number): number {
return this.memory.readMemory(address);
const valueRead = this.memory.readMemory(address);
if (this.isContendedMemoryAddress(address)) {
this.lastContendedValue = valueRead;
}
return valueRead;
}

/**
Expand All @@ -322,6 +337,9 @@ export class ZxSpectrumP3EMachine extends ZxSpectrumBase {
*/
doWriteMemory (address: number, value: number): void {
this.memory.writeMemory(address, value);
if (this.isContendedMemoryAddress(address)) {
this.lastContendedValue = value;
}
}

/**
Expand All @@ -333,14 +351,8 @@ export class ZxSpectrumP3EMachine extends ZxSpectrumBase {
* the Z80 CPU takes 3 T-states to read or write the memory contents.
*/
delayAddressBusAccess (address: number): void {
const page = address & 0xc000;

if (this.inSpecialPagingMode) {
if (page === 0xc000) {
if (this.specialConfigMode !== 1) return;
} else if (!this.specialConfigMode) return;
} else {
if (page !== 0x4000 && (page !== 0xc000 || this.selectedBank < 4)) return;
if (!this.isContendedMemoryAddress(address)) {
return;
}

// --- We read from contended memory
Expand All @@ -350,6 +362,23 @@ export class ZxSpectrumP3EMachine extends ZxSpectrumBase {
this.contentionDelaySincePause += delay;
}

/**
* Test if the memory address is in contended memory
* @param address Memory address to test
* @returns True, if the memory address is in contended memory
*/
isContendedMemoryAddress (address: number): boolean {
const page = address & 0xc000;
if (this.inSpecialPagingMode) {
if (page === 0xc000) {
if (this.specialConfigMode !== 1) return false;
} else if (!this.specialConfigMode) return false;
} else {
if (page !== 0x4000 && (page !== 0xc000 || this.selectedBank < 4)) return false;
}
return true;
}

/**
* This function reads a byte (8-bit) from an I/O port using the provided 16-bit address.
* @param address
Expand Down Expand Up @@ -385,7 +414,9 @@ export class ZxSpectrumP3EMachine extends ZxSpectrumBase {
return this.hasFloppy ? this.floppyDevice.readDataRegister() : 0xff;
}

return this.floatingBusDevice.readFloatingBus();
return (address in zxSpectrumP32FloatingBusPorts && this.pagingEnabled)
? this.floatingBusDevice.readFloatingBus()
: 0xff;
}

/**
Expand Down

0 comments on commit 926547c

Please sign in to comment.