Files
SMS_MiSTer/rtl/system.vhd
Fabricio Breve f9911f681d Added support for loading Master System BIOS files. (#175)
Master System BIOS files can now be used to boot games and play built-in titles.
2026-04-12 05:56:58 +08:00

910 lines
26 KiB
VHDL

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
--use IEEE.STD_LOGIC_ARITH.ALL;
-- use IEEE.STD_LOGIC_UNSIGNED.ALL;
use work.jt89.all;
entity system is
generic (
MAX_SPPL : integer := 7;
BASE_DIR : string := ""
);
port (
clk_sys: in STD_LOGIC;
ce_cpu: in STD_LOGIC;
ce_vdp: in STD_LOGIC;
ce_pix: in STD_LOGIC;
ce_sp: in STD_LOGIC;
turbo: in STD_LOGIC;
gg: in STD_LOGIC;
ggres: in STD_LOGIC;
systeme: in STD_LOGIC;
-- sg: in STD_LOGIC; -- sg1000
bios_en: in STD_LOGIC;
ext_bios_sel: in STD_LOGIC;
ext_bios_loaded: in STD_LOGIC;
GG_EN : in std_logic; -- Game Genie not game gear
GG_CODE : in std_logic_vector(128 downto 0); -- game genie code
GG_RESET : in std_logic;
GG_AVAIL : out std_logic;
RESET_n: in STD_LOGIC;
rom_rd: out STD_LOGIC;
rom_a: out STD_LOGIC_VECTOR(21 downto 0);
rom_do: in STD_LOGIC_VECTOR(7 downto 0);
j1_up: in STD_LOGIC;
j1_down: in STD_LOGIC;
j1_left: in STD_LOGIC;
j1_right: in STD_LOGIC;
j1_tl: in STD_LOGIC;
j1_tr: in STD_LOGIC;
j1_th: in STD_LOGIC;
j1_start: in STD_LOGIC;
j1_coin: in STD_LOGIC;
j1_a3: in STD_LOGIC;
j2_up: in STD_LOGIC;
j2_down: in STD_LOGIC;
j2_left: in STD_LOGIC;
j2_right: in STD_LOGIC;
j2_tl: in STD_LOGIC;
j2_tr: in STD_LOGIC;
j2_th: in STD_LOGIC;
j2_start: in STD_LOGIC;
j2_coin: in STD_LOGIC;
j2_a3: in STD_LOGIC;
pause: in STD_LOGIC;
E0Type: in STD_LOGIC_VECTOR(1 downto 0);
E1Use: in STD_LOGIC;
E2Use: in STD_LOGIC;
E0: in STD_LOGIC_VECTOR(7 downto 0);
F2: in STD_LOGIC_VECTOR(7 downto 0);
F3: in STD_LOGIC_VECTOR(7 downto 0);
has_paddle: in STD_LOGIC;
has_pedal: in STD_LOGIC;
paddle: in STD_LOGIC_VECTOR(7 downto 0);
paddle2: in STD_LOGIC_VECTOR(7 downto 0);
pedal: in STD_LOGIC_VECTOR(7 downto 0);
j1_tr_out: out STD_LOGIC;
j1_th_out: out STD_LOGIC;
j2_tr_out: out STD_LOGIC;
j2_th_out: out STD_LOGIC;
x: in STD_LOGIC_VECTOR(8 downto 0);
y: in STD_LOGIC_VECTOR(8 downto 0);
color: out STD_LOGIC_VECTOR(11 downto 0);
palettemode: in STD_LOGIC;
mask_column:out STD_LOGIC;
black_column: in STD_LOGIC;
smode_M1: out STD_LOGIC;
smode_M2: out STD_LOGIC;
smode_M3: out STD_LOGIC;
ysj_quirk: in STD_LOGIC;
pal: in STD_LOGIC;
region: in STD_LOGIC;
mapper_lock: in STD_LOGIC;
vdp_enables: in STD_LOGIC_VECTOR(1 downto 0);
psg_enables: in STD_LOGIC_VECTOR(1 downto 0);
audioL: out STD_LOGIC_VECTOR(15 downto 0);
audioR: out STD_LOGIC_VECTOR(15 downto 0);
fm_ena: in STD_LOGIC;
dbr: in STD_LOGIC;
sp64: in STD_LOGIC;
-- Work RAM
ram_a: out STD_LOGIC_VECTOR(13 downto 0);
ram_d: out STD_LOGIC_VECTOR( 7 downto 0);
ram_we: out STD_LOGIC;
ram_q: in STD_LOGIC_VECTOR( 7 downto 0);
-- Backup RAM
nvram_a: out STD_LOGIC_VECTOR(14 downto 0);
nvram_d: out STD_LOGIC_VECTOR( 7 downto 0);
nvram_we: out STD_LOGIC;
nvram_q: in STD_LOGIC_VECTOR( 7 downto 0);
-- MC8123 decryption
encrypt: in STD_LOGIC_VECTOR(1 downto 0);
key_a : out STD_LOGIC_VECTOR(12 downto 0);
key_d : in STD_LOGIC_VECTOR(7 downto 0);
ROMCL : IN STD_LOGIC;
ROMAD : IN STD_LOGIC_VECTOR(24 downto 0);
ROMDT : IN STD_LOGIC_VECTOR(7 downto 0);
ROMEN : IN STD_LOGIC;
BIOSWEN: IN STD_LOGIC
);
end system;
architecture Behavioral of system is
signal RD_n: std_logic;
signal WR_n: std_logic;
signal IRQ_n: std_logic;
signal IORQ_n: std_logic;
signal M1_n: std_logic;
signal MREQ_n: std_logic;
signal A: std_logic_vector(15 downto 0);
signal D_in: std_logic_vector(7 downto 0);
signal D_out: std_logic_vector(7 downto 0);
signal last_read_addr: std_logic_vector(15 downto 0);
signal ce_z80: std_logic;
signal vdp_RD_n: std_logic;
signal vdp_WR_n: std_logic;
signal vdp_D_out: std_logic_vector(7 downto 0);
signal vdp_IRQ_n: std_logic;
signal vdp_color: std_logic_vector(11 downto 0);
-- signal vdp_y1: std_logic;
signal vdp2_RD_n: std_logic;
signal vdp2_WR_n: std_logic;
signal vdp2_D_out: std_logic_vector(7 downto 0);
signal vdp2_IRQ_n: std_logic;
signal vdp2_color: std_logic_vector(11 downto 0);
signal vdp2_y1: std_logic;
signal ctl_WR_n: std_logic;
signal io_RD_n: std_logic;
signal io_WR_n: std_logic;
signal io_D_out: std_logic_vector(7 downto 0);
signal ram_WR: std_logic;
signal ram_D_out: std_logic_vector(7 downto 0);
signal vram_WR: std_logic;
signal vram2_WR: std_logic;
signal boot_rom_D_out: std_logic_vector(7 downto 0);
signal ext_bios_D_out: std_logic_vector(7 downto 0);
signal active_bios_D_out: std_logic_vector(7 downto 0);
signal ext_bios_addr: std_logic_vector(17 downto 0);
signal ext_bios_wren: std_logic;
signal rom_a_i: std_logic_vector(21 downto 0);
signal bootloader_n: std_logic := '0';
signal irom_D_out: std_logic_vector(7 downto 0);
signal irom_RD_n: std_logic := '1';
signal bank0: std_logic_vector(7 downto 0) := "00000000";
signal bank1: std_logic_vector(7 downto 0) := "00000001";
signal bank2: std_logic_vector(7 downto 0) := "00000010";
signal bank3: std_logic_vector(7 downto 0) := "00000011";
signal vdp_se_bank: std_logic := '0';
signal vdp2_se_bank: std_logic := '0';
signal vdp_cpu_bank: std_logic := '0';
signal rom_bank: std_logic_vector(3 downto 0) := "0000";
signal PSG_disable: std_logic;
signal PSG_outL: std_logic_vector(10 downto 0);
signal PSG_outR: std_logic_vector(10 downto 0);
signal PSG_mux: std_logic_vector(7 downto 0);
signal psg_WR_n: std_logic;
signal bal_WR_n: std_logic;
signal PSG2_outL: std_logic_vector(10 downto 0);
signal PSG2_outR: std_logic_vector(10 downto 0);
signal psg2_WR_n: std_logic;
signal bal2_WR_n: std_logic;
signal FM_out: std_logic_vector(13 downto 0);
signal FM_gated: std_logic_vector(12 downto 0);
alias FM_sign: std_logic is FM_out(13);
alias FM_adj: std_logic is FM_out(12);
signal fm_a: std_logic;
signal fm_d: std_logic_vector(7 downto 0);
signal fm_WR_n: std_logic;
signal mix_inL: std_logic_vector(12 downto 0);
signal mix_inR: std_logic_vector(12 downto 0);
signal mix2_inL: std_logic_vector(12 downto 0);
signal mix2_inR: std_logic_vector(12 downto 0);
signal det_D: std_logic_vector(2 downto 0);
signal det_WR_n: std_logic;
signal HL: std_logic;
signal TH_Ain: std_logic;
signal TH_Bin: std_logic;
signal nvram_WR: std_logic;
signal nvram_e: std_logic := '0';
signal nvram_ex: std_logic := '0';
signal nvram_p: std_logic := '0';
signal nvram_cme: std_logic := '0'; -- codemasters ram extension
signal nvram_D_out: std_logic_vector(7 downto 0);
signal lock_mapper_B: std_logic := '0';
signal mapper_codies: std_logic := '0'; -- Ernie Els Golf mapper
signal mapper_codies_lock: std_logic := '0';
signal mapper_msx_check0 : boolean := false ;
signal mapper_msx_check1 : boolean := false ;
signal mapper_msx_lock0 : boolean := false ;
signal mapper_msx_lock : boolean := false ;
signal mapper_msx : std_logic := '0' ;
signal mc8123_D_out : std_logic_vector(7 downto 0);
signal segadect2_D_out : std_logic_vector(7 downto 0);
signal GENIE : boolean;
signal GENIE_DO : std_logic_vector(7 downto 0);
signal GENIE_DI : std_logic_vector(7 downto 0);
component CODES is
generic(
ADDR_WIDTH : in integer := 16;
DATA_WIDTH : in integer := 8
);
port(
clk : in std_logic;
reset : in std_logic;
enable : in std_logic;
addr_in : in std_logic_vector(15 downto 0);
data_in : in std_logic_vector(7 downto 0);
code : in std_logic_vector(128 downto 0);
available : out std_logic;
genie_ovr : out boolean;
genie_data : out std_logic_vector(7 downto 0)
);
end component;
COMPONENT MC8123_rom_decrypt IS
PORT (
clk : IN STD_LOGIC;
m1 : IN STD_LOGIC;
a : IN STD_LOGIC_VECTOR(15 downto 0);
d : OUT STD_LOGIC_VECTOR(7 downto 0);
prog_d : IN STD_LOGIC_VECTOR(7 downto 0);
key_a : OUT STD_LOGIC_VECTOR(12 downto 0);
key_d : IN STD_LOGIC_VECTOR(7 downto 0)
);
END COMPONENT;
COMPONENT SEGASYS1_DECT2 IS
PORT (
clk : IN STD_LOGIC;
mrom_m1: IN STD_LOGIC;
mrom_ad: IN STD_LOGIC_VECTOR(14 downto 0);
mrom_dt: OUT STD_LOGIC_VECTOR(7 downto 0);
rad : OUT STD_LOGIC_VECTOR(14 downto 0);
rdt : IN STD_LOGIC_VECTOR(7 downto 0);
ROMCL : IN STD_LOGIC;
ROMAD : IN STD_LOGIC_VECTOR(24 downto 0);
ROMDT : IN STD_LOGIC_VECTOR(7 downto 0);
ROMEN : IN STD_LOGIC
);
END COMPONENT;
begin
-- Game Genie
GAMEGENIE : component CODES
generic map(
ADDR_WIDTH => 16,
DATA_WIDTH => 8
)
port map(
clk => clk_sys,
reset => GG_RESET,
enable => not GG_EN,
addr_in => A,
data_in => D_out,
code => GG_CODE,
available => GG_AVAIL,
genie_ovr => GENIE,
genie_data => GENIE_DO
);
GENIE_DI <= GENIE_DO when GENIE else D_out;
z80_inst: entity work.T80s
generic map(
T2Write => 0
)
port map
(
RESET_n => RESET_n,
CLK => clk_sys,
CEN => ce_z80,
INT_n => IRQ_n,
NMI_n => pause or gg,
MREQ_n => MREQ_n,
IORQ_n => IORQ_n,
M1_n => M1_n,
RD_n => RD_n,
WR_n => WR_n,
A => A,
DI => GENIE_DI,
DO => D_in
);
vdp_inst: entity work.vdp
generic map(
MAX_SPPL => MAX_SPPL
)
port map
(
clk_sys => clk_sys,
ce_vdp => ce_vdp,
ce_pix => ce_pix,
ce_sp => ce_sp,
sp64 => sp64,
HL => HL,
gg => gg,
ggres => ggres,
-- Bsg => sg, -- sg1000
se_bank => vdp_se_bank,
RD_n => vdp_RD_n,
WR_n => vdp_WR_n,
IRQ_n => vdp_IRQ_n,
WR_direct => vram_WR,
A_direct => A(13 downto 8),
A => A(7 downto 0),
D_in => D_in,
D_out => vdp_D_out,
x => x,
y => y,
color => vdp_color,
palettemode => palettemode,
-- y1 => vdp_y1,
smode_M1 => smode_M1,
smode_M2 => smode_M2,
smode_M3 => smode_M3,
ysj_quirk => ysj_quirk,
mask_column => mask_column,
black_column => black_column,
reset_n => RESET_n
);
vdp2_inst: entity work.vdp
generic map(
MAX_SPPL => MAX_SPPL
)
port map
(
clk_sys => clk_sys,
ce_vdp => ce_vdp,
ce_pix => ce_pix,
ce_sp => ce_sp,
sp64 => sp64,
HL => HL,
gg => gg,
ggres => ggres,
-- Bsg => sg, -- sg1000
se_bank => vdp2_se_bank,
RD_n => vdp2_RD_n,
WR_n => vdp2_WR_n,
IRQ_n => vdp2_IRQ_n,
WR_direct => vram2_WR,
A_direct => A(13 downto 8),
A => A(7 downto 0),
D_in => D_in,
D_out => vdp2_D_out,
x => x,
y => y,
color => vdp2_color,
palettemode => palettemode,
y1 => vdp2_y1,
-- smode_M1 => smode2_M1,
-- smode_M2 => smode2_M2,
-- smode_M3 => smode2_M3,
ysj_quirk => ysj_quirk,
-- mask_column => mask2_column,
black_column => black_column,
reset_n => RESET_n
);
psg_inst: jt89
port map
(
clk => clk_sys,
clk_en => ce_cpu,
wr_n => psg_WR_n,
din => D_in,
mux => PSG_mux,
soundL => PSG_outL,
soundR => PSG_outR,
rst => not RESET_n
);
psg2_inst: jt89
port map
(
clk => clk_sys,
clk_en => ce_cpu,
wr_n => psg2_WR_n,
din => D_in,
mux => PSG_mux,
soundL => PSG2_outL,
soundR => PSG2_outR,
rst => not RESET_n
);
fm: work.opll
port map
(
xin => clk_sys,
xena => ce_cpu,
d => fm_d,
a => fm_a,
cs_n => '0',
we_n => '0',
ic_n => RESET_n,
mixout => FM_out
);
process (clk_sys)
begin
if rising_edge(clk_sys) then
if RESET_n='0' then
fm_d <= (others => '0');
fm_a <= '0';
elsif fm_WR_n='0' then
fm_d <= D_in;
fm_a <= A(0);
end if;
end if;
end process;
-- AMR - Clamped volume boosting - if the top two bits match, truncate the topmost bit.
-- If the top two bits don't match, duplicate the second bit across the output.
FM_gated <= (others=>'0') when fm_ena='0' or det_D(0)='0' else -- All zero if FM is disabled
FM_out(FM_out'high-1 downto 0) when FM_sign=FM_adj else -- Pass through
(FM_gated'high=>FM_sign,others=>FM_adj); -- Clamp
PSG_disable <= '1' when (systeme='0' and fm_ena='1' and (not det_D(1)=det_D(0))) else '0';
mix_inL <= (others=>'0') when psg_enables(0)='1' or PSG_disable='1' else (PSG_outL(10) & PSG_outL & '0');
mix_inR <= (others=>'0') when psg_enables(0)='1' or PSG_disable='1' else (PSG_outR(10) & PSG_outR & '0');
mix2_inL <= (others=>'0') when psg_enables(1)='1' else (PSG2_outL(10) & PSG2_outL & '0') when systeme='1' else FM_gated;
mix2_inR <= (others=>'0') when psg_enables(1)='1' else (PSG2_outR(10) & PSG2_outR & '0') when systeme='1' else FM_gated;
-- The old code shifts FM right by one place and PSG right by three places.
-- This version shift FM left one place and PSG right by one place, so the volume
-- is four times higher. I haven't yet found a game in which this clips.
mix : entity work.AudioMix
port map(
clk => clk_sys,
reset_n => RESET_n,
audio_in_l1 => signed(mix_inL & "000"),
audio_in_l2 => signed(mix2_inL & "000"),
audio_in_r1 => signed(mix_inR & "000"),
audio_in_r2 => signed(mix2_inR & "000"),
std_logic_vector(audio_l) => audioL,
std_logic_vector(audio_r) => audioR
);
-- audioL <= (PSG_outL(10) & PSG_outL(10) & PSG_outL(10) & PSG_outL & "00") + (FM_out(13) & FM_out & "0") when fm_ena = '1'
-- else (PSG_outL(10) & PSG_outL(10) & PSG_outL(10) & PSG_outL & "00");
-- audioR <= (PSG_outR(10) & PSG_outR(10) & PSG_outR(10) & PSG_outR & "00") + (FM_out(13) & FM_out & "0") when fm_ena = '1'
-- else (PSG_outR(10) & PSG_outR(10) & PSG_outR(10) & PSG_outL & "00");
io_inst: entity work.io
port map
(
clk => clk_sys,
WR_n => io_WR_n,
RD_n => io_RD_n,
A => A(7 downto 0),
D_in => D_in,
D_out => io_D_out,
HL_out => HL,
vdp1_bank => vdp_se_bank,
vdp2_bank => vdp2_se_bank,
vdp_cpu_bank => vdp_cpu_bank,
rom_bank => rom_bank,
J1_tr_out => j1_tr_out,
J1_th_out => j1_th_out,
J2_tr_out => j2_tr_out,
J2_th_out => j2_th_out,
J1_up => j1_up,
J1_down => j1_down,
J1_left => j1_left,
J1_right => j1_right,
J1_tl => j1_tl,
J1_tr => j1_tr,
J1_th => j1_th,
J1_start => j1_start,
J1_coin => j1_coin,
J1_a3 => j1_a3,
J2_up => j2_up,
J2_down => j2_down,
J2_left => j2_left,
J2_right => j2_right,
J2_tl => j2_tl,
J2_tr => j2_tr,
J2_th => j2_th,
J2_start => j2_start,
J2_coin => j2_coin,
J2_a3 => j2_a3,
Pause => pause,
E0Type => E0Type,
E1Use => E1Use,
E2Use => E2Use,
E0 => E0,
F2 => F2,
F3 => F3,
has_paddle=> has_paddle,
has_pedal=> has_pedal,
paddle => paddle,
paddle2 => paddle2,
pedal => pedal,
pal => pal,
gg => gg,
systeme => systeme,
region => region,
RESET_n => RESET_n
);
ce_z80 <= ce_pix when (systeme = '1' or turbo='1') else ce_cpu;
ram_a <= A(13 downto 0) when systeme = '1' else '0' & A(12 downto 0);
ram_we <= ram_WR;
ram_d <= D_in;
ram_D_out <= ram_q;
nvram_a <= (nvram_p and not A(14)) & A(13 downto 0);
nvram_we <= nvram_WR;
nvram_d <= D_in;
nvram_D_out <= nvram_q;
boot_rom_inst : entity work.sprom
generic map
(
init_file=> BASE_DIR & "rtl/mboot.mif",
widthad_a=> 14
)
port map
(
clock => clk_sys,
address => A(13 downto 0),
q => boot_rom_D_out
);
-- Drive the output port from the internal signal
rom_a <= rom_a_i;
-- External BIOS RAM: up to 256KB, written only during BIOS file download (BIOSWEN)
-- Read address uses rom_a_i (mapper-translated) so banking works correctly
ext_bios_wren <= BIOSWEN;
ext_bios_addr <= ROMAD(17 downto 0) when BIOSWEN='1' else rom_a_i(17 downto 0);
ext_bios_inst : entity work.spram
generic map
(
widthad_a=> 18
)
port map
(
clock => clk_sys,
address => ext_bios_addr,
wren => ext_bios_wren,
data => ROMDT,
q => ext_bios_D_out
);
mc8123_inst : component MC8123_rom_decrypt
port map
(
clk => clk_sys,
m1 => not M1_n,
a => A,
d => mc8123_D_out,
prog_d => rom_do,
key_a => key_a,
key_d => key_d
);
segadect2_inst : component SEGASYS1_DECT2
port map
(
clk => clk_sys,
mrom_m1 => not M1_n,
mrom_ad => A(14 downto 0),
mrom_dt => segadect2_D_out,
-- rad =>,
rdt => rom_do,
ROMCL => ROMCL,
ROMAD => ROMAD,
ROMDT => ROMDT,
ROMEN => ROMEN
);
-- glue logic
bal_WR_n <= WR_n when IORQ_n='0' and M1_n='1' and A(7 downto 0)="00000110" and gg='1' else '1';
vdp_WR_n <= WR_n when IORQ_n='0' and M1_n='1' and A(7 downto 6)="10" and (A(2)='0' or systeme='0') else '1';
vdp2_WR_n <= WR_n when IORQ_n='0' and M1_n='1' and A(7 downto 6)="10" and (A(2)='1' and systeme='1') else '1';
vdp_RD_n <= RD_n when IORQ_n='0' and M1_n='1' and (A(7 downto 6)="01" or A(7 downto 6)="10") and (A(2)='0' or systeme='0') else '1';
vdp2_RD_n <= RD_n when IORQ_n='0' and M1_n='1' and (A(7 downto 6)="01" or A(7 downto 6)="10") and (A(2)='1' and systeme='1') else '1';
psg_WR_n <= WR_n when IORQ_n='0' and M1_n='1' and A(7 downto 6)="01" and (A(2)='0' or systeme='0') else '1';
psg2_WR_n <= WR_n when IORQ_n='0' and M1_n='1' and A(7 downto 6)="01" and (A(2)='1' and systeme='1') else '1';
ctl_WR_n <= WR_n when IORQ_n='0' and M1_n='1' and A(7 downto 6)="00" and A(0)='0' else '1';
io_WR_n <= WR_n when IORQ_n='0' and M1_n='1' and ((A(7 downto 6)="00" and (A(0)='1' or (gg='1' and A(5 downto 3)="000"))) or (A(7 downto 6)="11" and systeme='1')) else '1';
io_RD_n <= RD_n when IORQ_n='0' and M1_n='1' and (A(7 downto 6)="11" or (gg='1' and A(7 downto 3)="00000" and A(2 downto 1)/="11")) else '1';
fm_WR_n <= WR_n when IORQ_n='0' and M1_n='1' and A(7 downto 1)="1111000" else '1';
det_WR_n <= WR_n when IORQ_n='0' and M1_n='1' and A(7 downto 0)=x"F2" else '1';
IRQ_n <= vdp_IRQ_n when systeme='0' else vdp2_IRQ_n;
ram_WR <= not WR_n when MREQ_n='0' and A(15 downto 14)="11" else '0';
vram_WR <= not WR_n when MREQ_n='0' and A(15 downto 14)="10" and vdp_cpu_bank='1' and systeme='1' else '0';
vram2_WR <= not WR_n when MREQ_n='0' and A(15 downto 14)="10" and vdp_cpu_bank='0' and systeme='1' else '0';
nvram_WR <= not WR_n when MREQ_n='0' and ((A(15 downto 14)="10" and nvram_e = '1')
or (A(15 downto 14)="11" and nvram_ex = '1')
or (A(15 downto 13)="101" and nvram_cme = '1')) else '0';
rom_RD <= not RD_n when MREQ_n='0' and A(15 downto 14)/="11" else '0';
color <= vdp2_color when (vdp2_y1='1' and systeme='1' and vdp_enables(1)='0') else vdp_color when vdp_enables(0)='0' else x"000";
process (clk_sys)
begin
if rising_edge(clk_sys) then
if RESET_n='0' then
bootloader_n <= not bios_en;
elsif ctl_WR_n='0' then
if ext_bios_sel='1' and ext_bios_loaded='1' then
-- For external BIOS: honour port $3E bit 3 (active low BIOS enable)
-- bit3=0 -> BIOS ROM enabled -> bootloader_n=0
-- bit3=1 -> BIOS ROM disabled (cartridge enabled) -> bootloader_n=1
bootloader_n <= D_in(3);
elsif bootloader_n='0' then
-- Internal BIOS (mboot.mif): any write disables BIOS, original behaviour
bootloader_n <= '1';
end if;
end if;
end if;
end process;
-- Reset the mapper to default state whenever the BIOS hands control to the
-- cartridge (bootloader_n goes 0->1 via port $3E). Merged into the mapper
-- process below to avoid multiple drivers on the bank registers.
-- When ext BIOS is active and BIOS ROM is enabled (bootloader_n=0):
-- serve all ROM banks (0, 1, 2) from SPRAM so the full 256KB BIOS can run.
-- When BIOS ROM is disabled (bootloader_n=1, triggered by port $3E bit3=1):
-- serve SDRAM (cartridge) so the BIOS detection code (running from RAM) can
-- read the cartridge header. The BIOS then re-enables itself (bit3=0) if no
-- valid cart is found, causing bootloader_n to go back to 0, and JP $0000
-- will fall back into the SPRAM BIOS - giving the correct no-cart loop.
active_bios_D_out <= ext_bios_D_out when (ext_bios_sel='1' and ext_bios_loaded='1') else boot_rom_D_out;
irom_D_out <= active_bios_D_out when (bootloader_n='0' and A(15 downto 14)="00")
else ext_bios_D_out when (bootloader_n='0' and ext_bios_sel='1' and ext_bios_loaded='1' and A(15 downto 14)/="11")
-- Empty cartridge slot: data lines float high on real hardware.
-- Without this, SDRAM returns stale data from the last loaded ROM,
-- causing BIOSes that check for non-0xFF bytes (Korea) to
-- incorrectly detect a cartridge when none is present.
else x"FF" when (bootloader_n='1' and dbr='0')
else segadect2_D_out when (encrypt(1 downto 0)="10" and A(15)='0')
else mc8123_D_out when (encrypt(0)='1' and A(15)='0') or (encrypt(1 downto 0)="11" and A(14)='0') else rom_do;
process (clk_sys)
begin
if rising_edge(clk_sys) then
if RESET_n='0' then
det_D <= "111";
PSG_mux <= x"FF";
elsif det_WR_n='0' then
det_D <= D_in(2 downto 0);
elsif bal_WR_n='0' then
PSG_mux <= D_in;
end if;
end if;
end process;
process (IORQ_n,A,vdp_D_out,vdp2_D_out,io_D_out,irom_D_out,ram_D_out,nvram_D_out,
nvram_ex,nvram_e,nvram_cme,gg,det_D,fm_ena,bootloader_n,systeme)
begin
if IORQ_n='0' then
if A(7 downto 0)=x"F2" and fm_ena = '1' and systeme='0' then
D_out <= "11111"&det_D;
elsif (A(7 downto 6)="11" or (gg='1' and A(7 downto 3)="00000" and A(2 downto 0)/="111")) then
D_out(6 downto 0) <= io_D_out(6 downto 0);
-- during bootload, we trick the io ports so bit 7 indicates gg or sms game
if (bootloader_n='0') then
D_out(7) <= gg;
else
D_out(7) <= io_D_out(7);
end if;
elsif (A(2)='1' and systeme='1') then
D_out <= vdp2_D_out;
else
D_out <= vdp_D_out;
end if;
else
if A(15 downto 14)="11" and nvram_ex = '1' then
D_out <= nvram_D_out;
elsif A(15 downto 14)="11" and nvram_ex = '0' then
D_out <= ram_D_out;
elsif A(15 downto 13)="101" and nvram_cme = '1' then
D_out <= nvram_D_out;
elsif A(15 downto 14)="10" and nvram_e = '1' then
D_out <= nvram_D_out;
else
D_out <= irom_D_out;
end if;
end if;
end process;
-- detect MSX mapper : we check the two first bytes of the rom, must be 41:42
process (RESET_n, clk_sys)
begin
if RESET_n='0' then
mapper_msx_check0 <= false ;
mapper_msx_check1 <= false ;
mapper_msx_lock0 <= false ;
mapper_msx_lock <= false ;
mapper_msx <= '0' ;
else
if rising_edge(clk_sys) then
if bootloader_n='1' and not mapper_msx_lock then
if MREQ_n='0' then
-- in this state, A is stable but not D_out
if A=x"0000" then
mapper_msx_check0 <= (D_out=x"41") ;
elsif A=x"0001" then
mapper_msx_check1 <= (D_out=x"42") ;
mapper_msx_lock0 <= true ;
end if;
else
-- this state is similar to old_MREQ_n
-- now we can lock values depending on D_out
if mapper_msx_check0 and mapper_msx_check1 then
mapper_msx <= '1'; -- if 4142 lock msx mapper on
end if;
-- be paranoid : give only 1 chance to the mapper to lock on
mapper_msx_lock <= mapper_msx_lock0 ;
end if;
end if;
end if;
end if;
end process;
-- external ram control
process (RESET_n,clk_sys)
begin
if RESET_n='0' then
bank0 <= "00000000";
bank1 <= "00000001";
bank2 <= "00000010";
bank3 <= "00000011";
nvram_e <= '0';
nvram_ex <= '0';
nvram_p <= '0';
nvram_cme <= '0';
lock_mapper_B <= '0' ;
mapper_codies <= '0' ;
mapper_codies_lock <= '0' ;
else
if rising_edge(clk_sys) then
if WR_n='1' and MREQ_n='0' then
last_read_addr <= A; -- gyurco anti-ldir patch
end if;
if systeme = '1' then
-- no systeme mappers
elsif mapper_msx = '1' then
if WR_n='0' and A(15 downto 2)="00000000000000" then
case A(1 downto 0) is
when "00" => bank2 <= D_in;
when "01" => bank3 <= D_in;
when "10" => bank0 <= D_in;
when "11" => bank1 <= D_in ;
end case;
end if ;
else
if WR_n='0' and A(15 downto 2)="11111111111111" then
mapper_codies <= '0' ;
case A(1 downto 0) is
when "00" =>
nvram_ex <= D_in(4);
nvram_e <= D_in(3);
nvram_p <= D_in(2);
when "01" => bank0 <= D_in;
when "10" => bank1 <= D_in;
when "11" => bank2 <= D_in ;
end case;
end if;
if WR_n='0' and nvram_e='0' and mapper_lock='0' then
case A(15 downto 0) is
-- Codemasters
-- do not accept writing in adr $0000 (canary) unless we are sure that Codemasters mapper is in use
when x"0000" =>
if (lock_mapper_B='1') then
bank0 <= D_in ;
-- we need a strong criteria to set mapper_codies, hopefully only Ernie Els Golf
-- will have written a zero in $4000 before coming here
if D_in /= "00000000" and mapper_codies_lock = '0' then
if bank1 = "00000001" then
mapper_codies <= '1' ;
end if;
mapper_codies_lock <= '1' ;
end if;
end if;
when x"4000" =>
if last_read_addr /= x"4000" then -- gyurco anti-ldir patch
bank1(6 downto 0) <= D_in(6 downto 0) ;
bank1(7) <= '0' ;
-- mapper_codies <= mapper_codies or D_in(7) ;
nvram_cme <= D_in(7) ;
lock_mapper_B <= '1' ;
end if ;
when x"8000" =>
if last_read_addr /= x"8000" then -- gyurco anti-ldir patch
bank2 <= D_in ;
lock_mapper_B <= '1' ;
end if;
-- Korean mapper (Sangokushi 3, Dodgeball King)
when x"A000" =>
if last_read_addr /= x"A000" then -- gyurco anti-ldir patch
if mapper_codies='0' then
bank2 <= D_in ;
end if ;
end if ;
when others => null ;
end case ;
end if;
end if;
end if;
end if;
end process;
rom_a_i(12 downto 0) <= A(12 downto 0);
process (A,bank0,bank1,bank2,bank3,mapper_msx,mapper_codies,systeme,rom_bank)
begin
if systeme = '1' then
case A(15 downto 14) is
when "10" =>
rom_a_i(21 downto 13) <= "0000" & rom_bank & A(13);
when others =>
rom_a_i(21 downto 13) <= "000100" & A(15 downto 13);
end case;
elsif mapper_msx = '1' then
case A(15 downto 13) is
when "010" =>
rom_a_i(21 downto 13) <= '0' & bank0;
when "011" =>
rom_a_i(21 downto 13) <= '0' & bank1;
when "100" =>
rom_a_i(21 downto 13) <= '0' & bank2;
when "101" =>
rom_a_i(21 downto 13) <= '0' & bank3;
when others =>
rom_a_i(21 downto 13) <= "000000" & A(15 downto 13);
end case;
else
rom_a_i(13) <= A(13);
case A(15 downto 14) is
when "00" =>
-- first kilobyte is always from bank 0
if A(13 downto 10)="0000" and mapper_codies='0' then
rom_a_i(21 downto 14) <= (others=>'0');
else
rom_a_i(21 downto 14) <= bank0;
end if;
when "01" =>
rom_a_i(21 downto 14) <= bank1;
when others =>
rom_a_i(21 downto 14) <= bank2;
end case;
end if;
end process;
end Behavioral;