]>
Commit | Line | Data |
---|---|---|
1 | -- | |
2 | -- Z80 compatible microprocessor core | |
3 | -- | |
4 | -- Version : 0247 | |
5 | -- | |
6 | -- Copyright (c) 2001-2002 Daniel Wallner (jesus@opencores.org) | |
7 | -- | |
8 | -- All rights reserved | |
9 | -- | |
10 | -- Redistribution and use in source and synthezised forms, with or without | |
11 | -- modification, are permitted provided that the following conditions are met: | |
12 | -- | |
13 | -- Redistributions of source code must retain the above copyright notice, | |
14 | -- this list of conditions and the following disclaimer. | |
15 | -- | |
16 | -- Redistributions in synthesized form must reproduce the above copyright | |
17 | -- notice, this list of conditions and the following disclaimer in the | |
18 | -- documentation and/or other materials provided with the distribution. | |
19 | -- | |
20 | -- Neither the name of the author nor the names of other contributors may | |
21 | -- be used to endorse or promote products derived from this software without | |
22 | -- specific prior written permission. | |
23 | -- | |
24 | -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
25 | -- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | |
26 | -- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
27 | -- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE | |
28 | -- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
29 | -- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
30 | -- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
31 | -- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
32 | -- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
33 | -- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
34 | -- POSSIBILITY OF SUCH DAMAGE. | |
35 | -- | |
36 | -- Please report bugs to the author, but before you do so, please | |
37 | -- make sure that this is not a derivative work and that | |
38 | -- you have the latest version of this file. | |
39 | -- | |
40 | -- The latest version of this file can be found at: | |
41 | -- http://www.opencores.org/cvsweb.shtml/t80/ | |
42 | -- | |
43 | -- Limitations : | |
44 | -- | |
45 | -- File history : | |
46 | -- | |
47 | -- 0214 : Fixed mostly flags, only the block instructions now fail the zex regression test | |
48 | -- | |
49 | -- 0238 : Fixed zero flag for 16 bit SBC and ADC | |
50 | -- | |
51 | -- 0240 : Added GB operations | |
52 | -- | |
53 | -- 0242 : Cleanup | |
54 | -- | |
55 | -- 0247 : Cleanup | |
56 | -- | |
57 | ||
58 | library IEEE; | |
59 | use IEEE.std_logic_1164.all; | |
60 | use IEEE.numeric_std.all; | |
61 | ||
62 | entity T80_ALU is | |
63 | generic( | |
64 | Mode : integer := 0; | |
65 | Flag_C : integer := 0; | |
66 | Flag_N : integer := 1; | |
67 | Flag_P : integer := 2; | |
68 | Flag_X : integer := 3; | |
69 | Flag_H : integer := 4; | |
70 | Flag_Y : integer := 5; | |
71 | Flag_Z : integer := 6; | |
72 | Flag_S : integer := 7 | |
73 | ); | |
74 | port( | |
75 | Arith16 : in std_logic; | |
76 | Z16 : in std_logic; | |
77 | ALU_Op : in std_logic_vector(3 downto 0); | |
78 | IR : in std_logic_vector(5 downto 0); | |
79 | ISet : in std_logic_vector(1 downto 0); | |
80 | BusA : in std_logic_vector(7 downto 0); | |
81 | BusB : in std_logic_vector(7 downto 0); | |
82 | F_In : in std_logic_vector(7 downto 0); | |
83 | Q : out std_logic_vector(7 downto 0); | |
84 | F_Out : out std_logic_vector(7 downto 0) | |
85 | ); | |
86 | end T80_ALU; | |
87 | ||
88 | architecture rtl of T80_ALU is | |
89 | ||
90 | procedure AddSub(A : std_logic_vector; | |
91 | B : std_logic_vector; | |
92 | Sub : std_logic; | |
93 | Carry_In : std_logic; | |
94 | signal Res : out std_logic_vector; | |
95 | signal Carry : out std_logic) is | |
96 | variable B_i : unsigned(A'length - 1 downto 0); | |
97 | variable Res_i : unsigned(A'length + 1 downto 0); | |
98 | begin | |
99 | if Sub = '1' then | |
100 | B_i := not unsigned(B); | |
101 | else | |
102 | B_i := unsigned(B); | |
103 | end if; | |
104 | Res_i := unsigned("0" & A & Carry_In) + unsigned("0" & B_i & "1"); | |
105 | Carry <= Res_i(A'length + 1); | |
106 | Res <= std_logic_vector(Res_i(A'length downto 1)); | |
107 | end; | |
108 | ||
109 | -- AddSub variables (temporary signals) | |
110 | signal UseCarry : std_logic; | |
111 | signal Carry7_v : std_logic; | |
112 | signal Overflow_v : std_logic; | |
113 | signal HalfCarry_v : std_logic; | |
114 | signal Carry_v : std_logic; | |
115 | signal Q_v : std_logic_vector(7 downto 0); | |
116 | ||
117 | signal BitMask : std_logic_vector(7 downto 0); | |
118 | ||
119 | begin | |
120 | ||
121 | with IR(5 downto 3) select BitMask <= "00000001" when "000", | |
122 | "00000010" when "001", | |
123 | "00000100" when "010", | |
124 | "00001000" when "011", | |
125 | "00010000" when "100", | |
126 | "00100000" when "101", | |
127 | "01000000" when "110", | |
128 | "10000000" when others; | |
129 | ||
130 | UseCarry <= not ALU_Op(2) and ALU_Op(0); | |
131 | AddSub(BusA(3 downto 0), BusB(3 downto 0), ALU_Op(1), ALU_Op(1) xor (UseCarry and F_In(Flag_C)), Q_v(3 downto 0), HalfCarry_v); | |
132 | AddSub(BusA(6 downto 4), BusB(6 downto 4), ALU_Op(1), HalfCarry_v, Q_v(6 downto 4), Carry7_v); | |
133 | AddSub(BusA(7 downto 7), BusB(7 downto 7), ALU_Op(1), Carry7_v, Q_v(7 downto 7), Carry_v); | |
134 | OverFlow_v <= Carry_v xor Carry7_v; | |
135 | ||
136 | process (Arith16, ALU_OP, F_In, BusA, BusB, IR, Q_v, Carry_v, HalfCarry_v, OverFlow_v, BitMask, ISet, Z16) | |
137 | variable Q_t : std_logic_vector(7 downto 0); | |
138 | variable DAA_Q : unsigned(8 downto 0); | |
139 | begin | |
140 | Q_t := "--------"; | |
141 | F_Out <= F_In; | |
142 | DAA_Q := "---------"; | |
143 | case ALU_Op is | |
144 | when "0000" | "0001" | "0010" | "0011" | "0100" | "0101" | "0110" | "0111" => | |
145 | F_Out(Flag_N) <= '0'; | |
146 | F_Out(Flag_C) <= '0'; | |
147 | case ALU_OP(2 downto 0) is | |
148 | when "000" | "001" => -- ADD, ADC | |
149 | Q_t := Q_v; | |
150 | F_Out(Flag_C) <= Carry_v; | |
151 | F_Out(Flag_H) <= HalfCarry_v; | |
152 | F_Out(Flag_P) <= OverFlow_v; | |
153 | when "010" | "011" | "111" => -- SUB, SBC, CP | |
154 | Q_t := Q_v; | |
155 | F_Out(Flag_N) <= '1'; | |
156 | F_Out(Flag_C) <= not Carry_v; | |
157 | F_Out(Flag_H) <= not HalfCarry_v; | |
158 | F_Out(Flag_P) <= OverFlow_v; | |
159 | when "100" => -- AND | |
160 | Q_t(7 downto 0) := BusA and BusB; | |
161 | F_Out(Flag_H) <= '1'; | |
162 | when "101" => -- XOR | |
163 | Q_t(7 downto 0) := BusA xor BusB; | |
164 | F_Out(Flag_H) <= '0'; | |
165 | when others => -- OR "110" | |
166 | Q_t(7 downto 0) := BusA or BusB; | |
167 | F_Out(Flag_H) <= '0'; | |
168 | end case; | |
169 | if ALU_Op(2 downto 0) = "111" then -- CP | |
170 | F_Out(Flag_X) <= BusB(3); | |
171 | F_Out(Flag_Y) <= BusB(5); | |
172 | else | |
173 | F_Out(Flag_X) <= Q_t(3); | |
174 | F_Out(Flag_Y) <= Q_t(5); | |
175 | end if; | |
176 | if Q_t(7 downto 0) = "00000000" then | |
177 | F_Out(Flag_Z) <= '1'; | |
178 | if Z16 = '1' then | |
179 | F_Out(Flag_Z) <= F_In(Flag_Z); -- 16 bit ADC,SBC | |
180 | end if; | |
181 | else | |
182 | F_Out(Flag_Z) <= '0'; | |
183 | end if; | |
184 | F_Out(Flag_S) <= Q_t(7); | |
185 | case ALU_Op(2 downto 0) is | |
186 | when "000" | "001" | "010" | "011" | "111" => -- ADD, ADC, SUB, SBC, CP | |
187 | when others => | |
188 | F_Out(Flag_P) <= not (Q_t(0) xor Q_t(1) xor Q_t(2) xor Q_t(3) xor | |
189 | Q_t(4) xor Q_t(5) xor Q_t(6) xor Q_t(7)); | |
190 | end case; | |
191 | if Arith16 = '1' then | |
192 | F_Out(Flag_S) <= F_In(Flag_S); | |
193 | F_Out(Flag_Z) <= F_In(Flag_Z); | |
194 | F_Out(Flag_P) <= F_In(Flag_P); | |
195 | end if; | |
196 | when "1100" => | |
197 | -- DAA | |
198 | F_Out(Flag_H) <= F_In(Flag_H); | |
199 | F_Out(Flag_C) <= F_In(Flag_C); | |
200 | DAA_Q(7 downto 0) := unsigned(BusA); | |
201 | DAA_Q(8) := '0'; | |
202 | if F_In(Flag_N) = '0' then | |
203 | -- After addition | |
204 | -- Alow > 9 or H = 1 | |
205 | if DAA_Q(3 downto 0) > 9 or F_In(Flag_H) = '1' then | |
206 | if (DAA_Q(3 downto 0) > 9) then | |
207 | F_Out(Flag_H) <= '1'; | |
208 | else | |
209 | F_Out(Flag_H) <= '0'; | |
210 | end if; | |
211 | DAA_Q := DAA_Q + 6; | |
212 | end if; | |
213 | -- new Ahigh > 9 or C = 1 | |
214 | if DAA_Q(8 downto 4) > 9 or F_In(Flag_C) = '1' then | |
215 | DAA_Q := DAA_Q + 96; -- 0x60 | |
216 | end if; | |
217 | else | |
218 | -- After subtraction | |
219 | if DAA_Q(3 downto 0) > 9 or F_In(Flag_H) = '1' then | |
220 | if DAA_Q(3 downto 0) > 5 then | |
221 | F_Out(Flag_H) <= '0'; | |
222 | end if; | |
223 | DAA_Q(7 downto 0) := DAA_Q(7 downto 0) - 6; | |
224 | end if; | |
225 | if unsigned(BusA) > 153 or F_In(Flag_C) = '1' then | |
226 | DAA_Q := DAA_Q - 352; -- 0x160 | |
227 | end if; | |
228 | end if; | |
229 | F_Out(Flag_X) <= DAA_Q(3); | |
230 | F_Out(Flag_Y) <= DAA_Q(5); | |
231 | F_Out(Flag_C) <= F_In(Flag_C) or DAA_Q(8); | |
232 | Q_t := std_logic_vector(DAA_Q(7 downto 0)); | |
233 | if DAA_Q(7 downto 0) = "00000000" then | |
234 | F_Out(Flag_Z) <= '1'; | |
235 | else | |
236 | F_Out(Flag_Z) <= '0'; | |
237 | end if; | |
238 | F_Out(Flag_S) <= DAA_Q(7); | |
239 | F_Out(Flag_P) <= not (DAA_Q(0) xor DAA_Q(1) xor DAA_Q(2) xor DAA_Q(3) xor | |
240 | DAA_Q(4) xor DAA_Q(5) xor DAA_Q(6) xor DAA_Q(7)); | |
241 | when "1101" | "1110" => | |
242 | -- RLD, RRD | |
243 | Q_t(7 downto 4) := BusA(7 downto 4); | |
244 | if ALU_Op(0) = '1' then | |
245 | Q_t(3 downto 0) := BusB(7 downto 4); | |
246 | else | |
247 | Q_t(3 downto 0) := BusB(3 downto 0); | |
248 | end if; | |
249 | F_Out(Flag_H) <= '0'; | |
250 | F_Out(Flag_N) <= '0'; | |
251 | F_Out(Flag_X) <= Q_t(3); | |
252 | F_Out(Flag_Y) <= Q_t(5); | |
253 | if Q_t(7 downto 0) = "00000000" then | |
254 | F_Out(Flag_Z) <= '1'; | |
255 | else | |
256 | F_Out(Flag_Z) <= '0'; | |
257 | end if; | |
258 | F_Out(Flag_S) <= Q_t(7); | |
259 | F_Out(Flag_P) <= not (Q_t(0) xor Q_t(1) xor Q_t(2) xor Q_t(3) xor | |
260 | Q_t(4) xor Q_t(5) xor Q_t(6) xor Q_t(7)); | |
261 | when "1001" => | |
262 | -- BIT | |
263 | Q_t(7 downto 0) := BusB and BitMask; | |
264 | F_Out(Flag_S) <= Q_t(7); | |
265 | if Q_t(7 downto 0) = "00000000" then | |
266 | F_Out(Flag_Z) <= '1'; | |
267 | F_Out(Flag_P) <= '1'; | |
268 | else | |
269 | F_Out(Flag_Z) <= '0'; | |
270 | F_Out(Flag_P) <= '0'; | |
271 | end if; | |
272 | F_Out(Flag_H) <= '1'; | |
273 | F_Out(Flag_N) <= '0'; | |
274 | F_Out(Flag_X) <= '0'; | |
275 | F_Out(Flag_Y) <= '0'; | |
276 | if IR(2 downto 0) /= "110" then | |
277 | F_Out(Flag_X) <= BusB(3); | |
278 | F_Out(Flag_Y) <= BusB(5); | |
279 | end if; | |
280 | when "1010" => | |
281 | -- SET | |
282 | Q_t(7 downto 0) := BusB or BitMask; | |
283 | when "1011" => | |
284 | -- RES | |
285 | Q_t(7 downto 0) := BusB and not BitMask; | |
286 | when "1000" => | |
287 | -- ROT | |
288 | case IR(5 downto 3) is | |
289 | when "000" => -- RLC | |
290 | Q_t(7 downto 1) := BusA(6 downto 0); | |
291 | Q_t(0) := BusA(7); | |
292 | F_Out(Flag_C) <= BusA(7); | |
293 | when "010" => -- RL | |
294 | Q_t(7 downto 1) := BusA(6 downto 0); | |
295 | Q_t(0) := F_In(Flag_C); | |
296 | F_Out(Flag_C) <= BusA(7); | |
297 | when "001" => -- RRC | |
298 | Q_t(6 downto 0) := BusA(7 downto 1); | |
299 | Q_t(7) := BusA(0); | |
300 | F_Out(Flag_C) <= BusA(0); | |
301 | when "011" => -- RR | |
302 | Q_t(6 downto 0) := BusA(7 downto 1); | |
303 | Q_t(7) := F_In(Flag_C); | |
304 | F_Out(Flag_C) <= BusA(0); | |
305 | when "100" => -- SLA | |
306 | Q_t(7 downto 1) := BusA(6 downto 0); | |
307 | Q_t(0) := '0'; | |
308 | F_Out(Flag_C) <= BusA(7); | |
309 | when "110" => -- SLL (Undocumented) / SWAP | |
310 | if Mode = 3 then | |
311 | Q_t(7 downto 4) := BusA(3 downto 0); | |
312 | Q_t(3 downto 0) := BusA(7 downto 4); | |
313 | F_Out(Flag_C) <= '0'; | |
314 | else | |
315 | Q_t(7 downto 1) := BusA(6 downto 0); | |
316 | Q_t(0) := '1'; | |
317 | F_Out(Flag_C) <= BusA(7); | |
318 | end if; | |
319 | when "101" => -- SRA | |
320 | Q_t(6 downto 0) := BusA(7 downto 1); | |
321 | Q_t(7) := BusA(7); | |
322 | F_Out(Flag_C) <= BusA(0); | |
323 | when others => -- SRL | |
324 | Q_t(6 downto 0) := BusA(7 downto 1); | |
325 | Q_t(7) := '0'; | |
326 | F_Out(Flag_C) <= BusA(0); | |
327 | end case; | |
328 | F_Out(Flag_H) <= '0'; | |
329 | F_Out(Flag_N) <= '0'; | |
330 | F_Out(Flag_X) <= Q_t(3); | |
331 | F_Out(Flag_Y) <= Q_t(5); | |
332 | F_Out(Flag_S) <= Q_t(7); | |
333 | if Q_t(7 downto 0) = "00000000" then | |
334 | F_Out(Flag_Z) <= '1'; | |
335 | else | |
336 | F_Out(Flag_Z) <= '0'; | |
337 | end if; | |
338 | F_Out(Flag_P) <= not (Q_t(0) xor Q_t(1) xor Q_t(2) xor Q_t(3) xor | |
339 | Q_t(4) xor Q_t(5) xor Q_t(6) xor Q_t(7)); | |
340 | if ISet = "00" then | |
341 | F_Out(Flag_P) <= F_In(Flag_P); | |
342 | F_Out(Flag_S) <= F_In(Flag_S); | |
343 | F_Out(Flag_Z) <= F_In(Flag_Z); | |
344 | end if; | |
345 | when others => | |
346 | null; | |
347 | end case; | |
348 | Q <= Q_t; | |
349 | end process; | |
350 | ||
351 | end; |