Skip to content

Commit

Permalink
z80ctc counter/timer tests
Browse files Browse the repository at this point in the history
  • Loading branch information
floooh committed Jul 15, 2024
1 parent 1a056c5 commit 2c77ceb
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 17 deletions.
46 changes: 33 additions & 13 deletions src/chips/z80ctc.zig
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,12 @@ pub fn Z80CTC(comptime P: Pins, comptime Bus: anytype) type {

pub fn tick(self: *Self, in_bus: Bus) Bus {
var bus = in_bus;
// catch io requests
bus = self._tick(bus);
switch (bus & (CE | IORQ | RD | M1)) {
CE | IORQ | RD => bus = self.ioRead(bus),
CE | IORQ => bus = self.ioWrite(bus),
else => {},
}
bus = self._tick(bus);
bus = self.irq(bus);
return bus;
}
Expand All @@ -175,25 +174,25 @@ pub fn Z80CTC(comptime P: Pins, comptime Bus: anytype) type {
const data = getData(bus);
const chn_id: usize = (bus >> P.CS[0]) & 3;
var chn = &self.chn[chn_id];
if (chn.control & CTRL.CONST_FOLLOWS != 0) {
if ((chn.control & CTRL.CONST_FOLLOWS) != 0) {
// timer constant following control word
chn.control &= ~(CTRL.CONST_FOLLOWS | CTRL.RESET);
chn.constant = data;
if (chn.control & CTRL.MODE == CTRL.MODE_TIMER) {
if (chn.control & CTRL.TRIGGER == CTRL.TRIGGER_WAIT) {
if ((chn.control & CTRL.MODE) == CTRL.MODE_TIMER) {
if ((chn.control & CTRL.TRIGGER) == CTRL.TRIGGER_WAIT) {
chn.waiting_for_trigger = true;
} else {
chn.down_counter = chn.constant;
}
} else {
chn.down_counter = chn.constant;
}
} else if (data & CTRL.CONTROL != 0) {
} else if ((data & CTRL.CONTROL) != 0) {
// a control word
const old_control = chn.control;
chn.control = data;
chn.trigger_edge = data & CTRL.EDGE == CTRL.EDGE_RISING;
if (chn.control & CTRL.PRESCALER == CTRL.PRESCALER_16) {
if ((chn.control & CTRL.PRESCALER) == CTRL.PRESCALER_16) {
chn.prescaler_mask = 0x0F;
} else {
chn.prescaler_mask = 0xFF;
Expand All @@ -215,15 +214,36 @@ pub fn Z80CTC(comptime P: Pins, comptime Bus: anytype) type {
return bus;
}

fn _tick(self: *Self, bus: Bus) Bus {
_ = self; // autofix
// FIXME
fn _tick(self: *Self, in_bus: Bus) Bus {
var bus = in_bus & ~(ZCTO0 | ZCTO1 | ZCTO2);
for (&self.chn, 0..) |*chn, chn_id| {
// check if externally triggered
if (chn.waiting_for_trigger or (chn.control & CTRL.MODE) == CTRL.MODE_COUNTER) {
const trg = 0 != (bus & (CLKTRG0 << @truncate(chn_id)));
if (trg != chn.ext_trigger) {
chn.ext_trigger = trg;
// rising/falling edge trigger
if (chn.trigger_edge == trg) {
bus = activeEdge(chn, bus, chn_id);
}
}
} else if ((chn.control & (CTRL.MODE | CTRL.RESET | CTRL.CONST_FOLLOWS)) == CTRL.MODE_TIMER) {
// handle timer mode down-counting
chn.prescaler -%= 1;
if (0 == (chn.prescaler & chn.prescaler_mask)) {
// prescaler has reached zero, tick the down-counter
chn.down_counter -%= 1;
if (0 == chn.down_counter) {
bus = counterZero(chn, bus, chn_id);
}
}
}
}
return bus;
}

fn irq(self: *Self, bus: Bus) Bus {
_ = self; // autofix
// FIXME:
return bus;
}

Expand All @@ -237,7 +257,7 @@ pub fn Z80CTC(comptime P: Pins, comptime Bus: anytype) type {
//
fn activeEdge(chn: *Channel, in_bus: Bus, chn_id: usize) Bus {
var bus = in_bus;
if (chn.control & CTRL.MODE == CTRL.MODE_COUNTER) {
if ((chn.control & CTRL.MODE) == CTRL.MODE_COUNTER) {
// counter mode
chn.down_counter -%= 1;
if (0 == chn.down_counter) {
Expand All @@ -257,7 +277,7 @@ pub fn Z80CTC(comptime P: Pins, comptime Bus: anytype) type {
fn counterZero(chn: *Channel, in_bus: Bus, chn_id: usize) Bus {
var bus = in_bus;
// down counter has reached zero, trigger interrupt and ZCTO pin
if (chn.control & CTRL.EI != 0) {
if ((chn.control & CTRL.EI) != 0) {
chn.irq_state |= IRQ.INT_NEEDED;
}
// last channel doesn't have a ZCTO pin
Expand Down
71 changes: 67 additions & 4 deletions tests/z80ctc.test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const IORQ = Z80CTC.IORQ;
const CS0 = Z80CTC.CS0;
const CS1 = Z80CTC.CS1;
const ZCTO1 = Z80CTC.ZCTO1;
const CLKTRG1 = Z80CTC.CLKTRG1;

const CTRL = Z80CTC.CTRL;

Expand All @@ -36,11 +37,11 @@ test "write int vector" {
test "timer" {
var ctc = Z80CTC.init();
const ctrl = CTRL.EI | CTRL.MODE_TIMER | CTRL.PRESCALER_16 | CTRL.TRIGGER_AUTO | CTRL.CONST_FOLLOWS | CTRL.CONTROL;
// write control word
// write control word for channel 1
var bus = ctc.tick(setData(CE | IORQ | CS0, ctrl));
try expect(ctc.chn[1].control == ctrl);
try expect(ctc.chn[1].prescaler_mask == 0x0F);
// write timer constant
// write timer constant for channel 1
bus = ctc.tick(setData(CE | IORQ | CS0, 10));
try expect(0 == ctc.chn[1].control & CTRL.CONST_FOLLOWS);
try expect(10 == ctc.chn[1].constant);
Expand All @@ -51,9 +52,71 @@ test "timer" {
bus = ctc.tick(bus);
if (tick != 159) {
try expect(0 == bus & ZCTO1);
} else {
try expect(0 != bus & ZCTO1);
try expect(10 == ctc.chn[1].down_counter);
}
}
}
}

test "timer wait trigger" {
var ctc = Z80CTC.init();
const ctrl = CTRL.EI | CTRL.MODE_TIMER | CTRL.PRESCALER_16 | CTRL.TRIGGER_WAIT | CTRL.EDGE_RISING | CTRL.CONST_FOLLOWS | CTRL.CONTROL;
// write control word for channel 1
var bus = ctc.tick(setData(CE | IORQ | CS0, ctrl));
try expect(ctc.chn[1].control == ctrl);
try expect(ctc.chn[1].prescaler_mask == 0x0F);
// write timer constant
bus = ctc.tick(setData(CE | IORQ | CS0, 10));
try expect(0 == ctc.chn[1].control & CTRL.CONST_FOLLOWS);
try expect(10 == ctc.chn[1].constant);
bus = 0;
// tick the CTC without starting the timer
for (0..300) |_| {
bus = ctc.tick(bus);
try expect(0 == bus & ZCTO1);
}
// now start the timer on next tick
bus = ctc.tick(bus | CLKTRG1);
try expect(10 == ctc.chn[1].down_counter);
for (0..3) |_| {
for (0..160) |tick| {
bus = ctc.tick(bus);
if (tick != 159) {
try expect(0 == bus & ZCTO1);
} else {
try expect(0 != bus & ZCTO1);
try expect(10 == ctc.chn[1].down_counter);
}
}
try expect(0 != bus & ZCTO1);
try expect(10 == ctc.chn[1].down_counter);
}
}

test "counter" {
var ctc = Z80CTC.init();
const ctrl = CTRL.EI | CTRL.MODE_COUNTER | CTRL.EDGE_RISING | CTRL.CONST_FOLLOWS | CTRL.CONTROL;
// write control word for channel 1
var bus = ctc.tick(setData(CE | IORQ | CS0, ctrl));
try expect(ctc.chn[1].control == ctrl);
// write counter constant
bus = ctc.tick(setData(CE | IORQ | CS0, 10));
try expect(0 == ctc.chn[1].control & CTRL.CONST_FOLLOWS);
try expect(10 == ctc.chn[1].constant);

for (0..3) |_| {
for (0..10) |tick| {
// switch CLKTRG1 on/off
bus |= CLKTRG1;
bus = ctc.tick(bus);
if (tick != 9) {
try expect(0 == bus & ZCTO1);
} else {
try expect(0 != bus & ZCTO1);
try expect(10 == ctc.chn[1].down_counter);
}
bus &= ~CLKTRG1;
bus = ctc.tick(bus);
}
}
}

0 comments on commit 2c77ceb

Please sign in to comment.