summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/alto2/a2ram.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/alto2/a2ram.cpp')
-rw-r--r--src/devices/cpu/alto2/a2ram.cpp438
1 files changed, 438 insertions, 0 deletions
diff --git a/src/devices/cpu/alto2/a2ram.cpp b/src/devices/cpu/alto2/a2ram.cpp
new file mode 100644
index 00000000000..0f133c2dfd6
--- /dev/null
+++ b/src/devices/cpu/alto2/a2ram.cpp
@@ -0,0 +1,438 @@
+// license:BSD-3-Clause
+// copyright-holders:Juergen Buchmueller
+/*****************************************************************************
+ *
+ * Xerox AltoII RAM related functions
+ *
+ *****************************************************************************/
+#include "alto2cpu.h"
+
+#define DEBUG_WRTRAM 0 //!< define to 1 to printf disassembled CRAM writes
+
+//! direct read access to the microcode CRAM
+#define RD_CRAM(addr) (*reinterpret_cast<UINT32 *>(m_ucode_cram + addr * 4))
+
+//! direct write access to the microcode CRAM
+#define WR_CRAM(addr,data) do { \
+ *reinterpret_cast<UINT32 *>(m_ucode_cram + addr * 4) = data; \
+} while (0)
+
+/**
+ * @brief read the microcode ROM/RAM halfword
+ *
+ * Note: HALFSEL is selecting the even (0) or odd (1) half of the
+ * microcode RAM 32-bit word. Here's how the demultiplexers (74298)
+ * u8, u18, u28 and u38 select the bits:
+ *
+ * SN74298
+ * +---+-+---+
+ * | +-+ |
+ * B2 -|1 16|- Vcc
+ * | |
+ * A2 -|2 15|- QA
+ * | |
+ * A1 -|3 14|- QB
+ * | |
+ * B1 -|4 13|- QC
+ * | |
+ * C2 -|5 12|- QD
+ * | |
+ * D2 -|6 11|- CLK
+ * | |
+ * D1 -|7 10|- SEL
+ * | |
+ * GND -|8 9|- C1
+ * | |
+ * +---------+
+ *
+ * chip out pin BUS in pin HSEL=0 in pin HSEL=1
+ * --------------------------------------------------------------
+ * u8 QA 15 0 A1 3 DRSEL(0)' A2 2 DF2(0)
+ * u8 QB 14 1 B1 4 DRSEL(1)' B2 1 DF2(1)'
+ * u8 QC 13 2 C1 9 DRSEL(2)' C2 5 DF2(2)'
+ * u8 QD 12 3 D1 7 DRSEL(3)' D2 6 DF2(3)'
+ *
+ * u18 QA 15 4 A1 3 DRSEL(4)' A2 2 LOADT'
+ * u18 QB 14 5 B1 4 DALUF(0)' B2 1 LOADL
+ * u18 QC 13 6 C1 9 DALUF(1)' C2 5 NEXT(00)'
+ * u18 QD 12 7 D1 7 DALUF(2)' D2 6 NEXT(01)'
+ *
+ * u28 QA 15 8 A1 3 DALUF(3)' A2 2 NEXT(02)'
+ * u28 QB 14 9 B1 4 DBS(0)' B2 1 NEXT(03)'
+ * u28 QC 13 10 C1 9 DBS(1)' C2 5 NEXT(04)'
+ * u28 QD 12 11 D1 7 DBS(2)' D2 6 NEXT(05)'
+ *
+ * u38 QA 15 12 A1 3 DF1(0) A2 2 NEXT(06)'
+ * u38 QB 14 13 B1 4 DF1(1)' B2 1 NEXT(07)'
+ * u38 QC 13 14 C1 9 DF1(2)' C2 5 NEXT(08)'
+ * u38 QD 12 15 D1 7 DF1(3)' D2 6 NEXT(09)'
+ *
+ * The HALFSEL signal to the demultiplexers is the inverted bit BUS(5):
+ * BUS(5)=1, HALFSEL=0, A1,B1,C1,D1 inputs, upper half of the 32-bit word
+ * BUS(5)=0, HALFSEL=1, A2,B2,C2,D2 inputs, lower half of the 32-bit word
+ */
+void alto2_cpu_device::rdram()
+{
+ UINT32 addr, value;
+ UINT32 bank = GET_CRAM_BANKSEL(m_cram_addr);
+ UINT32 wordaddr = GET_CRAM_WORDADDR(m_cram_addr);
+
+ if (GET_CRAM_RAMROM(m_cram_addr)) {
+ /* read CROM 0 at current mpc */
+ addr = m_mpc & ALTO2_UCODE_PAGE_MASK;
+ LOG((this,LOG_CPU,0," rdram: ROM [%05o] ", addr));
+ } else {
+ /* read CRAM[bank] */
+ addr = bank * ALTO2_UCODE_PAGE_SIZE + wordaddr;
+ LOG((this,LOG_CPU,0," rdram: RAM%d [%04o] ", bank, wordaddr));
+ }
+
+ m_rdram_flag = false;
+ if (ALTO2_UCODE_RAM_BASE + addr >= ALTO2_UCODE_SIZE) {
+ value = 0177777; /* ??? */
+ LOG((this,LOG_CPU,0,"invalid address (%06o)\n", addr));
+ return;
+ }
+ value = RD_CRAM(addr) ^ ALTO2_UCODE_INVERTED;
+ if (GET_CRAM_HALFSEL(m_cram_addr)) {
+ value = value >> 16;
+ LOG((this,LOG_CPU,0,"upper:%06o\n", value & 0177777));
+ } else {
+ LOG((this,LOG_CPU,0,"lower:%06o\n", value & 0177777));
+ }
+ m_bus &= value;
+}
+
+/**
+ * @brief write the microcode RAM from M register and ALU
+ *
+ * Note: M is a latch (MYL, i.e. memory L) on the CRAM board that latches
+ * the ALU whenever LOADL and GOODTASK are met. GOODTASK is the Emulator
+ * task and something I have not yet found out about: TASKA' and TASKB'.
+ *
+ * There's also an undumped PROM u21 which is addressed by GOODTASK and
+ * 7 other signals...
+ */
+void alto2_cpu_device::wrtram()
+{
+ UINT32 bank = GET_CRAM_BANKSEL(m_cram_addr);
+ UINT32 wordaddr = GET_CRAM_WORDADDR(m_cram_addr);
+ UINT32 value = ((m_m << 16) | m_alu) ^ ALTO2_UCODE_INVERTED;
+
+ UINT32 addr = bank * ALTO2_UCODE_PAGE_SIZE + wordaddr; // write RAM 0,1,2
+ LOG((this,LOG_CPU,0," wrtram: RAM%d [%04o] upper:%06o lower:%06o", bank, wordaddr, m_m, m_alu));
+
+#if DEBUG_WRTRAM
+ char buff[128];
+ UINT8 oprom[4];
+ oprom[0] = m_m / 256;
+ oprom[1] = m_m % 256;
+ oprom[2] = m_m / 256;
+ oprom[3] = m_m % 256;
+ disasm_disassemble(buff, addr, oprom, oprom, 0);
+ printf("WR CRAM_BANKSEL=%d RAM%d [%04o] upper:%06o lower:%06o *** %s\n",
+ GET_CRAM_BANKSEL(m_cram_addr), bank, wordaddr, m_m, m_alu, buff);
+#endif
+
+ m_wrtram_flag = false;
+ if (ALTO2_UCODE_RAM_BASE + addr >= ALTO2_UCODE_SIZE) {
+ LOG((this,LOG_CPU,0," invalid address %06o\n", addr));
+ return;
+ }
+ LOG((this,LOG_CPU,0,"\n"));
+ WR_CRAM(addr, value);
+}
+
+/**
+ * @brief bs_read_sreg early: drive bus by S register or M (MYL), if rsel is = 0
+ *
+ * Note: RSEL == 0 can't be read, because it is decoded as
+ * access to the M register (MYL latch access, LREF' in the schematics)
+ */
+void alto2_cpu_device::bs_early_read_sreg()
+{
+ UINT16 r;
+
+ if (m_d_rsel) {
+ UINT8 bank = m_s_reg_bank[m_task];
+ r = m_s[bank][m_d_rsel];
+ LOG((this,LOG_RAM,2," <-S%02o; bus &= S[%o][%02o] (%#o)\n", m_d_rsel, bank, m_d_rsel, r));
+ } else {
+ r = m_m;
+ LOG((this,LOG_RAM,2," <-S%02o; bus &= M (%#o)\n", m_d_rsel, r));
+ }
+ m_bus &= r;
+}
+
+/**
+ * @brief bs_load_sreg early: load S register puts garbage on the bus
+ */
+void alto2_cpu_device::bs_early_load_sreg()
+{
+ int r = 0; /* ??? */
+ LOG((this,LOG_RAM,2," S%02o<- BUS &= garbage (%#o)\n", m_d_rsel, r));
+ m_bus &= r;
+}
+
+/**
+ * @brief bs_load_sreg late: load S register from M
+ */
+void alto2_cpu_device::bs_late_load_sreg()
+{
+ UINT8 bank = m_s_reg_bank[m_task];
+ m_s[bank][m_d_rsel] = m_m;
+ LOG((this,LOG_RAM,2," S%02o<- S[%o][%02o] := %#o\n", m_d_rsel, bank, m_d_rsel, m_m));
+}
+
+/**
+ * @brief branch to ROM page
+ */
+void alto2_cpu_device::branch_ROM(const char *from, int page)
+{
+ (void)from;
+ m_next2 = (m_next2 & ALTO2_UCODE_PAGE_MASK) + page * ALTO2_UCODE_PAGE_SIZE;
+ LOG((this,LOG_RAM,2," SWMODE: branch from %s to ROM%d (%#o)\n", from, page, m_next2));
+}
+
+/**
+ * @brief branch to RAM page
+ */
+void alto2_cpu_device::branch_RAM(const char *from, int page)
+{
+ (void)from;
+ m_next2 = (m_next2 & ALTO2_UCODE_PAGE_MASK) + ALTO2_UCODE_RAM_BASE + page * ALTO2_UCODE_PAGE_SIZE;
+ LOG((this,LOG_RAM,2," SWMODE: branch from %s to RAM%d\n", from, page, m_next2));
+}
+
+/**
+ * @brief f1_swmode early: switch to micro program counter BUS[6-15] in other bank
+ *
+ * Note: Jumping to uninitialized CRAM
+ *
+ * When jumping to uninitialized RAM, which, because of the inverted bits of the
+ * microcode words F1(0), F2(0) and LOADL, it is then read as F1=010 (SWMODE),
+ * F2=010 (BUSODD) and LOADL=1, loading the M register (MYL latch), too.
+ * This causes control to go back to the Emulator task at 0, because the
+ * NEXT[0-9] of uninitialized RAM is 0.
+ *
+ */
+void alto2_cpu_device::f1_late_swmode()
+{
+ /* currently executing in what page? */
+ UINT16 current = m_mpc / ALTO2_UCODE_PAGE_SIZE;
+
+#if (ALTO2_UCODE_ROM_PAGES == 1 && ALTO2_UCODE_RAM_PAGES == 1)
+ switch (current) {
+ case 0:
+ branch_RAM("ROM0", 0);
+ break;
+ case 1:
+ branch_ROM("RAM0", 0);
+ break;
+ default:
+ fatal(1, "Impossible current mpc %d\n", current);
+ }
+#endif
+#if (ALTO2_UCODE_ROM_PAGES == 2 && ALTO2_UCODE_RAM_PAGES == 1)
+ UINT16 next = X_RDBITS(m_next2,10,1,1);
+
+ switch (current) {
+ case 0: /* ROM0 to RAM0 or ROM1 */
+ switch (next) {
+ case 0:
+ branch_RAM("ROM0", 0);
+ break;
+ case 1:
+ branch_ROM("ROM0", 1);
+ break;
+ default:
+ fatal(1, "Impossible next %d\n", next);
+ }
+ break;
+ case 1: /* ROM1 to ROM0 or RAM0 */
+ switch (next) {
+ case 0:
+ branch_ROM("ROM1", 0);
+ break;
+ case 1:
+ branch_RAM("ROM1", 0);
+ break;
+ default:
+ fatal(1, "Impossible next %d\n", next);
+ }
+ break;
+ case 2: /* RAM0 to ROM0 or ROM1 */
+ switch (next) {
+ case 0:
+ branch_ROM("RAM0", 0);
+ break;
+ case 1:
+ branch_ROM("RAM0", 1);
+ break;
+ default:
+ fatal(1, "Impossible next %d\n", next);
+ }
+ break;
+ default:
+ fatal(1, "Impossible current mpc %d\n", current);
+ }
+#endif
+#if (ALTO2_UCODE_ROM_PAGES == 1 && ALTO2_UCODE_RAM_PAGES == 3)
+ UINT16 next = X_RDBITS(m_next2,10,1,2);
+
+ switch (current) {
+ case 0: /* ROM0 to RAM0, RAM2, RAM1, RAM0 */
+ switch (next) {
+ case 0:
+ branch_RAM("ROM0", 0);
+ break;
+ case 1:
+ branch_RAM("ROM0", 2);
+ break;
+ case 2:
+ branch_RAM("ROM0", 1);
+ break;
+ case 3:
+ branch_RAM("ROM0", 0);
+ break;
+ default:
+ fatal(1, "Impossible next %d\n", next);
+ }
+ break;
+ case 1: /* RAM0 to ROM0, RAM2, RAM1, RAM1 */
+ switch (next) {
+ case 0:
+ branch_ROM("RAM0", 0);
+ break;
+ case 1:
+ branch_RAM("RAM0", 2);
+ break;
+ case 2:
+ branch_RAM("RAM0", 1);
+ break;
+ case 3:
+ branch_RAM("RAM0", 1);
+ break;
+ default:
+ fatal(1, "Impossible next %d\n", next);
+ }
+ break;
+ case 2: /* RAM1 to ROM0, RAM2, RAM0, RAM0 */
+ switch (next) {
+ case 0:
+ branch_ROM("RAM1", 0);
+ break;
+ case 1:
+ branch_RAM("RAM1", 2);
+ break;
+ case 2:
+ branch_RAM("RAM1", 0);
+ break;
+ case 3:
+ branch_RAM("RAM1", 0);
+ break;
+ default:
+ fatal(1, "Impossible next %d\n", next);
+ }
+ break;
+ case 3: /* RAM2 to ROM0, RAM1, RAM0, RAM0 */
+ switch (next) {
+ case 0:
+ branch_ROM("RAM2", 0);
+ break;
+ case 1:
+ branch_RAM("RAM2", 1);
+ break;
+ case 2:
+ branch_RAM("RAM2", 0);
+ break;
+ case 3:
+ branch_RAM("RAM2", 0);
+ break;
+ default:
+ fatal(1, "Impossible next %d\n", next);
+ }
+ break;
+ default:
+ fatal(1, "Impossible current mpc %d\n", current);
+ }
+#else
+ fatal(1, "Impossible control ROM/RAM combination %d/%d\n", ALTO2_UCODE_ROM_PAGES, ALTO2_UCODE_RAM_PAGES);
+#endif
+}
+
+/**
+ * @brief f1_wrtram late: start WRTRAM cycle
+ */
+void alto2_cpu_device::f1_late_wrtram()
+{
+ m_wrtram_flag = true;
+ LOG((this,LOG_RAM,2," WRTRAM\n"));
+}
+
+/**
+ * @brief f1_rdram late: start RDRAM cycle
+ */
+void alto2_cpu_device::f1_late_rdram()
+{
+ m_rdram_flag = true;
+ LOG((this,LOG_RAM,2," RDRAM\n"));
+}
+
+#if (ALTO2_UCODE_RAM_PAGES == 3)
+
+/**
+ * @brief f1_load_rmr late: load the reset mode register
+ *
+ * F1=013 corresponds to RMR<- in the emulator. In Altos with the 3K
+ * RAM option, F1=013 performs RMR<- in all RAM-related tasks, including
+ * the emulator.
+ */
+void alto2_cpu_device::f1_late_load_rmr()
+{
+ LOG((this,LOG_RAM,2," RMR<-; BUS (%#o)\n", m_bus));
+ m_reset_mode = m_bus;
+}
+#else // ALTO2_UCODE_RAM_PAGES != 3
+/**
+ * @brief f1_load_srb late: load the S register bank from BUS[12-14]
+ */
+void alto2_cpu_device::f1_late_load_srb()
+{
+ m_s_reg_bank[m_task] = X_RDBITS(m_bus,16,12,14) % ALTO2_SREG_BANKS;
+ LOG((this,LOG_RAM,2," SRB<-; srb[%d] := %#o\n", m_task, m_s_reg_bank[m_task]));
+}
+#endif
+
+/**
+ * @brief RAM related task slots initialization
+ */
+void alto2_cpu_device::init_ram(int task)
+{
+ m_ram_related[task] = true;
+
+ set_bs(task, bs_ram_read_slocation, &alto2_cpu_device::bs_early_read_sreg, 0);
+ set_bs(task, bs_ram_load_slocation, &alto2_cpu_device::bs_early_load_sreg, &alto2_cpu_device::bs_late_load_sreg);
+
+ set_f1(task, f1_ram_swmode, 0, &alto2_cpu_device::f1_late_swmode);
+ set_f1(task, f1_ram_wrtram, 0, &alto2_cpu_device::f1_late_wrtram);
+ set_f1(task, f1_ram_rdram, 0, &alto2_cpu_device::f1_late_rdram);
+#if (ALTO2_UCODE_RAM_PAGES == 3)
+ set_f1(task, f1_ram_load_rmr, 0, &alto2_cpu_device::f1_late_load_rmr);
+#else // ALTO2_UCODE_RAM_PAGES != 3
+ set_f1(task, f1_ram_load_srb, 0, &alto2_cpu_device::f1_late_load_srb);
+#endif
+}
+
+void alto2_cpu_device::exit_ram()
+{
+ // nothing to do yet
+}
+
+void alto2_cpu_device::reset_ram()
+{
+ m_rdram_flag = false;
+ m_wrtram_flag = false;
+ m_m = 0;
+ memset(m_s, 0, sizeof(m_s));
+}