Skip to content

Commit

Permalink
[#314] first draft of half-working zx spectrum 48K
Browse files Browse the repository at this point in the history
  • Loading branch information
vbmacher committed Jan 27, 2023
1 parent d80c417 commit 98882cf
Show file tree
Hide file tree
Showing 36 changed files with 879 additions and 1,107 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
*/
package net.emustudio.plugins.cpu.intel8080;

import net.emustudio.emulib.plugins.device.DeviceContext;
import net.emustudio.plugins.cpu.intel8080.api.Context8080;
import net.jcip.annotations.ThreadSafe;
import org.slf4j.Logger;
Expand All @@ -30,8 +29,9 @@
@ThreadSafe
public class Context8080Impl implements Context8080 {
private final static Logger LOGGER = LoggerFactory.getLogger(Context8080Impl.class);
private final static byte NO_DATA = (byte) 0xFF; // ha! from survey.mac in cpm2.dsk: "inactive port could return 0xFF or echo port#"

private final ConcurrentMap<Integer, DeviceContext<Byte>> devices = new ConcurrentHashMap<>();
private final ConcurrentMap<Integer, CpuPortDevice> devices = new ConcurrentHashMap<>();

private volatile EmulatorEngine cpu;
private volatile int clockFrequency = 2000; // kHz
Expand All @@ -42,14 +42,17 @@ public void setCpu(EmulatorEngine cpu) {

// device mapping = only one device can be attached to one port
@Override
public boolean attachDevice(DeviceContext<Byte> device, int port) {
if (devices.containsKey(port)) {
LOGGER.debug("[port={}, device={}] Could not attach device to given port. The port is already taken by: {}", port, device, devices.get(port));
public boolean attachDevice(int port, CpuPortDevice device) {
CpuPortDevice oldDevice = devices.get(port);
if (oldDevice != null) {
LOGGER.debug("[port={}, device={}] Could not attach device to given port. The port is already taken by: {}", port, device.getName(), oldDevice.getName());
return false;
}
if (devices.putIfAbsent(port, device) == null) {
LOGGER.debug("[port={},device={}] Device was attached to CPU", port, device);
if (devices.putIfAbsent(port, device) != null) {
LOGGER.debug("[port={}, device={}] Could not attach device to given port. The port is already taken.", port, device.getName());
return false;
}
LOGGER.debug("[port={},device={}] Device was attached to CPU", port, device.getName());
return true;
}

Expand All @@ -65,24 +68,19 @@ public void clearDevices() {
devices.clear();
}

/**
* Performs I/O operation.
*
* @param port I/O port
* @param read whether method should read or write to the port
* @param data data to be written to the port. if parameter read is set to true, then data are ignored.
* @return value from the port if read is true, otherwise 0
*/
public byte fireIO(int port, boolean read, byte data) {
DeviceContext<Byte> device = devices.get(port);
void writeIO(int portAddress, byte data) {
CpuPortDevice device = devices.get(portAddress & 0xFF);
if (device != null) {
device.write(portAddress, data);
}
}

byte readIO(int portAddress) {
CpuPortDevice device = devices.get(portAddress & 0xFF);
if (device != null) {
if (read) {
return device.readData();
} else {
device.writeData(data);
}
return device.read(portAddress);
}
return read ? (byte) 0xFF : 0; // ha! from survey.mac in cpm2.dsk: "inactive port could return 0xFF or echo port#"
return NO_DATA;
}

@Override
Expand All @@ -94,7 +92,7 @@ public boolean isInterruptSupported() {
* Signals raw interrupt to the CPU.
* <p>
* The interrupting device can insert any instruction on the data bus for
* execution by the CPU. The first byte of a multi-byte instruction is read
* execution by the CPU. The first byte of a multibyte instruction is read
* during the interrupt acknowledge cycle. Subsequent bytes are read in by a
* normal memory read sequence.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -509,10 +509,10 @@ public int I_ACI() {
return 7;
}

public int I_OUT() throws IOException {
public int I_OUT() {
int DAR = readByte(PC);
PC = (PC + 1) & 0xFFFF;
context.fireIO(DAR, false, (byte) regs[REG_A]);
context.writeIO(DAR, (byte) regs[REG_A]);
return 10;
}

Expand All @@ -529,10 +529,10 @@ public int I_SUI() {
return 7;
}

public int I_IN() throws IOException {
public int I_IN() {
int DAR = readByte(PC);
PC = (PC + 1) & 0xFFFF;
regs[REG_A] = context.fireIO(DAR, true, (byte) 0) & 0xFF;
regs[REG_A] = context.readIO(DAR) & 0xFF;
return 10;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

import net.emustudio.emulib.plugins.annotations.PluginContext;
import net.emustudio.emulib.plugins.cpu.CPUContext;
import net.emustudio.emulib.plugins.device.DeviceContext;

/**
* Extended CPU context for 8080 processor.
Expand All @@ -31,11 +30,11 @@ public interface Context8080 extends CPUContext {
/**
* Attach a device into the CPU.
*
* @param device the device
* @param port CPU port where the device should be attached
* @param device the device
* @return true on success, false otherwise
*/
boolean attachDevice(DeviceContext<Byte> device, int port);
boolean attachDevice(int port, CpuPortDevice device);

/**
* Detach a device from the CPU.
Expand All @@ -50,4 +49,34 @@ public interface Context8080 extends CPUContext {
* @param freq new frequency in kHZ
*/
void setCPUFrequency(int freq);

/**
* Device attachable to CPU port. It's not a DeviceContext because some machines need port address (low + high byte)
* for being able to respond (e.g. ZX-spectrum port 0xFE).
*/
interface CpuPortDevice {

/**
* Read a byte data from device
*
* @param portAddress port address. Low 8 bits is the port number.
* @return byte data from the port
*/
byte read(int portAddress);

/**
* Write data to the device
*
* @param portAddress port address. Low 8 bits is the port number.
* @param data byte data to be written
*/
void write(int portAddress, byte data);

/**
* Get device port name
*
* @return device port name
*/
String getName();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
package net.emustudio.plugins.cpu.zilogZ80;

import net.emustudio.emulib.plugins.cpu.TimedEventsProcessor;
import net.emustudio.emulib.plugins.device.DeviceContext;
import net.emustudio.plugins.cpu.zilogZ80.api.ContextZ80;
import net.jcip.annotations.ThreadSafe;
import org.slf4j.Logger;
Expand All @@ -34,8 +33,10 @@ public final class ContextZ80Impl implements ContextZ80 {
public final static int DEFAULT_FREQUENCY_KHZ = 20000;
private final static byte NO_DATA = (byte) 0xFF;
private final static Logger LOGGER = LoggerFactory.getLogger(ContextZ80Impl.class);
private final ConcurrentMap<Integer, DeviceContext<Byte>> devices = new ConcurrentHashMap<>();

private final ConcurrentMap<Integer, CpuPortDevice> devices = new ConcurrentHashMap<>();
private final TimedEventsProcessor tep = new TimedEventsProcessor();

private volatile EmulatorEngine engine;
private volatile int clockFrequency = DEFAULT_FREQUENCY_KHZ;

Expand All @@ -45,14 +46,17 @@ public void setEngine(EmulatorEngine engine) {

// device mapping = only one device can be attached to one port
@Override
public boolean attachDevice(DeviceContext<Byte> device, int port) {
if (devices.containsKey(port)) {
LOGGER.debug("[port={}, device={}] Could not attach device to given port. The port is already taken by: {}", port, device, devices.get(port));
public boolean attachDevice(int port, CpuPortDevice device) {
CpuPortDevice oldDevice = devices.get(port);
if (oldDevice != null) {
LOGGER.debug("[port={}, device={}] Could not attach device to given port. The port is already taken by: {}", port, device.getName(), oldDevice.getName());
return false;
}
if (devices.putIfAbsent(port, device) == null) {
LOGGER.debug("[port={}] Attached device: {}", port, device);
if (devices.putIfAbsent(port, device) != null) {
LOGGER.debug("[port={}, device={}] Could not attach device to given port. The port is already taken.", port, device.getName());
return false;
}
LOGGER.debug("[port={},device={}] Device was attached to CPU", port, device.getName());
return true;
}

Expand All @@ -67,17 +71,17 @@ void clearDevices() {
devices.clear();
}

void writeIO(int port, byte val) {
DeviceContext<Byte> device = devices.get(port);
void writeIO(int portAddress, byte data) {
CpuPortDevice device = devices.get(portAddress & 0xFF);
if (device != null) {
device.writeData(val);
device.write(portAddress, data);
}
}

byte readIO(int port) {
DeviceContext<Byte> device = devices.get(port);
byte readIO(int portAddress) {
CpuPortDevice device = devices.get(portAddress & 0xFF);
if (device != null) {
return device.readData();
return device.read(portAddress);
}
return NO_DATA;
}
Expand Down
Loading

0 comments on commit 98882cf

Please sign in to comment.