見出し画像

A2PDP11 (16) コンソール ODT

Apple II をコンソールとして使い、ODT で DCJ11 を制御できるようにします。

Apple II 側ソフトウェア
Apple II 側のソフトウェアは、cc65 で書きました。Apple2BuildPipeline を使うと Xcode 上で、Apple II のソフトウェアを開発することができます。

/*
 * main.c
 * a2odt
 *
 * Created by ushicow on 2024/11/24.
 * Copyright (c) 2024 nanja.info All rights reserved.
 *
 */
// TEST18

#include <stdio.h>
#include <conio.h>
#include <peekpoke.h>
#include <stdint.h>

#define SLOT 7
#define RCSR (0xc080 + SLOT * 16)
#define RBUF (0xc081 + SLOT * 16)
#define XCSR (0xc082 + SLOT * 16)
#define XBUF (0xc083 + SLOT * 16)

int main(void)
{
    uint8_t rdata;
    uint8_t wdata = 0;
    
    printf("Serial Start\n");
    POKE(XCSR, 0);
    while (1) {
        if (PEEK(XCSR) >= 128) {    // xstb == 1
            POKE(XCSR, 0);          // xrdy <= 0
            rdata = PEEK(XBUF);
            putchar(rdata);
        } else if (PEEK(RCSR) >= 128) {  // rstb == 1
            POKE(RBUF, wdata);
            POKE(RCSR, 0);          // rrdy <= 0
        } else if (kbhit()) {
            wdata = cgetc();
            if (wdata == 24) {      // control-X
                return(0);
            }
            POKE(RCSR, 128);        // rrdy <= 1
        } else {
            POKE(XCSR, 128);        // xrdy <= 1
        }
    }
    return 0;
}

XCSR のビット7 (xstb) が 1 なら、XBUF を読み、ディスプレイに表示します。
RCSR のビット7 (rstb) が 1 なら、RBUF にキー入力を書き込みます。
キー入力があれば、RCSR のビット7 (rrdy) に 1 を書き込みます。
Control-X で終了できるようにしました。
表示データもキー入力もなければ、XCSRのビット 7 (xrdy) に 1 を書き込みます。

PDP-11/HACK の Test #1 プログラムを実行してみます。

PDP-11 Test #1

Tang Nano 9K コードの修正
最初非常に不安定でしたが、試行錯誤の結果、以下のように修正することで、完全ではありませんが、ある程度安定するようになりました。

RAM のアクセス用クロック memory_clk を、DCJ11 のクロックの 9 倍 (162 MHz) に変更し、3 分周した 54 MHz のクロック clk_x3 で、各種信号の処理を行うようにしました。

top.sv

`default_nettype none
// DCJ11 TangNano interface by nanja.info
// TEST18 2024.11.24 Use Extarnal clock

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

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 [4: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 == 2) 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;
            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

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;

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
always_ff@(posedge clk_x3) begin
      devsel0 <= !devsel_n;
    devsel1 <= devsel0;
end

logic dev_done;
logic error;
//always_ff@(posedge clk) begin
always_ff@(posedge clk_x3) 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];
                    8'h0f  : error <= 1'b1;
                endcase
            end
            dev_done <= 1'b0;
        end
    end else begin
        dev_done <= 1'b1;
        error <= 1'b0;
    end
end

logic xdone;
logic xstb;
//always_ff@(posedge clk) begin
always_ff@(posedge clk_x3) 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

logic rdone;
logic rstb;
//always_ff@(posedge clk) begin
always_ff@(posedge clk_x3) 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

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;

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 == 3)) 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
always_ff@(posedge clk_x3) 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

ram.sv

`default_nettype none
// DCJ11 TangNano interface by nanja.info
// PSRAM interface module
// TEST18 2024.11.24 Use external clock

module ram (
    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 ram_read,
    input wire ram_write,
    input wire ram_byte,
    input wire [21:0] ram_addr,
    output reg [15:0] ram_rdata,
    input wire [15:0] ram_wdata,
    input wire clk,
    output reg clk_x3,
    input wire rst_n,
    output wire init
);

assign init = init_calib0;      // for debug

Gowin_rPLL_ram pll_ram(
    .clkout(memory_clk), //output clkout
    .lock(pll_lock), //output lock
    .clkoutp(memory_clk_p), //output clkoutp
    .clkoutd(clk_d), //output clkoutd
    .clkoutd3(clk_x3), //output clkoutd3
    .clkin(clk) //input clkin
);

logic clk_d;
logic memory_clk/* synthesis syn_keep=1 */;
logic memory_clk_p;
logic pll_lock;
logic init_calib0;
logic init_calib1;
logic clk_out/* synthesis syn_keep=1 */;
logic cmd0;
logic cmd1;
logic cmd_en0;
logic cmd_en1;
logic [20:0] addr0;
logic [20:0] addr1;
logic [31:0] wr_data0;
logic [31:0] wr_data1;
logic [31:0] rd_data0;
logic [31:0] rd_data1;
logic rd_data_valid0;
logic rd_data_valid1;
logic [3:0] data_mask0;
logic [3:0] data_mask1;

