`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