1
0
mirror of synced 2026-03-28 10:42:59 +00:00

added basic cpu; simulates correctly

This commit is contained in:
brad
2007-01-03 12:30:19 +00:00
parent ecaac3a25c
commit 9cf9fbd73a
8 changed files with 4068 additions and 0 deletions

36
cpu/Makefile Normal file
View File

@@ -0,0 +1,36 @@
#
#
#
IVERILOG=iverilog
VERILOG=cver
#all: igo
all: go
pdp8.o: pdp8.v
$(VERILOG) -o pdp8.o $(PARTS)
irun: $(PARTS) run.v pdp8.v
$(IVERILOG) -o run run.v
crun: $(PARTS) run.v pdp8.v
$(VERILOG) +change_port_type run.v
echo "exit 0" > run
chmod +x run
go: crun
./run
igo: irun
./run
display:
./maketraces.sh >traces
gtkwave caddr.vcd traces
snapshot:
(suffix=`date +%Y%m%d`; \
cd ..; \
tar cfz pdp8_verilog_$$suffix.tar.gz pdp8/*.v pdp8/Makefile ; \
mv pdp8_verilog* ~/html/heeltoe/html/download/pdp8)

75
cpu/NOTES.txt Normal file
View File

@@ -0,0 +1,75 @@
DEC-8I-H8NA-D KT8/I Time-Sharing Option Functional Description
DEC-8I-HOCA-D KE8/I Extended Arithmetic Element
interrupt occurs here
510 00516 7141 CLL CIA
511 00517 1155 TAD LASTV
512 00520 7630 SZL CLA
513 00521 4526 ERROR /STORAGE FILLED BY PUSHDOWN LIST
6000 PWS The contents of the AC register are loaded into the SW
register. This instruction performs no operation if the IM
flag is 1.
6001 ION The IE flag is set to 1, enabling interrupts, after a one
instruction delay.
6002 IOF The IE flag is set to 0, disabling interrupts.
6003 PSI Skip if the IE flag is 1. This instruction performs no
operation if the IM flag is 1.
6004 PAI The contents of the IB register are loaded into the IF
register, and the II flag is set to 0, just as if a JMP or
JMS was executed when the IM flag is 1. This instruction
performs no operation if the IM flag is 1.
6005 PAS The contents of the DF register are loaded into bits
[03..05] of the SF register, and the contents of the IF
register are loaded into bits [00..02] of the SF register,
just as they would be loaded on an interrupt. This
instruction performs no operation of the IM flag is 1.
6006 PDX The DM flag is set to 1 for the execution of the next
instruction. This instruction executes normally if IM is 1,
but since DM is 1 anytime IM is 1, it effectively performs
no operation.
6007 PEX The IM flag is set to 1, switching the CPU into normal mode,
after a one instruction delay. This instruction executes
notmally if IM is 1, but effectively performs no operation.
62x1 CDF Bits [06..08] of the instruction are loaded into the DF
register.
62x2 CIF Bits [06..08] of the instruction are loaded into the IB
register, and the II flag is set to 1. The contents of the
IB register will be loaded into the IF register, and the II
flag will be set to 0, when the next JMP or JMS instruction
is executed. The reload happens after any instruction and/or
indirect address words are read, but before the JMS writes
its return address; the return address is written to the new
instruction field. The II flag blocks interrupts between the
CIF and the JMP/JMS (page 53 of the 1970 small computer
handbook describes the effect of the II flag, even though
the flag itself is not described clearly).
6214 RDF The contents of the DF register are logically ORed into
bits [06..08] of the AC register.
6224 RIF The contents of the IF register are logically ORed into
bits [06..08] of the AC register.
6234 RIB The contents of the SF register are logically ORed into
bits [06..11] of the AC register; if the AC register is
initially 0, then bits [09..11] of the AC register get the
value which was in the DF register at the time of the last
interrupt, and bits [06..08] of the AC register get the
value which was in the IF register at the time of the last
interrupt.
6244 RMF Bits [03..05] of the SF register are loaded into the DF
register, bits [00..02] of the SF register are loaded into
the IB register, and the II flag is set to 1. The next JMP
or JMS instruction completes the restore.

1
cpu/README Normal file
View File

@@ -0,0 +1 @@
focal.v -cd win

2740
cpu/focal.v Normal file

File diff suppressed because it is too large Load Diff

1
cpu/maketraces.sh Executable file
View File

@@ -0,0 +1 @@
#!/bin/sh

728
cpu/pdp8.v Normal file
View File

@@ -0,0 +1,728 @@
// PDP-8
// Based on descriptions in "Computer Engineering"
// Nov 2005 Brad Parker brad@heeltoe.com
// initial work; runs focal a bit
// Dec 2006
// cleaned up a little; now runs focal to prompt
// moved i/o out to pdp8_io.v
// added IF, DF, user mode
//
// TODO:
// fully implement extended memory (IF & DF), user mode (KT8/I)
// add df32/rf08
// 6000 pws ac <= switches
//
//
// Instruction format:
//
// 0 1 2 3 4 5 6 7 8 9 10 11
// 11 10 9 8 7 6 5 4 3 2 1 0
// |--op--|
// 0 and
// 1 tad
// 2 isz
// 3 dca
// 4 jms
// 5 jmp
// 6 iot
// 7 opr
// 11 10 9 8 7 6 5 4 3 2 1 0
// group 1
// 0
// |cla|clf| | |
// | | |cma cml| |
// |bsw 001 |
// |ral 010 |
// |rtl 011 |
// |rar 100 |
// |rtr 101 |
// | |iac
//
// 11 10 9 8 7 6 5 4 3 2 1 0
// group 2
// 1 0
// |sma|sza|snl|skp| |
// |cla|
// |osr|hlt
//
// group 3
// 11 10 9 8 7 6 5 4 3 2 1 0
// eae
// 1 1
// |cla|
// |mqa|sca|mql|
// |isn |
//
//
/*
cpu states
F0 fetch
F1 incr pc
F2 write
F3 dispatch
E0 read
E1 decode
E2 write
E3 load
or
D0 read
D1 wait
D2 write
D3 load
------
F0 fetch
check for interrupt
F1 incr pc
if opr
group 1 processing
group 2 processing
if iot
incr pc or skip (incr pc by 2)
F2 write
ma <= pc
F3 dispatch
if opr
group1 processing
if !opr && !iot
possible defer
D0
mb <= memory
D1
*/
// extended memory
//
// 62n1 cdf change data field; df <= mb[5:3]
// 62n2 cif change instruction field; if <= mb[5:3], after next jmp or jms
// 6214 rdf read df into ac[5:3]
// 6224 rif read if into ac[5:3]
// 6234 rib read sf into ac[5:0], which is {if,df}
// 6244 rmf restore memory field, sf => ib, df
// (remember that on interrupt, sf <= {if,df})
//
`include "pdp8_io.v"
module pdp8(clk, reset_n, switches);
input clk, reset_n;
input[11:0] switches;
// memory buffer, holds data, instructions
reg[11:0] mb;
// hold address of work in memory being accessed
reg[14:0] ma;
// accumulator & link
reg[11:0] ac;
reg l;
// MQ
reg [11:0] mq;
// program counter
reg[11:0] pc;
wire pc_incr, pc_skip;
// instruction register
reg[2:0] ir;
// extended memory - instruction field & data field
reg [2:0] IF;
reg [2:0] DF;
reg [2:0] IB;
reg [5:0] SF;
reg ib_pending;
// user mode
reg UB;
reg UF;
//
wire[11:0] memory_bus;
reg ram_we_n;
// processor state
reg[3:0] state, next_state;
reg run;
reg interrupt_enable;
reg interrupt_cycle;
reg interrupt_inhibit;
reg interrupt_skip;
reg interrupt;
reg user_interrupt;
reg io_pulse_1, io_pulse_2, io_pulse_4;
wire [11:0] io_data;
wire io_data_avail;
wire io_interrupt;
wire io_skip;
wire[5:0] io_select;
assign io_select = mb[8:3];
wire skip_condition;
wire fetch; // memory cycle to fetch instruction
wire deferred; // memory cycle to get address of operand
wire execute; // memory cycle to getch (store) operand and execute isn
assign {fetch, deferred, execute} =
(state[3:2] == 2'b00) ? 3'b100 :
(state[3:2] == 2'b01) ? 3'b010 :
(state[3:2] == 2'b10) ? 3'b001 :
3'b000 ;
wire and,tad,isz,dca,jms,jmp,iot,opr;
assign {and,tad,isz,dca,jms,jmp,iot,opr} =
(ir == 3'b000) ? 8'b10000000 :
(ir == 3'b001) ? 8'b01000000 :
(ir == 3'b010) ? 8'b00100000 :
(ir == 3'b011) ? 8'b00010000 :
(ir == 3'b100) ? 8'b00001000 :
(ir == 3'b101) ? 8'b00000100 :
(ir == 3'b110) ? 8'b00000010 :
8'b00000001 ;
//-------------
parameter F0 = 4'b0000;
parameter F1 = 4'b0001;
parameter F2 = 4'b0010;
parameter F3 = 4'b0011;
parameter D0 = 4'b0100;
parameter D1 = 4'b0101;
parameter D2 = 4'b0110;
parameter D3 = 4'b0111;
parameter E0 = 4'b1000;
parameter E1 = 4'b1001;
parameter E2 = 4'b1010;
parameter E3 = 4'b1011;
ram_4kx12 ram(
.A(ma),
.DI(mb),
.DO(memory_bus),
.CE_N(1'b0),
.WE_N(ram_we_n));
/*
* note: bit numbering is opposite that used in "Computer Engineering"
*
* F1
* if opr
* if MB[8] and !MB[0]
* begin
* if skip.conditions ^ MB[3]
* pc <= pc + 2
* if skip.conditions == MB[3]
* pc <= pc + 1 next
* if MB[7]
* ac <= 0
*/
assign skip_condition =
(mb[6] && ac[11]) ||
(mb[5] && ac == 0) ||
(mb[4] && l == 1);
assign pc_incr =
(opr & !mb[8]) ||
(opr && (mb[8] && !mb[0]) && (skip_condition == mb[3])) ||
iot ||
(!(opr || iot) && !interrupt_cycle);
assign pc_skip =
(opr && (mb[8] && !mb[0]) && (skip_condition ^ mb[3])) ||
(iot && (io_skip || interrupt_skip));
// (iot && mb[0] && io_skip);
pdp8_io io(.clk(clk), .reset_n(reset_n),
.iot(iot), .state(state), .pc(pc), .ac(ac), .mb(mb),
.io_select(io_select),
.io_data_out(io_data),
.io_data_avail(io_data_avail),
.io_interrupt(io_interrupt),
.io_skip(io_skip));
always @(reset_n)
if (reset_n == 0)
begin
pc <= 0;
ma <= 0;
mb <= 0;
ac <= 0;
mq <= 0;
l <= 0;
ir <= 0;
ram_we_n <= 1;
state <= 0;
next_state <= 0;
run <= 1;
interrupt_enable <= 0;
interrupt_cycle <= 0;
interrupt_inhibit <= 0;
interrupt_skip <= 0;
interrupt <= 0;
user_interrupt <= 0;
io_pulse_1 <= 0;
io_pulse_2 <= 0;
io_pulse_4 <= 0;
IF <= 0;
DF <= 0;
IB <= 0;
SF <= 0;
UF <= 0;
UB <= 0;
ib_pending <= 0;
end
initial
begin
ram_we_n = 1;
end
/*
* cpu state state machine
*
* clock next cpu state at rising edge of clock
*/
always @(posedge clk)
state <= #1 next_state;
always @(state)
begin
case (state)
// FETCH
F0:
begin
interrupt_skip <= 0;
if (interrupt && interrupt_enable &&
!interrupt_inhibit && !interrupt_cycle)
begin
$display("interrupt; %b %b %b",
interrupt, interrupt_enable, interrupt_cycle);
interrupt_cycle <= 1;
interrupt <= 0;
mb <= 12'o4000;
ir <= 3'o4;
SF <= {IF,DF};
IF <= #1 3'b000;
DF <= #1 3'b000;
end
else
begin
interrupt_cycle <= 0;
mb <= memory_bus;
ir <= memory_bus[11:9];
end
next_state <= F1;
end
F1:
begin
//$display("f1: io_skip %b", io_skip);
//$display("f1 - ma %o, mb %o, ir %o", ma, mb, ir);
if (opr)
begin
// group 1
if (!mb[8])
begin
// $display("f1/g1 - ma %o, mb %o, ac %o l %o", ma, mb, ac,l);
if (mb[7]) ac <= 0;
if (mb[6]) l <= 0;
if (mb[5]) ac <= ~ac;
if (mb[4]) l <= ~l;
end
// group 2
if (mb[8] & !mb[0])
begin
// $display("f1/g2 - ma %o, mb %o, sc %o, i %o s %o, ac %o l %o",
// ma, mb, skip_condition, pc_incr, pc_skip, ac, l);
if (mb[7])
ac <= 0;
end
// group 3
if (mb[8] & mb[0])
begin
if (mb[7]) ac <= 0;
end
end
if (iot)
begin
$display("iot %t, run %b, state %b, pc %o, ir %o, mb %o, io_select %o",
$time, run, state, pc, ir, mb, io_select);
if (mb[0]) io_pulse_1 <= 1;
if (mb[1]) io_pulse_2 <= 1;
if (mb[2]) io_pulse_4 <= 1;
case (io_select)
6'b000000: // ION, IOF
case (mb[2:0])
3'b001: interrupt_enable <= 1;
3'b010: interrupt_enable <= 0;
3'b011: if (interrupt_enable) interrupt_skip <= 1;
endcase // case(mb[2:0])
6'b010xxx: // CDF..RMF
begin
case (mb[2:0])
3'b001: DF <= mb[5:3]; // CDF
3'b010: // CIF
begin
IB <= mb[5:3];
ib_pending <= 1;
interrupt_inhibit <= 1;
end
3'b100:
case (io_select[2:0])
3'b001: ac <= { 6'b0, DF, 3'b0 }; // RDF
3'b010: ac <= { 6'b0, IF, 3'b0 }; // RIF
3'b011: ac <= { 6'b0, SF }; // RIB
3'b100: begin // RMF
IB <= SF[5:3];
DF <= SF[2:0];
end
endcase // case(io_select[2:0])
endcase // case(mb[2:0])
end
endcase
if (io_data_avail)
begin
ac <= io_data;
end
end
if (io_interrupt)
interrupt <= 1;
//$display("f1 io_skip %b skip %b, incr %b", io_skip, pc_skip, pc_incr);
if (pc_skip)
pc <= pc + 2;
else
if (pc_incr)
pc <= pc + 1;
next_state <= F2;
end
F2:
begin
io_pulse_1 <= 0;
io_pulse_2 <= 0;
io_pulse_4 <= 0;
if (opr)
begin
ma <= {IF,pc};
// group 3
if (mb[8] & mb[0])
begin
$display("f2/g3 - ma %o, mb %o, ac %o l %o", ma, mb, ac,l);
case ({mb[6:4]})
3'b001: mq <= ac;
3'b100: ac <= ac | mq;
// 3'b101: tmq <= mq;
3'b100: ac <= mq;
3'b101: ac <= mq;
endcase // case({mb[6:4]})
end // if (mb[8] & mb[0])
end // if (opr)
if (iot)
ma <= {IF,pc};
if (!(opr || iot))
begin
ma[6:0] <= mb[6:0];
if (!mb[7])
ma[11:7] <= 0;
end
next_state <= F3;
end
F3:
begin
if (opr)
begin
// group 1
if (!mb[8])
begin
if (mb[0]) // IAC
{l,ac} <= {l,ac} + 1'b1;
if (mb[3:1] == 3'b001) // BSW
{l,ac} <= {l,ac[5:0],ac[11:6]};
if (mb[3] && !mb[1]) // RAR
{l,ac} <= {ac[0],l,ac[11:1]};
if (mb[3] && mb[1]) // RTR
{l,ac} <= {ac[1:0],l,ac[11:2]};
if (mb[2] && !mb[1]) // RAL
{l,ac} <= {ac[11:0],l};
if (mb[2] && mb[1]) // RTL
{l,ac} <= {ac[10:0],l,ac[11]};
end
if (!UF)
begin
// group 2
if (mb[8] & !mb[0])
if (mb[2])
ac <= ac | switches;
if (mb[1])
run <= 0;
end
if (UF)
begin
// group 2 - user mode (halt & osr)
if (mb[8] & !mb[0])
if (mb[2])
user_interrupt <= 1;
if (mb[1])
user_interrupt <= 1;
end
// group 3
if (mb[8] & mb[0])
begin
if (mb[7:4] == 4'b1101) mq <= 0;
end
ir <= 0;
mb <= 0;
next_state <= F0;
end
if (iot)
begin
ir <= 0;
mb <= 0;
next_state <= F0;
end
if (!(opr || iot))
begin
if (!mb[8] & jmp)
begin
pc <= ma;
ir <= 0;
mb <= 0;
next_state <= F0;
end
if (mb[8])
begin
mb <= 0;
next_state <= D0; /* defer */
end
if (!mb[8] & !jmp)
begin
mb <= 0;
next_state <= E0;
end
end
end
// DEFER
D0:
begin
mb <= memory_bus;
next_state <= D1;
end
D1:
begin
// auto increment regs
if (ma[11:3] == 8'h01)
mb <= mb + 1;
next_state <= D2;
end
D2:
begin
// write back
if (ma[11:3] == 8'h01)
ram_we_n <= 0;
ma <= #1 {DF,mb};
next_state <= D3;
end
D3:
begin
ram_we_n <= 1;
if (jmp)
begin
pc <= mb;
ir <= 0;
mb <= 0;
next_state <= F0;
end
if (!jmp)
begin
mb <= 0;
next_state <= E0;
end
end
// EXECUTE
E0:
begin
mb <= memory_bus;
next_state <= E1;
end
E1:
begin
if (and)
;
if (isz)
begin
mb <= mb + 1;
if (mb == 12'b111111111111)
pc <= pc + 1;
end
if (dca)
mb <= ac;
if (jms)
mb <= pc;
next_state <= E2;
end
E2:
begin
if (isz || dca || jms)
ram_we_n <= 0;
if (~jms)
ma <= #1 {IF,pc};
if (jms)
ma <= #1 {ma[14:12], ma[11:0] + 1};
next_state <= E3;
end
E3:
begin
ram_we_n <= 1;
if (and)
ac <= ac & mb;
if (tad)
{l,ac} <= ac + mb;
//if (tad) $display("tad - mb %o, ac %o l %o", mb, ac, l);
if (dca)
ac <= 0;
if (jms)
pc <= ma;
ir <= 0;
next_state <= F0;
end
endcase
end
endmodule
/* 4kx12 static ram */
module ram_4kx12(A, DI, DO, CE_N, WE_N);
input[14:0] A;
input[11:0] DI;
input CE_N, WE_N;
output[11:0] DO;
reg[11:0] ram [0:32767];
integer i;
initial
begin
for (i = 0; i < 32768; i=i+1)
ram[i] = 12'b0;
ram[15'o0000] = 12'o5177;
ram[15'o0200] = 12'o7300;
ram[15'o0201] = 12'o1300;
ram[15'o0202] = 12'o1301;
ram[15'o0203] = 12'o3302;
ram[15'o0204] = 12'o7402;
ram[15'o0205] = 12'o5200;
`include "focal.v"
ram[15'o0000] = 12'o5404;
ram[15'o0004] = 12'o0200;
end
always @(negedge WE_N)
begin
if (CE_N == 0)
begin
$display("ram: write [%o] <- %o", A, DI);
ram[ A ] = DI;
end
end
//always @(A)
// begin
// $display("ram: ce %b, we %b [%o] -> %o", CE_N, WE_N, A, ram[A]);
// end
// assign DO = ram[ A ];
assign DO = (^A === 1'bX || A === 1'bz) ? ram[0] : ram[A];
endmodule

419
cpu/pdp8_io.v Normal file
View File

@@ -0,0 +1,419 @@
// PDP-8 i/o
// Based on descriptions in "Computer Engineering"
// Dev 2006 Brad Parker brad@heeltoe.com
//
/*
iot's touched by focal
6022 PCF
6203 CDF CIF 00
6402 PT08
6412
6422
6432
6442
6452
6462
6472
6764 DECTAPE
6772
*/
/*
RF08
7750 word count
7751 current address
2048 words/track
660x
661x
662x
664x
6601 DCMA Generates System Clear Pulse (SCLP) at IOP time 1.
Clears disk memory eaddress(DMA), Parity Eror Flag (PEF),
Data Request Late Flag (DRL), and sets logic to
initial state for read or write. Does not clear
interrupt enable or extended address register.
6603 DMAR Generate SCLP at IOP time 1. At IOP time 2, loads DMA
with AC and clears AC. Read continues for number words
in WC register (7750)
6605 DMAW Generate SCLP at IOP time 1. At IOP time 4, loads DMA
with AC and clears AC. When the disk word address is located
writing begins, disk address is incremented for each
word written
6611 DCIM clears disk interrupt enable and the extended address
registers at IOP time 1.
6612 DSAC At IOP time 2, skip if Address Confirmed (ADC) set indicating
the DMA address and disk word address compare. AC is then
cleared.
6615 DIML At IOP time 1, clear interrupt enable and memory address
extension registers. At IOP time 4, load interrupt enable
and memory address extension register with AC, clear AC.
6616 DIMA Clear ??? at IOP time 2. At IOP time 4 load AC with status
register.
6621 DFSE skip on error skip if DRL, PER WLS or NXD set
6622 ??? skip if data completion DCF set
6623 DISK skip on error or data completion; enabled at IOP 2
6626 DMAC clear ac at IOP time 2 load AC from DMA at IOP time 4.
6641 DCXA Clears EMA
6643 DXAL Clears and loads EMA from AC. At IOP time 1, clear EMA, at
IOP time 2, load EMA with AC. Clear AC
6645 DXAC Clears AC and loads EMA into AC
6646 DMMT Maintenance
uses 3 cycle data break
ac 7:0, ac 11:0 => 20 bit {EMA,DMA}
20 bit {EMA,DMA} = { disk-select, track-select 6:0, word-select 11:0 }
status
*/
// EIE = WLS | DRL | NXD | PER
/*
3 cycle data break
1. An address is read from the device to indicate the location of the
word count register. This location specifies the number of words in
the block yet to be transferred. The address is always the same for a
given device.
2. The content of the specficified word count register is read from
memory and incremented by one. To transfer a block of n words, the
word count is set to -n during the programmed initialization of the
device. When this register is incremented to 0, a pulse is sent to
the device to terminate the transfer.
3. The location after the word count register contains the current
address register for the device transfer. The content of thise
register is set to 1 less than the location to be affected by the next
transfer. To transfer a block beginning at location A, the register is
originally set to A-1.
4. The content of the current address register is incremented by 1
and then used to specify the location affected by the transfer.
After the transfer of information has been accomplished through the
data break factility, input data (or new output data) is processed,
usually through the program interrupt facility. An interrupt is
requested when the data transfer is completed and the service routine
will process the information.
xxx:
if (databreak_req)
begin
databreak_done <= 0;
next_state <= DB0;
end
// read word count
DB0:
ma <= wc-address;
next_state <= DB1;
// write word count - 1
DB1:
mb <= memory_bus - 1;
ram_we_n <= 0;
if (mb == 0) databreak_done <= 1;
next_state <= DB2;
// finish write
DB2:
ram_we_n <= 1;
next_state <= DB3;
// read current address
DB3:
ma <= ma | 1;
next_state <= DB4;
// write current address - 1
DB4:
mb <= memory_bus + 1;
ram_we_n <= 0;
next_state <= DB5;
// finish write
DB5:
ram_we_n <= 1;
next_state <= DB6;
// set up read/write address
DB6:
ma <= mb;
next_state <= DB7;
// do read or start write
DB7:
if (databreak_write)
begin
data <= memory_bus;
next_state <= F0;
end
else
begin
mb <= data;
ram_we_n <= 0;
next_state <= DB8;
end
// finish write
DB8:
ram_we_n < = 1;
next_state <= F0;
*/
module pdp8_io(clk, reset_n, iot, state, pc, ac, mb,
io_select,
io_data_out, io_data_avail, io_interrupt, io_skip, io_clear_ac);
input clk, reset_n, iot;
input [11:0] pc;
input [11:0] ac;
input [11:0] mb;
input [3:0] state;
input [5:0] io_select;
output reg [11:0] io_data_out;
output reg io_data_avail;
output reg io_interrupt;
output reg io_skip;
output reg io_clear_ac;
reg rx_int, tx_int;
reg [12:0] rx_data, tx_data;
reg tx_delaying;
reg [3:0] tx_delay;
parameter F0 = 4'b0000;
parameter F1 = 4'b0001;
parameter F2 = 4'b0010;
parameter F3 = 4'b0011;
parameter D0 = 4'b0100;
parameter D1 = 4'b0101;
parameter D2 = 4'b0110;
parameter D3 = 4'b0111;
parameter E0 = 4'b1000;
parameter E1 = 4'b1001;
parameter E2 = 4'b1010;
parameter E3 = 4'b1011;
parameter PCA = 12'o4000; // photocell status
parameter DRE = 12'o2000; // data req enable
parameter WLS = 12'o1000; // write lock status
parameter EIE = 12'o0400; // error int enable
parameter PIE = 12'o0200; // photocell int enb
parameter CIE = 12'o0100; // done int enable
parameter MEX = 12'o0070; // memory extension
parameter DRL = 12'o0004; // data late error
parameter NXD = 12'o0002; // non-existent disk
parameter PER = 12'o0001; // parity error
always @(state)
begin
case (state)
F0:
begin
// sampled during f1
io_skip <= 0;
io_data_avail <= 0;
io_clear_ac <= 0;
if (iot)
case (io_select)
6'o03:
if (mb[0])
io_skip <= rx_int;
6'o04:
if (mb[0])
begin
$display("tsf; tx_int %b, pc %o", tx_int, pc);
io_skip <= tx_int;
end
6'o60: // DCMA
if (mb[2:0] == 3'b001)
begin
DMA <= 0;
PEF < = 0;
DRL <= 0;
end
6'o61:
case (mb[2:0])
n 3'o1: // DCIM
begin
CIE <= 0;
EMA <= 0;
end
3'o2: // DSAC
// xxx
assign ADC = DMA == DWA;
begin
if (ADC)
begin
io_skip <= 1;
io_clear_ac <= 1;
end
end
3'o5: // DIML
begin
CIE <= AC[8];
EMA <= AC[7:0];
end
3'o6: // DIMA
begin
AC <= { PCA,DRE,WLS,EIE,PIE,CIE, MEX, DRL,NXD,PER };
end
endcase // case(mb[2:0])
6'o62:
case (mb[2:0])
3'o1: // DFSE
if (DRL | PER | WLS | NXD)
io_skip <= 1;
3'o2: // ???
if (DCF)
io_skip <= 1;
3'o3: // DISK
if (DRL | PER | WLS | NXD | DCF)
io_skip <= 1;
endcase
endcase
end
F1:
if (iot)
begin
$display("iot2 %t, state %b, mb %o, io_select %o",
$time, state, mb, io_select);
case (io_select)
6'o03:
begin
if (mb[1])
rx_int <= 0;
if (mb[2])
begin
io_data_out <= rx_data;
io_data_avail <= 1;
end
end
6'o04:
begin
if (mb[1])
tx_int <= 0;
if (mb[2])
begin
$display("tls; %o", ac);
tx_data <= ac;
tx_int <= 1;
tx_delaying <= 1;
tx_delay <= 4'b1111;
$display("set tx_int");
end
end // case: 6'o04
6'o60:
case (mb[2:0])
3'o03: // DMAR
begin
DMA <= AC;
io_clear_ac <= 0;
rf08_start_io <= 1;
rf08_rw <= 0;
end
3'o03: // DMAW
begin
DMA <= AC;
io_clear_ac <= 0;
rf08_start_io <= 1;
rf08_rw <= 1;
end
endcase // case(mb[2:0])
6'o62:
case (mb[2:0])
6: // DMAC
begin
// io_clear_ac <= 1;
io_ac <= DMA;
end
endcase
6'o64:
case (mb[2:0])
1: // DCXA
EMA <= 0;
3: // DXAL
begin
EMA <= AC;
io_clean_ac <= 1;
end
5: // DXAC
begin
AC <= EMA;
end
endcase
endcase
end // if (iot)
F2:
begin
if (io_interrupt)
$display("iot2 %t, reset io_interrupt", $time);
// sampled during f0
io_interrupt <= 0;
end
F3:
begin
if (tx_delaying)
begin
tx_delay <= tx_delay - 1;
if (tx_delay == 4'b0)
begin
$display("iot2 %t, set io_interrupt", $time);
tx_delaying <= 0;
io_interrupt <= 1;
end
end
end
endcase // case(state)
end
endmodule

68
cpu/run.v Normal file
View File

@@ -0,0 +1,68 @@
`include "pdp8.v"
`timescale 1ns / 1ns
module test;
reg clk, reset_n;
reg[11:0] switches;
pdp8 cpu(clk, reset_n, switches);
initial
begin
$timeformat(-9, 0, "ns", 7);
$dumpfile("pdp8.vcd");
$dumpvars(0, test.cpu);
end
initial
begin
clk = 0;
reset_n = 1;
#1 begin
reset_n = 0;
end
#100 begin
reset_n = 1;
end
// #1500000 $finish;
#3000000 $finish;
end
always
begin
#100 clk = 0;
#100 clk = 1;
end
//----
integer cycle;
initial
cycle = 0;
always @(posedge cpu.clk)
if (cpu.state == 4'b0000)
begin
cycle = cycle + 1;
#1 $display("cycle %d, r%b, pc %o, ir%o, ma %o, mb %o, jmp %b, l%b ac %o, i%b/%b",
cycle, cpu.run, cpu.pc,
cpu.ir, cpu.ma, cpu.mb, cpu.jmp, cpu.l, cpu.ac,
cpu.interrupt_enable, cpu.interrupt);
end
// always @(posedge cpu.clk)
// begin
// #1 $display("state %b, runs %b, pc %o, ir %o, ma %o mb %o, jmp %b, l %b ac %o",
// cpu.state, cpu.run, cpu.pc,
// cpu.ir, cpu.ma, cpu.mb, cpu.jmp, cpu.l, cpu.ac);
// end
endmodule