Apple_II_vhdl/src/cpu/t65/T65_MCode.vhd

1240 lines
45 KiB
VHDL

-- ****
-- T65(b) core. In an effort to merge and maintain bug fixes ....
--
-- See list of changes in T65 top file (T65.vhd)...
--
-- ****
-- 65xx compatible microprocessor core
--
-- FPGAARCADE SVN: $Id: T65_MCode.vhd 1234 2015-02-28 20:14:50Z wolfgang.scherr $
--
-- Copyright (c) 2002...2015
-- Daniel Wallner (jesus <at> opencores <dot> org)
-- Mike Johnson (mikej <at> fpgaarcade <dot> com)
-- Wolfgang Scherr (WoS <at> pin4 <dot> at>
-- Morten Leikvoll ()
--
-- All rights reserved
--
-- Redistribution and use in source and synthezised forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- Redistributions in synthesized form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- Neither the name of the author nor the names of other contributors may
-- be used to endorse or promote products derived from this software without
-- specific prior written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--
-- Please report bugs to the author(s), but before you do so, please
-- make sure that this is not a derivative work and that
-- you have the latest version of this file.
--
-- Limitations :
-- See in T65 top file (T65.vhd)...
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use ieee.std_logic_unsigned.all;
use work.T65_Pack.all;
entity T65_MCode is
port(
Mode : in std_logic_vector(1 downto 0); -- "00" => 6502, "01" => 65C02, "10" => 65816
IR : in std_logic_vector(7 downto 0);
MCycle : in T_Lcycle;
P : in std_logic_vector(7 downto 0);
LCycle : out T_Lcycle;
ALU_Op : out T_ALU_Op;
Set_BusA_To : out T_Set_BusA_To; -- DI,A,X,Y,S,P,DA,DAO,DAX,AAX
Set_Addr_To : out T_Set_Addr_To; -- PC Adder,S,AD,BA
Write_Data : out T_Write_Data; -- DL,A,X,Y,S,P,PCL,PCH,AX,AXB,XB,YB
Jump : out std_logic_vector(1 downto 0); -- PC,++,DIDL,Rel
BAAdd : out std_logic_vector(1 downto 0); -- None,DB Inc,BA Add,BA Adj
BreakAtNA : out std_logic;
ADAdd : out std_logic;
AddY : out std_logic;
PCAdd : out std_logic;
Inc_S : out std_logic;
Dec_S : out std_logic;
LDA : out std_logic;
LDP : out std_logic;
LDX : out std_logic;
LDY : out std_logic;
LDS : out std_logic;
LDDI : out std_logic;
LDALU : out std_logic;
LDAD : out std_logic;
LDBAL : out std_logic;
LDBAH : out std_logic;
SaveP : out std_logic;
Write : out std_logic
);
end T65_MCode;
architecture rtl of T65_MCode is
signal Branch : std_logic;
signal ALUmore:std_logic;
begin
with IR(7 downto 5) select
Branch <= not P(Flag_N) when "000",
P(Flag_N) when "001",
not P(Flag_V) when "010",
P(Flag_V) when "011",
not P(Flag_C) when "100",
P(Flag_C) when "101",
not P(Flag_Z) when "110",
P(Flag_Z) when others;
process (IR, MCycle, P, Branch, Mode)
begin
lCycle <= Cycle_1;
Set_BusA_To <= Set_BusA_To_ABC;
Set_Addr_To <= Set_Addr_To_PBR;
Write_Data <= Write_Data_DL;
Jump <= (others => '0');
BAAdd <= "00";
BreakAtNA <= '0';
ADAdd <= '0';
PCAdd <= '0';
Inc_S <= '0';
Dec_S <= '0';
LDA <= '0';
LDP <= '0';
LDX <= '0';
LDY <= '0';
LDS <= '0';
LDDI <= '0';
LDALU <= '0';
LDAD <= '0';
LDBAL <= '0';
LDBAH <= '0';
SaveP <= '0';
Write <= '0';
AddY <= '0';
ALUmore <= '0';
case IR(7 downto 5) is
when "100" => -- covers $8x,$9x
case IR(1 downto 0) is
when "00" => -- IR: $80,$84,$88,$8C,$90,$94,$98,$9C
Set_BusA_To <= Set_BusA_To_Y;
if IR(4 downto 2)="111" then -- SYA ($9C)
Write_Data <= Write_Data_YB;
else
Write_Data <= Write_Data_Y;
end if;
when "10" => -- IR: $82,$86,$8A,$8E,$92,$96,$9A,$9E
Set_BusA_To <= Set_BusA_To_X;
if IR(4 downto 2)="111" then -- SXA ($9E)
Write_Data <= Write_Data_XB;
else
Write_Data <= Write_Data_X;
end if;
when "11" => -- IR: $83,$87,$8B,$8F,$93,$97,$9B,$9F
if IR(4 downto 2)="110" then -- SHS ($9B)
Set_BusA_To <= Set_BusA_To_AAX;
LDS <= '1';
else
Set_BusA_To <= Set_BusA_To_ABC;
end if;
if IR(4 downto 2)="111" or IR(4 downto 2)="110" or IR(4 downto 2)="100" then -- SHA ($9F, $93), SHS ($9B)
Write_Data <= Write_Data_AXB;
else
Write_Data <= Write_Data_AX;
end if;
when others => -- IR: $81,$85,$89,$8D,$91,$95,$99,$9D
Write_Data <= Write_Data_ABC;
end case;
when "101" => -- covers $Ax,$Bx
Set_BusA_To <= Set_BusA_To_DI;
case IR(1 downto 0) is
when "00" => -- IR: $A0,$A4,$A8,$AC,$B0,$B4,$B8,$BC
if IR(4) /= '1' or IR(2) /= '0' then--only for $A0,$A4,$A8,$AC or $B4,$BC
LDY <= '1';
end if;
when "01" => -- IR: $A1,$A5,$A9,$AD,$B1,$B5,$B9,$BD
LDA <= '1';
when "10" => -- IR: $A2,$A6,$AA,$AE,$B2,$B6,$BA,$BE
LDX <= '1';
when others => -- IR: $A3,$A7,$AB,$AF,$B3,$B7,$BB,$BF (undoc)
LDX <= '1';
LDA <= '1';
if IR(4 downto 2)="110" then -- LAS (BB)
Set_BusA_To <= Set_BusA_To_S;
LDS <= '1';
end if;
end case;
when "110" => -- covers $Cx,$Dx
case IR(1 downto 0) is
when "00" => -- IR: $C0,$C4,$C8,$CC,$D0,$D4,$D8,$DC
if IR(4) = '0' then--only for $Cx
LDY <= '1';
end if;
Set_BusA_To <= Set_BusA_To_Y;
when others => -- IR: $C1,$C5,$C9,$CD,$D1,$D5,$D9,$DD, $C2,$C6,$CA,$CE,$D2,$D6,$DA,$DE, $C3,$C7,$CB,$CF,$D3,$D7,$DB,$DF
Set_BusA_To <= Set_BusA_To_ABC;
end case;
when "111" => -- covers $Ex,$Fx
case IR(1 downto 0) is
when "00" => -- IR: $E0,$E4,$E8,$EC,$F0,$F4,$F8,$FC
if IR(4) = '0' then -- only $Ex
LDX <= '1';
end if;
Set_BusA_To <= Set_BusA_To_X;
when others => -- IR: $E1,$E5,$E9,$ED,$F1,$F5,$F9,$FD, $E2,$E6,$EA,$EE,$F2,$F6,$FA,$FE, $E3,$E7,$EB,$EF,$F3,$F7,$FB,$FF
Set_BusA_To <= Set_BusA_To_ABC;
end case;
when others =>
end case;
if IR(7 downto 6) /= "10" and IR(1) = '1' and (mode="00" or IR(0)='0') then--covers $0x-$7x, $Cx-$Fx x=2,3,6,7,A,B,E,F, for 6502 undocs
if IR=x"eb" then
Set_BusA_To <= Set_BusA_To_ABC; -- alternate SBC ($EB)
else
Set_BusA_To <= Set_BusA_To_DI;
end if;
end if;
case IR(4 downto 0) is
-- IR: $00,$20,$40,$60,$80,$A0,$C0,$E0
-- $08,$28,$48,$68,$88,$A8,$C8,$E8
-- $0A,$2A,$4A,$6A,$8A,$AA,$CA,$EA
-- $18,$38,$58,$78,$98,$B8,$D8,$F8
-- $1A,$3A,$5A,$7A,$9A,$BA,$DA,$FA
when "00000" | "01000" | "01010" | "11000" | "11010" =>
-- Implied
case IR is
when x"00" =>
-- BRK ($00)
lCycle <= Cycle_6;
case MCycle is
when Cycle_1 =>
Set_Addr_To <= Set_Addr_To_SP;
Write_Data <= Write_Data_PCH;
Write <= '1';
when Cycle_2 =>
Dec_S <= '1';
Set_Addr_To <= Set_Addr_To_SP;
Write_Data <= Write_Data_PCL;
Write <= '1';
when Cycle_3 =>
Dec_S <= '1';
Set_Addr_To <= Set_Addr_To_SP;
Write_Data <= Write_Data_P;
Write <= '1';
when Cycle_4 =>
Dec_S <= '1';
Set_Addr_To <= Set_Addr_To_BA;
when Cycle_5 =>
LDDI <= '1';
Set_Addr_To <= Set_Addr_To_BA;
when Cycle_6 =>
Jump <= "10";
when others =>
end case;
when x"20" => -- JSR ($20)
lCycle <= Cycle_5;
case MCycle is
when Cycle_1 =>
Jump <= "01";
LDDI <= '1';
Set_Addr_To <= Set_Addr_To_SP;
when Cycle_2 =>
Set_Addr_To <= Set_Addr_To_SP;
Write_Data <= Write_Data_PCH;
Write <= '1';
when Cycle_3 =>
Dec_S <= '1';
Set_Addr_To <= Set_Addr_To_SP;
Write_Data <= Write_Data_PCL;
Write <= '1';
when Cycle_4 =>
Dec_S <= '1';
when Cycle_5 =>
Jump <= "10";
when others =>
end case;
when x"40" => -- RTI ($40)
lCycle <= Cycle_5;
case MCycle is
when Cycle_1 =>
Set_Addr_To <= Set_Addr_To_SP;
when Cycle_2 =>
Inc_S <= '1';
Set_Addr_To <= Set_Addr_To_SP;
when Cycle_3 =>
Inc_S <= '1';
Set_Addr_To <= Set_Addr_To_SP;
Set_BusA_To <= Set_BusA_To_DI;
when Cycle_4 =>
LDP <= '1';
Inc_S <= '1';
LDDI <= '1';
Set_Addr_To <= Set_Addr_To_SP;
when Cycle_5 =>
Jump <= "10";
when others =>
end case;
when x"60" => -- RTS ($60)
lCycle <= Cycle_5;
case MCycle is
when Cycle_1 =>
Set_Addr_To <= Set_Addr_To_SP;
when Cycle_2 =>
Inc_S <= '1';
Set_Addr_To <= Set_Addr_To_SP;
when Cycle_3 =>
Inc_S <= '1';
LDDI <= '1';
Set_Addr_To <= Set_Addr_To_SP;
when Cycle_4 =>
Jump <= "10";
when Cycle_5 =>
Jump <= "01";
when others =>
end case;
when x"08" | x"48" | x"5a" | x"da" => -- PHP, PHA, PHY*, PHX* ($08,$48,$5A,$DA)
lCycle <= Cycle_2;
if Mode = "00" and IR(1) = '1' then--2 cycle nop
lCycle <= Cycle_1;
end if;
case MCycle is
when Cycle_1 =>
if mode/="00" or IR(1)='0' then --wrong on 6502
Write <= '1';
case IR(7 downto 4) is
when "0000" =>
Write_Data <= Write_Data_P;
when "0100" =>
Write_Data <= Write_Data_ABC;
when "0101" =>
if Mode /= "00" then
Write_Data <= Write_Data_Y;
else
Write <= '0';
end if;
when "1101" =>
if Mode /= "00" then
Write_Data <= Write_Data_X;
else
Write <= '0';
end if;
when others =>
end case;
Set_Addr_To <= Set_Addr_To_SP;
end if;
when Cycle_2 =>
Dec_S <= '1';
when others =>
end case;
when x"28" | x"68" | x"7a" | x"fa" => -- PLP, PLA, PLY*, PLX* ($28,$68,$7A,$FA)
lCycle <= Cycle_3;
if Mode = "00" and IR(1) = '1' then--2 cycle nop
lCycle <= Cycle_1;
end if;
case IR(7 downto 4) is
when "0010" =>--plp
LDP <= '1';
when "0110" =>--pla
LDA <= '1';
when "0111" =>--ply not for 6502
if Mode /= "00" then
LDY <= '1';
end if;
when "1111" =>--plx not for 6502
if Mode /= "00" then
LDX <= '1';
end if;
when others =>
end case;
case MCycle is
when Cycle_sync =>
if Mode /= "00" or IR(1) = '0' then--wrong on 6502
SaveP <= '1';
end if;
when Cycle_1 =>
if Mode /= "00" or IR(1) = '0' then--wrong on 6502
Set_Addr_To <= Set_Addr_To_SP;
LDP <= '0';
end if;
when Cycle_2 =>
Inc_S <= '1';
Set_Addr_To <= Set_Addr_To_SP;
LDP <= '0';
when Cycle_3 =>
Set_BusA_To <= Set_BusA_To_DI;
when others =>
end case;
when x"a0" | x"c0" | x"e0" => -- LDY, CPY, CPX ($A0,$C0,$E0)
-- Immediate
case MCycle is
when Cycle_sync =>
when Cycle_1 =>
Jump <= "01";
when others =>
end case;
when x"88" => -- DEY ($88)
LDY <= '1';
case MCycle is
when Cycle_sync =>
when Cycle_1 =>
Set_BusA_To <= Set_BusA_To_Y;
when others =>
end case;
when x"ca" => -- DEX ($CA)
LDX <= '1';
case MCycle is
when Cycle_sync =>
when Cycle_1 =>
Set_BusA_To <= Set_BusA_To_X;
when others =>
end case;
when x"1a" | x"3a" => -- INC*, DEC* ($1A,$3A)
if Mode /= "00" then
LDA <= '1'; -- A
else
lCycle <= Cycle_1;--undoc 2 cycle nop
end if;
case MCycle is
when Cycle_sync =>
when Cycle_1 =>
Set_BusA_To <= Set_BusA_To_S;
when others =>
end case;
when x"0a" | x"2a" | x"4a" | x"6a" => -- ASL, ROL, LSR, ROR ($0A,$2A,$4A,$6A)
LDA <= '1'; -- A
Set_BusA_To <= Set_BusA_To_ABC;
case MCycle is
when Cycle_sync =>
when Cycle_1 =>
when others =>
end case;
when x"8a" | x"98" => -- TYA, TXA ($8A,$98)
LDA <= '1';
case MCycle is
when Cycle_sync =>
when Cycle_1 =>
when others =>
end case;
when x"aa" | x"a8" => -- TAX, TAY ($AA,$A8)
case MCycle is
when Cycle_sync =>
when Cycle_1 =>
Set_BusA_To <= Set_BusA_To_ABC;
when others =>
end case;
when x"9a" => -- TXS ($9A)
LDS <= '1'; -- will be set only in Cycle_sync
when x"ba" => -- TSX ($BA)
LDX <= '1';
case MCycle is
when Cycle_sync =>
when Cycle_1 =>
Set_BusA_To <= Set_BusA_To_S;
when others =>
end case;
when x"80" => -- undoc: NOP imm2 ($80)
case MCycle is
when Cycle_sync =>
when Cycle_1 =>
Jump <= "01";
when others =>
end case;
when others => -- others ($0A,$EA, $18,$38,$58,$78,$B8,$C8,$D8,$E8,$F8)
case MCycle is
when Cycle_sync =>
when others =>
end case;
end case;
-- IR: $01,$21,$41,$61,$81,$A1,$C1,$E1
-- $03,$23,$43,$63,$83,$A3,$C3,$E3
when "00001" | "00011" =>
-- Zero Page Indexed Indirect (d,x)
lCycle <= Cycle_5;
if IR(7 downto 6) /= "10" then -- ($01,$21,$41,$61,$C1,$E1,$03,$23,$43,$63,$C3,$E3)
LDA <= '1';
if Mode="00" and IR(1)='1' then
lCycle <= Cycle_7;
end if;
end if;
case MCycle is
when Cycle_1 =>
Jump <= "01";
LDAD <= '1';
Set_Addr_To <= Set_Addr_To_ZPG;
when Cycle_2 =>
ADAdd <= '1';
Set_Addr_To <= Set_Addr_To_ZPG;
when Cycle_3 =>
BAAdd <= "01";
LDBAL <= '1';
Set_Addr_To <= Set_Addr_To_ZPG;
when Cycle_4 =>
LDBAH <= '1';
if IR(7 downto 5) = "100" then
Write <= '1';
end if;
Set_Addr_To <= Set_Addr_To_BA;
when Cycle_5=>
if Mode="00" and IR(1)='1' and IR(7 downto 6)/="10" then
Set_Addr_To <= Set_Addr_To_BA;
Write <= '1';
LDDI<='1';
end if;
when Cycle_6=>
Write <= '1';
LDALU<='1';
SaveP<='1';
Set_Addr_To <= Set_Addr_To_BA;
when Cycle_7 =>
ALUmore <= '1';
Set_BusA_To <= Set_BusA_To_ABC;
when others =>
end case;
-- IR: $09,$29,$49,$69,$89,$A9,$C9,$E9
when "01001" =>
-- Immediate
if IR(7 downto 5)/="100" then -- all except undoc. NOP imm2 (not $89)
LDA <= '1';
end if;
case MCycle is
when Cycle_1 =>
Jump <= "01";
when others =>
end case;
-- IR: $0B,$2B,$4B,$6B,$8B,$AB,$CB,$EB
when "01011" =>
if Mode="00" then
-- Immediate undoc for 6500
case IR(7 downto 5) is
when "010"|"011"|"000"|"001" =>--ALR,ARR
Set_BusA_To<=Set_BusA_To_DA;
LDA <= '1';
when "100" =>--XAA
Set_BusA_To<=Set_BusA_To_DAX;
LDA <= '1';
when "110" =>--SAX (SBX)
Set_BusA_To<=Set_BusA_To_AAX;
LDX <= '1';
when "101" =>--OAL
Set_BusA_To<=Set_BusA_To_DAO;
LDA <= '1';
when others=>
LDA <= '1';
end case;
case MCycle is
when Cycle_1 =>
Jump <= "01";
when others =>
end case;
end if;
-- IR: $02,$22,$42,$62,$82,$A2,$C2,$E2
-- $12,$32,$52,$72,$92,$B2,$D2,$F2
when "00010" | "10010" =>
-- Immediate, SKB, KIL
case MCycle is
when Cycle_sync =>
when Cycle_1 =>
if IR = "10100010" then
-- LDX ($A2)
Jump <= "01";
LDX <= '1'; -- Moved, Lorenz test showed X changing on SKB (NOPx)
elsif IR(7 downto 4)="1000" or IR(7 downto 4)="1100" or IR(7 downto 4)="1110" then
-- undoc: NOP imm2
Jump <= "01";
else
-- KIL !!!
end if;
when others =>
end case;
-- IR: $04,$24,$44,$64,$84,$A4,$C4,$E4
when "00100" =>
-- Zero Page
lCycle <= Cycle_2;
case MCycle is
when Cycle_sync =>
if IR(7 downto 5) = "001" then--24=BIT zpg
SaveP <= '1';
end if;
when Cycle_1 =>
Jump <= "01";
LDAD <= '1';
if IR(7 downto 5) = "100" then--84=sty zpg (the only write in this group)
Write <= '1';
end if;
Set_Addr_To <= Set_Addr_To_ZPG;
when Cycle_2 =>
when others =>
end case;
-- IR: $05,$25,$45,$65,$85,$A5,$C5,$E5
-- $06,$26,$46,$66,$86,$A6,$C6,$E6
-- $07,$27,$47,$67,$87,$A7,$C7,$E7
when "00101" | "00110" | "00111" =>
-- Zero Page
if IR(7 downto 6) /= "10" and IR(1) = '1' and (mode="00" or IR(0)='0') then--covers 0x-7x,cx-fx x=2,3,6,7,a,b,e,f, for 6502 undocs
-- Read-Modify-Write
lCycle <= Cycle_4;
if Mode="00" and IR(0)='1' then
LDA<='1';
end if;
case MCycle is
when Cycle_1 =>
Jump <= "01";
LDAD <= '1';
Set_Addr_To <= Set_Addr_To_ZPG;
when Cycle_2 =>
LDDI <= '1';
if Mode="00" then--The old 6500 writes back what is just read, before changing. The 65c does another read
Write <= '1';
end if;
Set_Addr_To <= Set_Addr_To_ZPG;
when Cycle_3 =>
LDALU <= '1';
SaveP <= '1';
Write <= '1';
Set_Addr_To <= Set_Addr_To_ZPG;
when Cycle_4 =>
if Mode="00" and IR(0)='1' then
Set_BusA_To<=Set_BusA_To_ABC;
ALUmore <= '1'; -- For undoc DCP/DCM support
LDDI <= '1'; -- requires DIN to reflect DOUT!
end if;
when others =>
end case;
else
lCycle <= Cycle_2;
if IR(7 downto 6) /= "10" then
LDA <= '1';
end if;
case MCycle is
when Cycle_sync =>
when Cycle_1 =>
Jump <= "01";
LDAD <= '1';
if IR(7 downto 5) = "100" then
Write <= '1';
end if;
Set_Addr_To <= Set_Addr_To_ZPG;
when Cycle_2 =>
when others =>
end case;
end if;
-- IR: $0C,$2C,$4C,$6C,$8C,$AC,$CC,$EC
when "01100" =>
-- Absolute
if IR(7 downto 6) = "01" and IR(4 downto 0) = "01100" then -- JMP ($4C,$6C)
if IR(5) = '0' then
lCycle <= Cycle_2;
case MCycle is
when Cycle_1 =>
Jump <= "01";
LDDI <= '1';
when Cycle_2 =>
Jump <= "10";
when others =>
end case;
else
lCycle <= Cycle_4;
case MCycle is
when Cycle_1 =>
Jump <= "01";
LDDI <= '1';
LDBAL <= '1';
when Cycle_2 =>
LDBAH <= '1';
if Mode /= "00" then
Jump <= "10";
end if;
if Mode = "00" then
Set_Addr_To <= Set_Addr_To_BA;
end if;
when Cycle_3 =>
LDDI <= '1';
if Mode = "00" then
Set_Addr_To <= Set_Addr_To_BA;
BAAdd <= "01"; -- DB Inc
else
Jump <= "01";
end if;
when Cycle_4 =>
Jump <= "10";
when others =>
end case;
end if;
else
lCycle <= Cycle_3;
case MCycle is
when Cycle_sync =>
if IR(7 downto 5) = "001" then--2c-BIT
SaveP <= '1';
end if;
when Cycle_1 =>
Jump <= "01";
LDBAL <= '1';
when Cycle_2 =>
Jump <= "01";
LDBAH <= '1';
if IR(7 downto 5) = "100" then--80, sty, the only write in this group
Write <= '1';
end if;
Set_Addr_To <= Set_Addr_To_BA;
when Cycle_3 =>
when others =>
end case;
end if;
-- IR: $0D,$2D,$4D,$6D,$8D,$AD,$CD,$ED
-- $0E,$2E,$4E,$6E,$8E,$AE,$CE,$EE
-- $0F,$2F,$4F,$6F,$8F,$AF,$CF,$EF
when "01101" | "01110" | "01111" =>
-- Absolute
if IR(7 downto 6) /= "10" and IR(1) = '1' and (mode="00" or IR(0)='0') then -- ($0E,$2E,$4E,$6E,$CE,$EE, $0F,$2F,$4F,$6F,$CF,$EF)
-- Read-Modify-Write
lCycle <= Cycle_5;
if Mode="00" and IR(0) = '1' then
LDA <= '1';
end if;
case MCycle is
when Cycle_1 =>
Jump <= "01";
LDBAL <= '1';
when Cycle_2 =>
Jump <= "01";
LDBAH <= '1';
Set_Addr_To <= Set_Addr_To_BA;
when Cycle_3 =>
LDDI <= '1';
if Mode="00" then--The old 6500 writes back what is just read, before changing. The 65c does another read
Write <= '1';
end if;
Set_Addr_To <= Set_Addr_To_BA;
when Cycle_4 =>
Write <= '1';
LDALU <= '1';
SaveP <= '1';
Set_Addr_To <= Set_Addr_To_BA;
when Cycle_5 =>
if Mode="00" and IR(0)='1' then
ALUmore <= '1'; -- For undoc DCP/DCM support
Set_BusA_To<=Set_BusA_To_ABC;
end if;
when others =>
end case;
else
lCycle <= Cycle_3;
if IR(7 downto 6) /= "10" then -- all but $8D, $8E, $8F, $AD, $AE, $AF ($AD does set LDA in an earlier case statement)
LDA <= '1';
end if;
case MCycle is
when Cycle_sync =>
when Cycle_1 =>
Jump <= "01";
LDBAL <= '1';
when Cycle_2 =>
Jump <= "01";
LDBAH <= '1';
if IR(7 downto 5) = "100" then--8d
Write <= '1';
end if;
Set_Addr_To <= Set_Addr_To_BA;
when Cycle_3 =>
when others =>
end case;
end if;
-- IR: $10,$30,$50,$70,$90,$B0,$D0,$F0
when "10000" =>
-- Relative
-- This circuit dictates when the last
-- microcycle occurs for the branch depending on
-- whether or not the branch is taken and if a page
-- is crossed...
if (Branch = '1') then
lCycle <= Cycle_3; -- We're done @ T3 if branching...upper
-- level logic will stop at T2 if no page cross
-- (See the Break signal)
else
lCycle <= Cycle_1;
end if;
-- This decodes the current microcycle and takes the
-- proper course of action...
case MCycle is
-- On the T1 microcycle, increment the program counter
-- and instruct the upper level logic to fetch the offset
-- from the Din bus and store it in the data latches. This
-- will be the last microcycle if the branch isn't taken.
when Cycle_1 =>
Jump <= "01"; -- Increments the PC by one (PC will now be PC+2)
-- from microcycle T0.
LDDI <= '1'; -- Tells logic in top level (T65.vhd) to route
-- the Din bus to the memory data latch (DL)
-- so that the branch offset is fetched.
-- In microcycle T2, tell the logic in the top level to
-- add the offset. If the most significant byte of the
-- program counter (i.e. the current "page") does not need
-- updating, we are done here...the Break signal at the
-- T65.vhd level takes care of that...
when Cycle_2 =>
Jump <= "11"; -- Tell the PC Jump logic to use relative mode.
PCAdd <= '1'; -- This tells the PC adder to update itself with
-- the current offset recently fetched from
-- memory.
-- The following is microcycle T3 :
-- The program counter should be completely updated
-- on this cycle after the page cross is detected.
-- We don't need to do anything here...
when Cycle_3 =>
when others => null; -- Do nothing.
end case;
-- IR: $11,$31,$51,$71,$91,$B1,$D1,$F1
-- $13,$33,$53,$73,$93,$B3,$D3,$F3
when "10001" | "10011" =>
lCycle <= Cycle_5;
if IR(7 downto 6) /= "10" then -- ($11,$31,$51,$71,$D1,$F1,$13,$33,$53,$73,$D3,$F3)
LDA <= '1';
if Mode="00" and IR(1)='1' then
lCycle <= Cycle_7;
end if;
end if;
case MCycle is
when Cycle_1 =>
Jump <= "01";
LDAD <= '1';
Set_Addr_To <= Set_Addr_To_ZPG;
when Cycle_2 =>
LDBAL <= '1';
BAAdd <= "01"; -- DB Inc
Set_Addr_To <= Set_Addr_To_ZPG;
when Cycle_3 =>
Set_BusA_To <= Set_BusA_To_Y;
BAAdd <= "10"; -- BA Add
LDBAH <= '1';
Set_Addr_To <= Set_Addr_To_BA;
when Cycle_4 =>
BAAdd <= "11"; -- BA Adj
if IR(7 downto 5) = "100" then
Write <= '1';
elsif IR(1)='0' or IR=x"B3" then -- Dont do this on $x3, except undoc LAXiy $B3 (says real CPU and Lorenz tests)
BreakAtNA <= '1';
end if;
Set_Addr_To <= Set_Addr_To_BA;
when Cycle_5 =>
if Mode="00" and IR(1)='1' and IR(7 downto 6)/="10" then
Set_Addr_To <= Set_Addr_To_BA;
LDDI<='1';
Write <= '1';
end if;
when Cycle_6 =>
LDALU<='1';
SaveP<='1';
Write <= '1';
Set_Addr_To <= Set_Addr_To_BA;
when Cycle_7 =>
ALUmore <= '1';
Set_BusA_To<=Set_BusA_To_ABC;
when others =>
end case;
-- IR: $14,$34,$54,$74,$94,$B4,$D4,$F4
-- $15,$35,$55,$75,$95,$B5,$D5,$F5
-- $16,$36,$56,$76,$96,$B6,$D6,$F6
-- $17,$37,$57,$77,$97,$B7,$D7,$F7
when "10100" | "10101" | "10110" | "10111" =>
-- Zero Page, X
if IR(7 downto 6) /= "10" and IR(1) = '1' and (Mode="00" or IR(0)='0') then -- ($16,$36,$56,$76,$D6,$F6, $17,$37,$57,$77,$D7,$F7)
-- Read-Modify-Write
if Mode="00" and IR(0)='1' then
LDA<='1';
end if;
lCycle <= Cycle_5;
case MCycle is
when Cycle_1 =>
Jump <= "01";
LDAD <= '1';
Set_Addr_To <= Set_Addr_To_ZPG;
when Cycle_2 =>
ADAdd <= '1';
Set_Addr_To <= Set_Addr_To_ZPG;
when Cycle_3 =>
LDDI <= '1';
if Mode="00" then -- The old 6500 writes back what is just read, before changing. The 65c does another read
Write <= '1';
end if;
Set_Addr_To <= Set_Addr_To_ZPG;
when Cycle_4 =>
LDALU <= '1';
SaveP <= '1';
Write <= '1';
Set_Addr_To <= Set_Addr_To_ZPG;
if Mode="00" and IR(0)='1' then
LDDI<='1';
end if;
when Cycle_5 =>
if Mode="00" and IR(0)='1' then
ALUmore <= '1'; -- For undoc DCP/DCM support
Set_BusA_To<=Set_BusA_To_ABC;
end if;
when others =>
end case;
else
lCycle <= Cycle_3;
if IR(7 downto 6) /= "10" and IR(0)='1' then -- dont LDA on undoc skip
LDA <= '1';
end if;
case MCycle is
when Cycle_sync =>
when Cycle_1 =>
Jump <= "01";
LDAD <= '1';
Set_Addr_To <= Set_Addr_To_ZPG;
when Cycle_2 =>
ADAdd <= '1';
-- Added this check for Y reg. use, added undocs
if (IR(3 downto 1) = "011") then -- ($16,$36,$56,$76,$96,$B6,$D6,$F6,$17,$37,$57,$77,$97,$B7,$D7,$F7)
AddY <= '1';
end if;
if IR(7 downto 5) = "100" then -- ($14,$34,$15,$35,$16,$36,$17,$37) the only write instruction
Write <= '1';
end if;
Set_Addr_To <= Set_Addr_To_ZPG;
when Cycle_3 => null;
when others =>
end case;
end if;
-- IR: $19,$39,$59,$79,$99,$B9,$D9,$F9
-- $1B,$3B,$5B,$7B,$9B,$BB,$DB,$FB
when "11001" | "11011" =>
-- Absolute Y
lCycle <= Cycle_4;
if IR(7 downto 6) /= "10" then
LDA <= '1';
if Mode="00" and IR(1)='1' then
lCycle <= Cycle_6;
end if;
end if;
case MCycle is
when Cycle_1 =>
Jump <= "01";
LDBAL <= '1';
when Cycle_2 =>
Jump <= "01";
Set_BusA_To <= Set_BusA_To_Y;
BAAdd <= "10"; -- BA Add
LDBAH <= '1';
Set_Addr_To <= Set_Addr_To_BA;
when Cycle_3 =>
BAAdd <= "11"; -- BA adj
if IR(7 downto 5) = "100" then--99/9b
Write <= '1';
elsif IR(1)='0' or IR=x"BB" then -- Dont do this on $xB, except undoc $BB (says real CPU and Lorenz tests)
BreakAtNA <= '1';
end if;
Set_Addr_To <= Set_Addr_To_BA;
when Cycle_4 => -- just for undoc
if Mode="00" and IR(1)='1' and IR(7 downto 6)/="10" then
Set_Addr_To <= Set_Addr_To_BA;
LDDI<='1';
Write <= '1';
end if;
when Cycle_5 =>
Write <= '1';
LDALU<='1';
Set_Addr_To <= Set_Addr_To_BA;
SaveP<='1';
when Cycle_6 =>
ALUmore <= '1';
Set_BusA_To <= Set_BusA_To_ABC;
when others =>
end case;
-- IR: $1C,$3C,$5C,$7C,$9C,$BC,$DC,$FC
-- $1D,$3D,$5D,$7D,$9D,$BD,$DD,$FD
-- $1E,$3E,$5E,$7E,$9E,$BE,$DE,$FE
-- $1F,$3F,$5F,$7F,$9F,$BF,$DF,$FF
when "11100" | "11101" | "11110" | "11111" =>
-- Absolute X
if IR(7 downto 6) /= "10" and IR(1) = '1' and (Mode="00" or IR(0)='0') then -- ($1E,$3E,$5E,$7E,$DE,$FE, $1F,$3F,$5F,$7F,$DF,$FF)
-- Read-Modify-Write
lCycle <= Cycle_6;
if Mode="00" and IR(0)='1' then
LDA <= '1';
end if;
case MCycle is
when Cycle_1 =>
Jump <= "01";
LDBAL <= '1';
when Cycle_2 =>
Jump <= "01";
Set_BusA_To <= Set_BusA_To_X;
BAAdd <= "10"; -- BA Add
LDBAH <= '1';
Set_Addr_To <= Set_Addr_To_BA;
when Cycle_3 =>
BAAdd <= "11"; -- BA adj
Set_Addr_To <= Set_Addr_To_BA;
when Cycle_4 =>
LDDI <= '1';
if Mode="00" then--The old 6500 writes back what is just read, before changing. The 65c does another read
Write <= '1';
end if;
Set_Addr_To <= Set_Addr_To_BA;
when Cycle_5 =>
LDALU <= '1';
SaveP <= '1';
Write <= '1';
Set_Addr_To <= Set_Addr_To_BA;
when Cycle_6 =>
if Mode="00" and IR(0)='1' then
ALUmore <= '1';
Set_BusA_To <= Set_BusA_To_ABC;
end if;
when others =>
end case;
else -- ($1C,$3C,$5C,$7C,$9C,$BC,$DC,$FC, $1D,$3D,$5D,$7D,$9D,$BD,$DD,$FD, $9E,$BE,$9F,$BF)
lCycle <= Cycle_4;--Or 3 if not page crossing
if IR(7 downto 6) /= "10" then
if Mode/="00" or IR(4)='0' or IR(1 downto 0)/="00" then
LDA <= '1';
end if;
end if;
case MCycle is
when Cycle_sync =>
when Cycle_1 =>
Jump <= "01";
LDBAL <= '1';
when Cycle_2 =>
Jump <= "01";
-- special case $BE which uses Y reg as index!!
if(IR(7 downto 6)="10" and IR(4 downto 1)="1111") then
Set_BusA_To <= Set_BusA_To_Y;
else
Set_BusA_To <= Set_BusA_To_X;
end if;
BAAdd <= "10"; -- BA Add
LDBAH <= '1';
Set_Addr_To <= Set_Addr_To_BA;
when Cycle_3 =>
BAAdd <= "11"; -- BA adj
if IR(7 downto 5) = "100" then -- ($9E,$9F)
Write <= '1';
else
BreakAtNA <= '1';
end if;
Set_Addr_To <= Set_Addr_To_BA;
when Cycle_4 =>
when others =>
end case;
end if;
when others =>
end case;
end process;
process (IR, MCycle, Mode,ALUmore)
begin
-- ORA, AND, EOR, ADC, NOP, LD, CMP, SBC
-- ASL, ROL, LSR, ROR, BIT, LD, DEC, INC
case IR(1 downto 0) is
when "00" =>
case IR(4 downto 2) is
-- IR: $00,$20,$40,$60,$80,$A0,$C0,$E0
-- $04,$24,$44,$64,$84,$A4,$C4,$E4
-- $0C,$2C,$4C,$6C,$8C,$AC,$CC,$EC
when "000" | "001" | "011" =>
case IR(7 downto 5) is
when "110" | "111" => -- CP ($C0,$C4,$CC,$E0,$E4,$EC)
ALU_Op <= ALU_OP_CMP;
when "101" => -- LD ($A0,$A4,$AC)
ALU_Op <= ALU_OP_EQ2;
when "001" => -- BIT ($20,$24,$2C - $20 is ignored, as its a jmp)
ALU_Op <= ALU_OP_BIT;
when others => -- other, NOP/ST ($x0,$x4,$xC)
ALU_Op <= ALU_OP_EQ1;
end case;
-- IR: $08,$28,$48,$68,$88,$A8,$C8,$E8
when "010" =>
case IR(7 downto 5) is
when "111" | "110" => -- IN ($C8,$E8)
ALU_Op <= ALU_OP_INC;
when "100" => -- DEY ($88)
ALU_Op <= ALU_OP_DEC;
when others => -- LD
ALU_Op <= ALU_OP_EQ2;
end case;
-- IR: $18,$38,$58,$78,$98,$B8,$D8,$F8
when "110" =>
case IR(7 downto 5) is
when "100" => -- TYA ($98)
ALU_Op <= ALU_OP_EQ2;
when others =>
ALU_Op <= ALU_OP_EQ1;
end case;
-- IR: $10,$30,$50,$70,$90,$B0,$D0,$F0
-- $14,$34,$54,$74,$94,$B4,$D4,$F4
-- $1C,$3C,$5C,$7C,$9C,$BC,$DC,$FC
when others =>
case IR(7 downto 5) is
when "101" => -- LD ($B0,$B4,$BC)
ALU_Op <= ALU_OP_EQ2;
when others =>
ALU_Op <= ALU_OP_EQ1;
end case;
end case;
when "01" => -- OR
case(to_integer(unsigned(IR(7 downto 5)))) is
when 0=> -- IR: $01,$05,$09,$0D,$11,$15,$19,$1D
ALU_Op<=ALU_OP_OR;
when 1=> -- IR: $21,$25,$29,$2D,$31,$35,$39,$3D
ALU_Op<=ALU_OP_AND;
when 2=> -- IR: $41,$45,$49,$4D,$51,$55,$59,$5D
ALU_Op<=ALU_OP_EOR;
when 3=> -- IR: $61,$65,$69,$6D,$71,$75,$79,$7D
ALU_Op<=ALU_OP_ADC;
when 4=>-- IR: $81,$85,$89,$8D,$91,$95,$99,$9D
ALU_Op<=ALU_OP_EQ1; -- STA
when 5=> -- IR: $A1,$A5,$A9,$AD,$B1,$B5,$B9,$BD
ALU_Op<=ALU_OP_EQ2; -- LDA
when 6=> -- IR: $C1,$C5,$C9,$CD,$D1,$D5,$D9,$DD
ALU_Op<=ALU_OP_CMP;
when others=> -- IR: $E1,$E5,$E9,$ED,$F1,$F5,$F9,$FD
ALU_Op<=ALU_OP_SBC;
end case;
when "10" =>
case(to_integer(unsigned(IR(7 downto 5)))) is
when 0=> -- IR: $02,$06,$0A,$0E,$12,$16,$1A,$1E
ALU_Op<=ALU_OP_ASL;
if IR(4 downto 2) = "110" and Mode/="00" then -- 00011010,$1A -> INC acc, not on 6502
ALU_Op <= ALU_OP_INC;
end if;
when 1=> -- IR: $22,$26,$2A,$2E,$32,$36,$3A,$3E
ALU_Op<=ALU_OP_ROL;
if IR(4 downto 2) = "110" and Mode/="00" then -- 00111010,$3A -> DEC acc, not on 6502
ALU_Op <= ALU_OP_DEC;
end if;
when 2=> -- IR: $42,$46,$4A,$4E,$52,$56,$5A,$5E
ALU_Op<=ALU_OP_LSR;
when 3=> -- IR: $62,$66,$6A,$6E,$72,$76,$7A,$7E
ALU_Op<=ALU_OP_ROR;
when 4=> -- IR: $82,$86,$8A,$8E,$92,$96,$9A,$9E
ALU_Op<=ALU_OP_BIT;
if IR(4 downto 2) = "010" then -- 10001010, $8A -> TXA
ALU_Op <= ALU_OP_EQ2;
else -- 100xxx10, $82,$86,$8E,$92,$96,$9A,$9E
ALU_Op <= ALU_OP_EQ1;
end if;
when 5=> -- IR: $A2,$A6,$AA,$AE,$B2,$B6,$BA,$BE
ALU_Op<=ALU_OP_EQ2; -- LDX
when 6=> -- IR: $C2,$C6,$CA,$CE,$D2,$D6,$DA,$DE
ALU_Op<=ALU_OP_DEC;
when others=> -- IR: $E2,$E6,$EA,$EE,$F2,$F6,$FA,$FE
ALU_Op<=ALU_OP_INC;
end case;
when others => -- "11" undoc double alu ops
case(to_integer(unsigned(IR(7 downto 5)))) is
-- IR: $A3,$A7,$AB,$AF,$B3,$B7,$BB,$BF
when 5 =>
if IR=x"bb" then--LAS
ALU_Op <= ALU_OP_AND;
else
ALU_Op <= ALU_OP_EQ2;
end if;
-- IR: $03,$07,$0B,$0F,$13,$17,$1B,$1F
-- $23,$27,$2B,$2F,$33,$37,$3B,$3F
-- $43,$47,$4B,$4F,$53,$57,$5B,$5F
-- $63,$67,$6B,$6F,$73,$77,$7B,$7F
-- $83,$87,$8B,$8F,$93,$97,$9B,$9F
-- $C3,$C7,$CB,$CF,$D3,$D7,$DB,$DF
-- $E3,$E7,$EB,$EF,$F3,$F7,$FB,$FF
when others =>
if IR=x"6b" then -- ARR
ALU_Op<=ALU_OP_ARR;
elsif IR=x"8b" then -- ARR
ALU_Op<=ALU_OP_XAA; -- we can't use the bit operation as we don't set all flags...
elsif IR=x"0b" or IR=x"2b" then -- ANC
ALU_Op<=ALU_OP_ANC;
elsif IR=x"eb" then -- alternate SBC
ALU_Op<=ALU_OP_SBC;
elsif ALUmore='1' then
case(to_integer(unsigned(IR(7 downto 5)))) is
when 0=>
ALU_Op<=ALU_OP_OR;
when 1=>
ALU_Op<=ALU_OP_AND;
when 2=>
ALU_Op<=ALU_OP_EOR;
when 3=>
ALU_Op<=ALU_OP_ADC;
when 4=>
ALU_Op<=ALU_OP_EQ1; -- STA
when 5=>
ALU_Op<=ALU_OP_EQ2; -- LDA
when 6=>
ALU_Op<=ALU_OP_CMP;
when others=>
ALU_Op<=ALU_OP_SBC;
end case;
else
case(to_integer(unsigned(IR(7 downto 5)))) is
when 0=>
ALU_Op<=ALU_OP_ASL;
when 1=>
ALU_Op<=ALU_OP_ROL;
when 2=>
ALU_Op<=ALU_OP_LSR;
when 3=>
ALU_Op<=ALU_OP_ROR;
when 4=>
ALU_Op<=ALU_OP_BIT;
when 5=>
ALU_Op<=ALU_OP_EQ2; -- LDX
when 6=>
ALU_Op<=ALU_OP_DEC;
if IR(4 downto 2)="010" then -- $6B
ALU_Op<=ALU_OP_SAX; -- special SAX (SBX) case
end if;
when others=>
ALU_Op<=ALU_OP_INC;
end case;
end if;
end case;
end case;
end process;
end;