見出し画像

A2PDP11 (15) Apple II インターフェースの設計

Apple II との接続方法を検討します。

Apple II のバスインターフェースとして、スロット I/O ($C0nx) アドレスを使うことにします。

  • $C0n0 : 入力ハンドシェーク

    • rstb:Apple II からの読み出し時、ビット 7 が 1 なら、入力可能を示す。

    • rrdy:Apple II のキー入力時、ビット 7 に 1 を書き込む。

  • $C0n1  : 入力データ

    • rbuf:Apple II キー入力コード(ASCII 8 ビット)。

  • $C0n2  : 表示ハンドシェーク

    • xstb:Apple II からの読み出し時、ビット 7 が 1 なら、表示待ちデータがあることを示す。

    • xrdy:Apple II が表示データを受付可能な時、ビット 7 に 1 を書き込む。

  • $C0n3  : 表示データ

    • xbud:ODT からの表示データ(ASCII 8 ビット)。

Tang Nano 9K の入出力ポートが足りないので、Apple II のアドレス信号と DAL を多重化します。修正した回路図を下記に示します。
/DALLO_OE で、U2-U3 と U4-U5 の出力を切り替えます。

Apple II インターフェース回路図

tov.sv を修正します。

`default_nettype none
// TEST17 2024.11.17 Apple II Interface

module top ( 
    inout wire [15:0] dal,          // DAL<21:0>, BS<1:0>
    input wire [3:0] aio,           // AIO<3:0>
    inout wire [7:0] d,             // Apple II Data D<7:0>
    input wire devsel_n,            // Apple II /DEVSEL
    input wire rw,                  // Apple II R/W
    input wire bufctl_n,
    input wire ale_n,
    output wire nxm_n,
    input wire sctl_n,
    input wire clk,
    output reg cont_n,
    output reg miss_n,
    output reg dallo_oe_n,
    output reg event_n,
    output reg irq0,
    output reg irq1,
    output wire [1:0] O_psram_ck,
    output wire [1:0] O_psram_ck_n,
    inout wire [15:0] IO_psram_dq,
    inout wire [1:0] IO_psram_rwds,
    output wire [1:0] O_psram_cs_n,
    output wire [1:0] O_psram_reset_n,
    input wire mclk,
    input wire rst_n,
    output wire led1
);

// AIO CODE
parameter NIO           = 4'b1111;  // internal operation only, no I/O
parameter GP_READ       = 4'b1110;  // General-Purpose read
parameter INTERRUPT_ACK = 4'b1101;  // Interrupt acknowledge, vector read
parameter REQUEST_READ  = 4'b1100;  // Instruction-stream request read
parameter RMW_NOLOCK    = 4'b1011;  // Read/Modify/Write - no bus lock
parameter RMW_BUSLOCK   = 4'b1010;  // Read/Modify/Write - bus lock
parameter DATA_READ     = 4'b1001;  // Data-stream read
parameter DEMAND_READ   = 4'b1000;  // Instruction-stream demand read
parameter GP_WRITE      = 4'b0101;  // General-Purpose word write
parameter BYTE_WRITE    = 4'b0011;  // Bus byte write
parameter WORD_WRITE    = 4'b0001;  // Bus word write

// BANK SELECT
parameter BS_MEM        = 2'b00;    // Memory
parameter BS_SYS        = 2'b01;    // System register
parameter BS_EXT        = 2'b10;    // Extarnal I/O
parameter BS_INT        = 2'b11;    // Internal register

// GP CODE
parameter POWER_UP0     = 8'o000;   // Reads the power-up mode
parameter POWER_UP2     = 8'o002;   // Reads the power-up mode, clears the FPA’s FPS

// DLART
parameter DLART         = 19'o1777756;  // DLART registers
parameter RCSR          = 22'o17777560; // Receiver Control and Status Register
parameter RBUF          = 22'o17777562; // Receiver Buffer Register
parameter XCSR          = 22'o17777564; // Transmitter Control And Status Register
parameter XBUF          = 22'o17777566; // Transmitter Buffer Register

// PC11 Paper Tape Reader/Punch
parameter PC11          = 19'o1777755;  // PC11 registers
parameter PRS           = 22'o17777550; // Paper Tape Reader Status Register
parameter PRB           = 22'o17777552; // Paper Tape Reader Buffer Register
parameter PPS           = 22'o17777554; // Paper Tape Punch Status Register
parameter PPB           = 22'o17777556; // Paper Tape Punch Buffer Register

parameter HIMEM         = 22'o17757777; // End of RAM

// Apple II Register
parameter A2RCSR        = 8'h00;         // Console out status; Read = rstb, Write = rrdy
parameter A2RBUF        = 8'h01;         // Cousole out data
parameter A2XCSR        = 8'h02;         // Console in status; Read = xstb, Write = xrdy  
parameter A2XBUF        = 8'h03;         // Console in data

Apple II アドレスの取り込み
clk の 3 倍周期の clk_x3 を使い、dal に多重化された信号を取り出します。ALE がアサートされるタイミングで dallo_oe_n をアサートし、Apple II アドレス a[7:0] を取り込みます。

logic clk_x3;   // clk x 3 = 54 MHz
Gowin_rPLL rpll(
    .clkout(clk_x3), //output clkout
    .clkin(clk) //input clkin
);

assign event_n = 1'b1;
assign irq0 = 1'b0;
assign irq1 = 1'b0;

logic [3:0] count;
logic [15:0] mdallo;
logic [21:0] mdal;
logic [3:0] maio;
logic [1:0] mbs;
logic [7:0] gp_code;
logic [7:0] a;              // Apple II Address

always_ff@(posedge clk_x3) begin
    if (ale_n) begin
        count <= 0;
    end else begin
        if (count == 0) begin
            dallo_oe_n <= 1'b1;
            mdallo <= dal;
        end else if (count == 1) begin
            if ((aio == GP_READ) || (aio == GP_WRITE)) begin
                gp_code <= mdallo[7:0];
            end else begin
                gp_code <= 8'b11111111;
            end
            maio <= aio;
            mbs[0] <= dal[6];
            mbs[1] <= dal[7];
            mdal[21] <= dal[8];
            mdal[20] <= dal[0];
            mdal[19] <= dal[9];
            mdal[18] <= dal[10];
            mdal[17] <= dal[11];
            mdal[16] <= dal[12];
            mdal[15:0] <= mdallo;
        end else if (count == 2) begin
            a[0] <= dal[5];
            a[1] <= dal[4];
            a[2] <= dal[3];
            a[3] <= dal[2];
            a[4] <= dal[1];
            a[5] <= dal[15];
            a[6] <= dal[14];
            a[7] <= dal[13];
            dallo_oe_n <= 1'b0;
        end
        count <= count + 1'b1;
    end
end

ABORT (nxm_n) の生成
メモリ実装範囲外、DLART と PC11(ペーパーテープ)以外の外部 I/O は、エラーとします。

assign nxm_n = sctl_n ? 1'b1 :
    ((maio[2] == 0) && (mbs == BS_MEM) && (mdal > HIMEM)) ? 1'b0 :
    ((maio[2] == 0) && (mbs == BS_EXT) && 
        ((mdal[21:3] != DLART) && (mdal[21:3] != PC11))) ? 1'b0 :
    1'b1;

Console ODT の制御部
Apple II の /DEVSEL 信号を CLK に同期させます。
Apple II のスロット I/O アドレスにアクセスすると、/DEVSEL がローになります。
I/O アドレスの読み出し時に以下を DAL に出力します。
 rstb は、コンソール ODT が入力を受け付けることを示します。
 xstb は、コンソール ODT からの表示データがあることを示します。
 xbuf は、コンソール ODT からの表示データです。
I/O アドレスの書き込み時に以下を DAL から取り込みます。
 rrdy は、キー入力データがあることを示します。
 rbuf は、コンソール ODT への入力データです。
 xrdy は、コンソール ODT からの出力を Apple II が受け付けることを示します。

logic [7:0] xbuf;
logic [7:0] rbuf;
logic xrdy;
logic rrdy;
logic [7:0] d0;
assign d = rw ? d0 : 8'bz;

logic devsel0;
logic devsel1;
always_ff@(posedge clk) begin
    devsel0 <= !devsel_n;
    devsel1 <= devsel0;
end

logic dev_done;
always_ff@(posedge clk) begin
    if (devsel1) begin
        if (dev_done) begin;
            if (rw) begin
                case (a[3:0])
                    A2RCSR : d0 <= {rstb, 7'b0};
                    A2XCSR : d0 <= {xstb, 7'b0};
                    A2XBUF : d0 <= xbuf;
                endcase
            end else begin
                case (a[3:0])
                    A2RCSR : rrdy <= d[7];
                    A2RBUF : rbuf <= d;
                    A2XCSR : xrdy <= d[7];
                endcase
            end
            dev_done <= 1'b0;
        end
    end else begin
        dev_done <= 1'b1;
    end
end

コンソール ODT の表示処理
アドレス XBUF の読み出し時に、xbuf を設定し、xstb を 1 にし、表示データがあることを知らせます。
xstb が 1 の状態で、xrdy が 0 になるのを待ちます。Apple II 側では表示が完了すると xrdy を 0 にします。xrdy が 0 になると、xstb を 0 にします。
xstb が 0 の状態で、xrdy が 1 になるのを待ちます。Apple II 側では、xstb が 0 の時、xrdy を 1 にします。
xdone は、Transmitter Control And Status Register (XCSR) の done フラグで、コンソール ODT から出力があることを示します。

logic xdone;
logic xstb;
always_ff@(negedge clk) begin
    if (gp_code == 8'o014) begin
        xdone <= 1'b1;
        xstb <= 1'b0;
    end else if ((sctl_n == 0) && (mdal == XBUF)) begin
        xstb <= 1'b1;
        xdone <= 1'b0;
        xbuf <= dal[7:0];
    end else if (xstb & !xrdy) begin
        xstb <= 1'b0;
    end else if (!xstb & xrdy) begin
        xdone <= 1'b1;
    end
end

コンソール ODT の入力処理
Apple II のキー入力がある時、rrdy を 1 にします。
Apple II は、rstb が 1 の時、キー入力データをバスに出力します。
rrdy が 1 の時、rstb を 1 にし、入力を受け付けたことを示します。
rstb が 1 の時、rstb をクリアし、rdone をセットします。
RBUF の読み出し時に、キー入力データを dal に出力します。
rdone は、Receiver Control/Status Register (RCSR) の done フラグで、コンソールへの入力があることを示します。

logic rdone;
logic rstb;
always_ff@(negedge clk) begin
    if (gp_code == 8'o014) begin
        rdone <= 1'b0;
        rstb <= 1'b0;
    end else if (rrdy) begin
        rstb <= 1'b1;
    end else if (rstb) begin
        rstb <= 1'b0;
        rdone <= 1'b1;
    end else if (mdal == RBUF) begin
        rdone <= 1'b0;
    end
end

DAL の出力
コンソール ODT、ペーパーテープ(なし)、パワーアップ構成レジスタ、RAM 読み出しの処理を行います。

assign dal = bufctl_n ? 16'bz : 
    (mdal == RCSR) ? {8'b0, rdone, 7'b0} :
    (mdal == XCSR) ? {8'b0, xdone, 7'b0} :
    (mdal == RBUF) ? {8'b0, rbuf} :
    (mdal == PRS) ? 16'b1000_0000_0000_0000 :
    (mdal == PRB) ? 16'b0 :
    (mdal == PPS) ? 16'b0000_0000_1000_0000 :
    (gp_code == POWER_UP0) ? 16'b0000000_0_0000_0_01_1 :
    (gp_code == POWER_UP2) ? 16'b0000000_0_0000_0_01_1 :
    (mdal <= HIMEM) ? ram_rdata : 16'bz;

RAM の読み書き

logic init;
assign led1 = init;
ram u_ram(
    .*
);

assign cont_n = 1'b0;
logic [21:0] ram_addr;
logic [15:0] ram_rdata;
logic [15:0] ram_wdata;
logic ram_read;
logic ram_write;
logic ram_byte;
always_ff@(posedge clk_x3) begin
    if ((mbs == BS_MEM) && (count == 2)) begin
        ram_addr <= mdal;
        if ((maio[3:2] == 2'b10) || (maio == REQUEST_READ)) begin
            miss_n <= 1'b0;
            ram_read <= 1'b1;
        end
    end else if (count == 0) begin
        miss_n <= 1'b1;
        ram_read <= 1'b0;
    end
end

always_ff@(posedge clk) begin
    if ((mbs == BS_MEM) && (maio[3:2] == 2'b00) && (!sctl_n)) begin
        if (maio == BYTE_WRITE) begin
            ram_byte <= 1'b1;
        end
        ram_write <= 1'b1;
        ram_wdata <= dal[15:0];
    end else begin
        ram_write <= 1'b0;
        ram_byte <= 1'b0;
    end
end

endmodule

`default_nettype wire

参考文献

DCJ11 Microprocessor User's Guide, 5.3 CONSOLE ODT, 1983

Apple II Reference Manual,  Signal description for peripheral I/O, 1978


いいなと思ったら応援しよう!