Корзина

0  Товары

0.00 RUB

Вход

Классический вход

Сообщество

Блог

Speccy- живее всех живых!

Для многих компьютерных энтузиастов постсоветского пространства ZX Spectrum стал первым домашним компьютером. А для некоторых и вовсе отправной точкой в программировании. Не стал исключением и автор данных строк. Заполучив в свои руки отладочную плату, способную воспроизвести speccy, было решено незамедлительно этим заняться!

 На просторах интернета можно найти руководство: "Проектирование ПК «ZX-Spectrum» на ПЛИС. Туториал для «чайников» и не только…" Воспользовавшись данным руководством, создаем ZX Spectrum 48! По пути исправляем ошибку связанную с отсутствием бордюра. Загружаем в нашу плату, и видим заветную надпись (с) 1982 Sinclair Research Ltd. Но speccy 48 это достаточно скучно. Поэтому мы добавим музыкальный сопроцессор AY-8910 и целых 128 Кб памяти!

Собственно начнем с добавления AY-8910. Возьмем модуль музыкального процессора из проекта Aeon-Lite. Модуль называется "ay8910.vhd". Добавляем сигналы для нового модуля в основной файл "speccy.vhd":

VHDL Code:
  1. -- AY 8912
  2. AY_D_IN : in std_logic_vector(7 downto 0);
  3. AY_D_OUT : out std_logic_vector(7 downto 0);
  4. AY_RESET : out std_logic;
  5. AY_BDIR : out std_logic;
  6. AY_CS : out std_logic;
  7. AY_BC : out std_logic

Объявим сигналы для расширения 128 К и AY-8910:

VHDL Code:
  1. ------------------------- 128K -------------------------
  2. signal port_fffd_sel : std_logic; -- AY-8910
  3. signal port_7ffd_sel : std_logic; -- 128 K
  4. signal page_ram_sel : std_logic_vector(2 downto 0); -- RAM page
  5. signal page_shadow_scr : std_logic; -- SCREEN 1/0 select
  6. signal page_rom_sel : std_logic; -- ROM 48/128 select
  7. signal page_reg_disable : std_logic; -- 1 48 KByte
  8. -- RAM bank actually being accessed
  9. signal ram_page : std_logic_vector(2 downto 0); -- RAM bus adress

Далее займемся формирование сигналов выборки музыкального сопроцессора:

VHDL Code:
  1. --AY-8912
  2. port_fffd_sel <= '0' when (cpu_a_bus(15) = '1' and cpu_a_bus(7 downto 0) = x"FD" and cpu_iorq_n = '0') else '1';
  3. AY_CS <= port_fffd_sel;
  4. AY_RESET <= nRST and res_n;
  5. AY_BDIR <= not cpu_wr_n;
  6. AY_BC <= cpu_a_bus(14);
  7. AY_D_OUT <= cpu_do_bus when (port_fffd_sel = '0') else "00000000";

Тут все достаточно просто. Сигнал выбор музыкального сопроцессора формируется при обращении к портам 0xFFFD - порт управления, и 0xBFFD - порт данных. Они отличаются сигналом шины данных процессора cpu_a_bus(14). Поэтому мы проверяем что сигнал cpu_a_bus(15) находится в единице. А младшие 8 бит шины адреса равно 0xFD. В принципе никто не запрещает проверить полностью 16 битную шину адреса. На VHDL это просто изменение одной строки кода. Но вот в реальном железе это создает 16 битный компаратор, что для 1986 года являлось непозволительной роскошью :) Сигнал cpu_a_bus(14) подаем на вход AY-BC компонента AY-8910, управляющий типом входных данных (управление/данные). Далее нам нужно как то вывести звук на внешний усилитель. Для этого нам поможет сигма/дельта цап:

Verilog Code:
  1. module dac(DACout, DACin, Clk);
  2. output DACout; // This is the average output that feeds low pass filter
  3. reg DACout; // for optimum performance, ensure that this ff is in IOB
  4. input [7:0] DACin; // DAC input
  5. input Clk;
  6. reg [9:0] DeltaAdder; // Output of Delta adder
  7. reg [9:0] SigmaAdder; // Output of Sigma adder
  8. reg [9:0] SigmaLatch; // Latches output of Sigma adder
  9. reg [9:0] DeltaB; // B input of Delta adder
  10. always @(SigmaLatch) DeltaB = {SigmaLatch[9], SigmaLatch[9]} << (8);
  11. always @(DACin or DeltaB) DeltaAdder = DACin + DeltaB;
  12. always @(DeltaAdder or SigmaLatch) SigmaAdder = DeltaAdder + SigmaLatch;
  13. always @(posedge Clk)
  14. begin
  15. SigmaLatch <= SigmaAdder;
  16. DACout <= SigmaLatch[9];
  17. end
  18. endmodule

Некоторые demo требуют еще и чтение регистров AY-8910. Не проблема, добавляем чтение:

