1 |
3 |
yannv |
----------------------------------------------------------------------------------
|
2 |
|
|
-- Company: None
|
3 |
|
|
-- Engineer: Yann Vernier
|
4 |
|
|
--
|
5 |
|
|
-- Create Date: 13:29:13 02/09/2009
|
6 |
|
|
-- Design Name:
|
7 |
|
|
-- Module Name: pdp1cpu - Behavioral
|
8 |
|
|
-- Project Name: PDP-1
|
9 |
|
|
-- Target Devices: Xilinx Spartan 3A
|
10 |
|
|
-- Tool versions: WebPack 10.1
|
11 |
|
|
-- Description: PDP-1 CPU (main logic) module, executes instructions.
|
12 |
|
|
--
|
13 |
|
|
-- Dependencies: Requires a RAM and a clock source. RAM is accessed in alternating read and write.
|
14 |
|
|
-- Original clock is 200kHz memory cycle, where each cycle both
|
15 |
|
|
-- reads and writes (core memory). CPU itself must be clocked 10
|
16 |
|
|
-- times faster (original logic modules could keep up with 4MHz).
|
17 |
|
|
--
|
18 |
|
|
-- Revision:
|
19 |
|
|
-- Revision 0.01 - File Created
|
20 |
|
|
-- Additional Comments:
|
21 |
|
|
-- Aim is currently a PDP-1B level.
|
22 |
|
|
-- B version had multiply and divide Step to accelerate the subroutines,
|
23 |
|
|
-- and the first had pure software subroutines. C had full hardware multiply(?).
|
24 |
|
|
-- PDP-1D implements several more instructions not included here.
|
25 |
|
|
-- Extensions (including sequence break and extended memory) are not implemented.
|
26 |
|
|
--
|
27 |
|
|
-- Goal: Run Spacewar! in hardware. Initial target version is that used by Java
|
28 |
|
|
-- emulator, which is a PDP-1B, with multiply and divide steps.
|
29 |
|
|
-- That emulator doesn't have DIP.
|
30 |
|
|
-- PDP-1C had full multiply and divide instructions of variable time.
|
31 |
|
|
--
|
32 |
|
|
----------------------------------------------------------------------------------
|
33 |
|
|
library IEEE;
|
34 |
|
|
use IEEE.STD_LOGIC_1164.ALL;
|
35 |
9 |
yannv |
-- Why does isim behave like unsigned+natural doesn't exist?
|
36 |
3 |
yannv |
use IEEE.NUMERIC_STD.ALL;
|
37 |
|
|
|
38 |
|
|
|
39 |
|
|
entity pdp1cpu is
|
40 |
|
|
Port (
|
41 |
|
|
-- memory interface
|
42 |
|
|
M_DI : out STD_LOGIC_VECTOR(0 to 17) := (others=>'0');
|
43 |
|
|
M_DO : in STD_LOGIC_VECTOR(0 to 17);
|
44 |
|
|
MW : inout STD_LOGIC := '0';
|
45 |
|
|
MA : out std_logic_vector(0 to 11) := (others=>'0');
|
46 |
|
|
|
47 |
|
|
CLK : in STD_LOGIC; -- in progress: adapt to 10x clock
|
48 |
|
|
|
49 |
|
|
-- CPU status
|
50 |
|
|
AWAKE : out STD_LOGIC;
|
51 |
|
|
|
52 |
|
|
-- user visible registers
|
53 |
|
|
AC : inout STD_LOGIC_VECTOR(0 to 17) := (others=>'0'); -- accumulator
|
54 |
|
|
IO : inout STD_LOGIC_VECTOR(0 to 17) := (others=>'0'); -- I/O
|
55 |
9 |
yannv |
PC : inout STD_LOGIC_VECTOR(0 to 11) := (others=>'0'); -- program counter
|
56 |
3 |
yannv |
PF : inout STD_LOGIC_VECTOR(1 to 6) := (others=>'0'); -- program flags
|
57 |
|
|
OV : inout STD_LOGIC := '0'; -- overflow flag
|
58 |
|
|
|
59 |
|
|
-- user settable switches
|
60 |
|
|
SW_TESTA : in std_logic_vector(0 to 11) := (others => '0'); -- test address
|
61 |
|
|
SW_TESTW : in std_logic_vector(0 to 17) := (others => '0'); -- test word
|
62 |
|
|
SW_SENSE : in std_logic_vector(1 to 6) := (others => '0'); -- sense switches
|
63 |
|
|
|
64 |
|
|
-- I/O interface
|
65 |
|
|
IOT : out STD_LOGIC_VECTOR(0 to 63) := (others=>'0'); -- I/O transfer pulse lines
|
66 |
|
|
IODOPULSE : out STD_LOGIC := '0'; -- signal to I/O device to send a
|
67 |
|
|
-- pulse when done
|
68 |
|
|
IODONE : in STD_LOGIC := '0'; -- I/O device done signal
|
69 |
|
|
IO_SET : in STD_ULOGIC := '0'; -- used to set I/O register to IO_IN value (synchronous!)
|
70 |
|
|
IO_IN : in STD_LOGIC_VECTOR(0 to 17) := o"000000"; -- bus for I/O devices to report values
|
71 |
|
|
|
72 |
|
|
RESET : in STD_LOGIC
|
73 |
|
|
);
|
74 |
|
|
end pdp1cpu;
|
75 |
|
|
|
76 |
|
|
architecture Behavioral of pdp1cpu is
|
77 |
|
|
subtype word is STD_LOGIC_VECTOR(0 to 17);
|
78 |
|
|
subtype opcode is std_logic_vector(0 to 5);
|
79 |
|
|
-- Formally the opcode is 5 bits; I've included the indirection bit.
|
80 |
|
|
|
81 |
|
|
signal MB: word; -- memory buffer
|
82 |
|
|
signal op: opcode := o"00"; -- current operation (user visible originally)
|
83 |
|
|
|
84 |
|
|
signal IOWAIT, HALT : boolean := false;
|
85 |
|
|
|
86 |
|
|
alias ib : std_logic is op(5); -- indirection bit
|
87 |
|
|
alias y : std_logic_vector(0 to 11) is MB(6 to 17); -- address/operand
|
88 |
|
|
|
89 |
|
|
-- operations - note that "load" here is OR, for some reason.
|
90 |
|
|
alias cli : std_logic is MB(6); -- clear IO
|
91 |
|
|
alias lat : std_logic is MB(7); -- load AC with Test.Switches
|
92 |
|
|
alias cma : std_logic is MB(8); -- complement AC
|
93 |
|
|
alias hlt : std_logic is MB(9); -- halt
|
94 |
|
|
alias cla : std_logic is MB(10); -- clear AC
|
95 |
|
|
alias lap : std_logic is MB(11); -- load AC from PC
|
96 |
|
|
alias flag_setto : std_logic is MB(14); -- set program flag(s) - value
|
97 |
|
|
alias flag_which : std_logic_vector(2 downto 0) is MB(15 to 17);
|
98 |
|
|
-- skip conditions
|
99 |
|
|
alias spi : std_logic is MB(7); -- Skip if Positive IO
|
100 |
|
|
alias szo : std_logic is MB(8); -- skip if zero OV
|
101 |
|
|
alias sza : std_logic is MB(9); -- skip if zero AC
|
102 |
|
|
alias spa : std_logic is MB(10); -- skip if positive AC
|
103 |
|
|
alias sma : std_logic is MB(11); -- skip if negative AC
|
104 |
|
|
alias szs : std_logic_vector(0 to 2) is MB(12 to 14); -- skip if Zero Switches
|
105 |
|
|
alias szf : std_logic_vector(0 to 2) is MB(15 to 17); -- skip if Zero Flags
|
106 |
|
|
|
107 |
|
|
-- Opcodes -- loading group
|
108 |
|
|
constant op_and : opcode := o"02"; -- AC&=M
|
109 |
|
|
constant op_ior : opcode := o"04"; -- AC|=M
|
110 |
|
|
constant op_xor : opcode := o"06"; -- AC^=M
|
111 |
|
|
constant op_add : opcode := o"40"; -- AC+=M
|
112 |
|
|
constant op_sub : opcode := o"42"; -- AC-=M
|
113 |
|
|
constant op_lac : opcode := o"20"; -- load AC
|
114 |
|
|
constant op_lio : opcode := o"22"; -- load IO
|
115 |
|
|
constant op_sad : opcode := o"50"; -- skip if AC/=M(y)
|
116 |
|
|
constant op_sas : opcode := o"52"; -- skip if AC=M(y)
|
117 |
|
|
-- storing group
|
118 |
|
|
constant op_dac : opcode := o"24"; -- store AC
|
119 |
|
|
constant op_dap : opcode := o"26"; -- deposit address part of AC
|
120 |
|
|
constant op_dip : opcode := o"30"; -- deposit instruction part -- missing in Java emulator
|
121 |
|
|
constant op_dio : opcode := o"32"; -- deposit IO
|
122 |
|
|
constant op_dzm : opcode := o"34"; -- deposit zero
|
123 |
|
|
-- jumping group
|
124 |
|
|
constant op_skip: opcode := o"64"; -- adds 1 to IP; SAD and SAS, load group, also do this
|
125 |
|
|
constant op_skipi: opcode := o"65"; -- as above, but inverts condition
|
126 |
|
|
constant op_jmp : opcode := o"60"; -- jump
|
127 |
|
|
constant op_jsp : opcode := o"62"; -- jump and save PC
|
128 |
|
|
constant op_cal : opcode := o"16"; -- call subroutine
|
129 |
|
|
constant op_jda : opcode := o"17"; -- jump and deposit AC
|
130 |
|
|
-- immediate group
|
131 |
|
|
constant op_rotshiftl: opcode := o"66"; -- rotate/shift (IB is direction)
|
132 |
|
|
constant op_rotshiftr: opcode := o"67";
|
133 |
|
|
constant op_law : opcode := o"70"; -- load accumulator immediate
|
134 |
|
|
constant op_lawm: opcode := o"71";
|
135 |
|
|
constant op_opr : opcode := o"76"; -- operate group
|
136 |
|
|
-- miscellaneous
|
137 |
|
|
constant op_idx : opcode := o"44"; -- index - AC=++M[y]
|
138 |
|
|
constant op_isp : opcode := o"46"; -- same, and skip if positive
|
139 |
|
|
-- constant op_mul : opcode := o"54"; -- full multiply (PDP-1C)
|
140 |
|
|
-- constant op_div : opcode := o"56"; -- full divide (PDP-1C)
|
141 |
|
|
constant op_mus : opcode := o"54"; -- multiply step (PDP-1B)
|
142 |
|
|
constant op_dis : opcode := o"56"; -- divide step (PDP-1B)
|
143 |
|
|
constant op_xct : opcode := o"10"; -- execute
|
144 |
|
|
constant op_iot : opcode := o"73"; -- I/O transfer group, ib is wait for completion
|
145 |
|
|
constant op_iot_nw : opcode := o"72";
|
146 |
|
|
|
147 |
|
|
-- cycletype tracks the memory cycle reason
|
148 |
|
|
type cycle_type is (load_instruction, load_indirect, load_data, store_data);
|
149 |
|
|
signal cycletype : cycle_type;
|
150 |
|
|
signal cycle : integer range 0 to 9 := 0; -- 10 cycles per memory access cycle
|
151 |
|
|
-- NOTE: rotshift relies on this range!
|
152 |
|
|
constant cycle_setup_read: integer := 0;
|
153 |
|
|
constant cycle_read: integer := 2; -- memory is over-registered
|
154 |
|
|
constant cycle_execute: integer := 3;
|
155 |
|
|
constant cycle_setup_write: integer := 5;
|
156 |
|
|
constant cycle_wrote: integer := 6; -- not actually used
|
157 |
|
|
constant cycle_skip: integer := 9;
|
158 |
|
|
|
159 |
|
|
COMPONENT onecomplement_adder
|
160 |
|
|
PORT(
|
161 |
|
|
A : IN std_logic_vector(0 to 17);
|
162 |
|
|
B : IN std_logic_vector(0 to 17);
|
163 |
|
|
CI : IN std_logic;
|
164 |
|
|
SUM : OUT std_logic_vector(0 to 17);
|
165 |
|
|
OV : OUT std_logic;
|
166 |
|
|
CSUM : OUT std_logic_vector(0 to 17)
|
167 |
|
|
);
|
168 |
|
|
END COMPONENT;
|
169 |
|
|
|
170 |
|
|
COMPONENT pdp1rotshift
|
171 |
|
|
PORT(
|
172 |
|
|
ac : IN std_logic_vector(0 to 17);
|
173 |
|
|
io : IN std_logic_vector(0 to 17);
|
174 |
|
|
right : IN std_logic;
|
175 |
|
|
shift : IN std_logic;
|
176 |
|
|
words : IN std_logic_vector(0 to 1);
|
177 |
|
|
acout : OUT std_logic_vector(0 to 17);
|
178 |
|
|
ioout : OUT std_logic_vector(0 to 17)
|
179 |
|
|
);
|
180 |
|
|
END COMPONENT;
|
181 |
|
|
signal rotshift_ac, rotshift_io: word;
|
182 |
|
|
signal rotshift_right, rotshift_shift: std_logic;
|
183 |
|
|
signal rotshift_words: std_logic_vector(0 to 1);
|
184 |
|
|
signal add_a, add_b, add_sum, add_csum, dis_term: word;
|
185 |
|
|
signal add_ci, add_ov: std_logic;
|
186 |
|
|
signal skipcond: std_logic; -- skip condition for op_skip
|
187 |
|
|
signal ac_eq_mb : boolean;
|
188 |
|
|
-- purpose: value of a sense switch if n valid, else '1'
|
189 |
|
|
function sense_or_one (
|
190 |
|
|
n : integer; -- which sense flag
|
191 |
|
|
sense_sw : std_logic_vector(1 to 6)) -- sense switches
|
192 |
|
|
return std_logic is
|
193 |
|
|
begin -- sense_or_one
|
194 |
|
|
for i in 1 to 6 loop
|
195 |
|
|
if n=i then
|
196 |
|
|
return sense_sw(n);
|
197 |
|
|
end if;
|
198 |
|
|
end loop; -- i
|
199 |
|
|
return '1';
|
200 |
|
|
end sense_or_one;
|
201 |
|
|
begin
|
202 |
|
|
AWAKE <= '0' when IOWAIT or RESET='1' or HALT else '1';
|
203 |
|
|
|
204 |
|
|
Inst_pdp1rotshift: pdp1rotshift PORT MAP(
|
205 |
|
|
ac => AC, -- shift operation is read from memory
|
206 |
|
|
io => IO,
|
207 |
|
|
right => rotshift_right,
|
208 |
|
|
shift => rotshift_shift,
|
209 |
|
|
words => rotshift_words,
|
210 |
|
|
acout => rotshift_ac,
|
211 |
|
|
ioout => rotshift_io
|
212 |
|
|
);
|
213 |
|
|
with op select
|
214 |
|
|
rotshift_right <= '1' when op_mus,
|
215 |
|
|
'0' when op_dis,
|
216 |
|
|
M_DO(5) when others;
|
217 |
|
|
with op select
|
218 |
|
|
rotshift_shift <= '1' when op_mus,
|
219 |
|
|
'-' when op_dis,
|
220 |
|
|
M_DO(6) when others;
|
221 |
|
|
with op select
|
222 |
|
|
rotshift_words <= "11" when op_mus,
|
223 |
|
|
"11" when op_dis,
|
224 |
|
|
M_DO(7 to 8) when others;
|
225 |
|
|
|
226 |
|
|
Inst_onecomplement_adder: onecomplement_adder PORT MAP(
|
227 |
|
|
A => add_a,
|
228 |
|
|
B => add_b,
|
229 |
|
|
CI => add_ci,
|
230 |
|
|
SUM => add_sum,
|
231 |
|
|
OV => add_ov,
|
232 |
|
|
CSUM => add_csum
|
233 |
|
|
);
|
234 |
|
|
-- we use this same adder for addition, subtraction, indexing and multiply/divide step
|
235 |
|
|
with io(17) select
|
236 |
|
|
dis_term <= not MB when '1',
|
237 |
|
|
MB when '0',
|
238 |
|
|
(others=>'-') when others;
|
239 |
|
|
with op select
|
240 |
|
|
add_a <= o"000001" when op_idx | op_isp,
|
241 |
|
|
AC when op_add|op_mus|op_dis,
|
242 |
|
|
(others=>'-') when others;
|
243 |
|
|
with op select
|
244 |
|
|
add_b <= MB when op_add|op_idx|op_isp|op_mus,
|
245 |
|
|
not MB when op_sub,
|
246 |
|
|
dis_term when op_dis,
|
247 |
|
|
(others=>'-') when others;
|
248 |
|
|
add_ci <= '1' when op=op_dis and io(17)='0' else
|
249 |
|
|
'0';
|
250 |
|
|
|
251 |
|
|
M_DI <= MB;
|
252 |
|
|
ac_eq_mb <= AC=MB;
|
253 |
|
|
|
254 |
|
|
skipcond <= '1' when (
|
255 |
|
|
(sza='1' and AC=o"00_0000") or -- accumulator zero
|
256 |
|
|
(spa='1' and AC(0)='0') or -- accumulator positive
|
257 |
|
|
(sma='1' and AC(0)='1') or -- accumulator negative
|
258 |
|
|
(szo='1' and OV='0') or -- zero overflow
|
259 |
|
|
(spi='1' and IO(0)='0') or -- positive IO register
|
260 |
|
|
(MB(12 to 14)=o"7" and sw_sense=o"00") or -- all sense switches 0
|
261 |
|
|
(sense_or_one(to_integer(unsigned(MB(12 to 14))),sw_sense)='0') or
|
262 |
|
|
false -- so all lines above end with or
|
263 |
|
|
) else '0';
|
264 |
|
|
|
265 |
|
|
process (CLK, RESET)
|
266 |
|
|
variable idx: unsigned(0 to 17);
|
267 |
|
|
variable tmp_w: word;
|
268 |
|
|
begin
|
269 |
|
|
if RESET='1' then -- asynchronous reset
|
270 |
|
|
AC <= (others => '0');
|
271 |
|
|
IO <= (others => '0');
|
272 |
|
|
PC <= o"0000";
|
273 |
|
|
OV <= '0';
|
274 |
|
|
PF <= (others => '0');
|
275 |
|
|
-- memory control
|
276 |
|
|
MW <= '0';
|
277 |
|
|
MA <= o"0000";
|
278 |
|
|
-- reset our internal state
|
279 |
|
|
op <= o"00";
|
280 |
|
|
IOWAIT <= false;
|
281 |
|
|
IOT <= (others => '0');
|
282 |
|
|
cycletype <= load_instruction;
|
283 |
|
|
cycle <= cycle_setup_read;
|
284 |
|
|
elsif rising_edge(CLK) then
|
285 |
|
|
if IO_set='1' then
|
286 |
|
|
IO <= IO_in;
|
287 |
|
|
end if;
|
288 |
|
|
IOT <= (others => '0'); -- ordinarily no io trigger pulse
|
289 |
|
|
-- Advance the cycle, unless we're halted
|
290 |
|
|
if IOWAIT then
|
291 |
|
|
if IODONE='1' then
|
292 |
|
|
IOWAIT <= false;
|
293 |
|
|
end if;
|
294 |
|
|
end if;
|
295 |
|
|
-- pause during setup_read cycle for halt or iowait
|
296 |
|
|
if not ((IOWAIT or HALT) and (cycle=cycle_setup_read)) then
|
297 |
|
|
if cycle=9 then
|
298 |
|
|
cycle <= 0;
|
299 |
|
|
else
|
300 |
|
|
cycle <= cycle+1;
|
301 |
|
|
end if;
|
302 |
|
|
|
303 |
|
|
-- common logic for signals
|
304 |
|
|
if cycle=cycle_setup_write then
|
305 |
|
|
MW <= '1';
|
306 |
|
|
else
|
307 |
|
|
MW <= '0';
|
308 |
|
|
end if;
|
309 |
|
|
if (op=op_rotshiftr or op=op_rotshiftl) then
|
310 |
|
|
case cycle is
|
311 |
|
|
when 9 => -- don't shift on cycle 9
|
312 |
|
|
when others =>
|
313 |
|
|
if MB(9+cycle)='1' then
|
314 |
|
|
AC <= rotshift_ac; -- perform rotate/shift instructions
|
315 |
|
|
if IO_set/='1' then
|
316 |
|
|
IO <= rotshift_io;
|
317 |
|
|
end if;
|
318 |
|
|
end if;
|
319 |
|
|
end case;
|
320 |
|
|
end if;
|
321 |
|
|
|
322 |
|
|
case cycle is
|
323 |
|
|
when cycle_read => -- have read something from memory
|
324 |
|
|
MB <= M_DO;
|
325 |
|
|
case cycletype is
|
326 |
|
|
when load_instruction => -- it's our next instruction
|
327 |
|
|
op <= M_DO(0 to 5);
|
328 |
|
|
if op/=op_xct then -- indirect execution
|
329 |
9 |
yannv |
PC<=std_logic_vector(unsigned(PC)+1);
|
330 |
3 |
yannv |
end if;
|
331 |
|
|
when load_indirect => -- completing an indirect instruction
|
332 |
|
|
ib <= M_DO(5); -- update indirection bit
|
333 |
|
|
when others => -- data access cycle
|
334 |
|
|
end case;
|
335 |
|
|
when cycle_skip =>
|
336 |
|
|
if ((op=op_skip or op=op_skipi) and (skipcond xor ib)='1') or
|
337 |
|
|
(op=op_isp and cycletype=store_data and AC(0)='0') or
|
338 |
|
|
(op=op_sas and cycletype=load_data and ac_eq_mb) or
|
339 |
|
|
(op=op_sad and cycletype=load_data and not ac_eq_mb) or
|
340 |
|
|
FALSE then -- increase PC an extra time
|
341 |
9 |
yannv |
PC <= std_logic_vector(unsigned(PC)+1);
|
342 |
3 |
yannv |
end if;
|
343 |
|
|
if (op=op_skip or op=op_skipi) and szo='1' then
|
344 |
|
|
OV <= '0'; -- clear overflow after checking it
|
345 |
|
|
end if;
|
346 |
|
|
when cycle_setup_read => -- set up the memory address
|
347 |
|
|
case cycletype is
|
348 |
|
|
when load_instruction|load_indirect =>
|
349 |
|
|
case (op) is
|
350 |
|
|
-- memory loading instructions - will execute after loading data
|
351 |
|
|
when op_sas|op_sad|op_lac|op_lio|op_and|op_xor|op_ior|op_add|
|
352 |
|
|
op_sub|op_idx|op_isp|op_mus|op_dis =>
|
353 |
|
|
cycletype <= load_data;
|
354 |
|
|
MA <= y;
|
355 |
|
|
when op_xct => -- load specified instruction, do not change PC
|
356 |
|
|
cycletype <= load_instruction;
|
357 |
|
|
MA <= y;
|
358 |
|
|
when op_dac|op_dap|op_dip|op_dio|op_dzm => -- deposit instructions
|
359 |
|
|
cycletype <= store_data;
|
360 |
|
|
MA <= y;
|
361 |
|
|
when op_jmp|op_jsp|op_cal|op_jda => -- jumping instructions
|
362 |
|
|
if op/=op_jmp then
|
363 |
|
|
AC(0) <= OV;
|
364 |
|
|
AC(1 to 5) <= (others => '0'); -- extended PC
|
365 |
|
|
AC(6 to 17) <= std_logic_vector(PC);
|
366 |
|
|
end if;
|
367 |
|
|
if op=op_cal or op=op_jda then
|
368 |
|
|
if op=op_cal then
|
369 |
|
|
PC <= o"0101";
|
370 |
|
|
MA <= o"0100";
|
371 |
|
|
else
|
372 |
9 |
yannv |
PC <= std_logic_vector(unsigned(y)+1);
|
373 |
3 |
yannv |
MA <= y;
|
374 |
|
|
end if;
|
375 |
|
|
cycletype <= store_data;
|
376 |
|
|
else
|
377 |
|
|
MA <= y;
|
378 |
9 |
yannv |
PC <= y;
|
379 |
3 |
yannv |
cycletype <= load_instruction;
|
380 |
|
|
end if;
|
381 |
|
|
when op_skipi|op_rotshiftr|op_lawm|op_iot_nw =>
|
382 |
|
|
-- instructions with IB set, yet are immediate
|
383 |
|
|
MA <= std_logic_vector(PC);
|
384 |
|
|
cycletype <= load_instruction;
|
385 |
|
|
when others => -- most instructions are followed by the next instruction
|
386 |
|
|
if ib='1' then -- handle indirection bit
|
387 |
|
|
MA <= y;
|
388 |
|
|
cycletype <= load_indirect;
|
389 |
|
|
else
|
390 |
|
|
MA <= std_logic_vector(PC);
|
391 |
|
|
cycletype <= load_instruction;
|
392 |
|
|
end if;
|
393 |
|
|
end case; -- end of by-instruction memory setup
|
394 |
|
|
when others => -- have done data load/store
|
395 |
|
|
MA <= std_logic_vector(PC);
|
396 |
|
|
cycletype <= load_instruction;
|
397 |
|
|
end case;
|
398 |
|
|
when cycle_execute =>
|
399 |
|
|
-- execute common instructions - instr or operand has been read
|
400 |
|
|
case cycletype is
|
401 |
|
|
when load_instruction|load_indirect => -- new instr,
|
402 |
|
|
case op is
|
403 |
|
|
when op_law => -- load accumulator immediate
|
404 |
|
|
AC(0 to 5) <= (others => '0');
|
405 |
|
|
AC(6 to 17) <= y;
|
406 |
|
|
when op_lawm => -- load accumulator immediate negative
|
407 |
|
|
AC(0 to 5) <= (others => '1');
|
408 |
|
|
AC(6 to 17) <= not y;
|
409 |
|
|
when op_opr => -- operate group
|
410 |
|
|
if cli='1' and IO_set/='1' then IO <= o"00_0000"; end if;
|
411 |
|
|
if hlt='1' then HALT <= TRUE; end if; -- HALT
|
412 |
|
|
if cla='1' then
|
413 |
|
|
tmp_w := (others => '0');
|
414 |
|
|
else
|
415 |
|
|
tmp_w := AC;
|
416 |
|
|
end if;
|
417 |
|
|
if lat='1' then tmp_w := tmp_w or sw_testw; end if;
|
418 |
|
|
if lap='1' then -- or AC with PC and OV
|
419 |
|
|
tmp_w(6 to 17) := tmp_w(6 to 17) or std_logic_vector(PC);
|
420 |
|
|
tmp_w(0) := tmp_w(0) or OV;
|
421 |
|
|
end if;
|
422 |
|
|
if cma='1' then tmp_w := not tmp_w; end if;
|
423 |
|
|
AC <= tmp_w;
|
424 |
|
|
for j in 1 to 6 loop
|
425 |
|
|
if unsigned(flag_which)=j or unsigned(flag_which)=7 then
|
426 |
|
|
PF(j) <= flag_setto; -- set or clear program flags
|
427 |
|
|
end if;
|
428 |
|
|
end loop;
|
429 |
|
|
when op_rotshiftl|op_rotshiftr =>
|
430 |
|
|
-- handled in a separate block
|
431 |
|
|
when op_skip|op_skipi =>
|
432 |
|
|
-- inverted skip is not documented in 1960 manual.
|
433 |
|
|
-- it does occur in PDP-1B emulator and 1963 manual.
|
434 |
|
|
-- all skips are handled in skip phase, for no
|
435 |
|
|
-- real reason
|
436 |
|
|
when op_iot|op_iot_nw => -- I/O transfer
|
437 |
|
|
-- Java emulator Spacewar! binary supports:
|
438 |
|
|
-- typewriter sequence break input,
|
439 |
|
|
-- display on ordinary display (7),
|
440 |
|
|
-- reading of controls using undocumented device 11,
|
441 |
|
|
-- should load 4 bits of button controls in low and
|
442 |
|
|
-- high ends of the word
|
443 |
|
|
-- and does display 3 display commands (disabled point?).
|
444 |
|
|
-- It uses nowait+pulse, nowait, and wait+noop modes, so waiting
|
445 |
|
|
-- has to be implemented.
|
446 |
|
|
IODOPULSE <= MB(5) xor MB(6); -- generate an IODONE for this event
|
447 |
|
|
IOWAIT <= IB='1';
|
448 |
|
|
IOT(to_integer(unsigned(MB(12 to 17)))) <= '1';
|
449 |
|
|
|
450 |
|
|
when others =>
|
451 |
|
|
if ib='1' then -- likely turns into a valid op once IB is cleared
|
452 |
|
|
cycletype <= load_indirect;
|
453 |
|
|
end if;
|
454 |
|
|
end case; -- end of instruction check in execute phase
|
455 |
|
|
when load_data => -- loaded data for loading instruction
|
456 |
|
|
case (op) is
|
457 |
|
|
when op_sas|op_sad => -- handled in skip phase
|
458 |
|
|
when op_lac =>
|
459 |
|
|
AC <= M_DO;
|
460 |
|
|
when op_idx|op_isp =>
|
461 |
|
|
AC <= add_sum;
|
462 |
|
|
MB <= add_sum;
|
463 |
|
|
when op_lio =>
|
464 |
|
|
if IO_set/='1' then
|
465 |
|
|
IO <= MB;
|
466 |
|
|
end if;
|
467 |
|
|
when op_and =>
|
468 |
|
|
AC <= AC and MB;
|
469 |
|
|
when op_xor =>
|
470 |
|
|
AC <= AC xor MB;
|
471 |
|
|
when op_ior =>
|
472 |
|
|
AC <= AC or MB;
|
473 |
|
|
when op_add =>
|
474 |
|
|
AC <= add_csum; -- no negative 0
|
475 |
|
|
OV <= OV or add_ov;
|
476 |
|
|
when op_sub =>
|
477 |
|
|
AC <= add_sum;
|
478 |
|
|
OV <= OV or add_ov;
|
479 |
|
|
-- Multiply Step and Divide Step are the same opcode as Multiply and Divide.
|
480 |
|
|
-- There's no reasonable way for the CPU to determine which a program wants.
|
481 |
|
|
-- when op_mul => -- multiply originally takes 3-5 cycles. this one takes 2.
|
482 |
|
|
-- tmp_w := AC;
|
483 |
|
|
-- tmp_w2 := M_DO;
|
484 |
|
|
-- if tmp_w(0)='1' then tmp_w:=not tmp_w; end if;
|
485 |
|
|
-- if tmp_w2(0)='1' then tmp_w2:=not tmp_w2; end if;
|
486 |
|
|
-- product := tmp_w(1 to 17)*tmp_w2(1 to 17);
|
487 |
|
|
-- if AC(0)/=M_DO(0) then
|
488 |
|
|
-- product:=not product; -- preserve sign
|
489 |
|
|
-- AC(0)<='1';
|
490 |
|
|
-- IO(0)<='1';
|
491 |
|
|
-- else
|
492 |
|
|
-- AC(0)<='0';
|
493 |
|
|
-- IO(0)<='0';
|
494 |
|
|
-- end if;
|
495 |
|
|
-- AC(1 to 17)<=product(0 to 16);
|
496 |
|
|
-- IO(1 to 17)<=product(17 to 33);
|
497 |
|
|
when op_mus => -- PDP-1B multiply step
|
498 |
|
|
if IO(17)='1' then
|
499 |
|
|
AC <= add_csum;
|
500 |
|
|
end if;
|
501 |
|
|
-- continued in next cycle
|
502 |
|
|
when op_dis => -- PDP-1B divide step
|
503 |
|
|
AC <= rotshift_ac;
|
504 |
|
|
IO(0 to 16) <= rotshift_io(0 to 16);
|
505 |
|
|
IO(17) <= not AC(0);
|
506 |
|
|
-- continued in next cycle
|
507 |
|
|
when others =>
|
508 |
|
|
-- can't happen, see cases leading to load_data.
|
509 |
|
|
end case; -- end of load ops, execute cycle
|
510 |
|
|
when store_data =>
|
511 |
|
|
case op is
|
512 |
|
|
when op_dac =>
|
513 |
|
|
MB <= AC;
|
514 |
|
|
when op_dap =>
|
515 |
|
|
--MB(0 to 5) <= MB(0 to 5);
|
516 |
|
|
MB(6 to 17) <= AC(6 to 17);
|
517 |
|
|
when op_dip =>
|
518 |
|
|
MB(0 to 5) <= AC(0 to 5);
|
519 |
|
|
--MB(6 to 17) <= MB(6 to 17);
|
520 |
|
|
when op_dio =>
|
521 |
|
|
MB <= IO;
|
522 |
|
|
when op_dzm =>
|
523 |
|
|
MB <= o"00_0000";
|
524 |
|
|
when others => -- others should not occur
|
525 |
|
|
end case;
|
526 |
|
|
end case; -- end of cycletype cases for execute cycle
|
527 |
|
|
-- FIXME more to fix here
|
528 |
|
|
when cycle_execute+1 =>
|
529 |
|
|
case op is -- multiply, divide and I/O use two stages
|
530 |
|
|
when op_mus =>
|
531 |
|
|
AC <= rotshift_ac;
|
532 |
|
|
if IO_set/='1' then
|
533 |
|
|
IO <= rotshift_io;
|
534 |
|
|
end if;
|
535 |
|
|
when op_dis =>
|
536 |
|
|
AC <= add_csum;
|
537 |
|
|
when others => -- note: rotshift uses 9 cycles
|
538 |
|
|
end case;
|
539 |
|
|
when others =>
|
540 |
|
|
end case;
|
541 |
|
|
end if;
|
542 |
|
|
end if;
|
543 |
|
|
end process;
|
544 |
|
|
end Behavioral;
|
545 |
|
|
|