//--------------------------------------------------------------
// Implements I/O Model for simulation
//--------------------------------------------------------------
module io (Address, Data, CS, WE, OE);

// Set to 1 to have text output to the file "iolog.txt"
int iolog = 1;

// Set to 1 if you want debug printout on each IO access
int debug = 0;

int fd;
input [15:0] Address;
inout [7:0] Data;
input CS, WE, OE;

reg [7:0] IO [0:1<<16];

// Return data for the specified IO address:
//  1. If the current address is 0A00, that's the UART busy bit (which is never busy for ModelSim), so return 00
//  2. If the IO map is not defined for the current address, return FF
//  3. If the IO map is defined, return the value from it
//  4. Lastly, if !CS and !OE (not selecting the IO), tri-state the data bus
assign Data = (!CS && !OE) ? (Address==16'h0A00)? 8'h00 : (IO[Address]===8'hxx) ? 8'hFF : IO[Address] : {8{1'bz}};

// Read the initial content of the IO map from file
initial begin : init
    $readmemh("io.hex", IO);
    // If logging to a file was enabled, clear the file so we can append
    if (iolog) begin
        fd = $fopen("iolog.txt", "wb");
        $fclose(fd);
    end
end : init

always @(!CS && !OE) begin
    if (debug)
        $strobe("[IO] IN A=%H, D=%H", Address, Data);
end

always @(CS or WE)
    if (!CS && !WE) begin
        if (debug)
            $strobe("[IO] OUT A=%H, D=%H", Address, Data);
        if (Address==8*256) begin
            $write("%c", Data);
            // If logging to a file was enabled, append a character
            if (iolog) begin
                fd = $fopen("iolog.txt", "ab");
                $fwrite(fd, "%c", Data);
                $fclose(fd);
            end
        end
        IO[Address] = Data;
    end

always @(WE or OE)
    if (!WE && !OE)
        $display("[IO] error: OE and WE both active!");

endmodule
