`timescale 1ns / 1ps // Модуль картриджа для ZX Spectrum // 19.02.2026 Mikhael Kaa // // Аппаратная часть: // - 4 микросхем AM29F040 (по 512 КБ) -> всего 2 МБ = 256 страницы по 8 КБ. // - Адресные линии CPU A0..A12 подключены напрямую ко всем микросхемам ПЗУ. // - Старшие линии адреса A13..A18 формируются внутри этого модуля. // // Окно памяти 0x0000..0x3FFF (16 КБ) разделено на две 8‑килобайтные половины: // - Нижняя половина (A13 = 0, 0x0000..0x1FFF) : всегда отображается на страницу 0 (BIOS картриджа). // - Верхняя половина (A13 = 1, 0x2000..0x3FFF) : отображается на выбираемую страницу. // // Выбор страницы: // 8‑битный номер страницы формируется как reg_bank[7:0]. // - биты [7:6] : выбор одной из четырех микросхем (0‑3). // - биты [5:0] : выбор 8‑килобайтной страницы внутри выбранной микросхемы (0‑63). // Таким образом, можно адресовать любую из 256 страниц. // // Регистр управления reg_ctl (8 бит): // reg_ctl[7] : при установке в 1 отключает ПЗУ картриджа (все выходы пассивны). // Остальные биты зарезервированы. // // Порты ввода‑вывода (запись/чтение, активный уровень низкий): // bank : запись reg_bank (биты 7..0) – происходит, когда // A15=1, A14=1, A13=0, A7=0, IORQ=0, WR=0. Порт 0xdf7f. // control : запись регистра управления reg_ctl – происходит, когда // A15=1, A14=0, A13=1, A7=0, IORQ=0, WR=0. Порт 0xbf7f. // Чтение любого из этих портов возвращает значение соответствующего регистра. // // Выходы: // ZX_ROM_blk – активный высокий уровень; блокирует внутреннее ПЗУ ZX Spectrum. // CR_ROM_oe_n – выход разрешения выходов для всех микросхем ПЗУ (активный низкий). // CR_ROM_A[5:0] – линии адреса A13..A18 для микросхем ПЗУ. // Для нижнего окна (A13=0) на эту шину выставляется 0. // Для верхнего окна (A13=1) на ней передаётся 6‑битное смещение страницы. // CR_ROM_CS[3:0] – выбор микросхем (активный низкий). Одна линия становится низкой // только при обращении к картриджу (cpu_use_rom, MREQ и RD активны, // картридж не отключён) и совпадении выбранной микросхемы. // В нижнем окне всегда выбирается микросхема 0. // // Сброс: почти все регистры асинхронно очищаются низким уровнем reset_n. module zx_cartridge ( // Сброс input reset_n, // Управляющие сигналы CPU input iorq_n, input rd_n, input wr_n, input mreq_n, // Часть адресной шины CPU input A7, input A13, input A14, input A15, inout [7:0] D, // Сигнал блокировки внутреннего ПЗУ ZX Spectrum output ZX_ROM_blk, // Выход разрешения для ПЗУ картриджа (активный низкий) output CR_ROM_oe_n, // Старшие биты адреса для ПЗУ (A13..A18) output [5:0] CR_ROM_A, // Выбор кристаллов для четырех ПЗУ 29040 (активный низкий) output [3:0] CR_ROM_CS ); // 8‑битный банковый регистр (хранит биты 7..0 номера страницы) reg [7:0] reg_bank = 8'b0; // 8‑битный регистр управления: // reg_ctl[6:0] - доступны софтам после сброса // reg_ctl[7] – отключение картриджа (1 = отключён) reg [7:0] reg_ctl = 8'b0; // В спектруме декодирование порта 7ffd идет по А1, А15 == 0. // Декодирование портов ввода‑вывода картриджа wire bank = iorq_n | A7 | A13 | ~A14 | ~A15; // A15=1, A14=1, A13=0, A7=0 wire control = iorq_n | A7 | ~A13 | A14 | ~A15; // A15=1, A14=0, A13=1, A7=0 // CPU обращается к области ПЗУ 0x0000..0x3FFF (A15=0, A14=0) wire cpu_use_rom = ~(A14 | A15); wire is_enable = reg_ctl[7]; wire write_bank = ~bank & ~wr_n; always @(posedge write_bank or negedge reset_n) begin if (!reset_n) reg_bank <= 8'b0; else reg_bank <= D; end wire write_control = ~control & ~wr_n; always @(posedge write_control or negedge reset_n) begin if (!reset_n) reg_ctl[7] <= 1'b0; // только бит отключения сбрасывается else reg_ctl <= D; end // Чтение регистров обратно в CPU assign D = (~bank & ~rd_n) ? reg_bank[7:0] : (~control & ~rd_n) ? reg_ctl : 8'bz; // Разделение на выбор микросхемы (2 бита) и смещение страницы (6 бит) wire [1:0] chip_sel = reg_bank[7:6]; // какая из 4 микросхем (0..3) wire [5:0] page_offs = reg_bank[5:0]; // смещение внутри микросхемы (0..63) // Условие доступа к картриджу: // CPU читает область ПЗУ, MREQ и RD активны, картридж не отключён wire rom_access = cpu_use_rom & ~mreq_n & ~rd_n & ~is_enable; // Сигнал разрешения выходов и блокировки ПЗУ assign CR_ROM_oe_n = ~rom_access; assign ZX_ROM_blk = rom_access; // CR_ROM_A зависит от окна: // нижнее окно (A13=0) : принудительный адрес 0 (страница 0) // верхнее окно (A13=1) : используется смещение из регистра assign CR_ROM_A = (A13 == 1'b0) ? 6'b0 : page_offs; // Формирование сигналов выбора микросхем: // Для нижнего окна всегда включается микросхема 0. // Для верхнего окна включается микросхема, выбранная chip_sel. // CS активен низким уровнем и активен только при rom_access = истина. // CS активен (0) только при rom_access = 1 и выполнении условий: // - для микросхемы 0: либо нижнее окно (A13=0), либо верхнее окно с chip_sel = 0 // - для микросхем 1..3: только верхнее окно (A13=1) и chip_sel равен номеру микросхемы assign CR_ROM_CS[0] = ~( rom_access & ( (A13 == 1'b0) || // нижнее окно всегда выбирает чип 0 ( (A13 == 1'b1) && (chip_sel == 2'd0) ) ) ); assign CR_ROM_CS[1] = ~( rom_access & ( (A13 == 1'b1) && (chip_sel == 2'd1) ) ); assign CR_ROM_CS[2] = ~( rom_access & ( (A13 == 1'b1) && (chip_sel == 2'd2) ) ); assign CR_ROM_CS[3] = ~( rom_access & ( (A13 == 1'b1) && (chip_sel == 2'd3) ) ); endmodule