Skip to content

Commit ab41620

Browse files
tpaxiatpaxia
andauthored
z8000,z8010,zilog/s8000: correct CPU/MMU behavior and boot ZEUS (#15866)
cpu/z8000: Fixes and updates * fix addr_to_reg segment mask to replace full high byte * fix MULTW carry-flag boundary * preserve DIVW/DIVL quotient on representable overflow * toggle H flag in COMFLG * write POPL @rd,@rs result to memory * read indirect MULTL operand from memory * fix dynamic shift dispatch and count sign-extension [tpaxia] * fix RRDB temp register and RLDB/RRDB S flag * accumulate V flag across shift/rotate steps * fix compare-string repeat and block-move Z flag * fix SOTIRB/SOUTIB source not incrementing (bumped segment word instead of offset) * fix block-I/O instruction flags and address increment * fix COMB @rd destination register decode * trap the 4F extended EPU family at the correct instruction * preserve the indexed LDA operand across an overlapping destination * derive the DAB correction table from measured Z8001 behaviour * leave the tcc destination alone when the condition is false * fix the MULTL carry boundary * take the DIV overflow sign flag from the whole quotient * order the translate RH1 writeback against the pointer step * replace the whole high word when writing a segmented address back * do not mask PSAPSEG through LDCTL * read a byte for the non-indexed COMB * do not mask the FCW written through LDCTL * calculate DAB correction directly cpu/z8010: correct violation semantics per Olivetti UC3003 factory test [tpaxia] zilog/s8000: fix ZEUS clock, MMU and device handling, support multi-sector SMDC transfers, and fix Series Two interrupt daisy chain [tpaxia] --------- Co-authored-by: tpaxia <spaxia@notoslab.com>
1 parent bbe8f58 commit ab41620

15 files changed

Lines changed: 560 additions & 617 deletions

File tree

scripts/src/cpu.lua

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3250,9 +3250,7 @@ if CPUS["Z8000"] then
32503250
files {
32513251
MAME_DIR .. "src/devices/cpu/z8000/z8000.cpp",
32523252
MAME_DIR .. "src/devices/cpu/z8000/z8000.h",
3253-
--MAME_DIR .. "src/devices/cpu/z8000/makedab.cpp",
32543253
MAME_DIR .. "src/devices/cpu/z8000/z8000cpu.h",
3255-
MAME_DIR .. "src/devices/cpu/z8000/z8000dab.h",
32563254
MAME_DIR .. "src/devices/cpu/z8000/z8000ops.hxx",
32573255
MAME_DIR .. "src/devices/cpu/z8000/z8000tbl.hxx",
32583256
}

src/devices/bus/zbi/s8k_cpu.cpp

Lines changed: 73 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ E000 - FFFF Communication Devices
3434

3535
#include "bus/rs232/rs232.h"
3636
#include "bus/centronics/ctronics.h"
37+
#include "machine/clock.h"
3738

3839
//#define VERBOSE 1
3940
#include "logmacro.h"
@@ -84,6 +85,10 @@ void s8k_cpu_base::base_device_start()
8485
save_item(NAME(m_reg_snvr));
8586
save_item(NAME(m_reg_trpl));
8687
save_item(NAME(m_reg_if1l));
88+
save_item(NAME(m_lad_seg));
89+
save_item(NAME(m_lad_low));
90+
save_item(NAME(m_if1_low));
91+
save_item(NAME(m_segt_state));
8792
}
8893

8994
void s8k_cpu_base::base_device_reset()
@@ -93,6 +98,10 @@ void s8k_cpu_base::base_device_reset()
9398
m_reg_snvr = 0;
9499
m_reg_trpl = 0;
95100
m_reg_if1l = 0;
101+
m_lad_seg = 0;
102+
m_lad_low = 0;
103+
m_if1_low = 0;
104+
m_segt_state = false;
96105

97106
m_view_code.select(0);
98107
m_view_data.select(0);
@@ -115,9 +124,35 @@ void s8k_cpu_base::base_device_resolve_objects()
115124

116125
void s8k_cpu_base::segt_interrupt(int state)
117126
{
127+
if (state && !m_segt_state)
128+
{
129+
// U66 (sheet 2) and U48-U50 (sheet 3) capture the segment number
130+
// and the address low bytes -- which the Z8010s cannot record --
131+
// at the moment the segment trap is raised. A trap handler
132+
// combines them with the high bytes latched inside the MMUs.
133+
m_reg_snvr = m_lad_seg;
134+
m_reg_trpl = m_lad_low;
135+
m_reg_if1l = m_if1_low;
136+
}
137+
m_segt_state = (state != 0);
118138
m_maincpu->set_input_line(z8001_device::SEGT_LINE, state ? ASSERT_LINE : CLEAR_LINE);
119139
}
120140