VHDL Code:
  1. cpu_di_bus <= rom_do when (rom_sel = '1' and cpu_mreq_n = '0') else
  2. ram_data when (rom_sel = '0' and cpu_mreq_n = '0') else
  3. "1" & TAPE_IN & "1" & kb_do_bus when (port_fe_sel = '0') else
  4. AY_D_IN when (port_fffd_sel='0') else "11111111";

Теперь наш спектрум обзавелся стерео звуком! Можно загружать демки! Но мы пойдем дальше, добавим 128 Кбайт памяти:

VHDL Code:
  1. port_7ffd_sel <= '0' when (cpu_a_bus(15) = '0' and cpu_a_bus(7 downto 0) = x"FD" and cpu_iorq_n = '0') else '1';
  2.  
  3. ram_page <= page_ram_sel when cpu_a_bus(15 downto 14) = "11" else -- Selectable bank at 0xc000
  4. cpu_a_bus(14) & cpu_a_bus(15 downto 14); -- A=bank: 00=XXX, 01=101, 10=010, 11=XXX
  5.  
  6. -- 128K paging register
  7. process(clock,reset_n)
  8. begin
  9. if reset_n = '0' then
  10. page_reg_disable <= '0';
  11. page_rom_sel <= '0';
  12. page_shadow_scr <= '0';
  13. page_ram_sel <= (others => '0');
  14. elsif rising_edge(clock) then
  15. if port_7ffd_sel = '0' and page_reg_disable = '0' and cpu_wr_n = '0' then
  16. page_reg_disable <= cpu_do_bus(5);
  17. page_rom_sel <= cpu_do_bus(4);
  18. page_shadow_scr <= cpu_do_bus(3);
  19. page_ram_sel <= cpu_do_bus(2 downto 0);
  20. end if;
  21. end if;
  22. end process;

Тут в принципе все аналогично AY-8910. Управление дополнительными страницами памяти осуществляется с помощью порта 0x7FFD. В этом коде мы его и реализовали. Чтоб лучше понять внесенные изменения предлагаю взглянуть на схему:

62f19373ec3cb16eb66d8e874508b278.jpg

Осталось поменять управление видео и основной памятью:

VHDL Code:
  1. process (vid_sel, vcnt, hcnt, cpu_a_bus)
  2. begin
  3. if vid_sel = '1' then
  4.  
  5. if page_shadow_scr = '1' then --Video from bank 7
  6. case hcnt(0) is
  7. --when '0' => ram_adr <= "000010" & vcnt(8 downto 7) & vcnt(3 downto 1) & vcnt(6 downto 4) & hcnt(7 downto 3);
  8. --when '1' => ram_adr <= "000010110" & vcnt(8 downto 4) & hcnt(7 downto 3);
  9. when '0' => ram_adr <= "001110" & vcnt(8 downto 7) & vcnt(3 downto 1) & vcnt(6 downto 4) & hcnt(7 downto 3);
  10. when '1' => ram_adr <= "001110110" & vcnt(8 downto 4) & hcnt(7 downto 3);
  11. end case;
  12. end if;
  13.  
  14. if page_shadow_scr = '0' then --Video from bank 5
  15. case hcnt(0) is
  16. --when '0' => ram_adr <= "000010" & vcnt(8 downto 7) & vcnt(3 downto 1) & vcnt(6 downto 4) & hcnt(7 downto 3);
  17. --when '1' => ram_adr <= "000010110" & vcnt(8 downto 4) & hcnt(7 downto 3);
  18. when '0' => ram_adr <= "001010" & vcnt(8 downto 7) & vcnt(3 downto 1) & vcnt(6 downto 4) & hcnt(7 downto 3);
  19. when '1' => ram_adr <= "001010110" & vcnt(8 downto 4) & hcnt(7 downto 3);
  20. end case;
  21. end if;
  22.  
  23. else
  24. --ram_adr <= "000" & cpu_a_bus;
  25. ram_adr <= "00" & ram_page & cpu_a_bus(13 downto 0);
  26. end if;
  27. end process;

Также необходимо изменить компонент lpm_rom, изменив его размер до 32 Кбайт. Загрузив в него объединенное ПЗУ Spectrum 48K/128K. И добавив сигнал выборки пзу:

VHDL Code:
  1. ROM: lpm_rom0
  2. port map(
  3. address => page_rom_sel & cpu_a_bus(13 downto 0),
  4. clock => clock,
  5. q => rom_do
  6. );

Схема того что у нас получилось:

a768962e81c7c5e0a087882ba2e3ef5b.jpg

На радостях загружаем какое-нибудь супер мега демо!

Файлы проекта: zx_spectrum.zip

Карта сайта Визуальная электроника Полезные ссылки сайта Визуальная электроника

Алиса это умеет

Оплата товаров

Cookies make it easier for us to provide you with our services. With the usage of our services you permit us to use cookies.
More information Ok