-- PassMe CPLD code for patching Nintendo DS cartridge transfer allowing to run homebrew code. -- PassMe is designed by Natrium42. http://www.dslinux.org/blogs/natrium42 -- Basic concept, CRC16 implementation and optimization by DarkFader. http://darkfader.net/ds/ -- Tested with Xilinx XC9572XL-VQ44. Enable all area/density optimization settings. -- ARM7 boots from address 0x080000C0. -- Boot ARM9 with: *(volatile unsigned int *)0x027FFE24 = 0x02004000; -- standard libraries library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; -- Xilinx primitive components library UNISIM; use UNISIM.VComponents.all; entity PassMe is port ( DSSLOT_CLK : in std_logic; DSSLOT_ROMCS : in std_logic; DSSLOT_RESET : in std_logic; DSSLOT_EEPCS : in std_logic; DSSLOT_IRQ : out std_logic; DSSLOT_IO : inout std_logic_vector(7 downto 0); DSCART_CLK : out std_logic; DSCART_ROMCS : out std_logic; DSCART_RESET : out std_logic; DSCART_EEPCS : out std_logic; DSCART_IRQ : in std_logic; DSCART_IO : inout std_logic_vector(7 downto 0); LED0 : out std_logic ); end PassMe; architecture rtl of PassMe is signal is_command : boolean; signal cmddata_cnt : natural range 0 to 511; -- 8 + 504 signal dsslot_clk_buf : std_logic; signal patched_data : std_logic_vector(7 downto 0); signal patched_data_6to0 : std_logic; signal crc_6to0 : std_logic; signal x_6to0 : std_logic; signal x : std_logic_vector(7 downto 0); signal next_crc16 : std_logic_vector(15 downto 0); signal crc16 : std_logic_vector(15 downto 0); signal patch_en : boolean; begin -- direct passthrough dsslot_clk_ibuf : IBUF port map ( I => DSSLOT_CLK, O => dsslot_clk_buf ); dsslot_clk_obuf : OBUF port map ( I => dsslot_clk_buf, O => DSCART_CLK ); DSCART_ROMCS <= DSSLOT_ROMCS; DSCART_RESET <= DSSLOT_RESET; DSSLOT_IRQ <= DSCART_IRQ; DSCART_EEPCS <= DSSLOT_EEPCS; -- activity LED LED0 <= not DSSLOT_ROMCS; -- CRC16 x(7) <= patched_data(7) xor crc16(7); x(6) <= patched_data(6) xor crc16(6); x(5) <= patched_data(5) xor crc16(5); x(4) <= patched_data(4) xor crc16(4); x(3) <= patched_data(3) xor crc16(3); x(2) <= patched_data(2) xor crc16(2); x(1) <= patched_data(1) xor crc16(1); x(0) <= patched_data(0) xor crc16(0); x_6to0 <= x(6) xor x(5) xor x(4) xor x(3) xor x(2) xor x(1) xor x(0); patched_data_6to0 <= patched_data(0) xor patched_data(1) xor patched_data(2) xor patched_data(3) xor patched_data(4) xor patched_data(5) xor patched_data(6); crc_6to0 <= crc16(6) xor crc16(5) xor crc16(4) xor crc16(3) xor crc16(2) xor crc16(1) xor crc16(0); next_crc16(15) <= x_6to0 xor x(7); next_crc16(14) <= x_6to0; next_crc16(13) <= x(6) xor x(7); next_crc16(12) <= x(6) xor x(5); next_crc16(11) <= x(5) xor x(4); next_crc16(10) <= x(4) xor x(3); next_crc16(9) <= x(3) xor x(2); next_crc16(8) <= x(2) xor x(1); next_crc16(7) <= x(1) xor x(0) xor crc16(15); next_crc16(6) <= x(0) xor crc16(14); next_crc16(5 downto 1) <= crc16(13 downto 9); next_crc16(0) <= x_6to0 xor x(7) xor crc16(8); -- patch process (cmddata_cnt, crc16) begin case (cmddata_cnt - 8) is -- ldr pc, 0x027FFE24 when 16#004# => patched_data <= X"18"; when 16#005# => patched_data <= X"F0"; when 16#006# => patched_data <= X"9F"; when 16#007# => patched_data <= X"E5"; -- set autostart bits when 16#01F# => patched_data <= X"04"; when 16#022# => patched_data <= X"01"; -- patch ARM9 entry address to endless loop when 16#024# => patched_data <= X"04"; when 16#025# => patched_data <= X"FE"; when 16#026# => patched_data <= X"7F"; when 16#027# => patched_data <= X"02"; -- patch ARM7 entry address when 16#034# => patched_data <= X"C0"; when 16#035# => patched_data <= X"00"; when 16#036# => patched_data <= X"00"; when 16#037# => patched_data <= X"08"; -- calculated CRC16 when 16#15E# => patched_data <= crc16(7 downto 0); when 16#15F# => patched_data <= crc16(15 downto 8); when others => patched_data <= DSCART_IO; end case; end process; -- dataswitcher process (DSSLOT_RESET, DSSLOT_ROMCS, DSSLOT_EEPCS, DSSLOT_IO, DSCART_IO, patched_data) begin DSSLOT_IO <= (others => 'Z'); -- default is high impedance DSCART_IO <= (others => 'Z'); -- default is high impedance if (DSSLOT_RESET='1') then -- if not reset if (DSSLOT_ROMCS='0') then -- ROM is selected if (is_command) then -- is command byte DSCART_IO <= DSSLOT_IO; -- from DS to cartridge else -- is data byte if (patch_en) then -- patch enabled DSSLOT_IO <= patched_data; else DSSLOT_IO <= DSCART_IO; end if; end if; elsif (DSSLOT_EEPCS='0') then -- EEPROM is selected DSCART_IO(7) <= DSSLOT_IO(7); -- pass serial data DSSLOT_IO(6) <= DSCART_IO(6); -- pass serial data in opposite direction end if; end if; end process; -- patch_en process (DSSLOT_RESET, dsslot_clk_buf) begin if (DSSLOT_RESET='0') then patch_en <= true; -- patch header elsif (rising_edge(dsslot_clk_buf)) then if (is_command) then if (DSCART_IO(5) = '1') then -- detect 3C command, assume other command bytes are 00 patch_en <= false; -- do not patch other data end if; end if; end if; end process; -- crc16 process (is_command, dsslot_clk_buf) begin if (is_command) then crc16 <= (others => '1'); -- init CRC16 elsif (rising_edge(dsslot_clk_buf)) then if (cmddata_cnt /= 8+16#15E#) then -- do not destroy high byte of header CRC crc16 <= next_crc16; end if; end if; end process; -- cmddata_cnt, is_command process (DSSLOT_ROMCS, dsslot_clk_buf) begin if (DSSLOT_ROMCS='1') then cmddata_cnt <= 0; -- new transfer is_command <= true; -- start with command elsif (rising_edge(dsslot_clk_buf)) then if (cmddata_cnt mod 8 = 7) then is_command <= false; -- next byte is data end if; cmddata_cnt <= cmddata_cnt + 1; -- next byte end if; end process; end rtl;