mirror of
https://github.com/MikhaelKaa/zx_cartridge.git
synced 2026-04-30 19:13:54 +03:00
142 lines
8.2 KiB
Verilog
142 lines
8.2 KiB
Verilog
`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 |