141+
void s8k_cpu_base::observe_bus_cycle(offs_t offset, bool if1)
142+
{
143+
m_lad_seg = uint8_t(offset >> 16) & 0x7f;
144+
m_lad_low = uint8_t(offset);
145+
if (if1)
146+
{
147+
m_if1_low = uint8_t(offset);
148+
// Every MMU sits on the CPU's address/data bus and snoops each
149+
// IFETCH1 cycle, whether or not it translates it.
150+
m_mmu_code->ifetch1_observed(offset);
151+
m_mmu_data->ifetch1_observed(offset);
152+
m_mmu_stck->ifetch1_observed(offset);
153+
}
154+
}
155+
121156
uint16_t s8k_cpu_base::segtack_r()
122157
{
123158
uint16_t code = (m_mmu_code->segtack_r() |
@@ -188,9 +223,6 @@ z8010_device *s8k_cpu_base::select_code_mmu(offs_t offset)
188223
{
189224
if (m_normal_mode) // Trying to access in normal mode?
190225
{
191-
// SEGTRAP!
192-
m_reg_snvr = seg;
193-
m_reg_if1l = offset;
194226
mmu = nullptr;
195227
segt_interrupt(1);
196228
}
@@ -239,9 +271,8 @@ z8010_device *s8k_cpu_base::select_data_mmu(offs_t offset, uint8_t sbr, uint8_t
239271
{
240272
if (m_normal_mode) // Trying to access in normal mode?
241273
{
242-
// SEGTRAP!
243-
m_reg_snvr = seg;
244-
m_reg_trpl = offset;
274+
// SEGTRAP! (the address latches are captured by the
275+
// segment trap flip-flop in segt_interrupt)
245276
mmu = nullptr;
246277
segt_interrupt(1);
247278
}
@@ -529,19 +560,21 @@ bool zbi_s8k_cpu10_card_device::translate_addr(int spacenum, bool write, offs_t
529560
if (m_reg_scr & SCR_MMU_ONH)
530561
{
531562
bool code_access = (spacenum == AS_PROGRAM);
563+
int st = code_access ?
564+
(m_maincpu->is_ifetch1() ?
565+
z8002_device::ST_IFETCH_1 :
566+
z8002_device::ST_IFETCH_N) :
567+
(stack_access ?
568+
z8002_device::ST_REQ_STACK :
569+
z8002_device::ST_REQ_DATA);
570+
571+
observe_bus_cycle(offset, st == z8002_device::ST_IFETCH_1);
572+
532573
z8010_device *mmu = code_access ?
533574
select_code_mmu(offset) : select_data_mmu(offset, m_reg_sbr, m_reg_nbr);
534575

535576
if (mmu)
536577
{
537-
int st = code_access ?
538-
(m_maincpu->is_ifetch1() ?
539-
z8002_device::ST_IFETCH_1 :
540-
z8002_device::ST_IFETCH_N) :
541-
(stack_access ?
542-
z8002_device::ST_REQ_STACK :
543-
z8002_device::ST_REQ_DATA);
544-
545578
LOG("%s MMU MEM REQ (space %d): %06x\n", machine().describe_context(), spacenum, offset);
546579

547580
offset &= 0x3f'ffff; // Mask off seg bit 7 to disable URS checking in MMUs
@@ -648,6 +681,11 @@ void zbi_s8k_cpu10_card_device::device_add_mconfig(machine_config &config)
648681
m_sio[0]->out_txdb_callback().set("sio0:chb:console", FUNC(rs232_port_device::write_txd));
649682
m_sio[0]->out_rtsb_callback().set("sio0:chb:console", FUNC(rs232_port_device::write_rts));
650683
m_sio[0]->out_dtrb_callback().set("sio0:chb:console", FUNC(rs232_port_device::write_dtr));
684+
// Local console terminal: loop the driver's own DTR/RTS back as DCD/CTS so
685+
// carrier is present when sioopen() does its carrier-wait on a read-only
686+
// open of /dev/console (otherwise the console shell sleeps forever).
687+
m_sio[0]->out_dtrb_callback().append(m_sio[0], FUNC(z80sio_device::dcdb_w));
688+
m_sio[0]->out_rtsb_callback().append(m_sio[0], FUNC(z80sio_device::ctsb_w));
651689

652690
rs232_port_device &rs232_0(RS232_PORT(config, "sio0:cha:tty0", default_rs232_devices, nullptr));
653691
rs232_0.rxd_handler().set(m_sio[0], FUNC(z80sio_device::rxa_w));
@@ -735,13 +773,21 @@ void zbi_s8k_cpu10_card_device::device_add_mconfig(machine_config &config)
735773
Z80CTC(config, m_ctc[2], CLK_CPU);
736774
m_ctc[2]->set_clk<0>(CLK_CTC);
737775
m_ctc[2]->set_clk<1>(CLK_CTC);
738-
m_ctc[2]->set_clk<2>(CLK_CTC);
739776
m_ctc[2]->zc_callback<0>().set(m_sio[3], FUNC(z80sio_device::rxca_w));
740777
m_ctc[2]->zc_callback<0>().append(m_sio[3], FUNC(z80sio_device::txca_w));
741778
m_ctc[2]->zc_callback<1>().set(m_sio[3], FUNC(z80sio_device::rxcb_w));
742779
m_ctc[2]->zc_callback<1>().append(m_sio[3], FUNC(z80sio_device::txcb_w));
743780
m_ctc[2]->zc_callback<2>().set(m_ctc[2], FUNC(z80ctc_device::trg3));
744781

782+
// Real-time (line) clock: CTC 2 channels 2 & 3 are cascaded and run in
783+
// COUNTER mode, counting the independent 1.2288 MHz baud-rate oscillator fed
784+
// to the CTC trigger inputs (S8000 CPU Hardware Manual sec 4.2.2). ZEUS
785+
// programs ch2 /256 and ch3 /80 -> a 60 Hz jiffy interrupt. Feed ch2 only
786+
// through trg2: configuring both its internal clock and this external trigger
787+
// would count the oscillator twice and make the ZEUS clock run at 120 Hz.
788+
clock_device &rtc_clk(CLOCK(config, "rtc_clk", CLK_CTC));
789+
rtc_clk.signal_handler().set(m_ctc[2], FUNC(z80ctc_device::trg2));
790+
745791
Z80PIO(config, m_pio, CLK_CPU);
746792
m_pio->in_pa_callback().set(FUNC(zbi_s8k_cpu10_card_device::pa_data_r));
747793
m_pio->out_pb_callback().set("pio0:printer1:data_out", FUNC(output_latch_device::write));
@@ -976,19 +1022,23 @@ bool zbi_s8k_hpcpu_card_device::translate_addr(int spacenum, bool write, offs_t
9761022
if (m_reg_scr & SCR_MMU_ONH)
9771023
{
9781024
bool code_access = (spacenum == AS_PROGRAM);
1025+
int st = code_access ?
1026+
(m_maincpu->is_ifetch1() ?
1027+
z8002_device::ST_IFETCH_1 :
1028+
z8002_device::ST_IFETCH_N) :
1029+
(stack_access ?
1030+
z8002_device::ST_REQ_STACK :
1031+
z8002_device::ST_REQ_DATA);
1032+
1033+
// Board latches and MMU bus snoop see the cycle before any
1034+
// violation can be raised for it.
1035+
observe_bus_cycle(offset, st == z8002_device::ST_IFETCH_1);
1036+
9791037
z8010_device *mmu = code_access ?
9801038
select_code_mmu(offset) : select_data_mmu(offset, 0, m_reg_ubr);
9811039

9821040
if (mmu)
9831041
{
984-
int st = code_access ?
985-
(m_maincpu->is_ifetch1() ?
986-
z8002_device::ST_IFETCH_1 :
987-
z8002_device::ST_IFETCH_N) :
988-
(stack_access ?
989-
z8002_device::ST_REQ_STACK :
990-
z8002_device::ST_REQ_DATA);
991-
9921042
LOG("%s MMU MEM REQ (space %d): %06x\n", machine().describe_context(), spacenum, offset);
9931043

9941044
offset &= 0x3f'ffff; // Mask off seg bit 7 to disable URS checking in MMUs

src/devices/bus/zbi/s8k_cpu.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ class s8k_cpu_base : public device_t, public device_zbi_card_interface
8181
uint8_t reg_trpl_r();
8282
uint8_t reg_if1l_r();
8383

84+
void observe_bus_cycle(offs_t offset, bool if1);
85+
8486
//helpers
8587
void out_ns_cb(int state);
8688
void out_busack_cb(int state);
@@ -95,11 +97,18 @@ class s8k_cpu_base : public device_t, public device_zbi_card_interface
9597
devcb_write_line m_ns_cb;
9698
devcb_write_line m_busack_cb;
9799

98-
// Board registers
100+
// Board registers: snapshots of the running latches below, captured by
101+
// the segment trap flip-flop (U20 sheet 8) when SEGT- is raised.
99102
uint8_t m_reg_snvr = 0; // Segment Violation Register
100103
uint8_t m_reg_trpl = 0; // Segment trap memory address low-byte
101104
uint8_t m_reg_if1l = 0; // Segment trap instruction low-byte
102105

106+
// Running bus-side latches and the SEGT- line state
107+
uint8_t m_lad_seg = 0; // segment number of the current memory cycle
108+
uint8_t m_lad_low = 0; // low address byte of the current memory cycle
109+
uint8_t m_if1_low = 0; // low address byte of the last IFETCH1 cycle
110+
bool m_segt_state = false;
111+
103112
bool m_is_seg_os = false;
104113
bool m_is_seg_user = false;
105114

src/devices/bus/zbi/s8k_smdc.cpp

Lines changed: 54 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -556,12 +556,6 @@ int zbi_s8k_smdc_card_device::get_lbasector()
556556
LOGDATA("%s: Unexpected sector number %d for range 0 to %d\n", machine().describe_context(), pkt->SC, info.sectors - 1);
557557
}
558558

559-
if ((pkt->CT == 0) || (pkt->CT > info.sectorbytes))
560-
{
561-
LOGDATA("%s: Unexpected sector bytes %d for range 1 to %d\n", machine().describe_context(), pkt->CT, info.sectorbytes);
562-
pkt->CT = 512;
563-
}
564-
565559
lbasector = pkt->CY;
566560
lbasector *= info.heads;
567561
lbasector += pkt->HD;
@@ -635,7 +629,7 @@ void zbi_s8k_smdc_card_device::smd_do_drive(int drv)
635629
LOGWRITE("%s SMD write: unit=%d cyl=%d hd=%d sec=%d count=%d addr=%04x:%04x\n", machine().describe_context(),
636630
unit, pkt->CY, pkt->HD, pkt->SC, pkt->CT, pkt->AH, pkt->AL);
637631

638-
if (pkt->CT & 1)
632+
if ((pkt->CT == 0) || (pkt->CT & 1))
639633
{
640634
m_es = SMD_ES_BADCT;
641635
pkt->DS |= SMD_DS_FT;
@@ -649,6 +643,12 @@ void zbi_s8k_smdc_card_device::smd_do_drive(int drv)
649643
pkt->DS |= SMD_DS_FT;
650644
break;
651645
}
646+
if (file->get_info().sectorbytes > sizeof(m_buffer))
647+
{
648+
m_es = SMD_ES_BADCT;
649+
pkt->DS |= SMD_DS_FT;
650+
break;
651+
}
652652

653653
dma_idx = (pkt->AH << 16) | pkt->AL;
654654

@@ -663,24 +663,38 @@ void zbi_s8k_smdc_card_device::smd_do_drive(int drv)
663663

664664
LOGSEEK(" --> seek to block $%d (offset %d)\n", block, block*512);
665665

666-
/* DMA from main memory */
667-
for (unsigned int n = 0; n < pkt->CT; n++)
666+
// CT is the total transfer size and may span sectors (swap I/O uses up to 0xfe00 bytes).
667+
for (unsigned remaining = pkt->CT; remaining != 0; block++)
668668
{
669-
m_buffer[n] = m_bus->ram8_r(dma_idx++);
670-
}
669+
unsigned const count = std::min<unsigned>(remaining, file->get_info().sectorbytes);
671670

672-
if (!file->write(block, m_buffer))
673-
{
674-
m_es = SMD_ES_NOSECTOR;
675-
pkt->DS |= SMD_DS_FT;
671+
// Preserve the unused part of a sector for a short final transfer.
672+
if ((count != file->get_info().sectorbytes) && !file->read(block, m_buffer))
673+
{
674+
m_es = SMD_ES_NOSECTOR;
675+
pkt->DS |= SMD_DS_FT;
676+
break;
677+
}
678+
679+
for (unsigned n = 0; n < count; n++)
680+
m_buffer[n] = m_bus->ram8_r(dma_idx++);
681+
682+
if (!file->write(block, m_buffer))
683+
{
684+
m_es = SMD_ES_NOSECTOR;
685+
pkt->DS |= SMD_DS_FT;
686+
break;
687+
}
688+
689+
remaining -= count;
676690
}
677691
break;
678692

679693
case SMD_CM_CMD_READ: /* read */
680694
LOGREAD("%s SMD read: unit=%d cyl=%d hd=%d sec=%d count=%d addr=%04x:%04x\n", machine().describe_context(),
681695
unit, pkt->CY, pkt->HD, pkt->SC, pkt->CT, pkt->AH, pkt->AL);
682696

683-
if (pkt->CT & 1)
697+
if ((pkt->CT == 0) || (pkt->CT & 1))
684698
{
685699
m_es = SMD_ES_BADCT;
686700
pkt->DS |= SMD_DS_FT;
@@ -694,6 +708,12 @@ void zbi_s8k_smdc_card_device::smd_do_drive(int drv)
694708
pkt->DS |= SMD_DS_FT;
695709
break;
696710
}
711+
if (file->get_info().sectorbytes > sizeof(m_buffer))
712+
{
713+
m_es = SMD_ES_BADCT;
714+
pkt->DS |= SMD_DS_FT;
715+
break;
716+
}
697717

698718
dma_idx = (pkt->AH << 16) | pkt->AL;
699719

@@ -708,18 +728,21 @@ void zbi_s8k_smdc_card_device::smd_do_drive(int drv)
708728

709729
LOGSEEK(" --> seek to block $%d (offset %d)\n", block, block*512);
710730

711-
if (!file->read(block, m_buffer))
712-
{
713-
m_es = SMD_ES_NOSECTOR;
714-
pkt->DS |= SMD_DS_FT;
715-
}
716-
else
731+
for (unsigned remaining = pkt->CT; remaining != 0; block++)
717732
{
718-
/* DMA to main memory */
719-
for (unsigned int n = 0; n < pkt->CT; n++)
733+
unsigned const count = std::min<unsigned>(remaining, file->get_info().sectorbytes);
734+
735+
if (!file->read(block, m_buffer))
720736
{
721-
m_bus->ram8_w(dma_idx++, m_buffer[n]);
737+
m_es = SMD_ES_NOSECTOR;
738+
pkt->DS |= SMD_DS_FT;
739+
break;
722740
}
741+
742+
for (unsigned n = 0; n < count; n++)
743+
m_bus->ram8_w(dma_idx++, m_buffer[n]);
744+
745+
remaining -= count;
723746
}
724747
break;
725748

@@ -742,11 +765,13 @@ void zbi_s8k_smdc_card_device::smd_do_drive(int drv)
742765
pkt->DS = SMD_DS_FT;
743766
}
744767

745-
if (drv_good)
746-
pkt->DS |= SMD_DS_OC;
747-
748768
pkt->DS &= 0x003f;
749-
pkt->DS |= SMD_DS_RY | ((1<<drv) << 8) | ((1<<drv) << 12);
769+
pkt->DS |= ((1<<drv) << 8) | ((1<<drv) << 12);
770+
// Only report the drive ready / on-cylinder when a disk is actually present.
771+
// Reporting RY for an empty unit makes ZEUS's disk-config probe treat the 3
772+
// unpopulated SMD units as real (size 0) drives and spin on them.
773+
if (drv_good)
774+
pkt->DS |= SMD_DS_RY | SMD_DS_OC;
750775
}
751776

752777
//**************************************************************************

src/devices/bus/zbi/zbi.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,12 @@ void zbi_bus_device::ram32_w(offs_t offset, uint32_t data, uint32_t mask)
211211
uint16_t zbi_bus_device::viack_r()
212212
{
213213
device_z80daisy_interface *intf = daisy_get_irq_device();
214-
return intf ? intf->z80daisy_irq_ack() : 0;
214+
uint16_t vec = intf ? intf->z80daisy_irq_ack() : 0;
215+
// The ack changed the acked device's daisy state (INT -> IEO), so the VI
216+
// request line must be re-evaluated -- otherwise it stays asserted and the
217+
// CPU takes a phantom vectored interrupt that no device owns.
218+
vi_w(CLEAR_LINE);
219+
return vec;
215220
}
216221

217222
uint16_t zbi_bus_device::nviack_r()

0 commit comments

Comments
 (0)