//assign rst_n = 1'b1;
assign cmd1 = 1'b0;
assign cmd_en1 = 1'b0;
assign addr1 = 21'b0;
assign wr_data1 = 32'b0;
assign data_mask1 = 4'b0;
logic clk2;
assign clk_out = clk2;

PSRAM_Memory_Interface_HS_2CH_V2_Top psram(
    .clk_d(clk_d), //input clk_d
    .rst_n(rst_n), //input rst_n
    .memory_clk(memory_clk), //input memory_clk
    .memory_clk_p(memory_clk_p), //input memory_clk_p
    .pll_lock(pll_lock), //input pll_lock
    .O_psram_ck(O_psram_ck), //output [1:0] O_psram_ck
    .O_psram_ck_n(O_psram_ck_n), //output [1:0] O_psram_ck_n
    .IO_psram_rwds(IO_psram_rwds), //inout [1:0] IO_psram_rwds
    .O_psram_reset_n(O_psram_reset_n), //output [1:0] O_psram_reset_n
    .IO_psram_dq(IO_psram_dq), //inout [15:0] IO_psram_dq
    .O_psram_cs_n(O_psram_cs_n), //output [1:0] O_psram_cs_n
    .init_calib0(init_calib0), //output init_calib0
    .init_calib1(init_calib1), //output init_calib1
    .clk_out(clk2), //output clk_out
    .cmd0(cmd0), //input cmd0
    .cmd1(cmd1), //input cmd1
    .cmd_en0(cmd_en0), //input cmd_en0
    .cmd_en1(cmd_en1), //input cmd_en1
    .addr0(addr0), //input [20:0] addr0
    .addr1(addr1), //input [20:0] addr1
    .wr_data0(wr_data0), //input [31:0] wr_data0
    .wr_data1(wr_data1), //input [31:0] wr_data1
    .rd_data0(rd_data0), //output [31:0] rd_data0
    .rd_data1(rd_data1), //output [31:0] rd_data1
    .rd_data_valid0(rd_data_valid0), //output rd_data_valid0
    .rd_data_valid1(rd_data_valid1), //output rd_data_valid1
    .data_mask0(data_mask0), //input [3:0] data_mask0
    .data_mask1(data_mask1) //input [3:0] data_mask1
);

logic rd0;
logic rd1;
logic wr0;
logic wr1;
always_ff@(posedge clk_out) begin
    rd0 <= ram_read;
    rd1 <= rd0;
    wr0 <= ram_write;
    wr1 <= wr0;
end

logic [5:0] mcycle;
always_ff@(posedge clk_out or negedge rst_n) begin
    if (!rst_n) begin
        mcycle <= 0;
        wr_data0 <= 32'b0;
        cmd_en0 <= 0;
        data_mask0 <= 4'b1111;
    end else if (init_calib0) begin
        if (mcycle == 0) begin
            if (rd1) begin
                addr0 <= ram_addr[21:1];
                cmd0 <= 0;   // READ
                cmd_en0 <= 1;
                mcycle <= 1;
            end else if (wr1) begin
                if (ram_byte) begin
                    data_mask0 <= {!ram_addr[0], ram_addr[0], 2'b11};
                end else begin
                    data_mask0 <= 4'b0011;
                end
                wr_data0[31:16] <= ram_wdata;
                addr0 <= ram_addr[21:1];
                cmd0 <= 1;   // WRITE
                cmd_en0 <= 1;
                mcycle <= 15;
            end
        end else if (mcycle == 1) begin
            cmd_en0 <= 0;
            mcycle <= 2;
        end else if (mcycle < 14) begin
            mcycle <= mcycle + 1'b1;
        end else if (mcycle == 14) begin
            if (!rd1) begin
                mcycle <= 0;
            end
        end else if (mcycle == 15) begin
            cmd_en0 <= 0;
            data_mask0 <= 4'b1111;
            mcycle <= 16;
        end else if (mcycle < 28) begin
            mcycle <= mcycle + 1'b1;
        end else if (mcycle == 28) begin
            if (!wr1) begin
                mcycle <= 0;
            end
        end
    end
end

logic [1:0] rcycle;
always_ff@(negedge clk_out) begin
    if (mcycle == 1) begin
        rcycle <= 0;
    end else if (rd_data_valid0) begin
        if (rcycle == 1'b0) begin
            ram_rdata <= rd_data0[31:16];
        end
        rcycle <= rcycle + 1'b1;
    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

Apple2BuildPipeline


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