//
// Copyright (c) 2014-2022 by 1801BM1@gmail.com
//
// DC303 Control Chip model, for debug and simulating only
//______________________________________________________________________________
//
`timescale 1ns / 100ps

module dc303
#(parameter
//______________________________________________________________________________
//
// DC303_CS defines PLA and ROM content of the DC303 module
//  - DC303_CS = 0, 23-001C7-AA, rni = ma[8:0] == 9'x000
//  - DC303_CS = 1, 23-002C7-AA, rni = ma[8:0] == 9'x000
//  - DC303_CS = 2, 23-203C7-AA, rni = ma[8:4] == 5'x00
//
   DC303_CS = 0
)
(
   input          pin_clk,    // main clock
   input [15:0]   pin_ad,     // address/data bus
   inout [15:0]   pin_m,      // microinstruction bus
   input          pin_rst,    // reset
   input          pin_ez_n,   // enable Z-state
   output         pin_cs_n    // chip select
);

//______________________________________________________________________________
//
wire        clk;        // primary clock
wire        rst;        // synchronized reset
wire [4:0]  dc_cs;      //
                        //
wire        moe;        // MIB output enable
reg  [15:0] m;          // MIB output register
reg  [15:0] mi;         // MIB input register on clock low phase
reg  [15:0] d;          // data word input register
reg  [15:0] da;         // data word input for next address workaround
                        //
wire        rd_na;      // read next address on MIB (for test)
wire        rd_mc;      // read normal microcode on MIB (no test)
reg         test;       // test cycle
reg         cs;         // chip select
reg         ez;         // enable Z-state
                        //
reg         axt;        // address extension bit
reg  [8:0]  nar;        // next address register
wire [8:0]  na;         // next address mux value
wire [3:0]  clr_na;     // special address na clear
                        //
wire [8:0]  ma;         // address read from ROM matrix
wire [15:0] mc;         // microcode read from ROM matrix
reg  [9:0]  a_in;       // address inputs for ROM/PLA
reg  [15:0] d_in;       // data inputs for ROM/PLA
wire [8:0]  ma_pla;     // address from PLA
wire [8:0]  ma_rom;     // address from ROM
wire [15:0] mc_pla;     // microcode from PLA
wire [15:0] mc_rom;     // microcode from ROM
                        //
reg         axtt;       //
reg         rnit;       //
wire        rni;        // read next instruction
wire        jump;       // jump microinstruction
wire        cjmp;       // conditional jump microinstruction
wire        cs_stb;     // chip select strobe on jump
wire        di_stb;     // data input service word strobe
                        //
reg  [3:0]  pri_in;     // priority input latch
reg  [2:0]  pri;        // priority
reg         tbit;       // T-bit
reg         wpsw;       //
reg         lplm;       //
reg         sovf;       // stack overflow
                        //
reg  [7:0]  dc;         // processor opcode decoder
wire [4:0]  dcop;       // decoded opcodes
reg         na_x00;     // na == 9'b000001x00
reg         na_xxx;     // na == 9'b000001xxx
reg         na_111;     // na == 9'b000001111
                        //
wire        pri_stb;    // priority register strobe
wire        tbit_stb;   // T-bit strobe
wire        tgl_axt;    // toggle address extension
wire        mc_dt;      // replace microcode with data
wire        na_dt;      // replace next address with data
                        //
reg         set_soft;   //
reg         clr_soft;   //
reg         set_wpswt;  //
wire        set_sof;    // set stack overflow
wire        set_nusd;   // set unknown not used (in chip 0) flag
wire        set_lplm;   //
wire        set_wpsw;   //
wire        clr_lplm;   //

//______________________________________________________________________________
//
// Clocks and Reset
//
assign clk = pin_clk;
assign rst = pin_rst & ~clk;
assign pin_cs_n = cs ? 1'b0 : 1'bz;
assign dc_cs = DC303_CS;

wire #1 sim_dclk  = clk; // suppress simulation glitches

//______________________________________________________________________________
//
// MIB[10] - test_n, asserted to read next address on MIB, implemented
//                   for test purposes only, reads 9 bit of next address
//                   and AXT bit value, DC302 never asserts test_n
//
always @(*) if (clk) test <= ~pin_m[10];
assign rd_mc = ~test;
assign rd_na = test & clk;

//______________________________________________________________________________
//
// Microinstruction bus (MIB)
//
always @(*) if (clk) ez <= ~pin_ez_n;
assign moe = ~clk & ~ez & cs;
assign pin_m = moe ? m : 16'hzzzz;

always @(*)
begin
   if (rd_mc) m[15:0] = mc[15:0];
   if (rd_na)
   begin
      m[0] = nar[0] & ~clr_na[0];
      m[1] = nar[1] & ~clr_na[1];
      m[2] = nar[2] & ~clr_na[2];
      m[3] = nar[3] & ~clr_na[3];
      m[8:4] = na[8:4];
      m[9] = axt;
      m[15:10] = 6'b111111;
   end
end

always @(*) if (~clk) mi <= pin_m;
//______________________________________________________________________________
//
// Next microinstruction address (NA)
//
always @(*) if (~clk) nar <= ma;
assign cs_stb = mi[15:11] == 5'b00000;
assign jump = clk & cs_stb;
assign cjmp = clk & ~pin_m[11] & (mi[15:11] == 5'b00001);

//
// na[3:0] - reset at dedicated addresses
// na[7:0] - overriden by conditional jump
// na[5:0] - overriden by unconditional jump
// na[8:6] - cleared by unconditional jump
//
assign na[0] = (jump | cjmp) ? mi[0] : (nar[0] & ~clr_na[0]);
assign na[1] = (jump | cjmp) ? mi[1] : (nar[1] & ~clr_na[1]);
assign na[2] = (jump | cjmp) ? mi[2] : (nar[2] & ~clr_na[2]);
assign na[3] = (jump | cjmp) ? mi[3] : (nar[3] & ~clr_na[3]);
assign na[4] = (jump | cjmp) ? mi[4] : nar[4];
assign na[5] = (jump | cjmp) ? mi[5] : nar[5];
assign na[6] = cjmp ? mi[6] : (nar[6] & ~jump);
assign na[7] = cjmp ? mi[7] : (nar[7] & ~jump);
assign na[8] = nar[8] & ~jump;

//______________________________________________________________________________
//
always @(*) if (clk) pri_in[3:0] <= pin_ad[7:4];
always @(*) if (pri_stb) pri[2:0] <= pri_in[3:1];
always @(*) if (tbit_stb) tbit <= pri_in[0];
always @(*) if (clk) dc[7:0] <= {mi[7:4], pin_m[9], pin_m[6:4]};

function dc_cmp
(
   input [7:0] dc,
   input [3:0] mo,
   input [3:0] ms
);
begin
   casex(dc)
      {mo, ms}: dc_cmp = 1'b1;
      default:  dc_cmp = 1'b0;
   endcase
end
endfunction

assign pri_stb  = ~clk & dc_cmp(dc, 4'bxxxx, 4'bx10x);            // transfer prio
assign tbit_stb = ~clk & dc_cmp(dc, 4'bxxxx, 4'bx1x0);            // transfer tbit
assign tgl_axt  = ~clk & dc_cmp(dc, 4'bxx0x, 4'bx000);
assign mc_dt    = ~clk & dc_cmp(dc, 4'bxx10, 4'bx000);            // data to mc
assign na_dt    = ~clk & dc_cmp(dc, 4'bxxx1, 4'bx000);            // data to na
assign set_sof  = ~clk & dc_cmp(dc, 4'bxxxx, 4'bx010) & set_soft; // stack ovf
assign set_nusd = ~clk & dc_cmp(dc, 4'bx1xx, 4'b0100);
assign set_lplm = ~clk & dc_cmp(dc, 4'b000x, 4'b1001) & tbit;
assign set_wpsw = ~clk & dc_cmp(dc, 4'bxx1x, 4'b0100);
assign clr_lplm = ~clk & dc_cmp(dc, 4'bxxx1, 4'bx1x0);

always @(*)
begin
   if (rni)  set_soft <= 1'b1;
   if (sovf) set_soft <= 1'b0;
   if (clk)  clr_soft <= ~lplm;
   if (clk) set_wpswt <= wpsw;

   if (rst)
      cs <= (dc_cs == 5'b00000);
   else
      if (clk & cs_stb)
         cs <= mi[10:6] == dc_cs;

   if (set_lplm)
      lplm <= 1'b1;
   else
      if (clr_lplm)
         lplm <= 1'b0;

   if (set_sof)
      sovf <= 1'b1;
   else
      if (clr_lplm & clr_soft)
         sovf <= 1'b0;

   if (set_wpsw | set_wpswt & rni)
      wpsw <= 1'b1;
   else
      if (~clk)
         wpsw <= 1'b0;

   if (~clk) rnit <= rni;
   if (clk) axtt <= axt;

   if (~cs | rnit)
      axt <= 1'b0;
   else
      if (~clk & tgl_axt)
         axt <= ~axtt;
end

//______________________________________________________________________________
//
// Data input register can be written directly from AD bus on clock high (di_stb),
// or be written with interrupt statuses on clock low (rni))
//
assign rni = ~clk & (rst | (ma[8:4] == 5'b00000)
                         & ((ma[3:0] == 4'b0000) | (dc_cs == 5'b00010))
                         & cs);
assign di_stb = clk & ~pin_m[13] & ~pin_m[6] & ~pin_m[5] & sim_dclk;

//
// Internal requests, provided by internal logic
//
// DAL15    - stack overflow
// DAL14    - lplm
// DAL13    - wpsw
//
// External service word, provided by CPU board
//
// DAL12    - timer event request, high level
// DAL11    - IRQ[4], high level
// DAL10    - IRQ[5], high level
// DAL9     - IRQ[6], high level
// DAL8     - IRQ[7], high level
// DAL7     - ACLO (power down), low level
// DAL6     - ground (always asserted)
// DAL5     - HALT request, high level
// DAL4     - Control Chip Error, low level
// DAL3     - MMU requests abort, low level
// DAL2     - parity error, low level
// DAL1     - bus timeout, high level
// DAL0     - DCLO, low level means OK
//
always @(*)
begin
   if (di_stb | rni)
   begin
      d[7:0] <= pin_ad[7:0];
      d[8]  <= pin_ad[8]  & (clk | (pri[2:0] < 3'o7));   // IRQ7 masking
      d[9]  <= pin_ad[9]  & (clk | (pri[2:0] < 3'o6));   // IRQ6 masking
      d[10] <= pin_ad[10] & (clk | (pri[2:0] < 3'o5));   // IRQ5 masking
      d[11] <= pin_ad[11] & (clk | (pri[2:0] < 3'o4));   // IRQ4 masking
      d[12] <= pin_ad[12] & (clk | (pri[2:0] < 3'o6));   // EVNT masking
   end
   if (di_stb)
   begin
      d[13] <= pin_ad[13];
      d[14] <= pin_ad[14];
      d[15] <= pin_ad[15];
   end
   if (rni)
   begin
      d[13] <= sovf;
      d[14] <= lplm;
      d[15] <= wpsw;
   end
   if (di_stb)
      da <= pin_ad;
end

//______________________________________________________________________________
//
// PDP-11 opcode decoder for special microcode addresses
//
function op_cmp
(
   input [9:0] d,
   input [9:0] m
);
begin
   casex(d)
      m:       op_cmp = 1'b1;
      default: op_cmp = 1'b0;
   endcase
end
endfunction

//______________________________________________________________________________
//
// The results of reading Next Address matrix from physical chip (cs=00)
// and following ESPRESSO AB minimization
//
// na  d[15:0]                dcop  na_xxx
//
// 111 0000-00001------ 1000  1     111   na_clr[3]
// 111 0000100--------- 1000  1     111
//
// --- -1-0000---000--- 0100  4,2   xxx   na_clr[2]
// --- --01000---000--- 0100  4,2   xxx
// --- -01-000---000--- 0100  4,2   xxx
// -11 1000110-0-000--- 0100  4,0   111
// -11 -0001100--000--- 0100  4,0   111
// -11 -000110--1000--- 0100  4,0   111
// -11 -000101---000--- 0100  4,0   111
// -11 01110-----000--- 0100  4,0   111
// -11 0111-00---000--- 0100  4,0   111
// -11 0---000011000--- 0100  4,0   111
// -00 ----------000--- 0100  4     x00
//
// --- -1-0000--------- 0010  3,2   xxx   na_clr[1]
// --- --01000--------- 0010  3,2   xxx
// --- -01-000--------- 0010  3,2   xxx
// 1-1 1000110-0------- 0010  0     111
// 1-1 -0001100-------- 0010  0     111
// 1-1 -000110--1------ 0010  0     111
// 1-1 -000101--------- 0010  0     111
// 1-1 01110----------- 0010  0     111
// 1-1 0111-00--------- 0010  0     111
// 1-1 0---000011------ 0010  0     111
//
// --- -1-0------------ 0001  3     xxx   na_clr[0]
// --- --01------------ 0001  3     xxx
// --- -01------------- 0001  3     xxx
//
assign dcop[0] = op_cmp(d[15:6], 10'b1000110100)   // mtps
               | op_cmp(d[15:6], 10'bx0001100xx)   // single operand x06xxx
               | op_cmp(d[15:6], 10'bx0001101x1)   //
               | op_cmp(d[15:6], 10'bx000101xxx)   // single operand x05xxx
               | op_cmp(d[15:6], 10'b01110xxxxx)   // eis
               | op_cmp(d[15:6], 10'b0111100xxx)   // xor
               | op_cmp(d[15:6], 10'b0000000011);  // swab
                                                   //
assign dcop[1] = op_cmp(d[15:6], 10'b0000000001)   // jmp
               | op_cmp(d[15:6], 10'b0000100xxx);  // br
                                                   //
assign dcop[2] = d[11:9] == 3'b000;                // source register mode
assign dcop[4] = d[5:3] == 3'b000;                 // destination register mode
                                                   //
assign dcop[3] = op_cmp(d[15:6], 10'bxx01xxxxxx)   // not x0xxxx and not x7xxxx
               | op_cmp(d[15:6], 10'bx01xxxxxxx)   // normal opcode
               | op_cmp(d[15:6], 10'bx1x0xxxxxx);  //

always @(*)
begin
   if (~clk)
   begin
      na_x00 <= (na[8:3] == 6'b000001) & (na[1:0] == 2'b00);
      na_xxx <= na[8:3] == 6'b000001;
      na_111 <= na[8:0] == 9'b000001111;
   end
end

assign clr_na[0] = clk & na_xxx & dcop[3];
assign clr_na[1] = clk & na_xxx & dcop[3] & dcop[2]
                 | clk & na_111 & dcop[0];
assign clr_na[2] = clk & na_xxx & dcop[4] & dcop[2] & dcop[3]
                 | clk & na_111 & dcop[4] & dcop[0]
                 | clk & na_x00 & dcop[4];
assign clr_na[3] = clk & na_111 & dcop[1];

//______________________________________________________________________________
//
always @(*)
begin
   if (clk) a_in = {axt, na};
   if (clk) d_in = d;
end

assign ma = clk ? 9'o777 :
            rst ? 9'o000 :
            ((na_dt ? da[8:0] : 9'o777) & ((a_in[8:7] == 2'b00) ? ma_pla : ma_rom));

assign mc = clk ? 16'o177777 :
            rst ? 16'o000000 :
            ((mc_dt ? da : 16'o177777) & ((a_in[8:7] == 2'b00) ? mc_pla : mc_rom));

defparam pla.DC303_PLA = DC303_CS;
dc_pla pla
(
   .a_in(a_in[6:0]),
   .d_in(d_in),
   .ma(ma_pla),
   .mc(mc_pla)
);

defparam rom.DC303_ROM = DC303_CS;
dc_rom rom
(
   .a_in(a_in),
   .d_in(d_in),
   .ma(ma_rom),
   .mc(mc_rom)
);

//______________________________________________________________________________
//
endmodule
