From 03281591f617900b0cce4db0362f73aee48ed838 Mon Sep 17 00:00:00 2001 From: nino-porcino Date: Wed, 29 Dec 2021 16:18:10 +0100 Subject: [PATCH] fork Gehstock's project --- .gitignore | 25 + README.md | 15 + apple-one.qpf | 30 + apple-one.qsf | 192 + clean.bat | 37 + pll.qip | 0 rtl/apple1.v | 263 + rtl/apple1_mist.sv | 201 + rtl/arlet_6502/ALU.v | 108 + rtl/arlet_6502/arlet_6502.v | 72 + rtl/arlet_6502/chip_6502.v | 66 + rtl/arlet_6502/chip_6502_mux.v | 10 + rtl/arlet_6502/cpu.v | 1244 ++++ rtl/async_tx_rx.v | 227 + rtl/build_id.tcl | 35 + rtl/clock.v | 55 + rtl/debounce.v | 72 + rtl/font_rom.v | 70 + rtl/led_and_key.v | 167 + rtl/mist-modules/.gitignore | 1 + rtl/mist-modules/README.md | 33 + rtl/mist-modules/arcade_inputs.v | 191 + rtl/mist-modules/cofi.sv | 59 + rtl/mist-modules/dac.vhd | 48 + rtl/mist-modules/data_io.v | 259 + rtl/mist-modules/mist.qip | 10 + rtl/mist-modules/mist.vhd | 123 + rtl/mist-modules/mist_core.qip | 7 + rtl/mist-modules/mist_video.v | 156 + rtl/mist-modules/osd.v | 216 + rtl/mist-modules/pll.qip | 0 rtl/mist-modules/rgb2ypbpr.v | 103 + rtl/mist-modules/scandoubler.v | 229 + rtl/mist-modules/sd_card.v | 619 ++ rtl/mist-modules/user_io.v | 748 +++ rtl/mist-modules_old/hq2x.sv | 454 ++ rtl/mist-modules_old/mist_io.v | 491 ++ rtl/mist-modules_old/osd.v | 179 + rtl/mist-modules_old/scandoubler.v | 195 + rtl/mist-modules_old/video_mixer.sv | 242 + rtl/old/aholme_6502.v | 32 + rtl/old/tm1638.v | 122 + rtl/pll.qip | 4 + rtl/pll.v | 348 ++ rtl/ps2keyboard.v | 339 ++ rtl/pwr_reset.v | 55 + rtl/ram.v | 45 + rtl/rom_basic.v | 39 + rtl/rom_wozmon.v | 41 + rtl/roms/basic.hex | 4096 ++++++++++++++ rtl/roms/ram.hex | 8192 +++++++++++++++++++++++++++ rtl/roms/vga_font.bin | 703 +++ rtl/roms/vga_font.hex | 640 +++ rtl/roms/vga_font_bitreversed.hex | 1024 ++++ rtl/roms/vga_vram.bin | 2048 +++++++ rtl/roms/wozmon.hex | 256 + rtl/uart.v | 169 + rtl/vga.v | 299 + rtl/vram.v | 46 + 59 files changed, 25750 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 apple-one.qpf create mode 100644 apple-one.qsf create mode 100644 clean.bat create mode 100644 pll.qip create mode 100644 rtl/apple1.v create mode 100644 rtl/apple1_mist.sv create mode 100644 rtl/arlet_6502/ALU.v create mode 100644 rtl/arlet_6502/arlet_6502.v create mode 100644 rtl/arlet_6502/chip_6502.v create mode 100644 rtl/arlet_6502/chip_6502_mux.v create mode 100644 rtl/arlet_6502/cpu.v create mode 100644 rtl/async_tx_rx.v create mode 100644 rtl/build_id.tcl create mode 100644 rtl/clock.v create mode 100644 rtl/debounce.v create mode 100644 rtl/font_rom.v create mode 100644 rtl/led_and_key.v create mode 100644 rtl/mist-modules/.gitignore create mode 100644 rtl/mist-modules/README.md create mode 100644 rtl/mist-modules/arcade_inputs.v create mode 100644 rtl/mist-modules/cofi.sv create mode 100644 rtl/mist-modules/dac.vhd create mode 100644 rtl/mist-modules/data_io.v create mode 100644 rtl/mist-modules/mist.qip create mode 100644 rtl/mist-modules/mist.vhd create mode 100644 rtl/mist-modules/mist_core.qip create mode 100644 rtl/mist-modules/mist_video.v create mode 100644 rtl/mist-modules/osd.v create mode 100644 rtl/mist-modules/pll.qip create mode 100644 rtl/mist-modules/rgb2ypbpr.v create mode 100644 rtl/mist-modules/scandoubler.v create mode 100644 rtl/mist-modules/sd_card.v create mode 100644 rtl/mist-modules/user_io.v create mode 100644 rtl/mist-modules_old/hq2x.sv create mode 100644 rtl/mist-modules_old/mist_io.v create mode 100644 rtl/mist-modules_old/osd.v create mode 100644 rtl/mist-modules_old/scandoubler.v create mode 100644 rtl/mist-modules_old/video_mixer.sv create mode 100644 rtl/old/aholme_6502.v create mode 100644 rtl/old/tm1638.v create mode 100644 rtl/pll.qip create mode 100644 rtl/pll.v create mode 100644 rtl/ps2keyboard.v create mode 100644 rtl/pwr_reset.v create mode 100644 rtl/ram.v create mode 100644 rtl/rom_basic.v create mode 100644 rtl/rom_wozmon.v create mode 100644 rtl/roms/basic.hex create mode 100644 rtl/roms/ram.hex create mode 100644 rtl/roms/vga_font.bin create mode 100644 rtl/roms/vga_font.hex create mode 100644 rtl/roms/vga_font_bitreversed.hex create mode 100644 rtl/roms/vga_vram.bin create mode 100644 rtl/roms/wozmon.hex create mode 100644 rtl/uart.v create mode 100644 rtl/vga.v create mode 100644 rtl/vram.v diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4d4adce --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +Test +build_id.v +db +history.db +greybox_tmp +incremental_db +output_files +simulation +PLLJ_PLLSPE_INFO.txt +*.bak +*.orig +*.rej +*.qdf +*.rpt +*.smsg +*.summary +*.done +*.jdi +*.pin +*.sof +*.qws +*.ppf +*.ddb +*.srf + diff --git a/README.md b/README.md new file mode 100644 index 0000000..fc15349 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# Apple1_MIST + +Apple1 implementation for the MiST FPGA. + +This was forked from [Gehstock's project](https://github.com/Gehstock/Mist_FPGA). + +## CHANGELOG + +2021-12-28 + +- 15 kHz video output (NTSC) and use of MiST scandoubler/video pipeline +- more accurate 7x8 character matrix (5x7 + hardware spacing) +- clock is now derived from 14.31818 instead of 25 MHz (more accurate) +- serial port communication feature is disabled/removed + diff --git a/apple-one.qpf b/apple-one.qpf new file mode 100644 index 0000000..c3038e9 --- /dev/null +++ b/apple-one.qpf @@ -0,0 +1,30 @@ +# -------------------------------------------------------------------------- # +# +# Copyright (C) 1991-2013 Altera Corporation +# Your use of Altera Corporation's design tools, logic functions +# and other software and tools, and its AMPP partner logic +# functions, and any output files from any of the foregoing +# (including device programming or simulation files), and any +# associated documentation or information are expressly subject +# to the terms and conditions of the Altera Program License +# Subscription Agreement, Altera MegaCore Function License +# Agreement, or other applicable license agreement, including, +# without limitation, that your use is for the sole purpose of +# programming logic devices manufactured by Altera and sold by +# Altera or its authorized distributors. Please refer to the +# applicable agreement for further details. +# +# -------------------------------------------------------------------------- # +# +# Quartus II 64-Bit +# Version 13.1.0 Build 162 10/23/2013 SJ Web Edition +# Date created = 21:11:27 January 26, 2018 +# +# -------------------------------------------------------------------------- # + +QUARTUS_VERSION = "13.1" +DATE = "21:11:27 January 26, 2018" + +# Revisions + +PROJECT_REVISION = "apple-one" diff --git a/apple-one.qsf b/apple-one.qsf new file mode 100644 index 0000000..0611144 --- /dev/null +++ b/apple-one.qsf @@ -0,0 +1,192 @@ +# -------------------------------------------------------------------------- # +# +# Copyright (C) 1991-2013 Altera Corporation +# Your use of Altera Corporation's design tools, logic functions +# and other software and tools, and its AMPP partner logic +# functions, and any output files from any of the foregoing +# (including device programming or simulation files), and any +# associated documentation or information are expressly subject +# to the terms and conditions of the Altera Program License +# Subscription Agreement, Altera MegaCore Function License +# Agreement, or other applicable license agreement, including, +# without limitation, that your use is for the sole purpose of +# programming logic devices manufactured by Altera and sold by +# Altera or its authorized distributors. Please refer to the +# applicable agreement for further details. +# +# -------------------------------------------------------------------------- # +# +# Quartus II 64-Bit +# Version 13.1.0 Build 162 10/23/2013 SJ Web Edition +# Date created = 11:23:36 April 10, 2018 +# +# -------------------------------------------------------------------------- # +# +# Notes: +# +# 1) The default values for assignments are stored in the file: +# apple-one_assignment_defaults.qdf +# If this file doesn't exist, see file: +# assignment_defaults.qdf +# +# 2) Altera recommends that you do not modify this file. This +# file is updated automatically by the Quartus II software +# and any changes you make may be lost or overwritten. +# +# -------------------------------------------------------------------------- # + + + +# Project-Wide Assignments +# ======================== +set_global_assignment -name ORIGINAL_QUARTUS_VERSION 13.1 +set_global_assignment -name PROJECT_CREATION_TIME_DATE "21:11:27 JANUARY 26, 2018" +set_global_assignment -name LAST_QUARTUS_VERSION 13.1 +set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files + +# Classic Timing Assignments +# ========================== +set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0 +set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85 + +# Analysis & Synthesis Assignments +# ================================ +set_global_assignment -name FAMILY "Cyclone III" +set_global_assignment -name TOP_LEVEL_ENTITY apple1_mist +set_global_assignment -name DEVICE_FILTER_PIN_COUNT 144 +set_global_assignment -name DEVICE_FILTER_SPEED_GRADE 8 + +# Fitter Assignments +# ================== +set_global_assignment -name DEVICE EP3C25E144C8 +set_global_assignment -name ERROR_CHECK_FREQUENCY_DIVISOR 1 +set_global_assignment -name STRATIX_DEVICE_IO_STANDARD "2.5 V" +set_global_assignment -name CRC_ERROR_OPEN_DRAIN OFF +set_global_assignment -name CYCLONEII_RESERVE_NCEO_AFTER_CONFIGURATION "USE AS REGULAR IO" +set_global_assignment -name ENABLE_CONFIGURATION_PINS OFF +set_global_assignment -name ENABLE_NCE_PIN OFF +set_global_assignment -name ENABLE_BOOT_SEL_PIN OFF +set_global_assignment -name CYCLONEIII_CONFIGURATION_SCHEME "PASSIVE SERIAL" +set_global_assignment -name FORCE_CONFIGURATION_VCCIO ON +set_global_assignment -name RESERVE_DATA0_AFTER_CONFIGURATION "USE AS REGULAR IO" +set_global_assignment -name RESERVE_DATA1_AFTER_CONFIGURATION "USE AS REGULAR IO" +set_global_assignment -name RESERVE_FLASH_NCE_AFTER_CONFIGURATION "USE AS REGULAR IO" + +# EDA Netlist Writer Assignments +# ============================== +set_global_assignment -name EDA_SIMULATION_TOOL "ModelSim-Altera (VHDL)" + +# Assembler Assignments +# ===================== +set_global_assignment -name USE_CONFIGURATION_DEVICE OFF +set_global_assignment -name GENERATE_RBF_FILE ON + +# Power Estimation Assignments +# ============================ +set_global_assignment -name POWER_PRESET_COOLING_SOLUTION "23 MM HEAT SINK WITH 200 LFPM AIRFLOW" +set_global_assignment -name POWER_BOARD_THERMAL_MODEL "NONE (CONSERVATIVE)" + +# Advanced I/O Timing Assignments +# =============================== +set_global_assignment -name OUTPUT_IO_TIMING_NEAR_END_VMEAS "HALF VCCIO" -rise +set_global_assignment -name OUTPUT_IO_TIMING_NEAR_END_VMEAS "HALF VCCIO" -fall +set_global_assignment -name OUTPUT_IO_TIMING_FAR_END_VMEAS "HALF SIGNAL SWING" -rise +set_global_assignment -name OUTPUT_IO_TIMING_FAR_END_VMEAS "HALF SIGNAL SWING" -fall + +# start EDA_TOOL_SETTINGS(eda_simulation) +# --------------------------------------- + + # EDA Netlist Writer Assignments + # ============================== +set_global_assignment -name EDA_OUTPUT_DATA_FORMAT VHDL -section_id eda_simulation + +# end EDA_TOOL_SETTINGS(eda_simulation) +# ------------------------------------- + + # start DESIGN_PARTITION(Top) + # --------------------------- + + # Incremental Compilation Assignments + # =================================== +set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top +set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top +set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top + + # end DESIGN_PARTITION(Top) + # ------------------------- + +# Pin & Location Assignments +# ========================== +set_location_assignment PIN_7 -to LED +set_location_assignment PIN_54 -to CLOCK_27 +set_location_assignment PIN_144 -to VGA_R[5] +set_location_assignment PIN_143 -to VGA_R[4] +set_location_assignment PIN_142 -to VGA_R[3] +set_location_assignment PIN_141 -to VGA_R[2] +set_location_assignment PIN_137 -to VGA_R[1] +set_location_assignment PIN_135 -to VGA_R[0] +set_location_assignment PIN_133 -to VGA_B[5] +set_location_assignment PIN_132 -to VGA_B[4] +set_location_assignment PIN_125 -to VGA_B[3] +set_location_assignment PIN_121 -to VGA_B[2] +set_location_assignment PIN_120 -to VGA_B[1] +set_location_assignment PIN_115 -to VGA_B[0] +set_location_assignment PIN_114 -to VGA_G[5] +set_location_assignment PIN_113 -to VGA_G[4] +set_location_assignment PIN_112 -to VGA_G[3] +set_location_assignment PIN_111 -to VGA_G[2] +set_location_assignment PIN_110 -to VGA_G[1] +set_location_assignment PIN_106 -to VGA_G[0] +set_location_assignment PIN_136 -to VGA_VS +set_location_assignment PIN_119 -to VGA_HS +set_location_assignment PIN_65 -to AUDIO_L +set_location_assignment PIN_80 -to AUDIO_R +set_location_assignment PIN_105 -to SPI_DO +set_location_assignment PIN_88 -to SPI_DI +set_location_assignment PIN_126 -to SPI_SCK +set_location_assignment PIN_127 -to SPI_SS2 +set_location_assignment PIN_91 -to SPI_SS3 +set_location_assignment PIN_13 -to CONF_DATA0 +set_location_assignment PLL_1 -to "pll:pll|altpll:altpll_component" + +set_global_assignment -name PRE_FLOW_SCRIPT_FILE "quartus_sh:rtl/build_id.tcl" + + +# end ENTITY(apple1_mist) +# ----------------------- +set_global_assignment -name VERILOG_FILE "rtl/mist-modules/user_io.v" +set_global_assignment -name VERILOG_FILE "rtl/mist-modules/sd_card.v" +set_global_assignment -name VERILOG_FILE "rtl/mist-modules/scandoubler.v" +set_global_assignment -name VERILOG_FILE "rtl/mist-modules/rgb2ypbpr.v" +set_global_assignment -name VERILOG_FILE "rtl/mist-modules/osd.v" +set_global_assignment -name VERILOG_FILE "rtl/mist-modules/mist_video.v" +set_global_assignment -name QIP_FILE "rtl/mist-modules/mist_core.qip" +set_global_assignment -name VHDL_FILE "rtl/mist-modules/mist.vhd" +set_global_assignment -name QIP_FILE "rtl/mist-modules/mist.qip" +set_global_assignment -name VERILOG_FILE "rtl/mist-modules/data_io.v" +set_global_assignment -name VHDL_FILE "rtl/mist-modules/dac.vhd" +set_global_assignment -name SYSTEMVERILOG_FILE "rtl/mist-modules/cofi.sv" +set_global_assignment -name VERILOG_FILE "rtl/mist-modules/arcade_inputs.v" +set_global_assignment -name VERILOG_FILE rtl/arlet_6502/cpu.v +set_global_assignment -name VERILOG_FILE rtl/arlet_6502/ALU.v +set_global_assignment -name VERILOG_FILE rtl/arlet_6502/arlet_6502.v +set_global_assignment -name VERILOG_FILE rtl/apple1.v +set_global_assignment -name VERILOG_FILE rtl/clock.v +set_global_assignment -name VERILOG_FILE rtl/pwr_reset.v +set_global_assignment -name VERILOG_FILE rtl/ram.v +set_global_assignment -name VERILOG_FILE rtl/rom_basic.v +set_global_assignment -name VERILOG_FILE rtl/rom_wozmon.v +set_global_assignment -name VERILOG_FILE rtl/vram.v +set_global_assignment -name VERILOG_FILE rtl/uart.v +set_global_assignment -name VERILOG_FILE rtl/ps2keyboard.v +set_global_assignment -name VERILOG_FILE rtl/vga.v +set_global_assignment -name VERILOG_FILE rtl/async_tx_rx.v +set_global_assignment -name VERILOG_FILE rtl/font_rom.v +set_global_assignment -name SYSTEMVERILOG_FILE rtl/video_mixer.sv +set_global_assignment -name VERILOG_FILE rtl/scandoubler.v +set_global_assignment -name QIP_FILE rtl/pll.qip +set_global_assignment -name VERILOG_FILE rtl/osd.v +set_global_assignment -name VERILOG_FILE rtl/mist_io.v +set_global_assignment -name SYSTEMVERILOG_FILE rtl/hq2x.sv +set_global_assignment -name SYSTEMVERILOG_FILE rtl/apple1_mist.sv +set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top \ No newline at end of file diff --git a/clean.bat b/clean.bat new file mode 100644 index 0000000..b3b7c3b --- /dev/null +++ b/clean.bat @@ -0,0 +1,37 @@ +@echo off +del /s *.bak +del /s *.orig +del /s *.rej +del /s *~ +rmdir /s /q db +rmdir /s /q incremental_db +rmdir /s /q output_files +rmdir /s /q simulation +rmdir /s /q greybox_tmp +rmdir /s /q hc_output +rmdir /s /q .qsys_edit +rmdir /s /q hps_isw_handoff +rmdir /s /q sys\.qsys_edit +rmdir /s /q sys\vip +cd sys +for /d %%i in (*_sim) do rmdir /s /q "%%~nxi" +cd .. +for /d %%i in (*_sim) do rmdir /s /q "%%~nxi" +del build_id.v +del c5_pin_model_dump.txt +del PLLJ_PLLSPE_INFO.txt +del /s *.qws +del /s *.ppf +del /s *.ddb +del /s *.csv +del /s *.cmp +del /s *.sip +del /s *.spd +del /s *.bsf +del /s *.f +del /s *.sopcinfo +del /s *.xml +del /s new_rtl_netlist +del /s old_rtl_netlist + +pause diff --git a/pll.qip b/pll.qip new file mode 100644 index 0000000..e69de29 diff --git a/rtl/apple1.v b/rtl/apple1.v new file mode 100644 index 0000000..70dc627 --- /dev/null +++ b/rtl/apple1.v @@ -0,0 +1,263 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Description: Apple1 hardware core +// +// Author.....: Alan Garfield +// Niels A. Moseley +// Date.......: 26-1-2018 +// + +module apple1( + input clk14, // 25 MHz master clock + input rst_n, // active low synchronous reset (needed for simulation) + + // I/O interface to computer + input uart_rx, // asynchronous serial data input from computer + output uart_tx, // asynchronous serial data output to computer + output uart_cts, // clear to send flag to computer + + // I/O interface to keyboard + input ps2_clk, // PS/2 keyboard serial clock input + input ps2_din, // PS/2 keyboard serial data input + input ps2_select, // Input to select the PS/2 keyboard instead of the UART + + // Outputs to VGA display + output vga_h_sync, // hozizontal VGA sync pulse + output vga_v_sync, // vertical VGA sync pulse + output vga_red, // red VGA signal + output vga_grn, // green VGA signal + output vga_blu, // blue VGA signal + input vga_cls, // clear screen button + + // Debugging ports + output [15:0] pc_monitor // spy for program counter / debugging +); + ////////////////////////////////////////////////////////////////////////// + // Registers and Wires + + wire [15:0] ab; + wire [7:0] dbi; + wire [7:0] dbo; + wire we; + + ////////////////////////////////////////////////////////////////////////// + // Clocks + + wire cpu_clken; + clock clock( + .clk14(clk14), + .rst_n(rst_n), + .cpu_clken(cpu_clken) + ); + + ////////////////////////////////////////////////////////////////////////// + // Reset + + wire rst; + pwr_reset pwr_reset( + .clk14(clk14), + .rst_n(rst_n), + .enable(cpu_clken), + .rst(rst) + ); + + ////////////////////////////////////////////////////////////////////////// + // 6502 + + arlet_6502 arlet_6502( + .clk (clk14), + .enable (cpu_clken), + .rst (rst), + .ab (ab), + .dbi (dbi), + .dbo (dbo), + .we (we), + .irq_n (1'b1), + .nmi_n (1'b1), + .ready (cpu_clken), + .pc_monitor (pc_monitor) + ); + + ////////////////////////////////////////////////////////////////////////// + // Address Decoding + + wire ram_cs = (ab[15:13] == 3'b000); // 0x0000 -> 0x1FFF + + // font mode, background and foreground colour + wire vga_mode_cs = (ab[15:2] == 14'b11000000000000); // 0xC000 -> 0xC003 + + // RX: Either keyboard or UART input + // TX: Always VGA and UART output + wire rx_cs = (ab[15:1] == 15'b110100000001000); // 0xD010 -> 0xD011 + wire tx_cs = (ab[15:1] == 15'b110100000001001); // 0xD012 -> 0xD013 + + // select UART on transmit but only receive when PS/2 is not selected. + wire uart_cs = tx_cs | ((~ps2_select) & rx_cs); + + // select PS/2 keyboard input when selected. + wire ps2kb_cs = ps2_select & rx_cs; + + // VGA always get characters when they are sent. + wire vga_cs = tx_cs; + + wire basic_cs = (ab[15:12] == 4'b1110); // 0xE000 -> 0xEFFF + wire rom_cs = (ab[15:8] == 8'b11111111); // 0xFF00 -> 0xFFFF + + ////////////////////////////////////////////////////////////////////////// + // RAM and ROM + + // RAM + wire [7:0] ram_dout; + ram ram( + .clk(clk14), + .address(ab[12:0]), + .w_en(we & ram_cs), + .din(dbo), + .dout(ram_dout) + ); + + // WozMon ROM + wire [7:0] rom_dout; + rom_wozmon rom_wozmon( + .clk(clk14), + .address(ab[7:0]), + .dout(rom_dout) + ); + + // Basic ROM + wire [7:0] basic_dout; + rom_basic rom_basic( + .clk(clk14), + .address(ab[11:0]), + .dout(basic_dout) + ); + + ////////////////////////////////////////////////////////////////////////// + // Peripherals + + // UART + wire [7:0] uart_dout; + uart #( + `ifdef SIM + 100, 10, 2 // for simulation don't need real baud rates + `else + 25000000, 115200, 8 // 25MHz, 115200 baud, 8 times RX oversampling + `endif + ) my_uart( + .clk(clk14), + .enable(uart_cs & cpu_clken), + .rst(rst), + + .uart_rx(uart_rx), + .uart_tx(uart_tx), + .uart_cts(uart_cts), + + .address(ab[1:0]), // for uart + .w_en(we & uart_cs), + .din(dbo), + .dout(uart_dout) + ); + + // PS/2 keyboard interface + wire [7:0] ps2_dout; + ps2keyboard keyboard( + .clk14(clk14), + .rst(rst), + .key_clk(ps2_clk), + .key_din(ps2_din), + .cs(ps2kb_cs), + .address(ab[0]), + .dout(ps2_dout) + ); + + // VGA Display interface + reg [2:0] fg_colour; + reg [2:0] bg_colour; + reg [1:0] font_mode; + reg [7:0] vga_mode_dout; + + vga vga( + .clk14(clk14), + .enable(vga_cs & cpu_clken), + .rst(rst), + + .vga_h_sync(vga_h_sync), + .vga_v_sync(vga_v_sync), + .vga_red(vga_red), + .vga_grn(vga_grn), + .vga_blu(vga_blu), + + .address(ab[0]), + .w_en(we & vga_cs), + .din(dbo), + .mode(font_mode), + .fg_colour(fg_colour), + .bg_colour(bg_colour), + .clr_screen(vga_cls) + ); + + // Handle font mode and foreground and background + // colours. This so isn't Apple One authentic, but + // it can't hurt to have some fun. :D + always @(posedge clk14 or posedge rst) + begin + if (rst) + begin + font_mode <= 2'b0; + fg_colour <= 3'd7; + bg_colour <= 3'd0; + end + else + begin + case (ab[1:0]) + 2'b00: + begin + vga_mode_dout = {6'b0, font_mode}; + if (vga_mode_cs & we & cpu_clken) + font_mode <= dbo[1:0]; + end + 2'b01: + begin + vga_mode_dout = {5'b0, fg_colour}; + if (vga_mode_cs & we & cpu_clken) + fg_colour <= dbo[2:0]; + end + 2'b10: + begin + vga_mode_dout = {5'b0, bg_colour}; + if (vga_mode_cs & we & cpu_clken) + bg_colour <= dbo[2:0]; + end + default: + vga_mode_dout = 8'b0; + endcase + end + end + + ////////////////////////////////////////////////////////////////////////// + // CPU Data In MUX + + // link up chip selected device to cpu input + assign dbi = ram_cs ? ram_dout : + rom_cs ? rom_dout : + basic_cs ? basic_dout : + uart_cs ? uart_dout : + ps2kb_cs ? ps2_dout : + vga_mode_cs ? vga_mode_dout : + 8'hFF; +endmodule diff --git a/rtl/apple1_mist.sv b/rtl/apple1_mist.sv new file mode 100644 index 0000000..c71973a --- /dev/null +++ b/rtl/apple1_mist.sv @@ -0,0 +1,201 @@ +// Apple-1 for MiST +// +// Forked from Gehstock's implementation https://github.com/Gehstock/Mist_FPGA +// +// + +// TODO integrate with mist-modules +// TODO load binary files into memory +// TODO support ACI interface for load and save +// TODO additional RAM with SDRAM +// TODO reset key from keyboard +// TODO cls key from keyboard +// TODO power on-off +// TODO special expansion boards: TMS9918, SID +// TODO white/green/amber switch? +// TODO reset if pll not locked +// TODO rename "vga" into "display" +// TODO reorganize file structure +// TODO check ps2 clock + + +module apple1_mist( + input CLOCK_27, + + // SPI interface to arm io controller + input SPI_SCK, + output SPI_DO, + input SPI_DI, + //input SPI_SS2, + input SPI_SS3, + //input SPI_SS4, + input CONF_DATA0, + + // SDRAM interface + inout [15:0] SDRAM_DQ, // SDRAM Data bus 16 Bits + output [12:0] SDRAM_A, // SDRAM Address bus 13 Bits + output SDRAM_DQML, // SDRAM Low-byte Data Mask + output SDRAM_DQMH, // SDRAM High-byte Data Mask + output SDRAM_nWE, // SDRAM Write Enable + output SDRAM_nCAS, // SDRAM Column Address Strobe + output SDRAM_nRAS, // SDRAM Row Address Strobe + output SDRAM_nCS, // SDRAM Chip Select + output [1:0] SDRAM_BA, // SDRAM Bank Address + output SDRAM_CLK, // SDRAM Clock + output SDRAM_CKE, // SDRAM Clock Enable + + // VGA interface + output [5:0] VGA_R, + output [5:0] VGA_G, + output [5:0] VGA_B, + output VGA_HS, + output VGA_VS, + + // other + output LED, + input UART_RX, + output AUDIO_L, + output AUDIO_R +); + +`include "rtl\build_id.v" + +/******************************************************************************************/ +/******************************************************************************************/ +/***************************************** @user_io ***************************************/ +/******************************************************************************************/ +/******************************************************************************************/ + +localparam CONF_STR = { + "APPLE 1;;", + "O34,Scandoubler Fx,None,HQ2x,CRT 25%,CRT 50%;", + "T6,Reset;", + "V,v1.00.",`BUILD_DATE +}; + +localparam conf_str_len = $size(CONF_STR)>>3; + +wire st_reset_switch = buttons[1]; +wire st_menu_reset = status[6]; + +wire clk14; // the 14.31818 MHz clock +wire clk_osd; // x4 clock for the OSD menu +wire r, g, b; +wire hs, vs; + +wire [31:0] status; +wire [1:0] buttons; +wire [1:0] switches; + +wire scandoubler_disable; +wire ypbpr; +wire no_csync; + +wire ps2_kbd_clk; +wire ps2_kbd_data; + +assign LED = 1; + +wire reset_button = status[0] | st_menu_reset | st_reset_switch; + + +pll pll +( + .inclk0(CLOCK_27), + .c0(clk_osd), + .c1(clk14), + .locked(), + .areset() +); + +apple1 apple1 +( + .clk14(clk14), + .rst_n(~reset_button), + + .uart_rx(), // uart not connected + .uart_tx(), // uart not connected + .uart_cts(), // uart not connected + + .ps2_clk(ps2_kbd_clk), + .ps2_din(ps2_kbd_data), + .ps2_select(1'b1), + + .vga_h_sync(hs), + .vga_v_sync(vs), + .vga_red(r), + .vga_grn(g), + .vga_blu(b), + + .vga_cls(), // clear screen button (not connected yet) + .pc_monitor() // debug program counter (not used) +); + +mist_video +#( + .COLOR_DEPTH(1), + .OSD_AUTO_CE(1) +) +mist_video +( + .clk_sys(clk_osd), // 2x the VDP clock for the scandoubler + + // OSD SPI interface + .SPI_DI(SPI_DI), + .SPI_SCK(SPI_SCK), + .SPI_SS3(SPI_SS3), + + .scanlines(2'b00), // scanlines (00-none 01-25% 10-50% 11-75%) + .ce_divider(1), // non-scandoubled pixel clock divider 0 - clk_sys/4, 1 - clk_sys/2 + .scandoubler_disable(scandoubler_disable), + + .no_csync(no_csync), // 1 = disable csync without scandoubler + .ypbpr(ypbpr), // 1 = YPbPr output on composite sync + + .rotate(2'b00), // Rotate OSD [0] - rotate [1] - left or right + .blend(0), // composite-like blending + + // video input + .R(r), + .G(g), + .B(b), + .HSync(hs), + .VSync(vs), + + // MiST video output signals + .VGA_R(VGA_R), + .VGA_G(VGA_G), + .VGA_B(VGA_B), + .VGA_VS(VGA_VS), + .VGA_HS(VGA_HS), +); + +user_io +#( + .STRLEN(conf_str_len) + //.PS2DIV(14) // ps2 clock divider: CLOCK / 14 must be approx = 15 Khz +) +user_io ( + .conf_str (CONF_STR ), + + .clk_sys (clk14 ), + + .SPI_CLK (SPI_SCK ), + .SPI_SS_IO (CONF_DATA0 ), + .SPI_MISO (SPI_DO ), + .SPI_MOSI (SPI_DI ), + + .status (status ), + .buttons (buttons ), + .switches (switches ), + + .scandoubler_disable ( scandoubler_disable ), + .ypbpr ( ypbpr ), + .no_csync ( no_csync ), + + // ps2 interface + .ps2_kbd_clk (ps2_kbd_clk ), + .ps2_kbd_data (ps2_kbd_data ) +); + +endmodule \ No newline at end of file diff --git a/rtl/arlet_6502/ALU.v b/rtl/arlet_6502/ALU.v new file mode 100644 index 0000000..8d05fc0 --- /dev/null +++ b/rtl/arlet_6502/ALU.v @@ -0,0 +1,108 @@ +/* + * ALU. + * + * AI and BI are 8 bit inputs. Result in OUT. + * CI is Carry In. + * CO is Carry Out. + * + * op[3:0] is defined as follows: + * + * 0011 AI + BI + * 0111 AI - BI + * 1011 AI + AI + * 1100 AI | BI + * 1101 AI & BI + * 1110 AI ^ BI + * 1111 AI + * + */ + +module ALU( clk, op, right, AI, BI, CI, CO, BCD, OUT, V, Z, N, HC, RDY ); + input clk; + input right; + input [3:0] op; // operation + input [7:0] AI; + input [7:0] BI; + input CI; + input BCD; // BCD style carry + output [7:0] OUT; + output CO; + output V; + output Z; + output N; + output HC; + input RDY; + +reg [7:0] OUT; +reg CO; +wire V; +wire Z; +reg N; +reg HC; + +reg AI7; +reg BI7; +reg [8:0] temp_logic; +reg [7:0] temp_BI; +reg [4:0] temp_l; +reg [4:0] temp_h; +wire [8:0] temp = { temp_h, temp_l[3:0] }; +wire adder_CI = (right | (op[3:2] == 2'b11)) ? 0 : CI; + +// calculate the logic operations. The 'case' can be done in 1 LUT per +// bit. The 'right' shift is a simple mux that can be implemented by +// F5MUX. +always @* begin + case( op[1:0] ) + 2'b00: temp_logic = AI | BI; + 2'b01: temp_logic = AI & BI; + 2'b10: temp_logic = AI ^ BI; + 2'b11: temp_logic = AI; + endcase + + if( right ) + temp_logic = { AI[0], CI, AI[7:1] }; +end + +// Add logic result to BI input. This only makes sense when logic = AI. +// This stage can be done in 1 LUT per bit, using carry chain logic. +always @* begin + case( op[3:2] ) + 2'b00: temp_BI = BI; // A+B + 2'b01: temp_BI = ~BI; // A-B + 2'b10: temp_BI = temp_logic; // A+A + 2'b11: temp_BI = 0; // A+0 + endcase +end + +// HC9 is the half carry bit when doing BCD add +wire HC9 = BCD & (temp_l[3:1] >= 3'd5); + +// CO9 is the carry-out bit when doing BCD add +wire CO9 = BCD & (temp_h[3:1] >= 3'd5); + +// combined half carry bit +wire temp_HC = temp_l[4] | HC9; + +// perform the addition as 2 separate nibble, so we get +// access to the half carry flag +always @* begin + temp_l = temp_logic[3:0] + temp_BI[3:0] + adder_CI; + temp_h = temp_logic[8:4] + temp_BI[7:4] + temp_HC; +end + +// calculate the flags +always @(posedge clk) + if( RDY ) begin + AI7 <= AI[7]; + BI7 <= temp_BI[7]; + OUT <= temp[7:0]; + CO <= temp[8] | CO9; + N <= temp[7]; + HC <= temp_HC; + end + +assign V = AI7 ^ BI7 ^ CO ^ N; +assign Z = ~|OUT; + +endmodule diff --git a/rtl/arlet_6502/arlet_6502.v b/rtl/arlet_6502/arlet_6502.v new file mode 100644 index 0000000..7ea97d5 --- /dev/null +++ b/rtl/arlet_6502/arlet_6502.v @@ -0,0 +1,72 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Description: A wrapper for Arlet Ottens 6502 CPU core +// +// Author.....: Alan Garfield +// Niels A. Moseley +// Date.......: 26-1-2018 +// + +module arlet_6502( + input clk, // clock signal + input enable, // clock enable strobe + input rst, // active high reset signal + output reg [15:0] ab, // address bus + input [7:0] dbi, // 8-bit data bus (input) + output reg [7:0] dbo, // 8-bit data bus (output) + output reg we, // active high write enable strobe + input irq_n, // active low interrupt request + input nmi_n, // active low non-maskable interrupt + input ready, // CPU updates when ready = 1 + output [15:0] pc_monitor // program counter monitor signal for debugging +); + + wire [7:0] dbo_c; + wire [15:0] ab_c; + wire we_c; + + cpu arlet_cpu( + .clk(clk), + .reset(rst), + .AB(ab_c), + .DI(dbi), + .DO(dbo_c), + .WE(we_c), + .IRQ(~irq_n), + .NMI(~nmi_n), + .RDY(ready), + .PC_MONITOR(pc_monitor) + ); + + always @(posedge clk or posedge rst) + begin + if (rst) + begin + ab <= 16'd0; + dbo <= 8'd0; + we <= 1'b0; + end + else + if (enable) + begin + ab <= ab_c; + dbo <= dbo_c; + we <= we_c; + end + end +endmodule diff --git a/rtl/arlet_6502/chip_6502.v b/rtl/arlet_6502/chip_6502.v new file mode 100644 index 0000000..49de2be --- /dev/null +++ b/rtl/arlet_6502/chip_6502.v @@ -0,0 +1,66 @@ +`include "../rtl/cpu/aholme/chip_6502_nodes.inc" + +module LOGIC ( + input [`NUM_NODES-1:0] i, + output [`NUM_NODES-1:0] o); + + `include "chip_6502_logic.inc" +endmodule + + +module chip_6502 ( + input clk, // FPGA clock + input phi, // 6502 clock + input res, + input so, + input rdy, + input nmi, + input irq, + input [7:0] dbi, + output [7:0] dbo, + output rw, + output sync, + output [15:0] ab); + + // Node states + wire [`NUM_NODES-1:0] no; + reg [`NUM_NODES-1:0] ni; + reg [`NUM_NODES-1:0] q = 0; + + LOGIC logic_00 (.i(ni), .o(no)); + + always @ (posedge clk) + q <= no; + + always @* begin + ni = q; + + ni[`NODE_vcc ] = 1'b1; + ni[`NODE_vss ] = 1'b0; + ni[`NODE_res ] = res; + ni[`NODE_clk0] = phi; + ni[`NODE_so ] = so; + ni[`NODE_rdy ] = rdy; + ni[`NODE_nmi ] = nmi; + ni[`NODE_irq ] = irq; + + {ni[`NODE_db7],ni[`NODE_db6],ni[`NODE_db5],ni[`NODE_db4], + ni[`NODE_db3],ni[`NODE_db2],ni[`NODE_db1],ni[`NODE_db0]} = dbi[7:0]; + end + + assign dbo[7:0] = { + no[`NODE_db7],no[`NODE_db6],no[`NODE_db5],no[`NODE_db4], + no[`NODE_db3],no[`NODE_db2],no[`NODE_db1],no[`NODE_db0] + }; + + assign ab[15:0] = { + no[`NODE_ab15], no[`NODE_ab14], no[`NODE_ab13], no[`NODE_ab12], + no[`NODE_ab11], no[`NODE_ab10], no[`NODE_ab9], no[`NODE_ab8], + no[`NODE_ab7], no[`NODE_ab6], no[`NODE_ab5], no[`NODE_ab4], + no[`NODE_ab3], no[`NODE_ab2], no[`NODE_ab1], no[`NODE_ab0] + }; + + assign rw = no[`NODE_rw]; + assign sync = no[`NODE_sync]; + +endmodule diff --git a/rtl/arlet_6502/chip_6502_mux.v b/rtl/arlet_6502/chip_6502_mux.v new file mode 100644 index 0000000..a2b87e1 --- /dev/null +++ b/rtl/arlet_6502/chip_6502_mux.v @@ -0,0 +1,10 @@ +module MUX #( + parameter N=1 +) ( + output wire o, + input wire i, + input wire [N-1:0] s, + input wire [N-1:0] d); + + assign o = (|s) ? &(d|(~s)) : i; +endmodule diff --git a/rtl/arlet_6502/cpu.v b/rtl/arlet_6502/cpu.v new file mode 100644 index 0000000..1a7317d --- /dev/null +++ b/rtl/arlet_6502/cpu.v @@ -0,0 +1,1244 @@ +/* + * verilog model of 6502 CPU. + * + * (C) Arlet Ottens, + * + * Feel free to use this code in any project (commercial or not), as long as you + * keep this message, and the copyright notice. This code is provided "as is", + * without any warranties of any kind. + * + */ + +/* + * Note that not all 6502 interface signals are supported (yet). The goal + * is to create an Acorn Atom model, and the Atom didn't use all signals on + * the main board. + * + * The data bus is implemented as separate read/write buses. Combine them + * on the output pads if external memory is required. + */ + +// FIXME - Need to make this flag reach out to test bench +//`define SIM + +module cpu( clk, reset, AB, DI, DO, WE, IRQ, NMI, RDY, PC_MONITOR ); + +input clk; // CPU clock +input reset; // reset signal +output reg [15:0] AB; // address bus +input [7:0] DI; // data in, read bus +output [7:0] DO; // data out, write bus +output WE; // write enable +input IRQ; // interrupt request +input NMI; // non-maskable interrupt request +input RDY; // Ready signal. Pauses CPU when RDY=0 +output [15:0] PC_MONITOR; // signal to spy / monitor the program counter for debugging + +/* + * internal signals + */ + +reg [15:0] PC; // Program Counter +reg [7:0] ABL; // Address Bus Register LSB +reg [7:0] ABH; // Address Bus Register MSB +wire [7:0] ADD; // Adder Hold Register (registered in ALU) + +reg [7:0] DIHOLD; // Hold for Data In +wire [7:0] DIMUX; // + +reg [7:0] IRHOLD; // Hold for Instruction register +reg IRHOLD_valid; // Valid instruction in IRHOLD + +reg [7:0] AXYS[3:0]; // A, X, Y and S register file + +reg C = 0; // carry flag (init at zero to avoid X's in ALU sim) +reg Z = 0; // zero flag +reg I = 0; // interrupt flag +reg D = 0; // decimal flag +reg V = 0; // overflow flag +reg N = 0; // negative flag +wire AZ; // ALU Zero flag +wire AV; // ALU overflow flag +wire AN; // ALU negative flag +wire HC; // ALU half carry + +reg [7:0] AI; // ALU Input A +reg [7:0] BI; // ALU Input B +wire [7:0] DI; // Data In +wire [7:0] IR; // Instruction register +reg [7:0] DO; // Data Out +reg WE; // Write Enable +reg CI; // Carry In +wire CO; // Carry Out +wire [7:0] PCH = PC[15:8]; +wire [7:0] PCL = PC[7:0]; + +assign PC_MONITOR = PC; // generate PC monitor signal + +reg NMI_edge = 0; // captured NMI edge + +reg [1:0] regsel; // Select A, X, Y or S register +wire [7:0] regfile = AXYS[regsel]; // Selected register output + +parameter + SEL_A = 2'd0, + SEL_S = 2'd1, + SEL_X = 2'd2, + SEL_Y = 2'd3; + +/* + * define some signals for watching in simulator output + */ + + +`ifdef SIM +wire [7:0] A = AXYS[SEL_A]; // Accumulator +wire [7:0] X = AXYS[SEL_X]; // X register +wire [7:0] Y = AXYS[SEL_Y]; // Y register +wire [7:0] S = AXYS[SEL_S]; // Stack pointer +`endif + +wire [7:0] P = { N, V, 2'b11, D, I, Z, C }; + +/* + * instruction decoder/sequencer + */ + +reg [5:0] state; + +/* + * control signals + */ + +reg PC_inc; // Increment PC +reg [15:0] PC_temp; // intermediate value of PC + +reg [1:0] src_reg; // source register index +reg [1:0] dst_reg; // destination register index + +reg index_y; // if set, then Y is index reg rather than X +reg load_reg; // loading a register (A, X, Y, S) in this instruction +reg inc; // increment +reg write_back; // set if memory is read/modified/written +reg load_only; // LDA/LDX/LDY instruction +reg store; // doing store (STA/STX/STY) +reg adc_sbc; // doing ADC/SBC +reg compare; // doing CMP/CPY/CPX +reg shift; // doing shift/rotate instruction +reg rotate; // doing rotate (no shift) +reg backwards; // backwards branch +reg cond_true; // branch condition is true +reg [2:0] cond_code; // condition code bits from instruction +reg shift_right; // Instruction ALU shift/rotate right +reg alu_shift_right; // Current cycle shift right enable +reg [3:0] op; // Main ALU operation for instruction +reg [3:0] alu_op; // Current cycle ALU operation +reg adc_bcd; // ALU should do BCD style carry +reg adj_bcd; // results should be BCD adjusted + +/* + * some flip flops to remember we're doing special instructions. These + * get loaded at the DECODE state, and used later + */ +reg bit_ins; // doing BIT instruction +reg plp; // doing PLP instruction +reg php; // doing PHP instruction +reg clc; // clear carry +reg sec; // set carry +reg cld; // clear decimal +reg sed; // set decimal +reg cli; // clear interrupt +reg sei; // set interrupt +reg clv; // clear overflow +reg brk; // doing BRK + +reg res; // in reset + +/* + * ALU operations + */ + +parameter + OP_OR = 4'b1100, + OP_AND = 4'b1101, + OP_EOR = 4'b1110, + OP_ADD = 4'b0011, + OP_SUB = 4'b0111, + OP_ROL = 4'b1011, + OP_A = 4'b1111; + +/* + * Microcode state machine. Basically, every addressing mode has its own + * path through the state machine. Additional information, such as the + * operation, source and destination registers are decoded in parallel, and + * kept in separate flops. + */ + +parameter + ABS0 = 6'd0, // ABS - fetch LSB + ABS1 = 6'd1, // ABS - fetch MSB + ABSX0 = 6'd2, // ABS, X - fetch LSB and send to ALU (+X) + ABSX1 = 6'd3, // ABS, X - fetch MSB and send to ALU (+Carry) + ABSX2 = 6'd4, // ABS, X - Wait for ALU (only if needed) + BRA0 = 6'd5, // Branch - fetch offset and send to ALU (+PC[7:0]) + BRA1 = 6'd6, // Branch - fetch opcode, and send PC[15:8] to ALU + BRA2 = 6'd7, // Branch - fetch opcode (if page boundary crossed) + BRK0 = 6'd8, // BRK/IRQ - push PCH, send S to ALU (-1) + BRK1 = 6'd9, // BRK/IRQ - push PCL, send S to ALU (-1) + BRK2 = 6'd10, // BRK/IRQ - push P, send S to ALU (-1) + BRK3 = 6'd11, // BRK/IRQ - write S, and fetch @ fffe + DECODE = 6'd12, // IR is valid, decode instruction, and write prev reg + FETCH = 6'd13, // fetch next opcode, and perform prev ALU op + INDX0 = 6'd14, // (ZP,X) - fetch ZP address, and send to ALU (+X) + INDX1 = 6'd15, // (ZP,X) - fetch LSB at ZP+X, calculate ZP+X+1 + INDX2 = 6'd16, // (ZP,X) - fetch MSB at ZP+X+1 + INDX3 = 6'd17, // (ZP,X) - fetch data + INDY0 = 6'd18, // (ZP),Y - fetch ZP address, and send ZP to ALU (+1) + INDY1 = 6'd19, // (ZP),Y - fetch at ZP+1, and send LSB to ALU (+Y) + INDY2 = 6'd20, // (ZP),Y - fetch data, and send MSB to ALU (+Carry) + INDY3 = 6'd21, // (ZP),Y) - fetch data (if page boundary crossed) + JMP0 = 6'd22, // JMP - fetch PCL and hold + JMP1 = 6'd23, // JMP - fetch PCH + JMPI0 = 6'd24, // JMP IND - fetch LSB and send to ALU for delay (+0) + JMPI1 = 6'd25, // JMP IND - fetch MSB, proceed with JMP0 state + JSR0 = 6'd26, // JSR - push PCH, save LSB, send S to ALU (-1) + JSR1 = 6'd27, // JSR - push PCL, send S to ALU (-1) + JSR2 = 6'd28, // JSR - write S + JSR3 = 6'd29, // JSR - fetch MSB + PULL0 = 6'd30, // PLP/PLA - save next op in IRHOLD, send S to ALU (+1) + PULL1 = 6'd31, // PLP/PLA - fetch data from stack, write S + PULL2 = 6'd32, // PLP/PLA - prefetch op, but don't increment PC + PUSH0 = 6'd33, // PHP/PHA - send A to ALU (+0) + PUSH1 = 6'd34, // PHP/PHA - write A/P, send S to ALU (-1) + READ = 6'd35, // Read memory for read/modify/write (INC, DEC, shift) + REG = 6'd36, // Read register for reg-reg transfers + RTI0 = 6'd37, // RTI - send S to ALU (+1) + RTI1 = 6'd38, // RTI - read P from stack + RTI2 = 6'd39, // RTI - read PCL from stack + RTI3 = 6'd40, // RTI - read PCH from stack + RTI4 = 6'd41, // RTI - read PCH from stack + RTS0 = 6'd42, // RTS - send S to ALU (+1) + RTS1 = 6'd43, // RTS - read PCL from stack + RTS2 = 6'd44, // RTS - write PCL to ALU, read PCH + RTS3 = 6'd45, // RTS - load PC and increment + WRITE = 6'd46, // Write memory for read/modify/write + ZP0 = 6'd47, // Z-page - fetch ZP address + ZPX0 = 6'd48, // ZP, X - fetch ZP, and send to ALU (+X) + ZPX1 = 6'd49; // ZP, X - load from memory + +`ifdef SIM + +/* + * easy to read names in simulator output + */ +reg [8*6-1:0] statename; + +always @* + case( state ) + DECODE: statename = "DECODE"; + REG: statename = "REG"; + ZP0: statename = "ZP0"; + ZPX0: statename = "ZPX0"; + ZPX1: statename = "ZPX1"; + ABS0: statename = "ABS0"; + ABS1: statename = "ABS1"; + ABSX0: statename = "ABSX0"; + ABSX1: statename = "ABSX1"; + ABSX2: statename = "ABSX2"; + INDX0: statename = "INDX0"; + INDX1: statename = "INDX1"; + INDX2: statename = "INDX2"; + INDX3: statename = "INDX3"; + INDY0: statename = "INDY0"; + INDY1: statename = "INDY1"; + INDY2: statename = "INDY2"; + INDY3: statename = "INDY3"; + READ: statename = "READ"; + WRITE: statename = "WRITE"; + FETCH: statename = "FETCH"; + PUSH0: statename = "PUSH0"; + PUSH1: statename = "PUSH1"; + PULL0: statename = "PULL0"; + PULL1: statename = "PULL1"; + PULL2: statename = "PULL2"; + JSR0: statename = "JSR0"; + JSR1: statename = "JSR1"; + JSR2: statename = "JSR2"; + JSR3: statename = "JSR3"; + RTI0: statename = "RTI0"; + RTI1: statename = "RTI1"; + RTI2: statename = "RTI2"; + RTI3: statename = "RTI3"; + RTI4: statename = "RTI4"; + RTS0: statename = "RTS0"; + RTS1: statename = "RTS1"; + RTS2: statename = "RTS2"; + RTS3: statename = "RTS3"; + BRK0: statename = "BRK0"; + BRK1: statename = "BRK1"; + BRK2: statename = "BRK2"; + BRK3: statename = "BRK3"; + BRA0: statename = "BRA0"; + BRA1: statename = "BRA1"; + BRA2: statename = "BRA2"; + JMP0: statename = "JMP0"; + JMP1: statename = "JMP1"; + JMPI0: statename = "JMPI0"; + JMPI1: statename = "JMPI1"; + endcase + +//always @( PC ) +// $display( "%t, PC:%04x IR:%02x A:%02x X:%02x Y:%02x S:%02x C:%d Z:%d V:%d N:%d P:%02x", $time, PC, IR, A, X, Y, S, C, Z, V, N, P ); + +`endif + + + +/* + * Program Counter Increment/Load. First calculate the base value in + * PC_temp. + */ +always @* + case( state ) + DECODE: if( (~I & IRQ) | NMI_edge ) + PC_temp = { ABH, ABL }; + else + PC_temp = PC; + + + JMP1, + JMPI1, + JSR3, + RTS3, + RTI4: PC_temp = { DIMUX, ADD }; + + BRA1: PC_temp = { ABH, ADD }; + + BRA2: PC_temp = { ADD, PCL }; + + BRK2: PC_temp = res ? 16'hfffc : + NMI_edge ? 16'hfffa : 16'hfffe; + + default: PC_temp = PC; + endcase + +/* + * Determine wether we need PC_temp, or PC_temp + 1 + */ +always @* + case( state ) + DECODE: if( (~I & IRQ) | NMI_edge ) + PC_inc = 0; + else + PC_inc = 1; + + ABS0, + ABSX0, + FETCH, + BRA0, + BRA2, + BRK3, + JMPI1, + JMP1, + RTI4, + RTS3: PC_inc = 1; + + BRA1: PC_inc = CO ^~ backwards; + + default: PC_inc = 0; + endcase + +/* + * Set new PC + */ +always @(posedge clk) + if( RDY ) + PC <= PC_temp + PC_inc; + +/* + * Address Generator + */ + +parameter + ZEROPAGE = 8'h00, + STACKPAGE = 8'h01; + +always @* + case( state ) + ABSX1, + INDX3, + INDY2, + JMP1, + JMPI1, + RTI4, + ABS1: AB = { DIMUX, ADD }; + + BRA2, + INDY3, + ABSX2: AB = { ADD, ABL }; + + BRA1: AB = { ABH, ADD }; + + JSR0, + PUSH1, + RTS0, + RTI0, + BRK0: AB = { STACKPAGE, regfile }; + + BRK1, + JSR1, + PULL1, + RTS1, + RTS2, + RTI1, + RTI2, + RTI3, + BRK2: AB = { STACKPAGE, ADD }; + + INDY1, + INDX1, + ZPX1, + INDX2: AB = { ZEROPAGE, ADD }; + + ZP0, + INDY0: AB = { ZEROPAGE, DIMUX }; + + REG, + READ, + WRITE: AB = { ABH, ABL }; + + default: AB = PC; + endcase + +/* + * ABH/ABL pair is used for registering previous address bus state. + * This can be used to keep the current address, freeing up the original + * source of the address, such as the ALU or DI. + */ +always @(posedge clk) + if( state != PUSH0 && state != PUSH1 && RDY && + state != PULL0 && state != PULL1 && state != PULL2 ) + begin + ABL <= AB[7:0]; + ABH <= AB[15:8]; + end + +/* + * Data Out MUX + */ +always @* + case( state ) + WRITE: DO = ADD; + + JSR0, + BRK0: DO = PCH; + + JSR1, + BRK1: DO = PCL; + + PUSH1: DO = php ? P : ADD; + + BRK2: DO = (IRQ | NMI_edge) ? (P & 8'b1110_1111) : P; + + default: DO = regfile; + endcase + +/* + * Write Enable Generator + */ + +always @* + case( state ) + BRK0, // writing to stack or memory + BRK1, + BRK2, + JSR0, + JSR1, + PUSH1, + WRITE: WE = 1; + + INDX3, // only if doing a STA, STX or STY + INDY3, + ABSX2, + ABS1, + ZPX1, + ZP0: WE = store; + + default: WE = 0; + endcase + +/* + * register file, contains A, X, Y and S (stack pointer) registers. At each + * cycle only 1 of those registers needs to be accessed, so they combined + * in a small memory, saving resources. + */ + +reg write_register; // set when register file is written + +always @* + case( state ) + DECODE: write_register = load_reg & ~plp; + + PULL1, + RTS2, + RTI3, + BRK3, + JSR0, + JSR2 : write_register = 1; + + default: write_register = 0; + endcase + +/* + * BCD adjust logic + */ + +always @(posedge clk) + if ( RDY ) + adj_bcd <= adc_sbc & D; // '1' when doing a BCD instruction + +reg [3:0] ADJL; +reg [3:0] ADJH; + +// adjustment term to be added to ADD[3:0] based on the following +// adj_bcd: '1' if doing ADC/SBC with D=1 +// adc_bcd: '1' if doing ADC with D=1 +// HC : half carry bit from ALU +always @* begin + casex( {adj_bcd, adc_bcd, HC} ) + 3'b0xx: ADJL = 4'd0; // no BCD instruction + 3'b100: ADJL = 4'd10; // SBC, and digital borrow + 3'b101: ADJL = 4'd0; // SBC, but no borrow + 3'b110: ADJL = 4'd0; // ADC, but no carry + 3'b111: ADJL = 4'd6; // ADC, and decimal/digital carry + endcase +end + +// adjustment term to be added to ADD[7:4] based on the following +// adj_bcd: '1' if doing ADC/SBC with D=1 +// adc_bcd: '1' if doing ADC with D=1 +// CO : carry out bit from ALU +always @* begin + casex( {adj_bcd, adc_bcd, CO} ) + 3'b0xx: ADJH = 4'd0; // no BCD instruction + 3'b100: ADJH = 4'd10; // SBC, and digital borrow + 3'b101: ADJH = 4'd0; // SBC, but no borrow + 3'b110: ADJH = 4'd0; // ADC, but no carry + 3'b111: ADJH = 4'd6; // ADC, and decimal/digital carry + endcase +end + +/* + * write to a register. Usually this is the (BCD corrected) output of the + * ALU, but in case of the JSR0 we use the S register to temporarily store + * the PCL. This is possible, because the S register itself is stored in + * the ALU during those cycles. + */ +always @(posedge clk or posedge reset) +begin + if (reset) + begin + AXYS[SEL_A] <= 8'b0; + AXYS[SEL_X] <= 8'b0; + AXYS[SEL_Y] <= 8'b0; + AXYS[SEL_S] <= 8'b0; + end + else + if( write_register & RDY ) + AXYS[regsel] <= (state == JSR0) ? DIMUX : { ADD[7:4] + ADJH, ADD[3:0] + ADJL }; +end + +/* + * register select logic. This determines which of the A, X, Y or + * S registers will be accessed. + */ + +always @* + case( state ) + INDY1, + INDX0, + ZPX0, + ABSX0 : regsel = index_y ? SEL_Y : SEL_X; + + + DECODE : regsel = dst_reg; + + BRK0, + BRK3, + JSR0, + JSR2, + PULL0, + PULL1, + PUSH1, + RTI0, + RTI3, + RTS0, + RTS2 : regsel = SEL_S; + + default: regsel = src_reg; + endcase + +/* + * ALU + */ + +ALU ALU( .clk(clk), + .op(alu_op), + .right(alu_shift_right), + .AI(AI), + .BI(BI), + .CI(CI), + .BCD(adc_bcd & (state == FETCH)), + .CO(CO), + .OUT(ADD), + .V(AV), + .Z(AZ), + .N(AN), + .HC(HC), + .RDY(RDY) ); + +/* + * Select current ALU operation + */ + +always @* + case( state ) + READ: alu_op = op; + + BRA1: alu_op = backwards ? OP_SUB : OP_ADD; + + FETCH, + REG : alu_op = op; + + DECODE, + ABS1: alu_op = 1'bx; + + PUSH1, + BRK0, + BRK1, + BRK2, + JSR0, + JSR1: alu_op = OP_SUB; + + default: alu_op = OP_ADD; + endcase + +/* + * Determine shift right signal to ALU + */ + +always @* + if( state == FETCH || state == REG || state == READ ) + alu_shift_right = shift_right; + else + alu_shift_right = 0; + +/* + * Sign extend branch offset. + */ + +always @(posedge clk) + if( RDY ) + backwards <= DIMUX[7]; + +/* + * ALU A Input MUX + */ + +always @* + case( state ) + JSR1, + RTS1, + RTI1, + RTI2, + BRK1, + BRK2, + INDX1: AI = ADD; + + REG, + ZPX0, + INDX0, + ABSX0, + RTI0, + RTS0, + JSR0, + JSR2, + BRK0, + PULL0, + INDY1, + PUSH0, + PUSH1: AI = regfile; + + BRA0, + READ: AI = DIMUX; + + BRA1: AI = ABH; // don't use PCH in case we're + + FETCH: AI = load_only ? 0 : regfile; + + DECODE, + ABS1: AI = 8'hxx; // don't care + + default: AI = 0; + endcase + + +/* + * ALU B Input mux + */ + +always @* + case( state ) + BRA1, + RTS1, + RTI0, + RTI1, + RTI2, + INDX1, + READ, + REG, + JSR0, + JSR1, + JSR2, + BRK0, + BRK1, + BRK2, + PUSH0, + PUSH1, + PULL0, + RTS0: BI = 8'h00; + + BRA0: BI = PCL; + + DECODE, + ABS1: BI = 8'hxx; + + default: BI = DIMUX; + endcase + +/* + * ALU CI (carry in) mux + */ + +always @* + case( state ) + INDY2, + BRA1, + ABSX1: CI = CO; + + DECODE, + ABS1: CI = 1'bx; + + READ, + REG: CI = rotate ? C : + shift ? 0 : inc; + + FETCH: CI = rotate ? C : + compare ? 1 : + (shift | load_only) ? 0 : C; + + PULL0, + RTI0, + RTI1, + RTI2, + RTS0, + RTS1, + INDY0, + INDX1: CI = 1; + + default: CI = 0; + endcase + +/* + * Processor Status Register update + * + */ + +/* + * Update C flag when doing ADC/SBC, shift/rotate, compare + */ +always @(posedge clk ) + if ( RDY ) + if( shift && state == WRITE ) + C <= CO; + else if( state == RTI2 ) + C <= DIMUX[0]; + else if( ~write_back && state == DECODE ) begin + if( adc_sbc | shift | compare ) + C <= CO; + else if( plp ) + C <= ADD[0]; + else begin + if( sec ) C <= 1; + if( clc ) C <= 0; + end + end + +/* + * Update Z, N flags when writing A, X, Y, Memory, or when doing compare + */ + +always @(posedge clk) + if ( RDY ) + if( state == WRITE ) + Z <= AZ; + else if( state == RTI2 ) + Z <= DIMUX[1]; + else if( state == DECODE ) begin + if( plp ) + Z <= ADD[1]; + else if( (load_reg & (regsel != SEL_S)) | compare | bit_ins ) + Z <= AZ; + end + +always @(posedge clk) + if ( RDY ) + if( state == WRITE ) + N <= AN; + else if( state == RTI2 ) + N <= DIMUX[7]; + else if( state == DECODE ) begin + if( plp ) + N <= ADD[7]; + else if( (load_reg & (regsel != SEL_S)) | compare ) + N <= AN; + end else if( state == FETCH && bit_ins ) + N <= DIMUX[7]; + +/* + * Update I flag + */ + +always @(posedge clk) + if ( RDY ) + if( state == BRK3 ) + I <= 1; + else if( state == RTI2 ) + I <= DIMUX[2]; + else if( state == REG ) begin + if( sei ) I <= 1; + if( cli ) I <= 0; + end else if( state == DECODE ) + if( plp ) I <= ADD[2]; + +/* + * Update D flag + */ +always @(posedge clk ) + if ( RDY ) + if( state == RTI2 ) + D <= DIMUX[3]; + else if( state == DECODE ) begin + if( sed ) D <= 1; + if( cld ) D <= 0; + if( plp ) D <= ADD[3]; + end + +/* + * Update V flag + */ +always @(posedge clk ) + if ( RDY ) + if( state == RTI2 ) + V <= DIMUX[6]; + else if( state == DECODE ) begin + if( adc_sbc ) V <= AV; + if( clv ) V <= 0; + if( plp ) V <= ADD[6]; + end else if( state == FETCH && bit_ins ) + V <= DIMUX[6]; + +/* + * Instruction decoder + */ + +/* + * IR register/mux. Hold previous DI value in IRHOLD in PULL0 and PUSH0 + * states. In these states, the IR has been prefetched, and there is no + * time to read the IR again before the next decode. + */ + +always @(posedge clk ) + if( reset ) + IRHOLD_valid <= 0; + else if( RDY ) begin + if( state == PULL0 || state == PUSH0 ) begin + IRHOLD <= DIMUX; + IRHOLD_valid <= 1; + end else if( state == DECODE ) + IRHOLD_valid <= 0; + end + +assign IR = (IRQ & ~I) | NMI_edge ? 8'h00 : + IRHOLD_valid ? IRHOLD : DIMUX; + +always @(posedge clk ) + if( RDY ) + DIHOLD <= DI; + +assign DIMUX = ~RDY ? DIHOLD : DI; + +/* + * Microcode state machine + */ +always @(posedge clk or posedge reset) + if( reset ) + state <= BRK0; + else if( RDY ) case( state ) + DECODE : + casex ( IR ) + 8'b0000_0000: state <= BRK0; + 8'b0010_0000: state <= JSR0; + 8'b0010_1100: state <= ABS0; // BIT abs + 8'b0100_0000: state <= RTI0; // + 8'b0100_1100: state <= JMP0; + 8'b0110_0000: state <= RTS0; + 8'b0110_1100: state <= JMPI0; + 8'b0x00_1000: state <= PUSH0; + 8'b0x10_1000: state <= PULL0; + 8'b0xx1_1000: state <= REG; // CLC, SEC, CLI, SEI + 8'b1xx0_00x0: state <= FETCH; // IMM + 8'b1xx0_1100: state <= ABS0; // X/Y abs + 8'b1xxx_1000: state <= REG; // DEY, TYA, ... + 8'bxxx0_0001: state <= INDX0; + 8'bxxx0_01xx: state <= ZP0; + 8'bxxx0_1001: state <= FETCH; // IMM + 8'bxxx0_1101: state <= ABS0; // even E column + 8'bxxx0_1110: state <= ABS0; // even E column + 8'bxxx1_0000: state <= BRA0; // odd 0 column + 8'bxxx1_0001: state <= INDY0; // odd 1 column + 8'bxxx1_01xx: state <= ZPX0; // odd 4,5,6,7 columns + 8'bxxx1_1001: state <= ABSX0; // odd 9 column + 8'bxxx1_11xx: state <= ABSX0; // odd C, D, E, F columns + 8'bxxxx_1010: state <= REG; // A, TXA, ... NOP + endcase + + ZP0 : state <= write_back ? READ : FETCH; + + ZPX0 : state <= ZPX1; + ZPX1 : state <= write_back ? READ : FETCH; + + ABS0 : state <= ABS1; + ABS1 : state <= write_back ? READ : FETCH; + + ABSX0 : state <= ABSX1; + ABSX1 : state <= (CO | store | write_back) ? ABSX2 : FETCH; + ABSX2 : state <= write_back ? READ : FETCH; + + INDX0 : state <= INDX1; + INDX1 : state <= INDX2; + INDX2 : state <= INDX3; + INDX3 : state <= FETCH; + + INDY0 : state <= INDY1; + INDY1 : state <= INDY2; + INDY2 : state <= (CO | store) ? INDY3 : FETCH; + INDY3 : state <= FETCH; + + READ : state <= WRITE; + WRITE : state <= FETCH; + FETCH : state <= DECODE; + + REG : state <= DECODE; + + PUSH0 : state <= PUSH1; + PUSH1 : state <= DECODE; + + PULL0 : state <= PULL1; + PULL1 : state <= PULL2; + PULL2 : state <= DECODE; + + JSR0 : state <= JSR1; + JSR1 : state <= JSR2; + JSR2 : state <= JSR3; + JSR3 : state <= FETCH; + + RTI0 : state <= RTI1; + RTI1 : state <= RTI2; + RTI2 : state <= RTI3; + RTI3 : state <= RTI4; + RTI4 : state <= DECODE; + + RTS0 : state <= RTS1; + RTS1 : state <= RTS2; + RTS2 : state <= RTS3; + RTS3 : state <= FETCH; + + BRA0 : state <= cond_true ? BRA1 : DECODE; + BRA1 : state <= (CO ^ backwards) ? BRA2 : DECODE; + BRA2 : state <= DECODE; + + JMP0 : state <= JMP1; + JMP1 : state <= DECODE; + + JMPI0 : state <= JMPI1; + JMPI1 : state <= JMP0; + + BRK0 : state <= BRK1; + BRK1 : state <= BRK2; + BRK2 : state <= BRK3; + BRK3 : state <= JMP0; + + endcase + +/* + * Additional control signals + */ + +always @(posedge clk) + if( reset ) + res <= 1; + else if( state == DECODE && RDY ) + res <= 0; + +always @(posedge clk) + if( state == DECODE && RDY ) + casex( IR ) + 8'b0xx01010, // ASLA, ROLA, LSRA, RORA + 8'b0xxxxx01, // ORA, AND, EOR, ADC + 8'b100x10x0, // DEY, TYA, TXA, TXS + 8'b1010xxx0, // LDA/LDX/LDY + 8'b10111010, // TSX + 8'b1011x1x0, // LDX/LDY + 8'b11001010, // DEX + 8'b1x1xxx01, // LDA, SBC + 8'bxxx01000: // DEY, TAY, INY, INX + load_reg <= 1; + + default: load_reg <= 0; + endcase + +always @(posedge clk) + if( state == DECODE && RDY ) + casex( IR ) + 8'b1110_1000, // INX + 8'b1100_1010, // DEX + 8'b101x_xx10: // LDX, TAX, TSX + dst_reg <= SEL_X; + + 8'b0x00_1000, // PHP, PHA + 8'b1001_1010: // TXS + dst_reg <= SEL_S; + + 8'b1x00_1000, // DEY, DEX + 8'b101x_x100, // LDY + 8'b1010_x000: // LDY #imm, TAY + dst_reg <= SEL_Y; + + default: dst_reg <= SEL_A; + endcase + +always @(posedge clk) + if( state == DECODE && RDY ) + casex( IR ) + 8'b1011_1010: // TSX + src_reg <= SEL_S; + + 8'b100x_x110, // STX + 8'b100x_1x10, // TXA, TXS + 8'b1110_xx00, // INX, CPX + 8'b1100_1010: // DEX + src_reg <= SEL_X; + + 8'b100x_x100, // STY + 8'b1001_1000, // TYA + 8'b1100_xx00, // CPY + 8'b1x00_1000: // DEY, INY + src_reg <= SEL_Y; + + default: src_reg <= SEL_A; + endcase + +always @(posedge clk) + if( state == DECODE && RDY ) + casex( IR ) + 8'bxxx1_0001, // INDY + 8'b10x1_x110, // LDX/STX zpg/abs, Y + 8'bxxxx_1001: // abs, Y + index_y <= 1; + + default: index_y <= 0; + endcase + + +always @(posedge clk) + if( state == DECODE && RDY ) + casex( IR ) + 8'b100x_x1x0, // STX, STY + 8'b100x_xx01: // STA + store <= 1; + + default: store <= 0; + + endcase + +always @(posedge clk ) + if( state == DECODE && RDY ) + casex( IR ) + 8'b0xxx_x110, // ASL, ROL, LSR, ROR + 8'b11xx_x110: // DEC/INC + write_back <= 1; + + default: write_back <= 0; + endcase + + +always @(posedge clk ) + if( state == DECODE && RDY ) + casex( IR ) + 8'b101x_xxxx: // LDA, LDX, LDY + load_only <= 1; + default: load_only <= 0; + endcase + +always @(posedge clk ) + if( state == DECODE && RDY ) + casex( IR ) + 8'b111x_x110, // INC + 8'b11x0_1000: // INX, INY + inc <= 1; + + default: inc <= 0; + endcase + +always @(posedge clk ) + if( (state == DECODE || state == BRK0) && RDY ) + casex( IR ) + 8'bx11x_xx01: // SBC, ADC + adc_sbc <= 1; + + default: adc_sbc <= 0; + endcase + +always @(posedge clk ) + if( (state == DECODE || state == BRK0) && RDY ) + casex( IR ) + 8'b011x_xx01: // ADC + adc_bcd <= D; + + default: adc_bcd <= 0; + endcase + +always @(posedge clk ) + if( state == DECODE && RDY ) + casex( IR ) + 8'b0xxx_x110, // ASL, ROL, LSR, ROR (abs, absx, zpg, zpgx) + 8'b0xxx_1010: // ASL, ROL, LSR, ROR (acc) + shift <= 1; + + default: shift <= 0; + endcase + +always @(posedge clk ) + if( state == DECODE && RDY ) + casex( IR ) + 8'b11x0_0x00, // CPX, CPY (imm/zp) + 8'b11x0_1100, // CPX, CPY (abs) + 8'b110x_xx01: // CMP + compare <= 1; + + default: compare <= 0; + endcase + +always @(posedge clk ) + if( state == DECODE && RDY ) + casex( IR ) + 8'b01xx_xx10: // ROR, LSR + shift_right <= 1; + + default: shift_right <= 0; + endcase + +always @(posedge clk ) + if( state == DECODE && RDY ) + casex( IR ) + 8'b0x1x_1010, // ROL A, ROR A + 8'b0x1x_x110: // ROR, ROL + rotate <= 1; + + default: rotate <= 0; + endcase + +always @(posedge clk ) + if( state == DECODE && RDY ) + casex( IR ) + 8'b00xx_xx10: // ROL, ASL + op <= OP_ROL; + + 8'b0010_x100: // BIT zp/abs + op <= OP_AND; + + 8'b01xx_xx10: // ROR, LSR + op <= OP_A; + + 8'b1000_1000, // DEY + 8'b1100_1010, // DEX + 8'b110x_x110, // DEC + 8'b11xx_xx01, // CMP, SBC + 8'b11x0_0x00, // CPX, CPY (imm, zpg) + 8'b11x0_1100: op <= OP_SUB; + + 8'b010x_xx01, // EOR + 8'b00xx_xx01: // ORA, AND + op <= { 2'b11, IR[6:5] }; + + default: op <= OP_ADD; + endcase + +always @(posedge clk ) + if( state == DECODE && RDY ) + casex( IR ) + 8'b0010_x100: // BIT zp/abs + bit_ins <= 1; + + default: bit_ins <= 0; + endcase + +/* + * special instructions + */ +always @(posedge clk ) + if( state == DECODE && RDY ) begin + php <= (IR == 8'h08); + clc <= (IR == 8'h18); + plp <= (IR == 8'h28); + sec <= (IR == 8'h38); + cli <= (IR == 8'h58); + sei <= (IR == 8'h78); + clv <= (IR == 8'hb8); + cld <= (IR == 8'hd8); + sed <= (IR == 8'hf8); + brk <= (IR == 8'h00); + end + +always @(posedge clk) + if( RDY ) + cond_code <= IR[7:5]; + +always @* + case( cond_code ) + 3'b000: cond_true = ~N; + 3'b001: cond_true = N; + 3'b010: cond_true = ~V; + 3'b011: cond_true = V; + 3'b100: cond_true = ~C; + 3'b101: cond_true = C; + 3'b110: cond_true = ~Z; + 3'b111: cond_true = Z; + endcase + + +reg NMI_1 = 0; // delayed NMI signal + +always @(posedge clk) + if ( RDY ) + NMI_1 <= NMI; + +always @(posedge clk ) + if ( RDY ) + if( NMI_edge && state == BRK3 ) + NMI_edge <= 0; + else if( NMI & ~NMI_1 ) + NMI_edge <= 1; + +endmodule diff --git a/rtl/async_tx_rx.v b/rtl/async_tx_rx.v new file mode 100644 index 0000000..79929eb --- /dev/null +++ b/rtl/async_tx_rx.v @@ -0,0 +1,227 @@ +//////////////////////////////////////////////////////// +// RS-232 RX and TX module +// (c) fpga4fun.com & KNJN LLC - 2003 to 2016 + +// The RS-232 settings are fixed +// TX: 8-bit data, 2 stop, no-parity +// RX: 8-bit data, 1 stop, no-parity (the receiver can accept more stop bits of course) + +//////////////////////////////////////////////////////// +module async_transmitter( + input clk, + input rst, + input TxD_start, + input [7:0] TxD_data, + output TxD, + output TxD_busy + ); + + // Assert TxD_start for (at least) one clock cycle to start transmission of TxD_data + // TxD_data is latched so that it doesn't have to stay valid while it is being sent + + parameter ClkFrequency = 25000000; // 25MHz + parameter Baud = 115200; + + //////////////////////////////// + wire BitTick; + BaudTickGen #(ClkFrequency, Baud) tickgen(.clk(clk), .rst(rst), .enable(TxD_busy), .tick(BitTick)); + + reg [3:0] TxD_state; + reg [7:0] TxD_shift; + + wire TxD_ready = (TxD_state==0); + assign TxD_busy = ~TxD_ready; + + always @(posedge clk or posedge rst) + begin + if (rst) + begin + TxD_state <= 0; + TxD_shift <= 0; + end + else + begin + if(TxD_ready & TxD_start) + TxD_shift <= TxD_data; + else + if(TxD_state[3] & BitTick) + TxD_shift <= (TxD_shift >> 1); + + case(TxD_state) + 4'b0000: if(TxD_start) TxD_state <= 4'b0100; + 4'b0100: if(BitTick) TxD_state <= 4'b1000; // start bit + 4'b1000: if(BitTick) TxD_state <= 4'b1001; // bit 0 + 4'b1001: if(BitTick) TxD_state <= 4'b1010; // bit 1 + 4'b1010: if(BitTick) TxD_state <= 4'b1011; // bit 2 + 4'b1011: if(BitTick) TxD_state <= 4'b1100; // bit 3 + 4'b1100: if(BitTick) TxD_state <= 4'b1101; // bit 4 + 4'b1101: if(BitTick) TxD_state <= 4'b1110; // bit 5 + 4'b1110: if(BitTick) TxD_state <= 4'b1111; // bit 6 + 4'b1111: if(BitTick) TxD_state <= 4'b0010; // bit 7 + 4'b0010: if(BitTick) TxD_state <= 4'b0011; // stop1 + 4'b0011: if(BitTick) TxD_state <= 4'b0000; // stop2 + default: if(BitTick) TxD_state <= 4'b0000; + endcase + end + end + + assign TxD = (TxD_state<4) | (TxD_state[3] & TxD_shift[0]); +endmodule + + +//////////////////////////////////////////////////////// +module async_receiver( + input clk, + input rst, + input RxD, + output reg RxD_data_ready, + output reg [7:0] RxD_data, // data received, valid only (for one clock cycle) when RxD_data_ready is asserted + + // We also detect if a gap occurs in the received stream of characters + // That can be useful if multiple characters are sent in burst + // so that multiple characters can be treated as a "packet" + output RxD_idle, // asserted when no data has been received for a while + output reg RxD_endofpacket = 0 // asserted for one clock cycle when a packet has been detected (i.e. RxD_idle is going high) + ); + + parameter ClkFrequency = 25000000; // 12MHz + parameter Baud = 115200; + + parameter Oversampling = 8; // needs to be a power of 2 + // we oversample the RxD line at a fixed rate to capture each RxD data bit at the "right" time + // 8 times oversampling by default, use 16 for higher quality reception + + //////////////////////////////// + reg [3:0] RxD_state; + + wire OversamplingTick; + BaudTickGen #(ClkFrequency, Baud, Oversampling) tickgen(.clk(clk), .rst(rst), .enable(1'b1), .tick(OversamplingTick)); + + // synchronize RxD to our clk domain + reg [1:0] RxD_sync; // 2'b11 + always @(posedge clk or posedge rst) + begin + if (rst) + RxD_sync <= 2'b11; + else + if(OversamplingTick) RxD_sync <= {RxD_sync[0], RxD}; + end + + // and filter it + reg [1:0] Filter_cnt; // 2'b11 + reg RxD_bit; // 1'b1 + always @(posedge clk or posedge rst) + begin + if (rst) + begin + Filter_cnt <= 2'b11; + RxD_bit <= 1'b1; + end + else + if(OversamplingTick) + begin + if(RxD_sync[1]==1'b1 && Filter_cnt!=2'b11) Filter_cnt <= Filter_cnt + 1'd1; + else if(RxD_sync[1]==1'b0 && Filter_cnt!=2'b00) Filter_cnt <= Filter_cnt - 1'd1; + + if(Filter_cnt==2'b11) RxD_bit <= 1'b1; + else if(Filter_cnt==2'b00) RxD_bit <= 1'b0; + end + end + + // and decide when is the good time to sample the RxD line + function integer log2(input integer v); + begin + log2=0; + while(v>>log2) + log2 = log2 + 1; + end + endfunction + + localparam l2o = log2(Oversampling); + reg [l2o-2:0] OversamplingCnt; + + always @(posedge clk) + if(OversamplingTick) OversamplingCnt <= (RxD_state==0) ? 1'd0 : OversamplingCnt + 1'd1; + + wire sampleNow = OversamplingTick && (OversamplingCnt==Oversampling/2-1); + + // now we can accumulate the RxD bits in a shift-register + always @(posedge clk or posedge rst) + begin + if (rst) + RxD_state <= 0; + else + case(RxD_state) + 4'b0000: if(~RxD_bit) RxD_state <= 4'b0001; // start bit found? + 4'b0001: if(sampleNow) RxD_state <= 4'b1000; // sync start bit to sampleNow + 4'b1000: if(sampleNow) RxD_state <= 4'b1001; // bit 0 + 4'b1001: if(sampleNow) RxD_state <= 4'b1010; // bit 1 + 4'b1010: if(sampleNow) RxD_state <= 4'b1011; // bit 2 + 4'b1011: if(sampleNow) RxD_state <= 4'b1100; // bit 3 + 4'b1100: if(sampleNow) RxD_state <= 4'b1101; // bit 4 + 4'b1101: if(sampleNow) RxD_state <= 4'b1110; // bit 5 + 4'b1110: if(sampleNow) RxD_state <= 4'b1111; // bit 6 + 4'b1111: if(sampleNow) RxD_state <= 4'b0010; // bit 7 + 4'b0010: if(sampleNow) RxD_state <= 4'b0000; // stop bit + default: RxD_state <= 4'b0000; + endcase + end + + always @(posedge clk or posedge rst) + begin + if (rst) + RxD_data <= 0; + else + if (sampleNow && RxD_state[3]) RxD_data <= {RxD_bit, RxD_data[7:1]}; + end + + always @(posedge clk or posedge rst) + begin + if (rst) + RxD_data_ready <= 0; + else + RxD_data_ready <= (sampleNow && RxD_state==4'b0010 && RxD_bit); // make sure a stop bit is received + end + + reg [l2o+1:0] GapCnt; + always @(posedge clk or posedge rst) + begin + if (rst) + GapCnt <= 0; + else + if (RxD_state!=0) GapCnt<=0; else if(OversamplingTick & ~GapCnt[log2(Oversampling)+1]) GapCnt <= GapCnt + 1'h1; + end + + assign RxD_idle = GapCnt[l2o+1]; + always @(posedge clk) + RxD_endofpacket <= OversamplingTick & ~GapCnt[l2o+1] & &GapCnt[l2o:0]; + +endmodule + +//////////////////////////////////////////////////////// +module BaudTickGen( + input clk, rst, enable, + output tick // generate a tick at the specified baud rate * oversampling + ); + + parameter ClkFrequency = 25000000; + parameter Baud = 115200; + parameter Oversampling = 1; + + function integer log2(input integer v); begin log2=0; while(v>>log2) log2=log2+1; end endfunction + + localparam AccWidth = log2(ClkFrequency/Baud)+8; // +/- 2% max timing error over a byte + reg [AccWidth:0] Acc; + localparam ShiftLimiter = log2(Baud*Oversampling >> (31-AccWidth)); // this makes sure Inc calculation doesn't overflow + localparam Inc = ((Baud*Oversampling << (AccWidth-ShiftLimiter))+(ClkFrequency>>(ShiftLimiter+1)))/(ClkFrequency>>ShiftLimiter); + + always @(posedge clk) + begin + if (rst) + Acc <= 0; + else + if(enable) Acc <= Acc[AccWidth-1:0] + Inc[AccWidth:0]; else Acc <= Inc[AccWidth:0]; + end + assign tick = Acc[AccWidth]; + +endmodule diff --git a/rtl/build_id.tcl b/rtl/build_id.tcl new file mode 100644 index 0000000..938515d --- /dev/null +++ b/rtl/build_id.tcl @@ -0,0 +1,35 @@ +# ================================================================================ +# +# Build ID Verilog Module Script +# Jeff Wiencrot - 8/1/2011 +# +# Generates a Verilog module that contains a timestamp, +# from the current build. These values are available from the build_date, build_time, +# physical_address, and host_name output ports of the build_id module in the build_id.v +# Verilog source file. +# +# ================================================================================ + +proc generateBuildID_Verilog {} { + + # Get the timestamp (see: http://www.altera.com/support/examples/tcl/tcl-date-time-stamp.html) + set buildDate [ clock format [ clock seconds ] -format %y%m%d ] + set buildTime [ clock format [ clock seconds ] -format %H%M%S ] + + # Create a Verilog file for output + set outputFileName "rtl/build_id.v" + set outputFile [open $outputFileName "w"] + + # Output the Verilog source + puts $outputFile "`define BUILD_DATE \"$buildDate\"" + puts $outputFile "`define BUILD_TIME \"$buildTime\"" + close $outputFile + + # Send confirmation message to the Messages window + post_message "Generated build identification Verilog module: [pwd]/$outputFileName" + post_message "Date: $buildDate" + post_message "Time: $buildTime" +} + +# Comment out this line to prevent the process from automatically executing when the file is sourced: +generateBuildID_Verilog \ No newline at end of file diff --git a/rtl/clock.v b/rtl/clock.v new file mode 100644 index 0000000..715112c --- /dev/null +++ b/rtl/clock.v @@ -0,0 +1,55 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Description: Clock divider to provide clock enables for +// devices. +// +// Author.....: Alan Garfield +// Niels A. Moseley +// Date.......: 29-1-2018 +// + +module clock +( + input clk14, // 14MHz clock master clock + input rst_n, // active low synchronous reset + + // Clock enables + output reg cpu_clken // 1MHz clock enable for the CPU and devices +); + + // generate clock enable once every + // 14 clocks. This will (hopefully) make + // the 6502 run at 1 MHz + // + // the clock division counter is synchronously + // reset using rst_n to avoid undefined signals + // in simulation + // + + reg [4:0] clk_div; + always @(posedge clk14) + begin + if ((clk_div == 14) || (rst_n == 1'b0)) + clk_div <= 0; + else + clk_div <= clk_div + 1'b1; + + cpu_clken <= (clk_div[4:0] == 0); + end + +endmodule diff --git a/rtl/debounce.v b/rtl/debounce.v new file mode 100644 index 0000000..bf54b08 --- /dev/null +++ b/rtl/debounce.v @@ -0,0 +1,72 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Description: PS/2 keyboard debounce logic to be used for the +// clock line +// +// Author.....: Niels A. Moseley +// Date.......: 8-2-2018 +// + +module debounce( + input clk14, // 25MHz clock + input rst, // active high reset + + input sig_in, // input signal + output reg sig_out // debounced output signal +); + +wire clk_enb; // enable triggering at clk14 divided by 64 +reg [5:0] clk_div; // clock divider counter + +reg sig_ff1; // first input signal synchronizer +reg sig_ff2; // second input signal synchronizer + +assign clk_enb = (clk_div == 6'd0); + +// clock divider +always @(posedge clk14 or posedge rst) +begin + if (rst) + clk_div <= 6'd0; + else + clk_div <= clk_div + 6'd1; +end + +// debounce timer +always @(posedge clk14 or posedge rst) +begin + if (rst) + begin + sig_out <= 1'b0; + sig_ff1 <= 1'b0; + sig_ff2 <= 1'b0; + end + else if (clk_enb) + begin + // this runs ar approximately 391k Hz + // giving a debounce time of around 2.5us + sig_ff1 <= sig_in; + sig_ff2 <= sig_ff1; + if ((sig_ff1 ^ sig_ff2) == 1'd0) + begin + sig_out <= sig_ff2; + end + end +end + +endmodule diff --git a/rtl/font_rom.v b/rtl/font_rom.v new file mode 100644 index 0000000..1c04a1d --- /dev/null +++ b/rtl/font_rom.v @@ -0,0 +1,70 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Description: 8KB RAM for system +// +// Author.....: Alan Garfield +// Date.......: 3-2-2018 +// + +module font_rom ( + input clk, // clock signal + input [1:0] mode, // character mode + input [5:0] character, // address bus + input [3:0] pixel, // address of the pixel to output + input [4:0] line, // address of the line to output + output reg out // single pixel from address and pixel pos + ); + + reg [7:0] rom[0:1023]; + + initial + $readmemh("roms/vga_font_bitreversed.hex", rom, 0, 1023); + + // double height of pixel by ignoring bit 0 + wire [3:0] line_ptr = line[4:1]; + + // Note: Quartus II reverses the pixels when we do: + // + // rom[address][bitindex] + // + // directly, so we use an intermediate + // signal, romout, to work around this + // problem. + // + // IceCube2 and Yosys don't seem to have this problem. + // + + reg [7:0] romout; + + always @(posedge clk) + begin + // mode + // 00 - normal + // 01 - vertical scanlines + // 10 - horizontal scanlines + // 11 - dotty mode + + romout = rom[(character * 10) + {2'd0, line_ptr}]; + + out <= (mode[1] & line[0]) ? 1'b0 : + (mode[0] & pixel[0]) ? 1'b0 : + romout[pixel[3:1]]; + end + +endmodule + diff --git a/rtl/led_and_key.v b/rtl/led_and_key.v new file mode 100644 index 0000000..911ab7d --- /dev/null +++ b/rtl/led_and_key.v @@ -0,0 +1,167 @@ +module ledAndKey( + input clk, + input clk_en, + input rst, + + input [3:0] display, + input [7:0] digit1, + input [7:0] digit2, + input [7:0] digit3, + input [7:0] digit4, + input [7:0] digit5, + input [7:0] digit6, + input [7:0] digit7, + input [7:0] digit8, + input [7:0] leds, + + output reg [7:0] keys, + + output reg tm_cs, + output tm_clk, + inout tm_dio +); + + localparam + HIGH = 1'b1, + LOW = 1'b0; + + localparam [7:0] + C_READ = 8'b01000010, + C_WRITE = 8'b01000000, + C_DISP = 8'b10001111, + C_ADDR = 8'b11000000; + + reg counter; + reg [5:0] instruction_step; + + // set up tristate IO pin for display + // tm_dio is physical pin + // dio_in for reading from display + // dio_out for sending to display + // tm_rw selects input or output + reg tm_rw; + wire dio_in, dio_out; + SB_IO #( + .PIN_TYPE(6'b101001), + .PULLUP(1'b1) + ) tm_dio_io ( + .PACKAGE_PIN(tm_dio), + .OUTPUT_ENABLE(tm_rw), + .D_IN_0(dio_in), + .D_OUT_0(dio_out) + ); + + // setup tm1638 module with it's tristate IO + // tm_in is read from module + // tm_out is written to module + // tm_latch triggers the module to read/write display + // tm_rw selects read or write mode to display + // busy indicates when module is busy + // (another latch will interrupt) + // tm_clk is the data clk + // dio_in for reading from display + // dio_out for sending to display + // + // tm_data the tristate io pin to module + reg tm_latch; + wire busy; + wire [7:0] tm_data, tm_in; + reg [7:0] tm_out; + + assign tm_in = tm_data; + assign tm_data = tm_rw ? tm_out : 8'hZZ; + + tm1638 u_tm1638 ( + .clk(clk), + .clk_en(clk_en), + .rst(rst), + .data_latch(tm_latch), + .data(tm_data), + .rw(tm_rw), + .busy(busy), + .sclk(tm_clk), + .dio_in(dio_in), + .dio_out(dio_out) + ); + + always @(posedge clk) begin + if (clk_en) begin + if (rst) begin + instruction_step <= 6'b0; + tm_cs <= HIGH; + tm_rw <= HIGH; + + counter <= 1'b0; + keys <= 8'b0; + + end else begin + if (counter && ~busy) begin + case (instruction_step) + // *** KEYS *** + 1: {tm_cs, tm_rw} <= {LOW, HIGH}; + 2: {tm_latch, tm_out} <= {HIGH, C_READ}; // read mode + 3: {tm_latch, tm_rw} <= {HIGH, LOW}; + + // read back keys S1 - S8 + 4: {keys[7], keys[3]} <= {tm_in[0], tm_in[4]}; + 5: {tm_latch} <= {HIGH}; + 6: {keys[6], keys[2]} <= {tm_in[0], tm_in[4]}; + 7: {tm_latch} <= {HIGH}; + 8: {keys[5], keys[1]} <= {tm_in[0], tm_in[4]}; + 9: {tm_latch} <= {HIGH}; + 10: {keys[4], keys[0]} <= {tm_in[0], tm_in[4]}; + 11: {tm_cs} <= {HIGH}; + + // *** DISPLAY *** + 12: {tm_cs, tm_rw} <= {LOW, HIGH}; + 13: {tm_latch, tm_out} <= {HIGH, C_WRITE}; // write mode + 14: {tm_cs} <= {HIGH}; + + 15: {tm_cs, tm_rw} <= {LOW, HIGH}; + 16: {tm_latch, tm_out} <= {HIGH, C_ADDR}; // set addr 0 pos + + 17: {tm_latch, tm_out} <= {HIGH, digit1}; // Digit 1 + 18: {tm_latch, tm_out} <= {HIGH, {7'b0, leds[7]}}; // LED 1 + + 19: {tm_latch, tm_out} <= {HIGH, digit2}; // Digit 2 + 20: {tm_latch, tm_out} <= {HIGH, {7'b0, leds[6]}}; // LED 2 + + 21: {tm_latch, tm_out} <= {HIGH, digit3}; // Digit 3 + 22: {tm_latch, tm_out} <= {HIGH, {7'b0, leds[5]}}; // LED 3 + + 23: {tm_latch, tm_out} <= {HIGH, digit4}; // Digit 4 + 24: {tm_latch, tm_out} <= {HIGH, {7'b0, leds[4]}}; // LED 4 + + 25: {tm_latch, tm_out} <= {HIGH, digit5}; // Digit 5 + 26: {tm_latch, tm_out} <= {HIGH, {7'b0, leds[3]}}; // LED 5 + + 27: {tm_latch, tm_out} <= {HIGH, digit6}; // Digit 6 + 28: {tm_latch, tm_out} <= {HIGH, {7'b0, leds[2]}}; // LED 6 + + 29: {tm_latch, tm_out} <= {HIGH, digit7}; // Digit 7 + 30: {tm_latch, tm_out} <= {HIGH, {7'b0, leds[1]}}; // LED 7 + + 31: {tm_latch, tm_out} <= {HIGH, digit8}; // Digit 8 + 32: {tm_latch, tm_out} <= {HIGH, {7'b0, leds[0]}}; // LED 8 + + 33: {tm_cs} <= {HIGH}; + + 34: {tm_cs, tm_rw} <= {LOW, HIGH}; + 35: {tm_latch, tm_out} <= {HIGH, {4'b1000, display}}; // display + 36: {tm_cs, instruction_step} <= {HIGH, 6'b0}; + + endcase + + instruction_step <= instruction_step + 1; + + end else if (busy) begin + // pull latch low next clock cycle after module has been + // latched + tm_latch <= LOW; + end + + counter <= ~counter; + end + end + end +endmodule diff --git a/rtl/mist-modules/.gitignore b/rtl/mist-modules/.gitignore new file mode 100644 index 0000000..7664704 --- /dev/null +++ b/rtl/mist-modules/.gitignore @@ -0,0 +1 @@ +*.bak \ No newline at end of file diff --git a/rtl/mist-modules/README.md b/rtl/mist-modules/README.md new file mode 100644 index 0000000..588d54c --- /dev/null +++ b/rtl/mist-modules/README.md @@ -0,0 +1,33 @@ +Common components for MiST board +================================ + +This repository contains common components, which should be used by almost all cores. +The modules: + +- user_io.v - communicating with the IO controller. +- data_io.v - handling file uploads from the IO controller. +- mist_video.v - a video pipeline, which gives an optional scandoubler, OSD and rgb2ypbpr conversion. +- osd.v, scandoubler.v, rgb2ypbpr.sv, cofi.sv - these are used in mist_video, but can be used separately, too. +- sd_card.v - gives an SPI interface with SD-Card commands towards the IO-Controller, accessing .VHD and other mounted files. +- dac.vhd - a simple sigma-delta DAC for audio output. +- arcade_inputs.v - mostly for arcade-style games, gives access to the joysticks with MAME-style keyboard mapping. +- mist.vhd - VHDL component declarations for user_io and mist_video. +- mist_core.qip - collects the core components, which are needed in almost every case. + +Usage hints +=========== + +All of these components should be clocked by a synchronous clock to the core. The data between the external SPI +interface and this internal clock are synchronized. However to make Quartus' job easier, you have to tell it to +don't try to optimize paths between the SPI and the system clock domain. Also you have to define the incoming +27 MHz and the SPI clocks. These lines in the .sdc file do that: + +``` +set sys_clk "your_system_clock" + +create_clock -name {clk_27} -period 37.037 -waveform { 0.000 18.500 } [get_ports {CLOCK_27[0]}] +create_clock -name {SPI_SCK} -period 41.666 -waveform { 20.8 41.666 } [get_ports {SPI_SCK}] +set_clock_groups -asynchronous -group [get_clocks {SPI_SCK}] -group [get_clocks $sys_clk] +``` + +Replace "your_system_clock" with the name of the pll clock, like "pll|altpll_component|auto_generated|pll1|clk[0]". diff --git a/rtl/mist-modules/arcade_inputs.v b/rtl/mist-modules/arcade_inputs.v new file mode 100644 index 0000000..9770913 --- /dev/null +++ b/rtl/mist-modules/arcade_inputs.v @@ -0,0 +1,191 @@ +/* Provides arcade controls from joystick/keyboard + Keyboard has a simplified (ESC-coin, F1-F4 start) and MAME-style mapping */ + +module arcade_inputs( + // clock, same as for userio + input clk, + // signals from userio + input key_strobe, + input key_pressed, + input [7:0] key_code, + input [19:0] joystick_0, + input [19:0] joystick_1, + input [19:0] joystick_2, + input [19:0] joystick_3, + + // required rotating of controls + input rotate, + // original orientation [1]-left/right if portrait, [0]-landscape/portrait + input [1:0] orientation, + // joystick_0 and joystick_1 should be swapped + input joyswap, + // player1 and player2 should get both joystick_0 and joystick_1 + input oneplayer, + + // tilt, coin4-1, start4-1 + output [8:0] controls, + // up2, down2, left2, right2, fire12-1, up, down, left, right + output [19:0] player1, + output [19:0] player2, + output [19:0] player3, + output [19:0] player4 +); + +assign controls = { btn_tilt, + btn_coin | btn_coin4_mame, btn_coin | btn_coin3_mame, btn_coin | btn_coin2_mame, btn_coin | btn_coin1_mame, + btn_four_players | btn_start4_mame, btn_three_players | btn_start3_mame, btn_two_players | btn_start2_mame, btn_one_player | btn_start1_mame }; + +wire [19:0] joy0 = joyswap ? joystick_1 : joystick_0; +wire [19:0] joy1 = joyswap ? joystick_0 : joystick_1; +wire [19:0] joy2 = joystick_2; +wire [19:0] joy3 = joystick_3; + +wire [19:0] p1; +wire [19:0] p2; +wire [19:0] p3; +wire [19:0] p4; + +assign p1[15:4] = joy0[15:4] | { 4'h0, btn_fireH, btn_fireG, btn_fireF, btn_fireE, btn_fireD, btn_fireC, btn_fireB, btn_fireA }; +assign p2[15:4] = joy1[15:4] | { 4'h0, btn_fire2H, btn_fire2G, btn_fire2F, btn_fire2E, btn_fire2D, btn_fire2C, btn_fire2B, btn_fire2A }; +assign p3[15:4] = joy2[15:4]; +assign p4[15:4] = joy3[15:4]; + +// Left or only stick +control_rotator l1(joy0[3:0], {btn_up, btn_down, btn_left, btn_right }, rotate, orientation, p1[3:0]); +control_rotator l2(joy1[3:0], {btn_up2, btn_down2, btn_left2, btn_right2}, rotate, orientation, p2[3:0]); +control_rotator l3(joy2[3:0], 4'd0, rotate, orientation, p3[3:0]); +control_rotator l4(joy3[3:0], 4'd0, rotate, orientation, p4[3:0]); + +// Right stick +control_rotator r1(joy0[19:16], 4'd0, rotate, orientation, p1[19:16]); +control_rotator r2(joy1[19:16], 4'd0, rotate, orientation, p2[19:16]); +control_rotator r3(joy2[19:16], 4'd0, rotate, orientation, p3[19:16]); +control_rotator r4(joy3[19:16], 4'd0, rotate, orientation, p4[19:16]); + +assign player1 = oneplayer ? p1 | p2 : p1; +assign player2 = oneplayer ? p1 | p2 : p2; +assign player3 = p3; +assign player4 = p4; + +// keyboard controls +reg btn_tilt = 0; +reg btn_one_player = 0; +reg btn_two_players = 0; +reg btn_three_players = 0; +reg btn_four_players = 0; +reg btn_left = 0; +reg btn_right = 0; +reg btn_down = 0; +reg btn_up = 0; +reg btn_fireA = 0; +reg btn_fireB = 0; +reg btn_fireC = 0; +reg btn_fireD = 0; +reg btn_fireE = 0; +reg btn_fireF = 0; +reg btn_fireG = 0; +reg btn_fireH = 0; +reg btn_coin = 0; +reg btn_start1_mame = 0; +reg btn_start2_mame = 0; +reg btn_start3_mame = 0; +reg btn_start4_mame = 0; +reg btn_coin1_mame = 0; +reg btn_coin2_mame = 0; +reg btn_coin3_mame = 0; +reg btn_coin4_mame = 0; +reg btn_up2 = 0; +reg btn_down2 = 0; +reg btn_left2 = 0; +reg btn_right2 = 0; +reg btn_fire2A = 0; +reg btn_fire2B = 0; +reg btn_fire2C = 0; +reg btn_fire2D = 0; +reg btn_fire2E = 0; +reg btn_fire2F = 0; +reg btn_fire2G = 0; +reg btn_fire2H = 0; + +always @(posedge clk) begin + if(key_strobe) begin + case(key_code) + 'h75: btn_up <= key_pressed; // up + 'h72: btn_down <= key_pressed; // down + 'h6B: btn_left <= key_pressed; // left + 'h74: btn_right <= key_pressed; // right + 'h76: btn_coin <= key_pressed; // ESC + 'h05: btn_one_player <= key_pressed; // F1 + 'h06: btn_two_players <= key_pressed; // F2 + 'h04: btn_three_players <= key_pressed; // F3 + 'h0C: btn_four_players <= key_pressed; // F4 + 'h14: btn_fireA <= key_pressed; // ctrl + 'h11: btn_fireB <= key_pressed; // alt + 'h29: btn_fireC <= key_pressed; // Space + 'h12: btn_fireD <= key_pressed; // l-shift + 'h1A: btn_fireE <= key_pressed; // Z + 'h22: btn_fireF <= key_pressed; // X + 'h21: btn_fireG <= key_pressed; // C + 'h2A: btn_fireH <= key_pressed; // V + 'h66: btn_tilt <= key_pressed; // Backspace + + // JPAC/IPAC/MAME Style Codes + 'h16: btn_start1_mame <= key_pressed; // 1 + 'h1E: btn_start2_mame <= key_pressed; // 2 + 'h26: btn_start3_mame <= key_pressed; // 3 + 'h25: btn_start4_mame <= key_pressed; // 4 + 'h2E: btn_coin1_mame <= key_pressed; // 5 + 'h36: btn_coin2_mame <= key_pressed; // 6 + 'h3D: btn_coin3_mame <= key_pressed; // 7 + 'h3E: btn_coin4_mame <= key_pressed; // 8 + 'h2D: btn_up2 <= key_pressed; // R + 'h2B: btn_down2 <= key_pressed; // F + 'h23: btn_left2 <= key_pressed; // D + 'h34: btn_right2 <= key_pressed; // G + 'h1C: btn_fire2A <= key_pressed; // A + 'h1B: btn_fire2B <= key_pressed; // S + 'h15: btn_fire2C <= key_pressed; // Q + 'h1D: btn_fire2D <= key_pressed; // W + 'h43: btn_fire2E <= key_pressed; // I + 'h42: btn_fire2F <= key_pressed; // K + 'h3B: btn_fire2G <= key_pressed; // J + 'h4B: btn_fire2H <= key_pressed; // L + endcase + end +end + +endmodule + +module control_rotator ( + input [3:0] joystick, //UDLR + input [3:0] keyboard, + input rotate, + input [1:0] orientation, + output [3:0] out +); + +assign out = { m_up, m_down, m_left, m_right }; + +wire m_up = ~(orientation[0] ^ rotate) ? keyboard[3] | joystick[3] : ((orientation[1] ^ orientation[0]) ? keyboard[0] | joystick[0] : keyboard[1] | joystick[1]); +wire m_down = ~(orientation[0] ^ rotate) ? keyboard[2] | joystick[2] : ((orientation[1] ^ orientation[0]) ? keyboard[1] | joystick[1] : keyboard[0] | joystick[0]); +wire m_left = ~(orientation[0] ^ rotate) ? keyboard[1] | joystick[1] : ((orientation[1] ^ orientation[0]) ? keyboard[3] | joystick[3] : keyboard[2] | joystick[2]); +wire m_right = ~(orientation[0] ^ rotate) ? keyboard[0] | joystick[0] : ((orientation[1] ^ orientation[0]) ? keyboard[2] | joystick[2] : keyboard[3] | joystick[3]); + +endmodule + +// A simple toggle-switch +module input_toggle( + input clk, + input reset, + input btn, + output reg state +); + +reg btn_old; +always @(posedge clk) begin + btn_old <= btn; + if (reset) state <= 0; + else if (~btn_old & btn) state <= ~state; +end + +endmodule diff --git a/rtl/mist-modules/cofi.sv b/rtl/mist-modules/cofi.sv new file mode 100644 index 0000000..c519bd5 --- /dev/null +++ b/rtl/mist-modules/cofi.sv @@ -0,0 +1,59 @@ +// Composite-like horizontal blending by Kitrinx + +// AMR - disable shift register recognition +(* altera_attribute = "-name AUTO_SHIFT_REGISTER_RECOGNITION OFF" *) +module cofi ( + input clk, + input pix_ce, + input enable, + + input hblank, + input vblank, + input hs, + input vs, + input [VIDEO_DEPTH-1:0] red, + input [VIDEO_DEPTH-1:0] green, + input [VIDEO_DEPTH-1:0] blue, + + output reg hblank_out, + output reg vblank_out, + output reg hs_out, + output reg vs_out, + output reg [VIDEO_DEPTH-1:0] red_out, + output reg [VIDEO_DEPTH-1:0] green_out, + output reg [VIDEO_DEPTH-1:0] blue_out +); + +parameter VIDEO_DEPTH=8; + + function bit [VIDEO_DEPTH-1:0] color_blend ( + input [VIDEO_DEPTH-1:0] color_prev, + input [VIDEO_DEPTH-1:0] color_curr, + input blank_last + ); + begin + color_blend = blank_last ? color_curr : (color_prev >> 1) + (color_curr >> 1); + end + endfunction + +reg [VIDEO_DEPTH-1:0] red_last; +reg [VIDEO_DEPTH-1:0] green_last; +reg [VIDEO_DEPTH-1:0] blue_last; + +wire ce = enable ? pix_ce : 1'b1; +always @(posedge clk) if (ce) begin + hblank_out <= hblank; + vblank_out <= vblank; + vs_out <= vs; + hs_out <= hs; + + red_last <= red; + blue_last <= blue; + green_last <= green; + + red_out <= enable ? color_blend(red_last, red, hblank_out) : red; + blue_out <= enable ? color_blend(blue_last, blue, hblank_out) : blue; + green_out <= enable ? color_blend(green_last, green, hblank_out) : green; +end + +endmodule diff --git a/rtl/mist-modules/dac.vhd b/rtl/mist-modules/dac.vhd new file mode 100644 index 0000000..db58d70 --- /dev/null +++ b/rtl/mist-modules/dac.vhd @@ -0,0 +1,48 @@ +------------------------------------------------------------------------------- +-- +-- Delta-Sigma DAC +-- +-- Refer to Xilinx Application Note XAPP154. +-- +-- This DAC requires an external RC low-pass filter: +-- +-- dac_o 0---XXXXX---+---0 analog audio +-- 3k3 | +-- === 4n7 +-- | +-- GND +-- +------------------------------------------------------------------------------- + +library ieee; + use ieee.std_logic_1164.all; + use ieee.numeric_std.all; + +entity dac is + generic ( + C_bits : integer := 8 + ); + port ( + clk_i : in std_logic; + res_n_i : in std_logic; + dac_i : in std_logic_vector(C_bits-1 downto 0); + dac_o : out std_logic + ); +end dac; + +architecture rtl of dac is + signal sig_in: unsigned(C_bits downto 0); +begin + seq: process(clk_i, res_n_i) + begin + if res_n_i = '0' then + sig_in <= to_unsigned(2**C_bits, sig_in'length); + dac_o <= '0'; + elsif rising_edge(clk_i) then + -- not dac_i(C_bits-1) effectively adds 0x8..0 to dac_i + --sig_in <= sig_in + unsigned(sig_in(C_bits) & (not dac_i(C_bits-1)) & dac_i(C_bits-2 downto 0)); + sig_in <= sig_in + unsigned(sig_in(C_bits) & dac_i); + dac_o <= sig_in(C_bits); + end if; + end process seq; +end rtl; diff --git a/rtl/mist-modules/data_io.v b/rtl/mist-modules/data_io.v new file mode 100644 index 0000000..7e14f12 --- /dev/null +++ b/rtl/mist-modules/data_io.v @@ -0,0 +1,259 @@ +// +// data_io.v +// +// data_io for the MiST board +// http://code.google.com/p/mist-board/ +// +// Copyright (c) 2014 Till Harbaum +// +// This source file is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +/////////////////////////////////////////////////////////////////////// + +module data_io +( + input clk_sys, + input SPI_SCK, + input SPI_SS2, + input SPI_SS4, + input SPI_DI, + inout SPI_DO, + + input clkref_n, // assert ioctl_wr one cycle after clkref stobe (negative active) + + // ARM -> FPGA download + output reg ioctl_download = 0, // signal indicating an active download + output reg ioctl_upload = 0, // signal indicating an active upload + output reg [7:0] ioctl_index, // menu index used to upload the file ([7:6] - extension index, [5:0] - menu index) + // Note: this is also set for user_io mounts. + // Valid when ioctl_download = 1 or when img_mounted strobe is active in user_io. + output reg ioctl_wr, // strobe indicating ioctl_dout valid + output reg [24:0] ioctl_addr, + output reg [7:0] ioctl_dout, + input [7:0] ioctl_din, + output reg [23:0] ioctl_fileext, // file extension + output reg [31:0] ioctl_filesize // file size +); + +parameter START_ADDR = 25'd0; +parameter ROM_DIRECT_UPLOAD = 0; + +/////////////////////////////// DOWNLOADING /////////////////////////////// + +reg [7:0] data_w; +reg [7:0] data_w2 = 0; +reg [3:0] cnt; +reg rclk = 0; +reg rclk2 = 0; +reg addr_reset = 0; +reg downloading_reg = 0; +reg uploading_reg = 0; +reg reg_do; + +localparam DIO_FILE_TX = 8'h53; +localparam DIO_FILE_TX_DAT = 8'h54; +localparam DIO_FILE_INDEX = 8'h55; +localparam DIO_FILE_INFO = 8'h56; +localparam DIO_FILE_RX = 8'h57; +localparam DIO_FILE_RX_DAT = 8'h58; + +assign SPI_DO = reg_do; + +// data_io has its own SPI interface to the io controller +always@(negedge SPI_SCK or posedge SPI_SS2) begin : SPI_TRANSMITTER + reg [7:0] dout_r; + + if(SPI_SS2) begin + reg_do <= 1'bZ; + end else begin + if (cnt == 15) dout_r <= ioctl_din; + reg_do <= dout_r[~cnt[2:0]]; + end +end + + +always@(posedge SPI_SCK, posedge SPI_SS2) begin : SPI_RECEIVER + reg [6:0] sbuf; + reg [24:0] addr; + reg [7:0] cmd; + reg [5:0] bytecnt; + + if(SPI_SS2) begin + bytecnt <= 0; + cnt <= 0; + end else begin + // don't shift in last bit. It is evaluated directly + // when writing to ram + if(cnt != 15) sbuf <= { sbuf[5:0], SPI_DI}; + + // count 0-7 8-15 8-15 ... + if(cnt != 15) cnt <= cnt + 1'd1; + else cnt <= 8; + + // finished command byte + if(cnt == 7) cmd <= {sbuf, SPI_DI}; + + if(cnt == 15) begin + case (cmd) + // prepare/end transmission + DIO_FILE_TX: begin + // prepare + if(SPI_DI) begin + addr_reset <= ~addr_reset; + downloading_reg <= 1; + end else begin + downloading_reg <= 0; + end + end + + DIO_FILE_RX: begin + // prepare + if(SPI_DI) begin + addr_reset <= ~addr_reset; + uploading_reg <= 1; + end else begin + uploading_reg <= 0; + end + end + + // command 0x57: DIO_FILE_RX_DAT + // command 0x54: DIO_FILE_TX_DAT + DIO_FILE_RX_DAT, + DIO_FILE_TX_DAT: begin + data_w <= {sbuf, SPI_DI}; + rclk <= ~rclk; + end + + // expose file (menu) index + DIO_FILE_INDEX: ioctl_index <= {sbuf, SPI_DI}; + + // receiving FAT directory entry (mist-firmware/fat.h - DIRENTRY) + DIO_FILE_INFO: begin + bytecnt <= bytecnt + 1'd1; + case (bytecnt) + 8'h08: ioctl_fileext[23:16] <= {sbuf, SPI_DI}; + 8'h09: ioctl_fileext[15: 8] <= {sbuf, SPI_DI}; + 8'h0A: ioctl_fileext[ 7: 0] <= {sbuf, SPI_DI}; + 8'h1C: ioctl_filesize[ 7: 0] <= {sbuf, SPI_DI}; + 8'h1D: ioctl_filesize[15: 8] <= {sbuf, SPI_DI}; + 8'h1E: ioctl_filesize[23:16] <= {sbuf, SPI_DI}; + 8'h1F: ioctl_filesize[31:24] <= {sbuf, SPI_DI}; + endcase + end + endcase + end + end +end + +// direct SD Card->FPGA transfer +generate if (ROM_DIRECT_UPLOAD == 1) begin + +always@(posedge SPI_SCK, posedge SPI_SS4) begin : SPI_DIRECT_RECEIVER + reg [6:0] sbuf2; + reg [2:0] cnt2; + reg [9:0] bytecnt; + + if(SPI_SS4) begin + cnt2 <= 0; + bytecnt <= 0; + end else begin + // don't shift in last bit. It is evaluated directly + // when writing to ram + if(cnt2 != 7) + sbuf2 <= { sbuf2[5:0], SPI_DO }; + + cnt2 <= cnt2 + 1'd1; + + // received a byte + if(cnt2 == 7) begin + bytecnt <= bytecnt + 1'd1; + // read 514 byte/sector (512 + 2 CRC) + if (bytecnt == 513) bytecnt <= 0; + // don't send the CRC bytes + if (~bytecnt[9]) begin + data_w2 <= {sbuf2, SPI_DO}; + rclk2 <= ~rclk2; + end + end + end +end + +end +endgenerate + +always@(posedge clk_sys) begin : DATA_OUT + // synchronisers + reg rclkD, rclkD2; + reg rclk2D, rclk2D2; + reg addr_resetD, addr_resetD2; + + reg wr_int, wr_int_direct, rd_int; + reg [24:0] addr; + reg [31:0] filepos; + + // bring flags from spi clock domain into core clock domain + { rclkD, rclkD2 } <= { rclk, rclkD }; + { rclk2D ,rclk2D2 } <= { rclk2, rclk2D }; + { addr_resetD, addr_resetD2 } <= { addr_reset, addr_resetD }; + + ioctl_wr <= 0; + + if (!downloading_reg) begin + ioctl_download <= 0; + wr_int <= 0; + wr_int_direct <= 0; + end + + if (!uploading_reg) begin + ioctl_upload <= 0; + rd_int <= 0; + end + + if (~clkref_n) begin + rd_int <= 0; + wr_int <= 0; + wr_int_direct <= 0; + if (wr_int || wr_int_direct) begin + ioctl_dout <= wr_int ? data_w : data_w2; + ioctl_wr <= 1; + addr <= addr + 1'd1; + ioctl_addr <= addr; + end + if (rd_int) begin + ioctl_addr <= ioctl_addr + 1'd1; + end + end + + // detect transfer start from the SPI receiver + if(addr_resetD ^ addr_resetD2) begin + addr <= START_ADDR; + ioctl_addr <= START_ADDR; + filepos <= 0; + ioctl_download <= downloading_reg; + ioctl_upload <= uploading_reg; + end + + // detect new byte from the SPI receiver + if (rclkD ^ rclkD2) begin + wr_int <= downloading_reg; + rd_int <= uploading_reg; + end + // direct transfer receiver + if (rclk2D ^ rclk2D2 && filepos != ioctl_filesize) begin + filepos <= filepos + 1'd1; + wr_int_direct <= 1; + end +end + +endmodule diff --git a/rtl/mist-modules/mist.qip b/rtl/mist-modules/mist.qip new file mode 100644 index 0000000..de36021 --- /dev/null +++ b/rtl/mist-modules/mist.qip @@ -0,0 +1,10 @@ +set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) mist.vhd] +set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) user_io.v] +set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) data_io.v] +set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) mist_video.v] +set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) scandoubler.v] +set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) osd.v] +set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) arcade_inputs.v] +set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) rgb2ypbpr.v] +set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) cofi.sv] +set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) dac.vhd] diff --git a/rtl/mist-modules/mist.vhd b/rtl/mist-modules/mist.vhd new file mode 100644 index 0000000..d37c947 --- /dev/null +++ b/rtl/mist-modules/mist.vhd @@ -0,0 +1,123 @@ +-- user_io +-- Interface to the MiST IO Controller + +-- mist_video +-- A video pipeline for MiST. Just insert between the core video output and the VGA pins +-- Provides an optional scandoubler, a rotateable OSD and (optional) RGb->YPbPr conversion + +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + +package mist is + +component user_io +generic( + STRLEN : integer := 0; + PS2DIV : integer := 100; + ROM_DIRECT_UPLOAD : boolean := false; + SD_IMAGES: integer := 2; + PS2BIDIR : boolean := false; + FEATURES: std_logic_vector(31 downto 0) := (others=>'0') +); +port ( + clk_sys : in std_logic; + clk_sd : in std_logic := '0'; + SPI_CLK, SPI_SS_IO, SPI_MOSI :in std_logic; + SPI_MISO : out std_logic; + conf_str : in std_logic_vector(8*STRLEN-1 downto 0) := (others => '0'); + conf_addr : out std_logic_vector(9 downto 0); + conf_chr : in std_logic_vector(7 downto 0) := (others => '0'); + joystick_0 : out std_logic_vector(31 downto 0); + joystick_1 : out std_logic_vector(31 downto 0); + joystick_2 : out std_logic_vector(31 downto 0); + joystick_3 : out std_logic_vector(31 downto 0); + joystick_4 : out std_logic_vector(31 downto 0); + joystick_analog_0 : out std_logic_vector(31 downto 0); + joystick_analog_1 : out std_logic_vector(31 downto 0); + status : out std_logic_vector(63 downto 0); + switches : out std_logic_vector(1 downto 0); + buttons : out std_logic_vector(1 downto 0); + scandoubler_disable : out std_logic; + ypbpr : out std_logic; + no_csync : out std_logic; + core_mod : out std_logic_vector(6 downto 0); + + sd_lba : in std_logic_vector(31 downto 0) := (others => '0'); + sd_rd : in std_logic_vector(SD_IMAGES-1 downto 0) := (others => '0'); + sd_wr : in std_logic_vector(SD_IMAGES-1 downto 0) := (others => '0'); + sd_ack : out std_logic; + sd_ack_conf : out std_logic; + sd_conf : in std_logic := '0'; + sd_sdhc : in std_logic := '1'; + img_size : out std_logic_vector(63 downto 0); + img_mounted : out std_logic_vector(SD_IMAGES-1 downto 0); + + sd_buff_addr : out std_logic_vector(8 downto 0); + sd_dout : out std_logic_vector(7 downto 0); + sd_din : in std_logic_vector(7 downto 0) := (others => '0'); + sd_dout_strobe : out std_logic; + sd_din_strobe : out std_logic; + + ps2_kbd_clk : out std_logic; + ps2_kbd_data : out std_logic; + ps2_kbd_clk_i : in std_logic := '1'; + ps2_kbd_data_i : in std_logic := '1'; + key_pressed : out std_logic; + key_extended : out std_logic; + key_code : out std_logic_vector(7 downto 0); + key_strobe : out std_logic; + + ps2_mouse_clk : out std_logic; + ps2_mouse_data : out std_logic; + ps2_mouse_clk_i : in std_logic := '1'; + ps2_mouse_data_i : in std_logic := '1'; + mouse_x : out signed(8 downto 0); + mouse_y : out signed(8 downto 0); + mouse_z : out signed(3 downto 0); + mouse_flags : out std_logic_vector(7 downto 0); -- YOvfl, XOvfl, dy8, dx8, 1, mbtn, rbtn, lbtn + mouse_strobe : out std_logic; + mouse_idx : out std_logic +); +end component user_io; + +component mist_video +generic ( + OSD_COLOR : std_logic_vector(2 downto 0) := "110"; + OSD_X_OFFSET : std_logic_vector(9 downto 0) := (others => '0'); + OSD_Y_OFFSET : std_logic_vector(9 downto 0) := (others => '0'); + SD_HCNT_WIDTH: integer := 9; + COLOR_DEPTH : integer := 6; + OSD_AUTO_CE : boolean := true; + SYNC_AND : boolean := false +); +port ( + clk_sys : in std_logic; + + SPI_SCK : in std_logic; + SPI_SS3 : in std_logic; + SPI_DI : in std_logic; + + scanlines : in std_logic_vector(1 downto 0); + ce_divider : in std_logic := '0'; + scandoubler_disable : in std_logic; + ypbpr : in std_logic; + rotate : in std_logic_vector(1 downto 0); + no_csync : in std_logic := '0'; + blend : in std_logic := '0'; + + HSync : in std_logic; + VSync : in std_logic; + R : in std_logic_vector(COLOR_DEPTH-1 downto 0); + G : in std_logic_vector(COLOR_DEPTH-1 downto 0); + B : in std_logic_vector(COLOR_DEPTH-1 downto 0); + + VGA_HS : out std_logic; + VGA_VS : out std_logic; + VGA_R : out std_logic_vector(5 downto 0); + VGA_G : out std_logic_vector(5 downto 0); + VGA_B : out std_logic_vector(5 downto 0) +); +end component mist_video; + +end package; diff --git a/rtl/mist-modules/mist_core.qip b/rtl/mist-modules/mist_core.qip new file mode 100644 index 0000000..1de1ee1 --- /dev/null +++ b/rtl/mist-modules/mist_core.qip @@ -0,0 +1,7 @@ +set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) mist.vhd] +set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) user_io.v] +set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) mist_video.v] +set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) scandoubler.v] +set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) osd.v] +set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) rgb2ypbpr.v] +set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) cofi.sv] diff --git a/rtl/mist-modules/mist_video.v b/rtl/mist-modules/mist_video.v new file mode 100644 index 0000000..070ab50 --- /dev/null +++ b/rtl/mist-modules/mist_video.v @@ -0,0 +1,156 @@ +// A video pipeline for MiST. Just insert between the core video output and the VGA pins +// Provides an optional scandoubler, a rotateable OSD and (optional) RGb->YPbPr conversion + +module mist_video +( + // master clock + // it should be 4x (or 2x) pixel clock for the scandoubler + input clk_sys, + + // OSD SPI interface + input SPI_SCK, + input SPI_SS3, + input SPI_DI, + + // scanlines (00-none 01-25% 10-50% 11-75%) + input [1:0] scanlines, + + // non-scandoubled pixel clock divider 0 - clk_sys/4, 1 - clk_sys/2 + input ce_divider, + + // 0 = HVSync 31KHz, 1 = CSync 15KHz + input scandoubler_disable, + // disable csync without scandoubler + input no_csync, + // YPbPr always uses composite sync + input ypbpr, + // Rotate OSD [0] - rotate [1] - left or right + input [1:0] rotate, + // composite-like blending + input blend, + + // video in + input [COLOR_DEPTH-1:0] R, + input [COLOR_DEPTH-1:0] G, + input [COLOR_DEPTH-1:0] B, + + input HSync, + input VSync, + + // MiST video output signals + output reg [5:0] VGA_R, + output reg [5:0] VGA_G, + output reg [5:0] VGA_B, + output reg VGA_VS, + output reg VGA_HS +); + +parameter OSD_COLOR = 3'd4; +parameter OSD_X_OFFSET = 10'd0; +parameter OSD_Y_OFFSET = 10'd0; +parameter SD_HCNT_WIDTH = 9; +parameter COLOR_DEPTH = 6; // 1-6 +parameter OSD_AUTO_CE = 1'b1; +parameter SYNC_AND = 1'b0; // 0 - XOR, 1 - AND + +wire [5:0] SD_R_O; +wire [5:0] SD_G_O; +wire [5:0] SD_B_O; +wire SD_HS_O; +wire SD_VS_O; + +wire pixel_ena; + +scandoubler #(SD_HCNT_WIDTH, COLOR_DEPTH) scandoubler +( + .clk_sys ( clk_sys ), + .bypass ( scandoubler_disable ), + .ce_divider ( ce_divider ), + .scanlines ( scanlines ), + .pixel_ena ( pixel_ena ), + .hs_in ( HSync ), + .vs_in ( VSync ), + .r_in ( R ), + .g_in ( G ), + .b_in ( B ), + .hs_out ( SD_HS_O ), + .vs_out ( SD_VS_O ), + .r_out ( SD_R_O ), + .g_out ( SD_G_O ), + .b_out ( SD_B_O ) +); + +wire [5:0] osd_r_o; +wire [5:0] osd_g_o; +wire [5:0] osd_b_o; + +osd #(OSD_X_OFFSET, OSD_Y_OFFSET, OSD_COLOR, OSD_AUTO_CE) osd +( + .clk_sys ( clk_sys ), + .rotate ( rotate ), + .ce ( pixel_ena ), + .SPI_DI ( SPI_DI ), + .SPI_SCK ( SPI_SCK ), + .SPI_SS3 ( SPI_SS3 ), + .R_in ( SD_R_O ), + .G_in ( SD_G_O ), + .B_in ( SD_B_O ), + .HSync ( SD_HS_O ), + .VSync ( SD_VS_O ), + .R_out ( osd_r_o ), + .G_out ( osd_g_o ), + .B_out ( osd_b_o ) +); + +wire [5:0] cofi_r, cofi_g, cofi_b; +wire cofi_hs, cofi_vs; + +cofi #(6) cofi ( + .clk ( clk_sys ), + .pix_ce ( pixel_ena ), + .enable ( blend ), + .hblank ( ~SD_HS_O ), + .hs ( SD_HS_O ), + .vs ( SD_VS_O ), + .red ( osd_r_o ), + .green ( osd_g_o ), + .blue ( osd_b_o ), + .hs_out ( cofi_hs ), + .vs_out ( cofi_vs ), + .red_out ( cofi_r ), + .green_out( cofi_g ), + .blue_out( cofi_b ) +); + +wire hs, vs, cs; +wire [5:0] r,g,b; + +RGBtoYPbPr #(6) rgb2ypbpr +( + .clk ( clk_sys ), + .ena ( ypbpr ), + + .red_in ( cofi_r ), + .green_in ( cofi_g ), + .blue_in ( cofi_b ), + .hs_in ( cofi_hs ), + .vs_in ( cofi_vs ), + .cs_in ( SYNC_AND ? (cofi_hs & cofi_vs) : ~(cofi_hs ^ cofi_vs) ), + .red_out ( r ), + .green_out ( g ), + .blue_out ( b ), + .hs_out ( hs ), + .vs_out ( vs ), + .cs_out ( cs ) +); + +always @(posedge clk_sys) begin + VGA_R <= r; + VGA_G <= g; + VGA_B <= b; + // a minimig vga->scart cable expects a composite sync signal on the VGA_HS output. + // and VCC on VGA_VS (to switch into rgb mode) + VGA_HS <= ((~no_csync & scandoubler_disable) || ypbpr)? cs : hs; + VGA_VS <= ((~no_csync & scandoubler_disable) || ypbpr)? 1'b1 : vs; +end +endmodule diff --git a/rtl/mist-modules/osd.v b/rtl/mist-modules/osd.v new file mode 100644 index 0000000..6f0b84b --- /dev/null +++ b/rtl/mist-modules/osd.v @@ -0,0 +1,216 @@ +// A simple OSD implementation. Can be hooked up between a cores +// VGA output and the physical VGA pins + +module osd ( + // OSDs pixel clock, should be synchronous to cores pixel clock to + // avoid jitter. + input clk_sys, + input ce, + + // SPI interface + input SPI_SCK, + input SPI_SS3, + input SPI_DI, + + input [1:0] rotate, //[0] - rotate [1] - left or right + + // VGA signals coming from core + input [5:0] R_in, + input [5:0] G_in, + input [5:0] B_in, + input HSync, + input VSync, + + // VGA signals going to video connector + output [5:0] R_out, + output [5:0] G_out, + output [5:0] B_out +); + +parameter OSD_X_OFFSET = 11'd0; +parameter OSD_Y_OFFSET = 11'd0; +parameter OSD_COLOR = 3'd0; +parameter OSD_AUTO_CE = 1'b1; + +localparam OSD_WIDTH = 11'd256; +localparam OSD_HEIGHT = 11'd128; + +localparam OSD_WIDTH_PADDED = OSD_WIDTH + (OSD_WIDTH >> 1); // 25% padding left and right + +// ********************************************************************************* +// spi client +// ********************************************************************************* + +// this core supports only the display related OSD commands +// of the minimig +reg osd_enable; +(* ramstyle = "no_rw_check" *) reg [7:0] osd_buffer[2047:0]; // the OSD buffer itself + +// the OSD has its own SPI interface to the io controller +always@(posedge SPI_SCK, posedge SPI_SS3) begin + reg [4:0] cnt; + reg [10:0] bcnt; + reg [7:0] sbuf; + reg [7:0] cmd; + + if(SPI_SS3) begin + cnt <= 0; + bcnt <= 0; + end else begin + sbuf <= {sbuf[6:0], SPI_DI}; + + // 0:7 is command, rest payload + if(cnt < 15) cnt <= cnt + 1'd1; + else cnt <= 8; + + if(cnt == 7) begin + cmd <= {sbuf[6:0], SPI_DI}; + + // lower three command bits are line address + bcnt <= {sbuf[1:0], SPI_DI, 8'h00}; + + // command 0x40: OSDCMDENABLE, OSDCMDDISABLE + if(sbuf[6:3] == 4'b0100) osd_enable <= SPI_DI; + end + + // command 0x20: OSDCMDWRITE + if((cmd[7:3] == 5'b00100) && (cnt == 15)) begin + osd_buffer[bcnt] <= {sbuf[6:0], SPI_DI}; + bcnt <= bcnt + 1'd1; + end + end +end + +// ********************************************************************************* +// video timing and sync polarity anaylsis +// ********************************************************************************* + +// horizontal counter +reg [10:0] h_cnt; +reg [10:0] hs_low, hs_high; +wire hs_pol = hs_high < hs_low; +wire [10:0] dsp_width = hs_pol ? hs_low : hs_high; + +// vertical counter +reg [10:0] v_cnt; +reg [10:0] vs_low, vs_high; +wire vs_pol = vs_high < vs_low; +wire [10:0] dsp_height = vs_pol ? vs_low : vs_high; + +wire doublescan = (dsp_height>350); + +reg auto_ce_pix; +always @(posedge clk_sys) begin + reg [15:0] cnt = 0; + reg [2:0] pixsz; + reg [2:0] pixcnt; + reg hs; + + cnt <= cnt + 1'd1; + hs <= HSync; + + pixcnt <= pixcnt + 1'd1; + if(pixcnt == pixsz) pixcnt <= 0; + auto_ce_pix <= !pixcnt; + + if(hs && ~HSync) begin + cnt <= 0; + if(cnt <= OSD_WIDTH_PADDED * 2) pixsz <= 0; + else if(cnt <= OSD_WIDTH_PADDED * 3) pixsz <= 1; + else if(cnt <= OSD_WIDTH_PADDED * 4) pixsz <= 2; + else if(cnt <= OSD_WIDTH_PADDED * 5) pixsz <= 3; + else if(cnt <= OSD_WIDTH_PADDED * 6) pixsz <= 4; + else pixsz <= 5; + + pixcnt <= 0; + auto_ce_pix <= 1; + end +end + +wire ce_pix = OSD_AUTO_CE ? auto_ce_pix : ce; + +always @(posedge clk_sys) begin + reg hsD; + reg vsD; + + if(ce_pix) begin + // bring hsync into local clock domain + hsD <= HSync; + + // falling edge of HSync + if(!HSync && hsD) begin + h_cnt <= 0; + hs_high <= h_cnt; + end + + // rising edge of HSync + else if(HSync && !hsD) begin + h_cnt <= 0; + hs_low <= h_cnt; + v_cnt <= v_cnt + 1'd1; + end else begin + h_cnt <= h_cnt + 1'd1; + end + + vsD <= VSync; + + // falling edge of VSync + if(!VSync && vsD) begin + v_cnt <= 0; + // if the difference is only one line, that might be interlaced picture + if (vs_high != v_cnt + 1'd1) vs_high <= v_cnt; + end + + // rising edge of VSync + else if(VSync && !vsD) begin + v_cnt <= 0; + // if the difference is only one line, that might be interlaced picture + if (vs_low != v_cnt + 1'd1) vs_low <= v_cnt; + end + end +end + +// area in which OSD is being displayed +reg [10:0] h_osd_start; +reg [10:0] h_osd_end; +reg [10:0] v_osd_start; +reg [10:0] v_osd_end; + +always @(posedge clk_sys) begin + h_osd_start <= ((dsp_width - OSD_WIDTH)>> 1) + OSD_X_OFFSET; + h_osd_end <= h_osd_start + OSD_WIDTH; + v_osd_start <= ((dsp_height- (OSD_HEIGHT<> 1) + OSD_Y_OFFSET; + v_osd_end <= v_osd_start + (OSD_HEIGHT<= h_osd_start) && ((h_cnt + 1'd1) < h_osd_end) && + (VSync != vs_pol) && (v_cnt >= v_osd_start) && (v_cnt < v_osd_end); + end +end + +assign R_out = !osd_de ? R_in : {osd_pixel, osd_pixel, OSD_COLOR[2], R_in[5:3]}; +assign G_out = !osd_de ? G_in : {osd_pixel, osd_pixel, OSD_COLOR[1], G_in[5:3]}; +assign B_out = !osd_de ? B_in : {osd_pixel, osd_pixel, OSD_COLOR[0], B_in[5:3]}; + +endmodule diff --git a/rtl/mist-modules/pll.qip b/rtl/mist-modules/pll.qip new file mode 100644 index 0000000..e69de29 diff --git a/rtl/mist-modules/rgb2ypbpr.v b/rtl/mist-modules/rgb2ypbpr.v new file mode 100644 index 0000000..ee3afc1 --- /dev/null +++ b/rtl/mist-modules/rgb2ypbpr.v @@ -0,0 +1,103 @@ +// Multiplier-based RGB -> YPbPr conversion + +// Copyright 2020/2021 by Alastair M. Robinson + +module RGBtoYPbPr +( + input clk, + input ena, + + input [WIDTH-1:0] red_in, + input [WIDTH-1:0] green_in, + input [WIDTH-1:0] blue_in, + input hs_in, + input vs_in, + input cs_in, + input pixel_in, + + output [WIDTH-1:0] red_out, + output [WIDTH-1:0] green_out, + output [WIDTH-1:0] blue_out, + output reg hs_out, + output reg vs_out, + output reg cs_out, + output reg pixel_out +); + +parameter WIDTH = 8; + +reg [8+WIDTH-1:0] r_y; +reg [8+WIDTH-1:0] g_y; +reg [8+WIDTH-1:0] b_y; + +reg [8+WIDTH-1:0] r_b; +reg [8+WIDTH-1:0] g_b; +reg [8+WIDTH-1:0] b_b; + +reg [8+WIDTH-1:0] r_r; +reg [8+WIDTH-1:0] g_r; +reg [8+WIDTH-1:0] b_r; + +reg [8+WIDTH-1:0] y; +reg [8+WIDTH-1:0] b; +reg [8+WIDTH-1:0] r; + +reg hs_d; +reg vs_d; +reg cs_d; +reg pixel_d; + +assign red_out = r[8+WIDTH-1:8]; +assign green_out = y[8+WIDTH-1:8]; +assign blue_out = b[8+WIDTH-1:8]; + +// Multiply in the first stage... +always @(posedge clk) begin + hs_d <= hs_in; // Register sync, pixel clock, etc + vs_d <= vs_in; // so they're delayed the same amount as the incoming video + cs_d <= cs_in; + pixel_d <= pixel_in; + + if(ena) begin + // (Y = 0.299*R + 0.587*G + 0.114*B) + r_y <= red_in * 8'd76; + g_y <= green_in * 8'd150; + b_y <= blue_in * 8'd29; + + // (Pb = -0.169*R - 0.331*G + 0.500*B) + r_b <= red_in * 8'd43; + g_b <= green_in * 8'd84; + b_b <= blue_in * 8'd128; + + // (Pr = 0.500*R - 0.419*G - 0.081*B) + r_r <= red_in * 8'd128; + g_r <= green_in * 8'd107; + b_r <= blue_in * 8'd20; + end else begin + r_r[8+WIDTH-1:8] <= red_in; // Passthrough + g_y[8+WIDTH-1:8] <= green_in; + b_b[8+WIDTH-1:8] <= blue_in; + end + +end + +// Second stage - adding + +always @(posedge clk) begin + hs_out <= hs_d; + vs_out <= vs_d; + cs_out <= cs_d; + pixel_out <= pixel_d; + + if(ena) begin + y <= r_y + g_y + b_y; + b <= 2'd2**(8+WIDTH-1) + b_b - r_b - g_b; + r <= 2'd2**(8+WIDTH-1) + r_r - g_r - b_r; + end else begin + y <= g_y; // Passthrough + b <= b_b; + r <= r_r; + end +end + +endmodule diff --git a/rtl/mist-modules/scandoubler.v b/rtl/mist-modules/scandoubler.v new file mode 100644 index 0000000..c9f0d68 --- /dev/null +++ b/rtl/mist-modules/scandoubler.v @@ -0,0 +1,229 @@ +// +// scandoubler.v +// +// Copyright (c) 2015 Till Harbaum +// +// This source file is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// TODO: Delay vsync one line + +module scandoubler +( + // system interface + input clk_sys, + input bypass, + input ce_divider, + output pixel_ena, + + // scanlines (00-none 01-25% 10-50% 11-75%) + input [1:0] scanlines, + + // shifter video interface + input hs_in, + input vs_in, + input [COLOR_DEPTH-1:0] r_in, + input [COLOR_DEPTH-1:0] g_in, + input [COLOR_DEPTH-1:0] b_in, + + // output interface + output reg hs_out, + output reg vs_out, + output reg [5:0] r_out, + output reg [5:0] g_out, + output reg [5:0] b_out +); + +parameter HCNT_WIDTH = 9; +parameter COLOR_DEPTH = 6; + +// pixel clock divider +reg [1:0] i_div; +reg ce_x1, ce_x2; + +always @(posedge clk_sys) begin + reg last_hs_in; + last_hs_in <= hs_in; + if(last_hs_in & !hs_in) begin + i_div <= 2'b00; + end else begin + i_div <= i_div + 2'd1; + end +end + +always @(*) begin + if (!ce_divider) begin + ce_x1 = (i_div == 2'b01); + ce_x2 = i_div[0]; + end else begin + ce_x1 = i_div[0]; + ce_x2 = 1'b1; + end +end + +assign pixel_ena = bypass ? ce_x1 : ce_x2; + +// --------------------- create output signals ----------------- +// latch everything once more to make it glitch free and apply scanline effect +reg scanline; +reg [5:0] r; +reg [5:0] g; +reg [5:0] b; + +always @(*) begin + if (COLOR_DEPTH == 6) begin + b = sd_out[5:0]; + g = sd_out[11:6]; + r = sd_out[17:12]; + end else if (COLOR_DEPTH == 2) begin + b = {3{sd_out[1:0]}}; + g = {3{sd_out[3:2]}}; + r = {3{sd_out[5:4]}}; + end else if (COLOR_DEPTH == 1) begin + b = {6{sd_out[0]}}; + g = {6{sd_out[1]}}; + r = {6{sd_out[2]}}; + end else begin + b = { sd_out[COLOR_DEPTH-1:0], sd_out[COLOR_DEPTH-1 -:(6-COLOR_DEPTH)] }; + g = { sd_out[COLOR_DEPTH*2-1:COLOR_DEPTH], sd_out[COLOR_DEPTH*2-1 -:(6-COLOR_DEPTH)] }; + r = { sd_out[COLOR_DEPTH*3-1:COLOR_DEPTH*2], sd_out[COLOR_DEPTH*3-1 -:(6-COLOR_DEPTH)] }; + end +end + +always @(posedge clk_sys) begin + if(bypass) begin + r_out <= r; + g_out <= g; + b_out <= b; + hs_out <= hs_sd; + vs_out <= vs_sd; + end else if(ce_x2) begin + hs_out <= hs_sd; + vs_out <= vs_sd; + + // reset scanlines at every new screen + if(vs_out != vs_in) scanline <= 0; + + // toggle scanlines at begin of every hsync + if(hs_out && !hs_sd) scanline <= !scanline; + + // if no scanlines or not a scanline + if(!scanline || !scanlines) begin + r_out <= r; + g_out <= g; + b_out <= b; + end else begin + case(scanlines) + 1: begin // reduce 25% = 1/2 + 1/4 + r_out <= {1'b0, r[5:1]} + {2'b00, r[5:2] }; + g_out <= {1'b0, g[5:1]} + {2'b00, g[5:2] }; + b_out <= {1'b0, b[5:1]} + {2'b00, b[5:2] }; + end + + 2: begin // reduce 50% = 1/2 + r_out <= {1'b0, r[5:1]}; + g_out <= {1'b0, g[5:1]}; + b_out <= {1'b0, b[5:1]}; + end + + 3: begin // reduce 75% = 1/4 + r_out <= {2'b00, r[5:2]}; + g_out <= {2'b00, g[5:2]}; + b_out <= {2'b00, b[5:2]}; + end + endcase + end + end +end + +// scan doubler output register +wire [COLOR_DEPTH*3-1:0] sd_out = bypass ? sd_bypass_out : sd_buffer_out; + +// ================================================================== +// ======================== the line buffers ======================== +// ================================================================== + +// 2 lines of 2**HCNT_WIDTH pixels 3*COLOR_DEPTH bit RGB +(* ramstyle = "no_rw_check" *) reg [COLOR_DEPTH*3-1:0] sd_buffer[2*2**HCNT_WIDTH]; + +// use alternating sd_buffers when storing/reading data +reg line_toggle; + +// total hsync time (in 16MHz cycles), hs_total reaches 1024 +reg [HCNT_WIDTH-1:0] hs_max; +reg [HCNT_WIDTH-1:0] hs_rise; +reg [HCNT_WIDTH-1:0] hcnt; + +always @(posedge clk_sys) begin + reg hsD, vsD; + + if(ce_x1) begin + hsD <= hs_in; + + // falling edge of hsync indicates start of line + if(hsD && !hs_in) begin + hs_max <= hcnt; + hcnt <= 0; + end else begin + hcnt <= hcnt + 1'd1; + end + + // save position of rising edge + if(!hsD && hs_in) hs_rise <= hcnt; + + vsD <= vs_in; + if(vsD != vs_in) line_toggle <= 0; + + // begin of incoming hsync + if(hsD && !hs_in) line_toggle <= !line_toggle; + + sd_buffer[{line_toggle, hcnt}] <= {r_in, g_in, b_in}; + end +end + +// ================================================================== +// ==================== output timing generation ==================== +// ================================================================== + +reg [COLOR_DEPTH*3-1:0] sd_buffer_out, sd_bypass_out; +reg [HCNT_WIDTH-1:0] sd_hcnt; +reg hs_sd, vs_sd; + +// timing generation runs 32 MHz (twice the input signal analysis speed) +always @(posedge clk_sys) begin + reg hsD; + + if(ce_x2) begin + hsD <= hs_in; + + // output counter synchronous to input and at twice the rate + sd_hcnt <= sd_hcnt + 1'd1; + if(hsD && !hs_in) sd_hcnt <= hs_max; + if(sd_hcnt == hs_max) sd_hcnt <= 0; + + // replicate horizontal sync at twice the speed + if(sd_hcnt == hs_max) hs_sd <= 0; + if(sd_hcnt == hs_rise) hs_sd <= 1; + + // read data from line sd_buffer + sd_buffer_out <= sd_buffer[{~line_toggle, sd_hcnt}]; + vs_sd <= vs_in; + end + if(bypass) begin + sd_bypass_out <= {r_in, g_in, b_in}; + hs_sd <= hs_in; + vs_sd <= vs_in; + end +end + +endmodule diff --git a/rtl/mist-modules/sd_card.v b/rtl/mist-modules/sd_card.v new file mode 100644 index 0000000..0d1ff58 --- /dev/null +++ b/rtl/mist-modules/sd_card.v @@ -0,0 +1,619 @@ +// +// sd_card.v +// +// This file implelents a sd card for the MIST board since on the board +// the SD card is connected to the ARM IO controller and the FPGA has no +// direct connection to the SD card. This file provides a SD card like +// interface to the IO controller easing porting of cores that expect +// a direct interface to the SD card. +// +// Copyright (c) 2014 Till Harbaum +// +// This source file is free software: you can redistribute it and/or modify +// it under the terms of the Lesser GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// http://elm-chan.org/docs/mmc/mmc_e.html + +module sd_card ( + input clk_sys, + // link to user_io for io controller + output reg[31:0] sd_lba, + output reg sd_rd, + output reg sd_wr, + input sd_ack, + input sd_ack_conf, + output sd_conf, + output sd_sdhc, + + input img_mounted, + input [63:0] img_size, + + output reg sd_busy = 0, + // data coming in from io controller + input [7:0] sd_buff_dout, + input sd_buff_wr, + + // data going out to io controller + output [7:0] sd_buff_din, + + input [8:0] sd_buff_addr, + + // configuration input + // in case of a VHD file, this will determine the SD Card type returned to the SPI master + // in case of a pass-through, the firmware will display a warning if SDHC is not allowed, + // but the card inserted is SDHC + input allow_sdhc, + + input sd_cs, + input sd_sck, + input sd_sdi, + output reg sd_sdo +); + +wire [31:0] OCR = { 1'b1, sdhc, 6'h0, 9'h1f, 15'h0 }; // bit31 = finished powerup + // bit30 = 1 -> high capaciry card (sdhc) + // 15-23 supported voltage range +wire [7:0] READ_DATA_TOKEN = 8'hfe; + +// number of bytes to wait after a command before sending the reply +localparam NCR=4; + +localparam RD_STATE_IDLE = 3'd0; +localparam RD_STATE_WAIT_BUSY = 3'd1; +localparam RD_STATE_WAIT_IO = 3'd2; +localparam RD_STATE_SEND_TOKEN = 3'd3; +localparam RD_STATE_SEND_DATA = 3'd4; +localparam RD_STATE_DELAY = 3'd5; +reg [2:0] read_state = RD_STATE_IDLE; + +localparam WR_STATE_IDLE = 3'd0; +localparam WR_STATE_EXP_DTOKEN = 3'd1; +localparam WR_STATE_RECV_DATA = 3'd2; +localparam WR_STATE_RECV_CRC0 = 3'd3; +localparam WR_STATE_RECV_CRC1 = 3'd4; +localparam WR_STATE_SEND_DRESP = 3'd5; +localparam WR_STATE_WRITE = 3'd6; +localparam WR_STATE_BUSY = 3'd7; +reg [2:0] write_state = WR_STATE_IDLE; + +reg card_is_reset = 1'b0; // flag that card has received a reset command +reg [6:0] sbuf; +reg cmd55; +reg terminate_cmd = 1'b0; +reg [7:0] cmd = 8'h00; +reg [2:0] bit_cnt = 3'd0; // counts bits 0-7 0-7 ... +reg [3:0] byte_cnt= 4'd15; // counts bytes +reg [4:0] delay_cnt; +reg wr_first; + +reg [39:0] args; + +reg [7:0] reply; +reg [7:0] reply0, reply1, reply2, reply3; +reg [3:0] reply_len; + +// ------------------------- SECTOR BUFFER ----------------------- + +// the buffer itself. Can hold two sectors +reg [9:0] buffer_ptr; +wire [7:0] buffer_dout; +reg [7:0] buffer_din; +reg buffer_write_strobe; +reg sd_buff_sel; + +sd_card_dpram #(8, 10) buffer_dpram +( + .clock_a (clk_sys), + .address_a ({sd_buff_sel, sd_buff_addr}), + .data_a (sd_buff_dout), + .wren_a (sd_buff_wr & sd_ack & read_state != RD_STATE_IDLE), + .q_a (sd_buff_din), + + .clock_b (clk_sys), + .address_b (buffer_ptr), + .data_b (buffer_din), + .wren_b (buffer_write_strobe), + .q_b (buffer_dout) +); + +wire [7:0] WRITE_DATA_RESPONSE = 8'h05; + +// ------------------------- CSD/CID BUFFER ---------------------- +reg [7:0] conf; +assign sd_conf = sd_configuring; + +reg sd_configuring = 1; +reg [4:0] conf_buff_ptr; +reg [7:0] conf_byte_orig; +reg[255:0] csdcid; + +reg vhd = 0; +reg [40:0] vhd_size; + +// conf[0]==1 -> io controller is using an sdhc card +wire sd_has_sdhc = conf[0]; +assign sd_sdhc = (allow_sdhc & sd_has_sdhc) | vhd; // report to user_io +wire sdhc = allow_sdhc & (sd_has_sdhc | vhd); // used internally + +always @(posedge clk_sys) begin + reg old_mounted; + + if (sd_buff_wr & sd_ack_conf) begin + if (sd_buff_addr == 32) begin + conf <= sd_buff_dout; + sd_configuring <= 0; + end + else csdcid[(31-sd_buff_addr) << 3 +:8] <= sd_buff_dout; + end + conf_byte_orig <= csdcid[(31-conf_buff_ptr) << 3 +:8]; + + old_mounted <= img_mounted; + if (~old_mounted & img_mounted) begin + vhd <= |img_size; + vhd_size <= img_size[40:0]; + end +end + +// CSD V1.0 no. of blocks = c_size ** (c_size_mult + 2) +wire [127:0] csd_sd = { + 8'h00, // CSD_STRUCTURE + reserved + 8'h2d, // TAAC + 8'd0, // NSAC + 8'h32, // TRAN_SPEED + 12'h5b5, // CCC + 4'h9, // READ_BL_LEN + 1'b1, 1'b0, 1'b0, 1'b0, // READ_BL_PARTIAL, WRITE_BLK_MISALIGN, READ_BLK_MISALIGN, DSR_IMP + 2'd0, vhd_size[29:18], // reserved + C_SIZE + 3'b111, // VDD_R_CURR_MIN + 3'b110, // VDD_R_CURR_MAX + 3'b111, // VDD_W_CURR_MIN + 3'b110, // VDD_W_CURR_MAX + 3'd7, // C_SIZE_MULT + 1'b1, // ERASE_BLK_EN + 7'd127, // SECTOR_SIZE + 7'd0, // WP_GRP_SIZE + 1'b0, // WP_GRP_ENABLE, + 2'b00, // reserved, + 3'd5, // R2W_FACTOR, + 4'h9, // WRITE_BL_LEN, + 1'b0, // WRITE_BL_PARTIAL, + 5'd0, // reserved, + 8'd0, + 7'h67, // CRC (wrong, but usually not checked) + 1'b1 }; + +// CSD V2.0 size = (c_size + 1) * 512K +wire [127:0] csd_sdhc = { + 8'h40, // CSD_STRUCTURE + reserved + 8'h0e, // TAAC + 8'd0, // NSAC + 8'h32, // TRAN_SPEED + 12'h5b5, // CCC + 4'h9, // READ_BL_LEN + 1'b0, 1'b0, 1'b0, 1'b0, // READ_BL_PARTIAL, WRITE_BLK_MISALIGN, READ_BLK_MISALIGN, DSR_IMP + 6'd0, // reserved + vhd_size[40:19] - 1'd1, // C_SIZE + 1'b0, // reserved + 1'b1, // ERASE_BLK_EN + 7'd127, // SECTOR_SIZE + 7'd0, // WP_GRP_SIZE + 1'b0, // WP_GRP_ENABLE, + 2'b00, // reserved, + 3'd2, // R2W_FACTOR, + 4'h9, // WRITE_BL_LEN, + 1'b0, // WRITE_BL_PARTIAL, + 5'd0, // reserved, + 8'd0, + 7'h78, // CRC (wrong, but usually not checked) + 1'b1 }; + +wire [7:0] conf_byte = (!conf_buff_ptr[4] | !vhd) ? conf_byte_orig : // CID or CSD if not VHD + sdhc ? csd_sdhc[(15-conf_buff_ptr[3:0]) << 3 +:8] : + csd_sd[(15-conf_buff_ptr[3:0]) << 3 +:8]; + +always@(posedge clk_sys) begin + + reg old_sd_sck; + reg [5:0] ack; + + ack <= {ack[4:0], sd_ack}; + if(ack[5:4] == 'b01) { sd_rd, sd_wr } <= 2'b00; + if(ack[5:4] == 'b10) sd_busy <= 0; + + buffer_write_strobe <= 0; + if (buffer_write_strobe) buffer_ptr <= buffer_ptr + 1'd1; + + old_sd_sck <= sd_sck; + + // advance transmitter state machine on falling sck edge, so data is valid on the + // rising edge + // ----------------- spi transmitter -------------------- + if(sd_cs == 0 && old_sd_sck && ~sd_sck) begin + + sd_sdo <= 1'b1; // default: send 1's (busy/wait) + + if(byte_cnt == 5+NCR) begin + sd_sdo <= reply[~bit_cnt]; + + if(bit_cnt == 7) begin + // these three commands all have a reply_len of 0 and will thus + // not send more than a single reply byte + + // CMD9: SEND_CSD + // CMD10: SEND_CID + if((cmd == 8'h49)||(cmd == 8'h4a)) + read_state <= RD_STATE_SEND_TOKEN; // jump directly to data transmission + + // CMD17: READ_SINGLE_BLOCK + // CMD18: READ_MULTIPLE_BLOCK + if((cmd == 8'h51 || cmd == 8'h52) && !terminate_cmd) + read_state <= RD_STATE_WAIT_BUSY; // start waiting for data from io controller + + end + end + else if((reply_len > 0) && (byte_cnt == 5+NCR+1)) + sd_sdo <= reply0[~bit_cnt]; + else if((reply_len > 1) && (byte_cnt == 5+NCR+2)) + sd_sdo <= reply1[~bit_cnt]; + else if((reply_len > 2) && (byte_cnt == 5+NCR+3)) + sd_sdo <= reply2[~bit_cnt]; + else if((reply_len > 3) && (byte_cnt == 5+NCR+4)) + sd_sdo <= reply3[~bit_cnt]; + + // ---------- read state machine processing ------------- + + case(read_state) + RD_STATE_IDLE: ; + // don't do anything + + // wait until the IO controller is free and issue a read + RD_STATE_WAIT_BUSY: + if (~sd_busy) begin + sd_buff_sel <= 0; + sd_lba <= sdhc?args[39:8]:{9'd0, args[39:17]}; + sd_rd <= 1; // trigger request to io controller + sd_busy <= 1; + read_state <= RD_STATE_WAIT_IO; + end + + // waiting for io controller to return data + RD_STATE_WAIT_IO: begin + buffer_ptr <= 0; + if(~sd_busy) begin + if (terminate_cmd) begin + cmd <= 0; + read_state <= RD_STATE_IDLE; + end else if (bit_cnt == 7) + read_state <= RD_STATE_SEND_TOKEN; + end + end + + // send data token + RD_STATE_SEND_TOKEN: begin + if(~sd_busy) begin + sd_sdo <= READ_DATA_TOKEN[~bit_cnt]; + + if(bit_cnt == 7) begin + read_state <= RD_STATE_SEND_DATA; // next: send data + conf_buff_ptr <= (cmd == 8'h4a) ? 5'h0 : 5'h10; + end + end + end + + // send data + RD_STATE_SEND_DATA: begin + if(cmd == 8'h51 || (cmd == 8'h52 && !terminate_cmd)) // CMD17: READ_SINGLE_BLOCK, CMD18: READ_MULTIPLE_BLOCK + sd_sdo <= buffer_dout[~bit_cnt]; + else if(cmd == 8'h49 || cmd == 8'h4a) // CMD9: SEND_CSD, CMD10: SEND CID + sd_sdo <= conf_byte[~bit_cnt]; + + if(bit_cnt == 7) begin + + // sent 512 sector data bytes? + if((cmd == 8'h51) && &buffer_ptr[8:0]) + read_state <= RD_STATE_IDLE; // next: send crc. It's ignored so return to idle state + + if (cmd == 8'h52) begin + if (terminate_cmd) begin + read_state <= RD_STATE_IDLE; + cmd <= 0; + end else if (buffer_ptr[8:0] == 10) begin + // prefetch the next sector into the other buffer + sd_lba <= sd_lba + 1'd1; + sd_rd <= 1; + sd_busy <= 1; + sd_buff_sel <= !sd_buff_sel; + end else if (&buffer_ptr[8:0]) begin + delay_cnt <= 20; + read_state <= RD_STATE_DELAY; + end + end + + // sent 16 cid/csd data bytes? + if(((cmd == 8'h49)||(cmd == 8'h4a)) && conf_buff_ptr[3:0] == 4'h0f) // && (buffer_rptr == 16)) + read_state <= RD_STATE_IDLE; // return to idle state + + buffer_ptr <= buffer_ptr + 1'd1; + conf_buff_ptr<= conf_buff_ptr+ 1'd1; + end + end + + RD_STATE_DELAY: + if(bit_cnt == 7) begin + if (delay_cnt == 0) begin + read_state <= RD_STATE_SEND_TOKEN; + end else begin + delay_cnt <= delay_cnt - 1'd1; + end + end + + endcase + + // ------------------ write support ---------------------- + // send write data response + if(write_state == WR_STATE_SEND_DRESP) + sd_sdo <= WRITE_DATA_RESPONSE[~bit_cnt]; + + // busy after write until the io controller sends ack + if(write_state == WR_STATE_WRITE || write_state == WR_STATE_BUSY) + sd_sdo <= 1'b0; + end + + // spi receiver + // cs is active low + if(sd_cs == 1) begin + bit_cnt <= 3'd0; + terminate_cmd <= 0; + cmd <= 0; + read_state <= RD_STATE_IDLE; + reply_len <= 0; + end else if (~old_sd_sck & sd_sck) begin + bit_cnt <= bit_cnt + 3'd1; + + // assemble byte + if(bit_cnt != 7) + sbuf[6:0] <= { sbuf[5:0], sd_sdi }; + else begin + // finished reading one byte + // byte counter runs against 15 byte boundary + if(byte_cnt != 15) + byte_cnt <= byte_cnt + 4'd1; + + // byte_cnt > 6 -> complete command received + // first byte of valid command is 01xxxxxx + // don't accept new commands (except STOP TRANSMISSION) once a write or read command has been accepted + if((byte_cnt > (reply_len == 0 ? 5 : (5+NCR+reply_len))) && + (write_state == WR_STATE_IDLE) && + (read_state == RD_STATE_IDLE || (read_state != RD_STATE_IDLE && { sbuf, sd_sdi} == 8'h4c)) && + sbuf[6:5] == 2'b01) + begin + byte_cnt <= 4'd0; + terminate_cmd <= 0; + if ({ sbuf, sd_sdi } == 8'h4c) begin + terminate_cmd <= 1; + end else + cmd <= { sbuf, sd_sdi}; + end + + // parse additional command bytes + if(byte_cnt == 0) args[39:32] <= { sbuf, sd_sdi}; + if(byte_cnt == 1) args[31:24] <= { sbuf, sd_sdi}; + if(byte_cnt == 2) args[23:16] <= { sbuf, sd_sdi}; + if(byte_cnt == 3) args[15:8] <= { sbuf, sd_sdi}; + if(byte_cnt == 4) args[7:0] <= { sbuf, sd_sdi}; + + // last byte received, evaluate + if(byte_cnt == 5) begin + + // default: + reply <= 8'h04; // illegal command + reply_len <= 4'd0; // no extra reply bytes + cmd55 <= 0; + + // CMD0: GO_IDLE_STATE + if(cmd == 8'h40) begin + card_is_reset <= 1'b1; + reply <= 8'h01; // ok, busy + end + + // every other command is only accepted after a reset + else if(card_is_reset) begin + // CMD12: STOP_TRANSMISSION + if (terminate_cmd) + reply <= 8'h00; // ok + else case(cmd) + // CMD1: SEND_OP_COND + 8'h41: reply <= 8'h00; // ok, not busy + + // CMD8: SEND_IF_COND (V2 only) + 8'h48: begin + reply <= 8'h01; // ok, busy + reply0 <= 8'h00; + reply1 <= 8'h00; + reply2 <= { 4'b0, args[19:16] }; + reply3 <= args[15:8]; + reply_len <= 4'd4; + end + + // CMD9: SEND_CSD + 8'h49: reply <= 8'h00; // ok + + // CMD10: SEND_CID + 8'h4a: reply <= 8'h00; // ok + + // CMD13: SEND_STATUS + 8'h4d: begin + reply <= 8'h00; // ok + reply0 <=8'h00; + reply_len <= 4'd1; + end + + // CMD16: SET_BLOCKLEN + 8'h50: + // we only support a block size of 512 + if(args[39:8] == 32'd512) + reply <= 8'h00; // ok + else + reply <= 8'h40; // parmeter error + + // CMD17: READ_SINGLE_BLOCK + 8'h51: reply <= 8'h00; // ok + + // CMD18: READ_MULTIPLE_BLOCK + 8'h52: reply <= 8'h00; // ok + + // CMD24: WRITE_BLOCK + // CMD25: WRITE_MULTIPLE_BLOCKS + 8'h58, 8'h59: begin + reply <= 8'h00; // ok + buffer_ptr <= 0; + wr_first <= 1; + write_state <= WR_STATE_EXP_DTOKEN; // expect data token + end + + // ACMD41: APP_SEND_OP_COND + 8'h69: if(cmd55) begin + reply <= 8'h00; // ok, not busy + end + + // CMD55: APP_COND + 8'h77: begin + reply <= 8'h01; // ok, busy + cmd55 <= 1; + end + + // CMD58: READ_OCR + 8'h7a: begin + reply <= 8'h00; // ok + + reply0 <= OCR[31:24]; // bit 30 = 1 -> high capacity card + reply1 <= OCR[23:16]; + reply2 <= OCR[15:8]; + reply3 <= OCR[7:0]; + reply_len <= 4'd4; + end + + endcase + end + end + end + end + + // ---------- handle write ----------- + case(write_state) + // don't do anything in idle state + WR_STATE_IDLE: ; + + // waiting for data token + WR_STATE_EXP_DTOKEN: + if (sd_cs) write_state <= WR_STATE_IDLE; + else if (~old_sd_sck && sd_sck && bit_cnt == 7) begin + if({ sbuf, sd_sdi} == 8'hfd && cmd == 8'h59) + // stop multiple write (and wait until the last write finishes) + write_state <= WR_STATE_BUSY; + else + if({ sbuf, sd_sdi} == ((cmd == 8'h59) ? 8'hfc : 8'hfe)) + write_state <= WR_STATE_RECV_DATA; + end + + // transfer 512 bytes + WR_STATE_RECV_DATA: + if (sd_cs) write_state <= WR_STATE_IDLE; + else if (~old_sd_sck && sd_sck && bit_cnt == 7) begin + // push one byte into local buffer + buffer_write_strobe <= 1'b1; + buffer_din <= { sbuf, sd_sdi }; + + // all bytes written? + if(&buffer_ptr[8:0]) + write_state <= WR_STATE_RECV_CRC0; + end + + // transfer 1st crc byte + WR_STATE_RECV_CRC0: + if (sd_cs) write_state <= WR_STATE_IDLE; + else if (~old_sd_sck && sd_sck && bit_cnt == 7) write_state <= WR_STATE_RECV_CRC1; + + // transfer 2nd crc byte + WR_STATE_RECV_CRC1: + if (sd_cs) write_state <= WR_STATE_IDLE; + else if (~old_sd_sck && sd_sck && bit_cnt == 7) write_state <= WR_STATE_SEND_DRESP; + + // send data response + WR_STATE_SEND_DRESP: + if (sd_cs) write_state <= WR_STATE_IDLE; + else if (~old_sd_sck && sd_sck && bit_cnt == 7) write_state <= WR_STATE_WRITE; + + WR_STATE_WRITE: + if (~sd_busy) begin + if (wr_first) begin + sd_buff_sel <= 0; + sd_lba <= sdhc?args[39:8]:{9'd0, args[39:17]}; + wr_first <= 0; + end else begin + sd_buff_sel <= !sd_buff_sel; + sd_lba <= sd_lba + 1'd1; + end + sd_wr <= 1; // trigger write request to io controller + sd_busy <= 1; + + if (sd_cs || cmd == 8'h58) + write_state <= WR_STATE_BUSY; + else + write_state <= WR_STATE_EXP_DTOKEN; // multi-sector writes + + end + + WR_STATE_BUSY: + if (~sd_busy) write_state <= WR_STATE_IDLE; + + default: ; + endcase +end + +endmodule + +module sd_card_dpram #(parameter DATAWIDTH=8, ADDRWIDTH=9) +( + input clock_a, + input [ADDRWIDTH-1:0] address_a, + input [DATAWIDTH-1:0] data_a, + input wren_a, + output reg [DATAWIDTH-1:0] q_a, + + input clock_b, + input [ADDRWIDTH-1:0] address_b, + input [DATAWIDTH-1:0] data_b, + input wren_b, + output reg [DATAWIDTH-1:0] q_b +); + +reg [DATAWIDTH-1:0] ram[0:(1< +// +// This source file is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +// parameter STRLEN and the actual length of conf_str have to match + +module user_io ( + input [(8*STRLEN)-1:0] conf_str, + output [9:0] conf_addr, // RAM address for config string, if STRLEN=0 + input [7:0] conf_chr, + + input clk_sys, // clock for system-related messages (kbd, joy, etc...) + input clk_sd, // clock for SD-card related messages + + input SPI_CLK, + input SPI_SS_IO, + output reg SPI_MISO, + input SPI_MOSI, + + output reg [31:0] joystick_0, + output reg [31:0] joystick_1, + output reg [31:0] joystick_2, + output reg [31:0] joystick_3, + output reg [31:0] joystick_4, + output reg [31:0] joystick_analog_0, + output reg [31:0] joystick_analog_1, + output [1:0] buttons, + output [1:0] switches, + output scandoubler_disable, + output ypbpr, + output no_csync, + output reg [63:0] status, + output reg [6:0] core_mod, // core variant, sent before the config string is requested + // RTC data from IO controller + // sec, min, hour, date, month, year, day (BCD) + output reg [63:0] rtc, + + // connection to sd card emulation + input [31:0] sd_lba, + input [SD_IMAGES-1:0] sd_rd, + input [SD_IMAGES-1:0] sd_wr, + output reg sd_ack, + output reg sd_ack_conf, + input sd_conf, + input sd_sdhc, + output reg [7:0] sd_dout, // valid on rising edge of sd_dout_strobe + output reg sd_dout_strobe, + input [7:0] sd_din, + output reg sd_din_strobe, + output reg [8:0] sd_buff_addr, + + output reg [SD_IMAGES-1:0] img_mounted, // rising edge if a new image is mounted + output reg [63:0] img_size, // size of image in bytes + + // ps2 keyboard/mouse emulation + output ps2_kbd_clk, + output reg ps2_kbd_data, + input ps2_kbd_clk_i, + input ps2_kbd_data_i, + output ps2_mouse_clk, + output reg ps2_mouse_data, + input ps2_mouse_clk_i, + input ps2_mouse_data_i, + + // keyboard data + output reg key_pressed, // 1-make (pressed), 0-break (released) + output reg key_extended, // extended code + output reg [7:0] key_code, // key scan code + output reg key_strobe, // key data valid + + // mouse data + output reg [8:0] mouse_x, + output reg [8:0] mouse_y, + output reg [3:0] mouse_z, + output reg [7:0] mouse_flags, // YOvfl, XOvfl, dy8, dx8, 1, mbtn, rbtn, lbtn + output reg mouse_strobe, // mouse data is valid on mouse_strobe + output reg mouse_idx, // which mouse? + + // serial com port + input [7:0] serial_data, + input serial_strobe +); + +parameter STRLEN=0; // config string length +parameter PS2DIV=100; // master clock divider for psk2_kbd/mouse clk +parameter ROM_DIRECT_UPLOAD=0; // direct upload used for file uploads from the ARM +parameter SD_IMAGES=2; // number of block-access images (max. 4 supported in current firmware) +parameter PS2BIDIR=0; // bi-directional PS2 interface +parameter FEATURES=0; // requested features from the firmware + +localparam W = $clog2(SD_IMAGES); + +reg [6:0] sbuf; +reg [7:0] cmd; +reg [2:0] bit_cnt; // counts bits 0-7 0-7 ... +reg [9:0] byte_cnt; // counts bytes +reg [7:0] but_sw; +reg [2:0] stick_idx; + +assign buttons = but_sw[1:0]; +assign switches = but_sw[3:2]; +assign scandoubler_disable = but_sw[4]; +assign ypbpr = but_sw[5]; +assign no_csync = but_sw[6]; + +assign conf_addr = byte_cnt; + +// this variant of user_io is for 8 bit cores (type == a4) only +// bit 4 indicates ROM direct upload capability +wire [7:0] core_type = ROM_DIRECT_UPLOAD ? 8'hb4 : 8'ha4; + +reg [W:0] drive_sel; +always begin + integer i; + drive_sel = 0; + for(i = 0; i < SD_IMAGES; i = i + 1) if(sd_rd[i] | sd_wr[i]) drive_sel = i[W:0]; +end + +// command byte read by the io controller +wire [7:0] sd_cmd = { 4'h6, sd_conf, sd_sdhc, sd_wr[drive_sel], sd_rd[drive_sel] }; + +wire spi_sck = SPI_CLK; + +// ---------------- PS2 --------------------- +// 16 byte fifos to store ps2 bytes +localparam PS2_FIFO_BITS = 4; + +reg ps2_clk; +always @(posedge clk_sys) begin + integer cnt; + cnt <= cnt + 1'd1; + if(cnt == PS2DIV) begin + ps2_clk <= ~ps2_clk; + cnt <= 0; + end +end + +// keyboard +reg [7:0] ps2_kbd_fifo [(2**PS2_FIFO_BITS)-1:0]; +reg [PS2_FIFO_BITS-1:0] ps2_kbd_wptr; +reg [PS2_FIFO_BITS-1:0] ps2_kbd_rptr; + +// ps2 transmitter state machine +reg [3:0] ps2_kbd_tx_state; +reg [7:0] ps2_kbd_tx_byte; +reg ps2_kbd_parity; + +// ps2 receiver state machine +reg [3:0] ps2_kbd_rx_state = 0; +reg [1:0] ps2_kbd_rx_start = 0; +reg [7:0] ps2_kbd_rx_byte = 0; +reg ps2_kbd_rx_strobe = 0; + +assign ps2_kbd_clk = ps2_clk || (ps2_kbd_tx_state == 0 && ps2_kbd_rx_state == 0); + +// ps2 transmitter/receiver +// Takes a byte from the FIFO and sends it in a ps2 compliant serial format. +// Sends a command to the IO controller if bidirectional mode is enabled. +always@(posedge clk_sys) begin : ps2_kbd + + reg ps2_clkD; + reg ps2_clk_iD, ps2_dat_iD; + reg ps2_kbd_r_inc; + + // send data + ps2_clkD <= ps2_clk; + if (~ps2_clkD & ps2_clk) begin + ps2_kbd_r_inc <= 1'b0; + + if(ps2_kbd_r_inc) + ps2_kbd_rptr <= ps2_kbd_rptr + 1'd1; + + // transmitter is idle? + if(ps2_kbd_tx_state == 0) begin + ps2_kbd_data <= 1; + // data in fifo present? + if(ps2_kbd_wptr != ps2_kbd_rptr && (ps2_kbd_clk_i | !PS2BIDIR)) begin + // load tx register from fifo + ps2_kbd_tx_byte <= ps2_kbd_fifo[ps2_kbd_rptr]; + ps2_kbd_r_inc <= 1'b1; + + // reset parity + ps2_kbd_parity <= 1'b1; + + // start transmitter + ps2_kbd_tx_state <= 4'd1; + + // put start bit on data line + ps2_kbd_data <= 1'b0; // start bit is 0 + end + end else begin + + // transmission of 8 data bits + if((ps2_kbd_tx_state >= 1)&&(ps2_kbd_tx_state < 9)) begin + ps2_kbd_data <= ps2_kbd_tx_byte[0]; // data bits + ps2_kbd_tx_byte[6:0] <= ps2_kbd_tx_byte[7:1]; // shift down + if(ps2_kbd_tx_byte[0]) + ps2_kbd_parity <= !ps2_kbd_parity; + end + + // transmission of parity + if(ps2_kbd_tx_state == 9) + ps2_kbd_data <= ps2_kbd_parity; + + // transmission of stop bit + if(ps2_kbd_tx_state == 10) + ps2_kbd_data <= 1'b1; // stop bit is 1 + + // advance state machine + if(ps2_kbd_tx_state < 11) + ps2_kbd_tx_state <= ps2_kbd_tx_state + 4'd1; + else + ps2_kbd_tx_state <= 4'd0; + end + end + + if (PS2BIDIR) begin + ps2_clk_iD <= ps2_kbd_clk_i; + ps2_dat_iD <= ps2_kbd_data_i; + + // receive command + case (ps2_kbd_rx_start) + 2'd0: + // first: host pulls down the clock line + if (ps2_clk_iD & ~ps2_kbd_clk_i) ps2_kbd_rx_start <= 1; + 2'd1: + // second: host pulls down the data line, while releasing the clock + if (ps2_dat_iD && !ps2_kbd_data_i) ps2_kbd_rx_start <= 2'd2; + // if it releases the clock without pulling down the data line: goto 0 + else if (ps2_kbd_clk_i) ps2_kbd_rx_start <= 0; + 2'd2: + if (ps2_clkD && ~ps2_clk) begin + ps2_kbd_rx_state <= 4'd1; + ps2_kbd_rx_start <= 0; + end + default: ; + endcase + + // host data is valid after the rising edge of the clock + if(ps2_kbd_rx_state != 0 && ~ps2_clkD && ps2_clk) begin + ps2_kbd_rx_state <= ps2_kbd_rx_state + 1'd1; + if (ps2_kbd_rx_state == 9) ;// parity + else if (ps2_kbd_rx_state == 10) begin + ps2_kbd_data <= 0; // ack the received byte + end else if (ps2_kbd_rx_state == 11) begin + ps2_kbd_rx_state <= 0; + ps2_kbd_rx_strobe <= ~ps2_kbd_rx_strobe; + end else begin + ps2_kbd_rx_byte <= {ps2_kbd_data_i, ps2_kbd_rx_byte[7:1]}; + end + end + end +end + +// mouse +reg [7:0] ps2_mouse_fifo [(2**PS2_FIFO_BITS)-1:0]; +reg [PS2_FIFO_BITS-1:0] ps2_mouse_wptr; +reg [PS2_FIFO_BITS-1:0] ps2_mouse_rptr; + +// ps2 transmitter state machine +reg [3:0] ps2_mouse_tx_state; +reg [7:0] ps2_mouse_tx_byte; +reg ps2_mouse_parity; + +// ps2 receiver state machine +reg [3:0] ps2_mouse_rx_state = 0; +reg [1:0] ps2_mouse_rx_start = 0; +reg [7:0] ps2_mouse_rx_byte = 0; +reg ps2_mouse_rx_strobe = 0; + +assign ps2_mouse_clk = ps2_clk || (ps2_mouse_tx_state == 0 && ps2_mouse_rx_state == 0); + +// ps2 transmitter/receiver +// Takes a byte from the FIFO and sends it in a ps2 compliant serial format. +// Sends a command to the IO controller if bidirectional mode is enabled. +always@(posedge clk_sys) begin : ps2_mouse + reg ps2_clkD; + reg ps2_clk_iD, ps2_dat_iD; + reg ps2_mouse_r_inc; + + ps2_clkD <= ps2_clk; + if (~ps2_clkD & ps2_clk) begin + ps2_mouse_r_inc <= 1'b0; + + if(ps2_mouse_r_inc) + ps2_mouse_rptr <= ps2_mouse_rptr + 1'd1; + + // transmitter is idle? + if(ps2_mouse_tx_state == 0) begin + ps2_mouse_data <= 1; + // data in fifo present? + if(ps2_mouse_wptr != ps2_mouse_rptr && (ps2_mouse_clk_i | !PS2BIDIR)) begin + // load tx register from fifo + ps2_mouse_tx_byte <= ps2_mouse_fifo[ps2_mouse_rptr]; + ps2_mouse_r_inc <= 1'b1; + + // reset parity + ps2_mouse_parity <= 1'b1; + + // start transmitter + ps2_mouse_tx_state <= 4'd1; + + // put start bit on data line + ps2_mouse_data <= 1'b0; // start bit is 0 + end + end else begin + + // transmission of 8 data bits + if((ps2_mouse_tx_state >= 1)&&(ps2_mouse_tx_state < 9)) begin + ps2_mouse_data <= ps2_mouse_tx_byte[0]; // data bits + ps2_mouse_tx_byte[6:0] <= ps2_mouse_tx_byte[7:1]; // shift down + if(ps2_mouse_tx_byte[0]) + ps2_mouse_parity <= !ps2_mouse_parity; + end + + // transmission of parity + if(ps2_mouse_tx_state == 9) + ps2_mouse_data <= ps2_mouse_parity; + + // transmission of stop bit + if(ps2_mouse_tx_state == 10) + ps2_mouse_data <= 1'b1; // stop bit is 1 + + // advance state machine + if(ps2_mouse_tx_state < 11) + ps2_mouse_tx_state <= ps2_mouse_tx_state + 4'd1; + else + ps2_mouse_tx_state <= 4'd0; + end + end + + if (PS2BIDIR) begin + + ps2_clk_iD <= ps2_mouse_clk_i; + ps2_dat_iD <= ps2_mouse_data_i; + + // receive command + case (ps2_mouse_rx_start) + 2'd0: + // first: host pulls down the clock line + if (ps2_clk_iD & ~ps2_mouse_clk_i) ps2_mouse_rx_start <= 1; + 2'd1: + // second: host pulls down the data line, while releasing the clock + if (ps2_dat_iD && !ps2_mouse_data_i) ps2_mouse_rx_start <= 2'd2; + // if it releases the clock without pulling down the data line: goto 0 + else if (ps2_mouse_clk_i) ps2_mouse_rx_start <= 0; + 2'd2: + if (ps2_clkD && ~ps2_clk) begin + ps2_mouse_rx_state <= 4'd1; + ps2_mouse_rx_start <= 0; + end + default: ; + endcase + + // host data is valid after the rising edge of the clock + if(ps2_mouse_rx_state != 0 && ~ps2_clkD && ps2_clk) begin + ps2_mouse_rx_state <= ps2_mouse_rx_state + 1'd1; + if (ps2_mouse_rx_state == 9) ;// parity + else if (ps2_mouse_rx_state == 10) begin + ps2_mouse_data <= 0; // ack the received byte + end else if (ps2_mouse_rx_state == 11) begin + ps2_mouse_rx_state <= 0; + ps2_mouse_rx_strobe <= ~ps2_mouse_rx_strobe; + end else begin + ps2_mouse_rx_byte <= {ps2_mouse_data_i, ps2_mouse_rx_byte[7:1]}; + end + end + end +end + +// fifo to receive serial data from core to be forwarded to io controller + +// 16 byte fifo to store serial bytes +localparam SERIAL_OUT_FIFO_BITS = 6; +reg [7:0] serial_out_fifo [(2**SERIAL_OUT_FIFO_BITS)-1:0]; +reg [SERIAL_OUT_FIFO_BITS-1:0] serial_out_wptr; +reg [SERIAL_OUT_FIFO_BITS-1:0] serial_out_rptr; + +wire serial_out_data_available = serial_out_wptr != serial_out_rptr; +wire [7:0] serial_out_byte = serial_out_fifo[serial_out_rptr] /* synthesis keep */; +wire [7:0] serial_out_status = { 7'b1000000, serial_out_data_available}; + +// status[0] is reset signal from io controller and is thus used to flush +// the fifo +always @(posedge serial_strobe or posedge status[0]) begin : serial_out + if(status[0] == 1) begin + serial_out_wptr <= 0; + end else begin + serial_out_fifo[serial_out_wptr] <= serial_data; + serial_out_wptr <= serial_out_wptr + 1'd1; + end +end + +always@(negedge spi_sck or posedge status[0]) begin : serial_in + if(status[0] == 1) begin + serial_out_rptr <= 0; + end else begin + if((byte_cnt != 0) && (cmd == 8'h1b)) begin + // read last bit -> advance read pointer + if((bit_cnt == 7) && !byte_cnt[0] && serial_out_data_available) + serial_out_rptr <= serial_out_rptr + 1'd1; + end + end +end + + +// SPI bit and byte counters +always@(posedge spi_sck or posedge SPI_SS_IO) begin : spi_counter + if(SPI_SS_IO == 1) begin + bit_cnt <= 0; + byte_cnt <= 0; + end else begin + if((bit_cnt == 7)&&(~&byte_cnt)) + byte_cnt <= byte_cnt + 8'd1; + + bit_cnt <= bit_cnt + 1'd1; + end +end + +// SPI transmitter FPGA -> IO +reg [7:0] spi_byte_out; + +always@(negedge spi_sck or posedge SPI_SS_IO) begin : spi_byteout + if(SPI_SS_IO == 1) begin + SPI_MISO <= 1'bZ; + end else begin + SPI_MISO <= spi_byte_out[~bit_cnt]; + end +end + +always@(posedge spi_sck or posedge SPI_SS_IO) begin : spi_transmitter + reg [31:0] sd_lba_r; + reg [W:0] drive_sel_r; + reg ps2_kbd_rx_strobeD; + reg ps2_mouse_rx_strobeD; + + if(SPI_SS_IO == 1) begin + spi_byte_out <= core_type; + end else begin + // read the command byte to choose the response + if(bit_cnt == 7) begin + if(!byte_cnt) cmd <= {sbuf, SPI_MOSI}; + + spi_byte_out <= 0; + case({(!byte_cnt) ? {sbuf, SPI_MOSI} : cmd}) + // PS2 keyboard command + 8'h0e: if (byte_cnt == 0) begin + ps2_kbd_rx_strobeD <= ps2_kbd_rx_strobe; + //echo the command code if there's a byte to send, indicating the core supports the command + spi_byte_out <= (ps2_kbd_rx_strobe ^ ps2_kbd_rx_strobeD) ? 8'h0e : 8'h00; + end else spi_byte_out <= ps2_kbd_rx_byte; + + // PS2 mouse command + 8'h0f: if (byte_cnt == 0) begin + ps2_mouse_rx_strobeD <= ps2_mouse_rx_strobe; + //echo the command code if there's a byte to send, indicating the core supports the command + spi_byte_out <= (ps2_mouse_rx_strobe ^ ps2_mouse_rx_strobeD) ? 8'h0f : 8'h00; + end else spi_byte_out <= ps2_mouse_rx_byte; + + // reading config string + 8'h14: if (STRLEN == 0) spi_byte_out <= conf_chr; else + if(byte_cnt < STRLEN) spi_byte_out <= conf_str[(STRLEN - byte_cnt - 1)<<3 +:8]; + + // reading sd card status + 8'h16: if(byte_cnt == 0) begin + spi_byte_out <= sd_cmd; + sd_lba_r <= sd_lba; + drive_sel_r <= drive_sel; + end + else if(byte_cnt == 1) spi_byte_out <= drive_sel_r; + else if(byte_cnt < 6) spi_byte_out <= sd_lba_r[(5-byte_cnt)<<3 +:8]; + + // reading sd card write data + 8'h18: spi_byte_out <= sd_din; + + 8'h1b: + // send alternating flag byte and data + if(byte_cnt[0]) spi_byte_out <= serial_out_status; + else spi_byte_out <= serial_out_byte; + + // core features + 8'h80: + if (byte_cnt == 0) spi_byte_out <= 8'h80; + else spi_byte_out <= FEATURES[(4-byte_cnt)<<3 +:8]; + + endcase + end + end +end + +// SPI receiver IO -> FPGA + +reg spi_receiver_strobe_r = 0; +reg spi_transfer_end_r = 1; +reg [7:0] spi_byte_in; + +// Read at spi_sck clock domain, assemble bytes for transferring to clk_sys +always@(posedge spi_sck or posedge SPI_SS_IO) begin : spi_receiver + + if(SPI_SS_IO == 1) begin + spi_transfer_end_r <= 1; + end else begin + spi_transfer_end_r <= 0; + + if(bit_cnt != 7) + sbuf[6:0] <= { sbuf[5:0], SPI_MOSI }; + + // finished reading a byte, prepare to transfer to clk_sys + if(bit_cnt == 7) begin + spi_byte_in <= { sbuf, SPI_MOSI}; + spi_receiver_strobe_r <= ~spi_receiver_strobe_r; + end + end +end + +// Process bytes from SPI at the clk_sys domain +always @(posedge clk_sys) begin : cmd_block + + reg spi_receiver_strobe; + reg spi_transfer_end; + reg spi_receiver_strobeD; + reg spi_transfer_endD; + reg [7:0] acmd; + reg [7:0] abyte_cnt; // counts bytes + + reg [7:0] mouse_flags_r; + reg [7:0] mouse_x_r; + reg [7:0] mouse_y_r; + + reg key_pressed_r; + reg key_extended_r; + + //synchronize between SPI and sys clock domains + spi_receiver_strobeD <= spi_receiver_strobe_r; + spi_receiver_strobe <= spi_receiver_strobeD; + spi_transfer_endD <= spi_transfer_end_r; + spi_transfer_end <= spi_transfer_endD; + + key_strobe <= 0; + mouse_strobe <= 0; + + if (spi_transfer_end) begin + abyte_cnt <= 8'd0; + end else if (spi_receiver_strobeD ^ spi_receiver_strobe) begin + + if(~&abyte_cnt) + abyte_cnt <= abyte_cnt + 8'd1; + + if(abyte_cnt == 0) begin + acmd <= spi_byte_in; + end else begin + case(acmd) + // buttons and switches + 8'h01: but_sw <= spi_byte_in; + 8'h60: if (abyte_cnt < 5) joystick_0[(abyte_cnt-1)<<3 +:8] <= spi_byte_in; + 8'h61: if (abyte_cnt < 5) joystick_1[(abyte_cnt-1)<<3 +:8] <= spi_byte_in; + 8'h62: if (abyte_cnt < 5) joystick_2[(abyte_cnt-1)<<3 +:8] <= spi_byte_in; + 8'h63: if (abyte_cnt < 5) joystick_3[(abyte_cnt-1)<<3 +:8] <= spi_byte_in; + 8'h64: if (abyte_cnt < 5) joystick_4[(abyte_cnt-1)<<3 +:8] <= spi_byte_in; + 8'h70,8'h71: begin + // store incoming ps2 mouse bytes + if (abyte_cnt < 4) begin + ps2_mouse_fifo[ps2_mouse_wptr] <= spi_byte_in; + ps2_mouse_wptr <= ps2_mouse_wptr + 1'd1; + end + + if (abyte_cnt == 1) mouse_flags_r <= spi_byte_in; + else if (abyte_cnt == 2) mouse_x_r <= spi_byte_in; + else if (abyte_cnt == 3) mouse_y_r <= spi_byte_in; + else if (abyte_cnt == 4) begin + // flags: YOvfl, XOvfl, dy8, dx8, 1, mbtn, rbtn, lbtn + mouse_flags <= mouse_flags_r; + mouse_x <= { mouse_flags_r[4], mouse_x_r }; + mouse_y <= { mouse_flags_r[5], mouse_y_r }; + mouse_z <= spi_byte_in[3:0]; + mouse_idx <= acmd[0]; + mouse_strobe <= 1; + end + end + 8'h05: begin + // store incoming ps2 keyboard bytes + ps2_kbd_fifo[ps2_kbd_wptr] <= spi_byte_in; + ps2_kbd_wptr <= ps2_kbd_wptr + 1'd1; + if (abyte_cnt == 1) begin + key_extended_r <= 0; + key_pressed_r <= 1; + end + if (spi_byte_in == 8'he0) key_extended_r <= 1'b1; + else if (spi_byte_in == 8'hf0) key_pressed_r <= 1'b0; + else begin + key_extended <= key_extended_r && abyte_cnt != 1; + key_pressed <= key_pressed_r || abyte_cnt == 1; + key_code <= spi_byte_in; + key_strobe <= 1'b1; + end + end + + // joystick analog + 8'h1a: begin + // first byte is joystick index + if(abyte_cnt == 1) + stick_idx <= spi_byte_in[2:0]; + else if(abyte_cnt == 2) begin + // second byte is x axis + if(stick_idx == 0) + joystick_analog_0[15:8] <= spi_byte_in; + else if(stick_idx == 1) + joystick_analog_1[15:8] <= spi_byte_in; + end else if(abyte_cnt == 3) begin + // third byte is y axis + if(stick_idx == 0) + joystick_analog_0[7:0] <= spi_byte_in; + else if(stick_idx == 1) + joystick_analog_1[7:0] <= spi_byte_in; + end else if(abyte_cnt == 4) begin + // fourth byte is 2nd x axis + if(stick_idx == 0) + joystick_analog_0[31:24] <= spi_byte_in; + else if(stick_idx == 1) + joystick_analog_1[31:24] <= spi_byte_in; + end else if(abyte_cnt == 5) begin + // fifth byte is 2nd y axis + if(stick_idx == 0) + joystick_analog_0[23:16] <= spi_byte_in; + else if(stick_idx == 1) + joystick_analog_1[23:16] <= spi_byte_in; + end + end + + 8'h15: status <= spi_byte_in; + + // status, 64bit version + 8'h1e: if(abyte_cnt<9) status[(abyte_cnt-1)<<3 +:8] <= spi_byte_in; + + // core variant + 8'h21: core_mod <= spi_byte_in[6:0]; + + // RTC + 8'h22: if(abyte_cnt<9) rtc[(abyte_cnt-1)<<3 +:8] <= spi_byte_in; + endcase + end + end +end + + +// Process SD-card related bytes from SPI at the clk_sd domain +always @(posedge clk_sd) begin : sd_block + + reg spi_receiver_strobe; + reg spi_transfer_end; + reg spi_receiver_strobeD; + reg spi_transfer_endD; + reg [SD_IMAGES-1:0] sd_wrD; + reg [7:0] acmd; + reg [7:0] abyte_cnt; // counts bytes + + //synchronize between SPI and sd clock domains + spi_receiver_strobeD <= spi_receiver_strobe_r; + spi_receiver_strobe <= spi_receiver_strobeD; + spi_transfer_endD <= spi_transfer_end_r; + spi_transfer_end <= spi_transfer_endD; + + if(sd_dout_strobe) begin + sd_dout_strobe<= 0; + if(~&sd_buff_addr) sd_buff_addr <= sd_buff_addr + 1'b1; + end + + sd_din_strobe<= 0; + sd_wrD <= sd_wr; + // fetch the first byte immediately after the write command seen + if (|(~sd_wrD & sd_wr)) begin + sd_buff_addr <= 0; + sd_din_strobe <= 1; + end + + img_mounted <= 0; + + if (spi_transfer_end) begin + abyte_cnt <= 8'd0; + sd_ack <= 1'b0; + sd_ack_conf <= 1'b0; + sd_buff_addr <= 0; + end else if (spi_receiver_strobeD ^ spi_receiver_strobe) begin + + if(~&abyte_cnt) + abyte_cnt <= abyte_cnt + 8'd1; + + if(abyte_cnt == 0) begin + acmd <= spi_byte_in; + + if(spi_byte_in == 8'h18) begin + sd_din_strobe <= 1'b1; + if(~&sd_buff_addr) sd_buff_addr <= sd_buff_addr + 1'b1; + end + + if(spi_byte_in == 8'h19) + sd_ack_conf <= 1'b1; + if((spi_byte_in == 8'h17) || (spi_byte_in == 8'h18)) + sd_ack <= 1'b1; + + end else begin + case(acmd) + + // send sector IO -> FPGA + 8'h17, + // send SD config IO -> FPGA + 8'h19: begin + // flag that download begins + sd_dout_strobe <= 1'b1; + sd_dout <= spi_byte_in; + end + + // send sector FPGA -> IO + 8'h18: begin + if(~&sd_buff_addr) begin + sd_din_strobe <= 1'b1; + sd_buff_addr <= sd_buff_addr + 1'b1; + end + end + + 8'h1c: img_mounted[spi_byte_in[W:0]] <= 1; + + // send image info + 8'h1d: if(abyte_cnt<9) img_size[(abyte_cnt-1)<<3 +:8] <= spi_byte_in; + endcase + end + end +end + +endmodule diff --git a/rtl/mist-modules_old/hq2x.sv b/rtl/mist-modules_old/hq2x.sv new file mode 100644 index 0000000..f17732b --- /dev/null +++ b/rtl/mist-modules_old/hq2x.sv @@ -0,0 +1,454 @@ +// +// +// Copyright (c) 2012-2013 Ludvig Strigeus +// Copyright (c) 2017 Sorgelig +// +// This program is GPL Licensed. See COPYING for the full license. +// +// +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +// synopsys translate_off +`timescale 1 ps / 1 ps +// synopsys translate_on + +`define BITS_TO_FIT(N) ( \ + N <= 2 ? 0 : \ + N <= 4 ? 1 : \ + N <= 8 ? 2 : \ + N <= 16 ? 3 : \ + N <= 32 ? 4 : \ + N <= 64 ? 5 : \ + N <= 128 ? 6 : \ + N <= 256 ? 7 : \ + N <= 512 ? 8 : \ + N <=1024 ? 9 : 10 ) + +module hq2x_in #(parameter LENGTH, parameter DWIDTH) +( + input clk, + + input [AWIDTH:0] rdaddr, + input rdbuf, + output[DWIDTH:0] q, + + input [AWIDTH:0] wraddr, + input wrbuf, + input [DWIDTH:0] data, + input wren +); + + localparam AWIDTH = `BITS_TO_FIT(LENGTH); + wire [DWIDTH:0] out[2]; + assign q = out[rdbuf]; + + hq2x_buf #(.NUMWORDS(LENGTH), .AWIDTH(AWIDTH), .DWIDTH(DWIDTH)) buf0(clk,data,rdaddr,wraddr,wren && (wrbuf == 0),out[0]); + hq2x_buf #(.NUMWORDS(LENGTH), .AWIDTH(AWIDTH), .DWIDTH(DWIDTH)) buf1(clk,data,rdaddr,wraddr,wren && (wrbuf == 1),out[1]); +endmodule + + +module hq2x_out #(parameter LENGTH, parameter DWIDTH) +( + input clk, + + input [AWIDTH:0] rdaddr, + input [1:0] rdbuf, + output[DWIDTH:0] q, + + input [AWIDTH:0] wraddr, + input [1:0] wrbuf, + input [DWIDTH:0] data, + input wren +); + + localparam AWIDTH = `BITS_TO_FIT(LENGTH*2); + wire [DWIDTH:0] out[4]; + assign q = out[rdbuf]; + + hq2x_buf #(.NUMWORDS(LENGTH*2), .AWIDTH(AWIDTH), .DWIDTH(DWIDTH)) buf0(clk,data,rdaddr,wraddr,wren && (wrbuf == 0),out[0]); + hq2x_buf #(.NUMWORDS(LENGTH*2), .AWIDTH(AWIDTH), .DWIDTH(DWIDTH)) buf1(clk,data,rdaddr,wraddr,wren && (wrbuf == 1),out[1]); + hq2x_buf #(.NUMWORDS(LENGTH*2), .AWIDTH(AWIDTH), .DWIDTH(DWIDTH)) buf2(clk,data,rdaddr,wraddr,wren && (wrbuf == 2),out[2]); + hq2x_buf #(.NUMWORDS(LENGTH*2), .AWIDTH(AWIDTH), .DWIDTH(DWIDTH)) buf3(clk,data,rdaddr,wraddr,wren && (wrbuf == 3),out[3]); +endmodule + + +module hq2x_buf #(parameter NUMWORDS, parameter AWIDTH, parameter DWIDTH) +( + input clock, + input [DWIDTH:0] data, + input [AWIDTH:0] rdaddress, + input [AWIDTH:0] wraddress, + input wren, + output [DWIDTH:0] q +); + + altsyncram altsyncram_component ( + .address_a (wraddress), + .clock0 (clock), + .data_a (data), + .wren_a (wren), + .address_b (rdaddress), + .q_b(q), + .aclr0 (1'b0), + .aclr1 (1'b0), + .addressstall_a (1'b0), + .addressstall_b (1'b0), + .byteena_a (1'b1), + .byteena_b (1'b1), + .clock1 (1'b1), + .clocken0 (1'b1), + .clocken1 (1'b1), + .clocken2 (1'b1), + .clocken3 (1'b1), + .data_b ({(DWIDTH+1){1'b1}}), + .eccstatus (), + .q_a (), + .rden_a (1'b1), + .rden_b (1'b1), + .wren_b (1'b0)); + defparam + altsyncram_component.address_aclr_b = "NONE", + altsyncram_component.address_reg_b = "CLOCK0", + altsyncram_component.clock_enable_input_a = "BYPASS", + altsyncram_component.clock_enable_input_b = "BYPASS", + altsyncram_component.clock_enable_output_b = "BYPASS", + altsyncram_component.intended_device_family = "Cyclone III", + altsyncram_component.lpm_type = "altsyncram", + altsyncram_component.numwords_a = NUMWORDS, + altsyncram_component.numwords_b = NUMWORDS, + altsyncram_component.operation_mode = "DUAL_PORT", + altsyncram_component.outdata_aclr_b = "NONE", + altsyncram_component.outdata_reg_b = "UNREGISTERED", + altsyncram_component.power_up_uninitialized = "FALSE", + altsyncram_component.read_during_write_mode_mixed_ports = "DONT_CARE", + altsyncram_component.widthad_a = AWIDTH+1, + altsyncram_component.widthad_b = AWIDTH+1, + altsyncram_component.width_a = DWIDTH+1, + altsyncram_component.width_b = DWIDTH+1, + altsyncram_component.width_byteena_a = 1; + +endmodule + +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +module DiffCheck +( + input [17:0] rgb1, + input [17:0] rgb2, + output result +); + + wire [5:0] r = rgb1[5:1] - rgb2[5:1]; + wire [5:0] g = rgb1[11:7] - rgb2[11:7]; + wire [5:0] b = rgb1[17:13] - rgb2[17:13]; + wire [6:0] t = $signed(r) + $signed(b); + wire [6:0] gx = {g[5], g}; + wire [7:0] y = $signed(t) + $signed(gx); + wire [6:0] u = $signed(r) - $signed(b); + wire [7:0] v = $signed({g, 1'b0}) - $signed(t); + + // if y is inside (-24..24) + wire y_inside = (y < 8'h18 || y >= 8'he8); + + // if u is inside (-4, 4) + wire u_inside = (u < 7'h4 || u >= 7'h7c); + + // if v is inside (-6, 6) + wire v_inside = (v < 8'h6 || v >= 8'hfA); + assign result = !(y_inside && u_inside && v_inside); +endmodule + +module InnerBlend +( + input [8:0] Op, + input [5:0] A, + input [5:0] B, + input [5:0] C, + output [5:0] O +); + + function [8:0] mul6x3; + input [5:0] op1; + input [2:0] op2; + begin + mul6x3 = 9'd0; + if(op2[0]) mul6x3 = mul6x3 + op1; + if(op2[1]) mul6x3 = mul6x3 + {op1, 1'b0}; + if(op2[2]) mul6x3 = mul6x3 + {op1, 2'b00}; + end + endfunction + + wire OpOnes = Op[4]; + wire [8:0] Amul = mul6x3(A, Op[7:5]); + wire [8:0] Bmul = mul6x3(B, {Op[3:2], 1'b0}); + wire [8:0] Cmul = mul6x3(C, {Op[1:0], 1'b0}); + wire [8:0] At = Amul; + wire [8:0] Bt = (OpOnes == 0) ? Bmul : {3'b0, B}; + wire [8:0] Ct = (OpOnes == 0) ? Cmul : {3'b0, C}; + wire [9:0] Res = {At, 1'b0} + Bt + Ct; + assign O = Op[8] ? A : Res[9:4]; +endmodule + +module Blend +( + input [5:0] rule, + input disable_hq2x, + input [17:0] E, + input [17:0] A, + input [17:0] B, + input [17:0] D, + input [17:0] F, + input [17:0] H, + output [17:0] Result +); + + reg [1:0] input_ctrl; + reg [8:0] op; + localparam BLEND0 = 9'b1_xxx_x_xx_xx; // 0: A + localparam BLEND1 = 9'b0_110_0_10_00; // 1: (A * 12 + B * 4) >> 4 + localparam BLEND2 = 9'b0_100_0_10_10; // 2: (A * 8 + B * 4 + C * 4) >> 4 + localparam BLEND3 = 9'b0_101_0_10_01; // 3: (A * 10 + B * 4 + C * 2) >> 4 + localparam BLEND4 = 9'b0_110_0_01_01; // 4: (A * 12 + B * 2 + C * 2) >> 4 + localparam BLEND5 = 9'b0_010_0_11_11; // 5: (A * 4 + (B + C) * 6) >> 4 + localparam BLEND6 = 9'b0_111_1_xx_xx; // 6: (A * 14 + B + C) >> 4 + localparam AB = 2'b00; + localparam AD = 2'b01; + localparam DB = 2'b10; + localparam BD = 2'b11; + wire is_diff; + DiffCheck diff_checker(rule[1] ? B : H, rule[0] ? D : F, is_diff); + + always @* begin + case({!is_diff, rule[5:2]}) + 1,17: {op, input_ctrl} = {BLEND1, AB}; + 2,18: {op, input_ctrl} = {BLEND1, DB}; + 3,19: {op, input_ctrl} = {BLEND1, BD}; + 4,20: {op, input_ctrl} = {BLEND2, DB}; + 5,21: {op, input_ctrl} = {BLEND2, AB}; + 6,22: {op, input_ctrl} = {BLEND2, AD}; + + 8: {op, input_ctrl} = {BLEND0, 2'bxx}; + 9: {op, input_ctrl} = {BLEND0, 2'bxx}; + 10: {op, input_ctrl} = {BLEND0, 2'bxx}; + 11: {op, input_ctrl} = {BLEND1, AB}; + 12: {op, input_ctrl} = {BLEND1, AB}; + 13: {op, input_ctrl} = {BLEND1, AB}; + 14: {op, input_ctrl} = {BLEND1, DB}; + 15: {op, input_ctrl} = {BLEND1, BD}; + + 24: {op, input_ctrl} = {BLEND2, DB}; + 25: {op, input_ctrl} = {BLEND5, DB}; + 26: {op, input_ctrl} = {BLEND6, DB}; + 27: {op, input_ctrl} = {BLEND2, DB}; + 28: {op, input_ctrl} = {BLEND4, DB}; + 29: {op, input_ctrl} = {BLEND5, DB}; + 30: {op, input_ctrl} = {BLEND3, BD}; + 31: {op, input_ctrl} = {BLEND3, DB}; + default: {op, input_ctrl} = 11'bx; + endcase + + // Setting op[8] effectively disables HQ2X because blend will always return E. + if (disable_hq2x) op[8] = 1; + end + + // Generate inputs to the inner blender. Valid combinations. + // 00: E A B + // 01: E A D + // 10: E D B + // 11: E B D + wire [17:0] Input1 = E; + wire [17:0] Input2 = !input_ctrl[1] ? A : + !input_ctrl[0] ? D : B; + + wire [17:0] Input3 = !input_ctrl[0] ? B : D; + InnerBlend inner_blend1(op, Input1[5:0], Input2[5:0], Input3[5:0], Result[5:0]); + InnerBlend inner_blend2(op, Input1[11:6], Input2[11:6], Input3[11:6], Result[11:6]); + InnerBlend inner_blend3(op, Input1[17:12], Input2[17:12], Input3[17:12], Result[17:12]); +endmodule + + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +module Hq2x #(parameter LENGTH, parameter HALF_DEPTH) +( + input clk, + input ce_x4, + input [DWIDTH:0] inputpixel, + input mono, + input disable_hq2x, + input reset_frame, + input reset_line, + input [1:0] read_y, + input [AWIDTH+1:0] read_x, + output [DWIDTH:0] outpixel +); + + +localparam AWIDTH = `BITS_TO_FIT(LENGTH); +localparam DWIDTH = HALF_DEPTH ? 8 : 17; + +wire [5:0] hqTable[256] = '{ + 19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 47, 35, 23, 15, 55, 39, + 19, 19, 26, 58, 19, 19, 26, 58, 23, 15, 35, 35, 23, 15, 7, 35, + 19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 55, 39, 23, 15, 51, 43, + 19, 19, 26, 58, 19, 19, 26, 58, 23, 15, 51, 35, 23, 15, 7, 43, + 19, 19, 26, 11, 19, 19, 26, 11, 23, 61, 35, 35, 23, 61, 51, 35, + 19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 51, 35, 23, 15, 51, 35, + 19, 19, 26, 11, 19, 19, 26, 11, 23, 61, 7, 35, 23, 61, 7, 43, + 19, 19, 26, 11, 19, 19, 26, 58, 23, 15, 51, 35, 23, 61, 7, 43, + 19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 47, 35, 23, 15, 55, 39, + 19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 51, 35, 23, 15, 51, 35, + 19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 55, 39, 23, 15, 51, 43, + 19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 51, 39, 23, 15, 7, 43, + 19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 51, 35, 23, 15, 51, 39, + 19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 51, 35, 23, 15, 7, 35, + 19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 51, 35, 23, 15, 7, 43, + 19, 19, 26, 11, 19, 19, 26, 11, 23, 15, 7, 35, 23, 15, 7, 43 +}; + +reg [17:0] Prev0, Prev1, Prev2, Curr0, Curr1, Next0, Next1, Next2; +reg [17:0] A, B, D, F, G, H; +reg [7:0] pattern, nextpatt; +reg [1:0] i; +reg [7:0] y; + +wire curbuf = y[0]; +reg prevbuf = 0; +wire iobuf = !curbuf; + +wire diff0, diff1; +DiffCheck diffcheck0(Curr1, (i == 0) ? Prev0 : (i == 1) ? Curr0 : (i == 2) ? Prev2 : Next1, diff0); +DiffCheck diffcheck1(Curr1, (i == 0) ? Prev1 : (i == 1) ? Next0 : (i == 2) ? Curr2 : Next2, diff1); + +wire [7:0] new_pattern = {diff1, diff0, pattern[7:2]}; + +wire [17:0] X = (i == 0) ? A : (i == 1) ? Prev1 : (i == 2) ? Next1 : G; +wire [17:0] blend_result; +Blend blender(hqTable[nextpatt], disable_hq2x, Curr0, X, B, D, F, H, blend_result); + +reg Curr2_addr1; +reg [AWIDTH:0] Curr2_addr2; +wire [17:0] Curr2 = HALF_DEPTH ? h2rgb(Curr2tmp) : Curr2tmp; +wire [DWIDTH:0] Curr2tmp; + +reg [AWIDTH:0] wrin_addr2; +reg [DWIDTH:0] wrpix; +reg wrin_en; + +function [17:0] h2rgb; + input [8:0] v; +begin + h2rgb = mono ? {v[5:3],v[2:0], v[5:3],v[2:0], v[5:3],v[2:0]} : {v[8:6],v[8:6],v[5:3],v[5:3],v[2:0],v[2:0]}; +end +endfunction + +function [8:0] rgb2h; + input [17:0] v; +begin + rgb2h = mono ? {3'b000, v[17:15], v[14:12]} : {v[17:15], v[11:9], v[5:3]}; +end +endfunction + +hq2x_in #(.LENGTH(LENGTH), .DWIDTH(DWIDTH)) hq2x_in +( + .clk(clk), + + .rdaddr(Curr2_addr2), + .rdbuf(Curr2_addr1), + .q(Curr2tmp), + + .wraddr(wrin_addr2), + .wrbuf(iobuf), + .data(wrpix), + .wren(wrin_en) +); + +reg [1:0] wrout_addr1; +reg [AWIDTH+1:0] wrout_addr2; +reg wrout_en; +reg [DWIDTH:0] wrdata; + +hq2x_out #(.LENGTH(LENGTH), .DWIDTH(DWIDTH)) hq2x_out +( + .clk(clk), + + .rdaddr(read_x), + .rdbuf(read_y), + .q(outpixel), + + .wraddr(wrout_addr2), + .wrbuf(wrout_addr1), + .data(wrdata), + .wren(wrout_en) +); + +always @(posedge clk) begin + reg [AWIDTH:0] offs; + reg old_reset_line; + reg old_reset_frame; + + wrout_en <= 0; + wrin_en <= 0; + + if(ce_x4) begin + + pattern <= new_pattern; + + if(~&offs) begin + if (i == 0) begin + Curr2_addr1 <= prevbuf; + Curr2_addr2 <= offs; + end + if (i == 1) begin + Prev2 <= Curr2; + Curr2_addr1 <= curbuf; + Curr2_addr2 <= offs; + end + if (i == 2) begin + Next2 <= HALF_DEPTH ? h2rgb(inputpixel) : inputpixel; + wrpix <= inputpixel; + wrin_addr2 <= offs; + wrin_en <= 1; + end + if (i == 3) begin + offs <= offs + 1'd1; + end + + if(HALF_DEPTH) wrdata <= rgb2h(blend_result); + else wrdata <= blend_result; + + wrout_addr1 <= {curbuf, i[1]}; + wrout_addr2 <= {offs, i[1]^i[0]}; + wrout_en <= 1; + end + + if(i==3) begin + nextpatt <= {new_pattern[7:6], new_pattern[3], new_pattern[5], new_pattern[2], new_pattern[4], new_pattern[1:0]}; + {A, G} <= {Prev0, Next0}; + {B, F, H, D} <= {Prev1, Curr2, Next1, Curr0}; + {Prev0, Prev1} <= {Prev1, Prev2}; + {Curr0, Curr1} <= {Curr1, Curr2}; + {Next0, Next1} <= {Next1, Next2}; + end else begin + nextpatt <= {nextpatt[5], nextpatt[3], nextpatt[0], nextpatt[6], nextpatt[1], nextpatt[7], nextpatt[4], nextpatt[2]}; + {B, F, H, D} <= {F, H, D, B}; + end + + i <= i + 1'b1; + if(old_reset_line && ~reset_line) begin + old_reset_frame <= reset_frame; + offs <= 0; + i <= 0; + y <= y + 1'd1; + prevbuf <= curbuf; + if(old_reset_frame & ~reset_frame) begin + y <= 0; + prevbuf <= 0; + end + end + + old_reset_line <= reset_line; + end +end + +endmodule // Hq2x diff --git a/rtl/mist-modules_old/mist_io.v b/rtl/mist-modules_old/mist_io.v new file mode 100644 index 0000000..ad233a3 --- /dev/null +++ b/rtl/mist-modules_old/mist_io.v @@ -0,0 +1,491 @@ +// +// mist_io.v +// +// mist_io for the MiST board +// http://code.google.com/p/mist-board/ +// +// Copyright (c) 2014 Till Harbaum +// +// This source file is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +/////////////////////////////////////////////////////////////////////// + +// +// Use buffer to access SD card. It's time-critical part. +// Made module synchroneous with 2 clock domains: clk_sys and SPI_SCK +// (Sorgelig) +// +// for synchronous projects default value for PS2DIV is fine for any frequency of system clock. +// clk_ps2 = clk_sys/(PS2DIV*2) +// + +module mist_io #(parameter STRLEN=0, parameter PS2DIV=100) +( + + // parameter STRLEN and the actual length of conf_str have to match + input [(8*STRLEN)-1:0] conf_str, + + // Global clock. It should be around 100MHz (higher is better). + input clk_sys, + + // Global SPI clock from ARM. 24MHz + input SPI_SCK, + + input CONF_DATA0, + input SPI_SS2, + output SPI_DO, + input SPI_DI, + + output reg [7:0] joystick_0, + output reg [7:0] joystick_1, + output reg [15:0] joystick_analog_0, + output reg [15:0] joystick_analog_1, + output [1:0] buttons, + output [1:0] switches, + output scandoubler_disable, + output ypbpr, + + output reg [31:0] status, + + // SD config + input sd_conf, + input sd_sdhc, + output img_mounted, // signaling that new image has been mounted + output reg [31:0] img_size, // size of image in bytes + + // SD block level access + input [31:0] sd_lba, + input sd_rd, + input sd_wr, + output reg sd_ack, + output reg sd_ack_conf, + + // SD byte level access. Signals for 2-PORT altsyncram. + output reg [8:0] sd_buff_addr, + output reg [7:0] sd_buff_dout, + input [7:0] sd_buff_din, + output reg sd_buff_wr, + + // ps2 keyboard emulation + output ps2_kbd_clk, + output reg ps2_kbd_data, + output ps2_mouse_clk, + output reg ps2_mouse_data, + input ps2_caps_led, + + // ARM -> FPGA download + output reg ioctl_download = 0, // signal indicating an active download + output reg [7:0] ioctl_index, // menu index used to upload the file + output ioctl_wr, + output reg [24:0] ioctl_addr, + output reg [7:0] ioctl_dout +); + +reg [7:0] b_data; +reg [6:0] sbuf; +reg [7:0] cmd; +reg [2:0] bit_cnt; // counts bits 0-7 0-7 ... +reg [9:0] byte_cnt; // counts bytes +reg [7:0] but_sw; +reg [2:0] stick_idx; + +reg mount_strobe = 0; +assign img_mounted = mount_strobe; + +assign buttons = but_sw[1:0]; +assign switches = but_sw[3:2]; +assign scandoubler_disable = but_sw[4]; +assign ypbpr = but_sw[5]; + +wire [7:0] spi_dout = { sbuf, SPI_DI}; + +// this variant of user_io is for 8 bit cores (type == a4) only +wire [7:0] core_type = 8'ha4; + +// command byte read by the io controller +wire [7:0] sd_cmd = { 4'h5, sd_conf, sd_sdhc, sd_wr, sd_rd }; + +reg spi_do; +assign SPI_DO = CONF_DATA0 ? 1'bZ : spi_do; + +wire [7:0] kbd_led = { 2'b01, 4'b0000, ps2_caps_led, 1'b1}; + +// drive MISO only when transmitting core id +always@(negedge SPI_SCK) begin + if(!CONF_DATA0) begin + // first byte returned is always core type, further bytes are + // command dependent + if(byte_cnt == 0) begin + spi_do <= core_type[~bit_cnt]; + + end else begin + case(cmd) + // reading config string + 8'h14: begin + // returning a byte from string + if(byte_cnt < STRLEN + 1) spi_do <= conf_str[{STRLEN - byte_cnt,~bit_cnt}]; + else spi_do <= 0; + end + + // reading sd card status + 8'h16: begin + if(byte_cnt == 1) spi_do <= sd_cmd[~bit_cnt]; + else if((byte_cnt >= 2) && (byte_cnt < 6)) spi_do <= sd_lba[{5-byte_cnt, ~bit_cnt}]; + else spi_do <= 0; + end + + // reading sd card write data + 8'h18: + spi_do <= b_data[~bit_cnt]; + + // reading keyboard LED status + 8'h1f: + spi_do <= kbd_led[~bit_cnt]; + + default: + spi_do <= 0; + endcase + end + end +end + +reg b_wr2,b_wr3; +always @(negedge clk_sys) begin + b_wr3 <= b_wr2; + sd_buff_wr <= b_wr3; +end + +// SPI receiver +always@(posedge SPI_SCK or posedge CONF_DATA0) begin + + if(CONF_DATA0) begin + b_wr2 <= 0; + bit_cnt <= 0; + byte_cnt <= 0; + sd_ack <= 0; + sd_ack_conf <= 0; + end else begin + b_wr2 <= 0; + + sbuf <= spi_dout[6:0]; + bit_cnt <= bit_cnt + 1'd1; + if(bit_cnt == 5) begin + if (byte_cnt == 0) sd_buff_addr <= 0; + if((byte_cnt != 0) & (sd_buff_addr != 511)) sd_buff_addr <= sd_buff_addr + 1'b1; + if((byte_cnt == 1) & ((cmd == 8'h17) | (cmd == 8'h19))) sd_buff_addr <= 0; + end + + // finished reading command byte + if(bit_cnt == 7) begin + if(~&byte_cnt) byte_cnt <= byte_cnt + 8'd1; + if(byte_cnt == 0) begin + cmd <= spi_dout; + + if(spi_dout == 8'h19) begin + sd_ack_conf <= 1; + sd_buff_addr <= 0; + end + if((spi_dout == 8'h17) || (spi_dout == 8'h18)) begin + sd_ack <= 1; + sd_buff_addr <= 0; + end + if(spi_dout == 8'h18) b_data <= sd_buff_din; + + mount_strobe <= 0; + + end else begin + + case(cmd) + // buttons and switches + 8'h01: but_sw <= spi_dout; + 8'h02: joystick_0 <= spi_dout; + 8'h03: joystick_1 <= spi_dout; + + // store incoming ps2 mouse bytes + 8'h04: begin + ps2_mouse_fifo[ps2_mouse_wptr] <= spi_dout; + ps2_mouse_wptr <= ps2_mouse_wptr + 1'd1; + end + + // store incoming ps2 keyboard bytes + 8'h05: begin + ps2_kbd_fifo[ps2_kbd_wptr] <= spi_dout; + ps2_kbd_wptr <= ps2_kbd_wptr + 1'd1; + end + + 8'h15: status[7:0] <= spi_dout; + + // send SD config IO -> FPGA + // flag that download begins + // sd card knows data is config if sd_dout_strobe is asserted + // with sd_ack still being inactive (low) + 8'h19, + // send sector IO -> FPGA + // flag that download begins + 8'h17: begin + sd_buff_dout <= spi_dout; + b_wr2 <= 1; + end + + 8'h18: b_data <= sd_buff_din; + + // joystick analog + 8'h1a: begin + // first byte is joystick index + if(byte_cnt == 1) stick_idx <= spi_dout[2:0]; + else if(byte_cnt == 2) begin + // second byte is x axis + if(stick_idx == 0) joystick_analog_0[15:8] <= spi_dout; + else if(stick_idx == 1) joystick_analog_1[15:8] <= spi_dout; + end else if(byte_cnt == 3) begin + // third byte is y axis + if(stick_idx == 0) joystick_analog_0[7:0] <= spi_dout; + else if(stick_idx == 1) joystick_analog_1[7:0] <= spi_dout; + end + end + + // notify image selection + 8'h1c: mount_strobe <= 1; + + // send image info + 8'h1d: if(byte_cnt<5) img_size[(byte_cnt-1)<<3 +:8] <= spi_dout; + + // status, 32bit version + 8'h1e: if(byte_cnt<5) status[(byte_cnt-1)<<3 +:8] <= spi_dout; + default: ; + endcase + end + end + end +end + + +/////////////////////////////// PS2 /////////////////////////////// +// 8 byte fifos to store ps2 bytes +localparam PS2_FIFO_BITS = 3; + +reg clk_ps2; +always @(negedge clk_sys) begin + integer cnt; + cnt <= cnt + 1'd1; + if(cnt == PS2DIV) begin + clk_ps2 <= ~clk_ps2; + cnt <= 0; + end +end + +// keyboard +reg [7:0] ps2_kbd_fifo[1<= 1)&&(ps2_kbd_tx_state < 9)) begin + ps2_kbd_data <= ps2_kbd_tx_byte[0]; // data bits + ps2_kbd_tx_byte[6:0] <= ps2_kbd_tx_byte[7:1]; // shift down + if(ps2_kbd_tx_byte[0]) + ps2_kbd_parity <= !ps2_kbd_parity; + end + + // transmission of parity + if(ps2_kbd_tx_state == 9) ps2_kbd_data <= ps2_kbd_parity; + + // transmission of stop bit + if(ps2_kbd_tx_state == 10) ps2_kbd_data <= 1; // stop bit is 1 + + // advance state machine + if(ps2_kbd_tx_state < 11) ps2_kbd_tx_state <= ps2_kbd_tx_state + 1'd1; + else ps2_kbd_tx_state <= 0; + end + end +end + +// mouse +reg [7:0] ps2_mouse_fifo[1<= 1)&&(ps2_mouse_tx_state < 9)) begin + ps2_mouse_data <= ps2_mouse_tx_byte[0]; // data bits + ps2_mouse_tx_byte[6:0] <= ps2_mouse_tx_byte[7:1]; // shift down + if(ps2_mouse_tx_byte[0]) + ps2_mouse_parity <= !ps2_mouse_parity; + end + + // transmission of parity + if(ps2_mouse_tx_state == 9) ps2_mouse_data <= ps2_mouse_parity; + + // transmission of stop bit + if(ps2_mouse_tx_state == 10) ps2_mouse_data <= 1; // stop bit is 1 + + // advance state machine + if(ps2_mouse_tx_state < 11) ps2_mouse_tx_state <= ps2_mouse_tx_state + 1'd1; + else ps2_mouse_tx_state <= 0; + end + end +end + + +/////////////////////////////// DOWNLOADING /////////////////////////////// + +reg [7:0] data_w; +reg [24:0] addr_w; +reg rclk = 0; + +localparam UIO_FILE_TX = 8'h53; +localparam UIO_FILE_TX_DAT = 8'h54; +localparam UIO_FILE_INDEX = 8'h55; + +// data_io has its own SPI interface to the io controller +always@(posedge SPI_SCK, posedge SPI_SS2) begin + reg [6:0] sbuf; + reg [7:0] cmd; + reg [4:0] cnt; + reg [24:0] addr; + + if(SPI_SS2) cnt <= 0; + else begin + rclk <= 0; + + // don't shift in last bit. It is evaluated directly + // when writing to ram + if(cnt != 15) sbuf <= { sbuf[5:0], SPI_DI}; + + // increase target address after write + if(rclk) addr <= addr + 1'd1; + + // count 0-7 8-15 8-15 ... + if(cnt < 15) cnt <= cnt + 1'd1; + else cnt <= 8; + + // finished command byte + if(cnt == 7) cmd <= {sbuf, SPI_DI}; + + // prepare/end transmission + if((cmd == UIO_FILE_TX) && (cnt == 15)) begin + // prepare + if(SPI_DI) begin + addr <= 0; + ioctl_download <= 1; + end else begin + addr_w <= addr; + ioctl_download <= 0; + end + end + + // command 0x54: UIO_FILE_TX + if((cmd == UIO_FILE_TX_DAT) && (cnt == 15)) begin + addr_w <= addr; + data_w <= {sbuf, SPI_DI}; + rclk <= 1; + end + + // expose file (menu) index + if((cmd == UIO_FILE_INDEX) && (cnt == 15)) ioctl_index <= {sbuf, SPI_DI}; + end +end + +assign ioctl_wr = |ioctl_wrd; +reg [1:0] ioctl_wrd; + +always@(negedge clk_sys) begin + reg rclkD, rclkD2; + + rclkD <= rclk; + rclkD2 <= rclkD; + ioctl_wrd<= {ioctl_wrd[0],1'b0}; + + if(rclkD & ~rclkD2) begin + ioctl_dout <= data_w; + ioctl_addr <= addr_w; + ioctl_wrd <= 2'b11; + end +end + +endmodule diff --git a/rtl/mist-modules_old/osd.v b/rtl/mist-modules_old/osd.v new file mode 100644 index 0000000..c62c10a --- /dev/null +++ b/rtl/mist-modules_old/osd.v @@ -0,0 +1,179 @@ +// A simple OSD implementation. Can be hooked up between a cores +// VGA output and the physical VGA pins + +module osd ( + // OSDs pixel clock, should be synchronous to cores pixel clock to + // avoid jitter. + input clk_sys, + + // SPI interface + input SPI_SCK, + input SPI_SS3, + input SPI_DI, + + // VGA signals coming from core + input [5:0] R_in, + input [5:0] G_in, + input [5:0] B_in, + input HSync, + input VSync, + + // VGA signals going to video connector + output [5:0] R_out, + output [5:0] G_out, + output [5:0] B_out +); + +parameter OSD_X_OFFSET = 10'd0; +parameter OSD_Y_OFFSET = 10'd0; +parameter OSD_COLOR = 3'd0; + +localparam OSD_WIDTH = 10'd256; +localparam OSD_HEIGHT = 10'd128; + +// ********************************************************************************* +// spi client +// ********************************************************************************* + +// this core supports only the display related OSD commands +// of the minimig +reg osd_enable; +(* ramstyle = "no_rw_check" *) reg [7:0] osd_buffer[2047:0]; // the OSD buffer itself + +// the OSD has its own SPI interface to the io controller +always@(posedge SPI_SCK, posedge SPI_SS3) begin + reg [4:0] cnt; + reg [10:0] bcnt; + reg [7:0] sbuf; + reg [7:0] cmd; + + if(SPI_SS3) begin + cnt <= 0; + bcnt <= 0; + end else begin + sbuf <= {sbuf[6:0], SPI_DI}; + + // 0:7 is command, rest payload + if(cnt < 15) cnt <= cnt + 1'd1; + else cnt <= 8; + + if(cnt == 7) begin + cmd <= {sbuf[6:0], SPI_DI}; + + // lower three command bits are line address + bcnt <= {sbuf[1:0], SPI_DI, 8'h00}; + + // command 0x40: OSDCMDENABLE, OSDCMDDISABLE + if(sbuf[6:3] == 4'b0100) osd_enable <= SPI_DI; + end + + // command 0x20: OSDCMDWRITE + if((cmd[7:3] == 5'b00100) && (cnt == 15)) begin + osd_buffer[bcnt] <= {sbuf[6:0], SPI_DI}; + bcnt <= bcnt + 1'd1; + end + end +end + +// ********************************************************************************* +// video timing and sync polarity anaylsis +// ********************************************************************************* + +// horizontal counter +reg [9:0] h_cnt; +reg [9:0] hs_low, hs_high; +wire hs_pol = hs_high < hs_low; +wire [9:0] dsp_width = hs_pol ? hs_low : hs_high; + +// vertical counter +reg [9:0] v_cnt; +reg [9:0] vs_low, vs_high; +wire vs_pol = vs_high < vs_low; +wire [9:0] dsp_height = vs_pol ? vs_low : vs_high; + +wire doublescan = (dsp_height>350); + +reg ce_pix; +always @(negedge clk_sys) begin + integer cnt = 0; + integer pixsz, pixcnt; + reg hs; + + cnt <= cnt + 1; + hs <= HSync; + + pixcnt <= pixcnt + 1; + if(pixcnt == pixsz) pixcnt <= 0; + ce_pix <= !pixcnt; + + if(hs && ~HSync) begin + cnt <= 0; + pixsz <= (cnt >> 9) - 1; + pixcnt <= 0; + ce_pix <= 1; + end +end + +always @(posedge clk_sys) begin + reg hsD, hsD2; + reg vsD, vsD2; + + if(ce_pix) begin + // bring hsync into local clock domain + hsD <= HSync; + hsD2 <= hsD; + + // falling edge of HSync + if(!hsD && hsD2) begin + h_cnt <= 0; + hs_high <= h_cnt; + end + + // rising edge of HSync + else if(hsD && !hsD2) begin + h_cnt <= 0; + hs_low <= h_cnt; + v_cnt <= v_cnt + 1'd1; + end else begin + h_cnt <= h_cnt + 1'd1; + end + + vsD <= VSync; + vsD2 <= vsD; + + // falling edge of VSync + if(!vsD && vsD2) begin + v_cnt <= 0; + vs_high <= v_cnt; + end + + // rising edge of VSync + else if(vsD && !vsD2) begin + v_cnt <= 0; + vs_low <= v_cnt; + end + end +end + +// area in which OSD is being displayed +wire [9:0] h_osd_start = ((dsp_width - OSD_WIDTH)>> 1) + OSD_X_OFFSET; +wire [9:0] h_osd_end = h_osd_start + OSD_WIDTH; +wire [9:0] v_osd_start = ((dsp_height- (OSD_HEIGHT<> 1) + OSD_Y_OFFSET; +wire [9:0] v_osd_end = v_osd_start + (OSD_HEIGHT<= h_osd_start) && (h_cnt < h_osd_end) && + (VSync != vs_pol) && (v_cnt >= v_osd_start) && (v_cnt < v_osd_end); + +reg [7:0] osd_byte; +always @(posedge clk_sys) if(ce_pix) osd_byte <= osd_buffer[{doublescan ? osd_vcnt[7:5] : osd_vcnt[6:4], osd_hcnt[7:0]}]; + +wire osd_pixel = osd_byte[doublescan ? osd_vcnt[4:2] : osd_vcnt[3:1]]; + +assign R_out = !osd_de ? R_in : {osd_pixel, osd_pixel, OSD_COLOR[2], R_in[5:3]}; +assign G_out = !osd_de ? G_in : {osd_pixel, osd_pixel, OSD_COLOR[1], G_in[5:3]}; +assign B_out = !osd_de ? B_in : {osd_pixel, osd_pixel, OSD_COLOR[0], B_in[5:3]}; + +endmodule diff --git a/rtl/mist-modules_old/scandoubler.v b/rtl/mist-modules_old/scandoubler.v new file mode 100644 index 0000000..0213d20 --- /dev/null +++ b/rtl/mist-modules_old/scandoubler.v @@ -0,0 +1,195 @@ +// +// scandoubler.v +// +// Copyright (c) 2015 Till Harbaum +// Copyright (c) 2017 Sorgelig +// +// This source file is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// TODO: Delay vsync one line + +module scandoubler #(parameter LENGTH, parameter HALF_DEPTH) +( + // system interface + input clk_sys, + input ce_pix, + input ce_pix_actual, + + input hq2x, + + // shifter video interface + input hs_in, + input vs_in, + input line_start, + + input [DWIDTH:0] r_in, + input [DWIDTH:0] g_in, + input [DWIDTH:0] b_in, + input mono, + + // output interface + output reg hs_out, + output vs_out, + output [DWIDTH:0] r_out, + output [DWIDTH:0] g_out, + output [DWIDTH:0] b_out +); + + +localparam DWIDTH = HALF_DEPTH ? 2 : 5; + +assign vs_out = vs_in; + +reg [2:0] phase; +reg [2:0] ce_div; +reg [7:0] pix_len = 0; +wire [7:0] pl = pix_len + 1'b1; + +reg ce_x1, ce_x4; +reg req_line_reset; +wire ls_in = hs_in | line_start; +always @(negedge clk_sys) begin + reg old_ce; + reg [2:0] ce_cnt; + + reg [7:0] pixsz2, pixsz4 = 0; + + old_ce <= ce_pix; + if(~&pix_len) pix_len <= pix_len + 1'd1; + + ce_x4 <= 0; + ce_x1 <= 0; + + // use such odd comparison to place c_x4 evenly if master clock isn't multiple 4. + if((pl == pixsz4) || (pl == pixsz2) || (pl == (pixsz2+pixsz4))) begin + phase <= phase + 1'd1; + ce_x4 <= 1; + end + + if(~old_ce & ce_pix) begin + pixsz2 <= {1'b0, pl[7:1]}; + pixsz4 <= {2'b00, pl[7:2]}; + ce_x1 <= 1; + ce_x4 <= 1; + pix_len <= 0; + phase <= phase + 1'd1; + + ce_cnt <= ce_cnt + 1'd1; + if(ce_pix_actual) begin + phase <= 0; + ce_div <= ce_cnt + 1'd1; + ce_cnt <= 0; + req_line_reset <= 0; + end + + if(ls_in) req_line_reset <= 1; + end +end + +reg ce_sd; +always @(*) begin + case(ce_div) + 2: ce_sd = !phase[0]; + 4: ce_sd = !phase[1:0]; + default: ce_sd <= 1; + endcase +end + +`define BITS_TO_FIT(N) ( \ + N <= 2 ? 0 : \ + N <= 4 ? 1 : \ + N <= 8 ? 2 : \ + N <= 16 ? 3 : \ + N <= 32 ? 4 : \ + N <= 64 ? 5 : \ + N <= 128 ? 6 : \ + N <= 256 ? 7 : \ + N <= 512 ? 8 : \ + N <=1024 ? 9 : 10 ) + +localparam AWIDTH = `BITS_TO_FIT(LENGTH); +Hq2x #(.LENGTH(LENGTH), .HALF_DEPTH(HALF_DEPTH)) Hq2x +( + .clk(clk_sys), + .ce_x4(ce_x4 & ce_sd), + .inputpixel({b_in,g_in,r_in}), + .mono(mono), + .disable_hq2x(~hq2x), + .reset_frame(vs_in), + .reset_line(req_line_reset), + .read_y(sd_line), + .read_x(sd_h_actual), + .outpixel({b_out,g_out,r_out}) +); + +reg [10:0] sd_h_actual; +always @(*) begin + case(ce_div) + 2: sd_h_actual = sd_h[10:1]; + 4: sd_h_actual = sd_h[10:2]; + default: sd_h_actual = sd_h; + endcase +end + +reg [10:0] sd_h; +reg [1:0] sd_line; +always @(posedge clk_sys) begin + + reg [11:0] hs_max,hs_rise,hs_ls; + reg [10:0] hcnt; + reg [11:0] sd_hcnt; + + reg hs, hs2, vs, ls; + + if(ce_x1) begin + hs <= hs_in; + ls <= ls_in; + + if(ls && !ls_in) hs_ls <= {hcnt,1'b1}; + + // falling edge of hsync indicates start of line + if(hs && !hs_in) begin + hs_max <= {hcnt,1'b1}; + hcnt <= 0; + if(ls && !ls_in) hs_ls <= {10'd0,1'b1}; + end else begin + hcnt <= hcnt + 1'd1; + end + + // save position of rising edge + if(!hs && hs_in) hs_rise <= {hcnt,1'b1}; + + vs <= vs_in; + if(vs && ~vs_in) sd_line <= 0; + end + + if(ce_x4) begin + hs2 <= hs_in; + + // output counter synchronous to input and at twice the rate + sd_hcnt <= sd_hcnt + 1'd1; + sd_h <= sd_h + 1'd1; + if(hs2 && !hs_in) sd_hcnt <= hs_max; + if(sd_hcnt == hs_max) sd_hcnt <= 0; + + // replicate horizontal sync at twice the speed + if(sd_hcnt == hs_max) hs_out <= 0; + if(sd_hcnt == hs_rise) hs_out <= 1; + + if(sd_hcnt == hs_ls) sd_h <= 0; + if(sd_hcnt == hs_ls) sd_line <= sd_line + 1'd1; + end +end + +endmodule diff --git a/rtl/mist-modules_old/video_mixer.sv b/rtl/mist-modules_old/video_mixer.sv new file mode 100644 index 0000000..04cfd4b --- /dev/null +++ b/rtl/mist-modules_old/video_mixer.sv @@ -0,0 +1,242 @@ +// +// +// Copyright (c) 2017 Sorgelig +// +// This program is GPL Licensed. See COPYING for the full license. +// +// +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +`timescale 1ns / 1ps + +// +// LINE_LENGTH: Length of display line in pixels +// Usually it's length from HSync to HSync. +// May be less if line_start is used. +// +// HALF_DEPTH: If =1 then color dept is 3 bits per component +// For half depth 6 bits monochrome is available with +// mono signal enabled and color = {G, R} + +module video_mixer +#( + parameter LINE_LENGTH = 768, + parameter HALF_DEPTH = 0, + + parameter OSD_COLOR = 3'd4, + parameter OSD_X_OFFSET = 10'd0, + parameter OSD_Y_OFFSET = 10'd0 +) +( + // master clock + // it should be multiple by (ce_pix*4). + input clk_sys, + + // Pixel clock or clock_enable (both are accepted). + input ce_pix, + + // Some systems have multiple resolutions. + // ce_pix_actual should match ce_pix where every second or fourth pulse is enabled, + // thus half or qurter resolutions can be used without brake video sync while switching resolutions. + // For fixed single resolution (or when video sync stability isn't required) ce_pix_actual = ce_pix. + input ce_pix_actual, + + // OSD SPI interface + input SPI_SCK, + input SPI_SS3, + input SPI_DI, + + // scanlines (00-none 01-25% 10-50% 11-75%) + input [1:0] scanlines, + + // 0 = HVSync 31KHz, 1 = CSync 15KHz + input scandoubler_disable, + + // High quality 2x scaling + input hq2x, + + // YPbPr always uses composite sync + input ypbpr, + + // 0 = 16-240 range. 1 = 0-255 range. (only for YPbPr color space) + input ypbpr_full, + + // color + input [DWIDTH:0] R, + input [DWIDTH:0] G, + input [DWIDTH:0] B, + + // Monochrome mode (for HALF_DEPTH only) + input mono, + + // interlace sync. Positive pulses. + input HSync, + input VSync, + + // Falling of this signal means start of informative part of line. + // It can be horizontal blank signal. + // This signal can be used to reduce amount of required FPGA RAM for HQ2x scan doubler + // If FPGA RAM is not an issue, then simply set it to 0 for whole line processing. + // Keep in mind: due to algo first and last pixels of line should be black to avoid side artefacts. + // Thus, if blank signal is used to reduce the line, make sure to feed at least one black (or paper) pixel + // before first informative pixel. + input line_start, + + // MiST video output signals + output [5:0] VGA_R, + output [5:0] VGA_G, + output [5:0] VGA_B, + output VGA_VS, + output VGA_HS +); + +localparam DWIDTH = HALF_DEPTH ? 2 : 5; + +wire [DWIDTH:0] R_sd; +wire [DWIDTH:0] G_sd; +wire [DWIDTH:0] B_sd; +wire hs_sd, vs_sd; + +scandoubler #(.LENGTH(LINE_LENGTH), .HALF_DEPTH(HALF_DEPTH)) scandoubler +( + .*, + .hs_in(HSync), + .vs_in(VSync), + .r_in(R), + .g_in(G), + .b_in(B), + + .hs_out(hs_sd), + .vs_out(vs_sd), + .r_out(R_sd), + .g_out(G_sd), + .b_out(B_sd) +); + +wire [DWIDTH:0] rt = (scandoubler_disable ? R : R_sd); +wire [DWIDTH:0] gt = (scandoubler_disable ? G : G_sd); +wire [DWIDTH:0] bt = (scandoubler_disable ? B : B_sd); + +generate + if(HALF_DEPTH) begin + wire [5:0] r = mono ? {gt,rt} : {rt,rt}; + wire [5:0] g = mono ? {gt,rt} : {gt,gt}; + wire [5:0] b = mono ? {gt,rt} : {bt,bt}; + end else begin + wire [5:0] r = rt; + wire [5:0] g = gt; + wire [5:0] b = bt; + end +endgenerate + +wire hs = (scandoubler_disable ? HSync : hs_sd); +wire vs = (scandoubler_disable ? VSync : vs_sd); + +reg scanline = 0; +always @(posedge clk_sys) begin + reg old_hs, old_vs; + + old_hs <= hs; + old_vs <= vs; + + if(old_hs && ~hs) scanline <= ~scanline; + if(old_vs && ~vs) scanline <= 0; +end + +wire [5:0] r_out, g_out, b_out; +always @(*) begin + case(scanlines & {scanline, scanline}) + 1: begin // reduce 25% = 1/2 + 1/4 + r_out = {1'b0, r[5:1]} + {2'b00, r[5:2]}; + g_out = {1'b0, g[5:1]} + {2'b00, g[5:2]}; + b_out = {1'b0, b[5:1]} + {2'b00, b[5:2]}; + end + + 2: begin // reduce 50% = 1/2 + r_out = {1'b0, r[5:1]}; + g_out = {1'b0, g[5:1]}; + b_out = {1'b0, b[5:1]}; + end + + 3: begin // reduce 75% = 1/4 + r_out = {2'b00, r[5:2]}; + g_out = {2'b00, g[5:2]}; + b_out = {2'b00, b[5:2]}; + end + + default: begin + r_out = r; + g_out = g; + b_out = b; + end + endcase +end + +wire [5:0] red, green, blue; +osd #(OSD_X_OFFSET, OSD_Y_OFFSET, OSD_COLOR) osd +( + .*, + + .R_in(r_out), + .G_in(g_out), + .B_in(b_out), + .HSync(hs), + .VSync(vs), + + .R_out(red), + .G_out(green), + .B_out(blue) +); + +wire [5:0] yuv_full[225] = '{ + 6'd0, 6'd0, 6'd0, 6'd0, 6'd1, 6'd1, 6'd1, 6'd1, + 6'd2, 6'd2, 6'd2, 6'd3, 6'd3, 6'd3, 6'd3, 6'd4, + 6'd4, 6'd4, 6'd5, 6'd5, 6'd5, 6'd5, 6'd6, 6'd6, + 6'd6, 6'd7, 6'd7, 6'd7, 6'd7, 6'd8, 6'd8, 6'd8, + 6'd9, 6'd9, 6'd9, 6'd9, 6'd10, 6'd10, 6'd10, 6'd11, + 6'd11, 6'd11, 6'd11, 6'd12, 6'd12, 6'd12, 6'd13, 6'd13, + 6'd13, 6'd13, 6'd14, 6'd14, 6'd14, 6'd15, 6'd15, 6'd15, + 6'd15, 6'd16, 6'd16, 6'd16, 6'd17, 6'd17, 6'd17, 6'd17, + 6'd18, 6'd18, 6'd18, 6'd19, 6'd19, 6'd19, 6'd19, 6'd20, + 6'd20, 6'd20, 6'd21, 6'd21, 6'd21, 6'd21, 6'd22, 6'd22, + 6'd22, 6'd23, 6'd23, 6'd23, 6'd23, 6'd24, 6'd24, 6'd24, + 6'd25, 6'd25, 6'd25, 6'd25, 6'd26, 6'd26, 6'd26, 6'd27, + 6'd27, 6'd27, 6'd27, 6'd28, 6'd28, 6'd28, 6'd29, 6'd29, + 6'd29, 6'd29, 6'd30, 6'd30, 6'd30, 6'd31, 6'd31, 6'd31, + 6'd31, 6'd32, 6'd32, 6'd32, 6'd33, 6'd33, 6'd33, 6'd33, + 6'd34, 6'd34, 6'd34, 6'd35, 6'd35, 6'd35, 6'd35, 6'd36, + 6'd36, 6'd36, 6'd36, 6'd37, 6'd37, 6'd37, 6'd38, 6'd38, + 6'd38, 6'd38, 6'd39, 6'd39, 6'd39, 6'd40, 6'd40, 6'd40, + 6'd40, 6'd41, 6'd41, 6'd41, 6'd42, 6'd42, 6'd42, 6'd42, + 6'd43, 6'd43, 6'd43, 6'd44, 6'd44, 6'd44, 6'd44, 6'd45, + 6'd45, 6'd45, 6'd46, 6'd46, 6'd46, 6'd46, 6'd47, 6'd47, + 6'd47, 6'd48, 6'd48, 6'd48, 6'd48, 6'd49, 6'd49, 6'd49, + 6'd50, 6'd50, 6'd50, 6'd50, 6'd51, 6'd51, 6'd51, 6'd52, + 6'd52, 6'd52, 6'd52, 6'd53, 6'd53, 6'd53, 6'd54, 6'd54, + 6'd54, 6'd54, 6'd55, 6'd55, 6'd55, 6'd56, 6'd56, 6'd56, + 6'd56, 6'd57, 6'd57, 6'd57, 6'd58, 6'd58, 6'd58, 6'd58, + 6'd59, 6'd59, 6'd59, 6'd60, 6'd60, 6'd60, 6'd60, 6'd61, + 6'd61, 6'd61, 6'd62, 6'd62, 6'd62, 6'd62, 6'd63, 6'd63, + 6'd63 +}; + +// http://marsee101.blog19.fc2.com/blog-entry-2311.html +// Y = 16 + 0.257*R + 0.504*G + 0.098*B (Y = 0.299*R + 0.587*G + 0.114*B) +// Pb = 128 - 0.148*R - 0.291*G + 0.439*B (Pb = -0.169*R - 0.331*G + 0.500*B) +// Pr = 128 + 0.439*R - 0.368*G - 0.071*B (Pr = 0.500*R - 0.419*G - 0.081*B) + +wire [18:0] y_8 = 19'd04096 + ({red, 8'd0} + {red, 3'd0}) + ({green, 9'd0} + {green, 2'd0}) + ({blue, 6'd0} + {blue, 5'd0} + {blue, 2'd0}); +wire [18:0] pb_8 = 19'd32768 - ({red, 7'd0} + {red, 4'd0} + {red, 3'd0}) - ({green, 8'd0} + {green, 5'd0} + {green, 3'd0}) + ({blue, 8'd0} + {blue, 7'd0} + {blue, 6'd0}); +wire [18:0] pr_8 = 19'd32768 + ({red, 8'd0} + {red, 7'd0} + {red, 6'd0}) - ({green, 8'd0} + {green, 6'd0} + {green, 5'd0} + {green, 4'd0} + {green, 3'd0}) - ({blue, 6'd0} + {blue , 3'd0}); + +wire [7:0] y = ( y_8[17:8] < 16) ? 8'd16 : ( y_8[17:8] > 235) ? 8'd235 : y_8[15:8]; +wire [7:0] pb = (pb_8[17:8] < 16) ? 8'd16 : (pb_8[17:8] > 240) ? 8'd240 : pb_8[15:8]; +wire [7:0] pr = (pr_8[17:8] < 16) ? 8'd16 : (pr_8[17:8] > 240) ? 8'd240 : pr_8[15:8]; + +assign VGA_R = ypbpr ? (ypbpr_full ? yuv_full[pr-8'd16] : pr[7:2]) : red; +assign VGA_G = ypbpr ? (ypbpr_full ? yuv_full[y -8'd16] : y[7:2]) : green; +assign VGA_B = ypbpr ? (ypbpr_full ? yuv_full[pb-8'd16] : pb[7:2]) : blue; +assign VGA_VS = (scandoubler_disable | ypbpr) ? 1'b1 : ~vs_sd; +assign VGA_HS = scandoubler_disable ? ~(HSync ^ VSync) : ypbpr ? ~(hs_sd ^ vs_sd) : ~hs_sd; + +endmodule diff --git a/rtl/old/aholme_6502.v b/rtl/old/aholme_6502.v new file mode 100644 index 0000000..3508389 --- /dev/null +++ b/rtl/old/aholme_6502.v @@ -0,0 +1,32 @@ +module aholme_6502( + input clk, + input enable, + input reset, + output [15:0] ab, + input [7:0] dbi, + output [7:0] dbo, + output we, + input irq, + input nmi, + input ready +); + + wire we_c; + + chip_6502 aholme_cpu ( + .clk(clk), + .phi(clk & enable), + .res(~reset), + .so(1'b0), + .rdy(ready), + .nmi(nmi_n), + .irq(irq_n), + .rw(we_c), + .dbi(dbi), + .dbo(dbo), + .ab(ab) + ); + + assign we = ~we_c; + +endmodule diff --git a/rtl/old/tm1638.v b/rtl/old/tm1638.v new file mode 100644 index 0000000..b40c5f1 --- /dev/null +++ b/rtl/old/tm1638.v @@ -0,0 +1,122 @@ +module tm1638( + input clk, + input clk_en, + input rst, + + input data_latch, + inout [7:0] data, + input rw, + + output busy, + + output sclk, + input dio_in, + output reg dio_out + ); + + localparam CLK_DIV = 3; // seems happy at 12MHz with 3 + localparam CLK_DIV1 = CLK_DIV - 1; + localparam [1:0] + S_IDLE = 2'h0, + S_WAIT = 2'h1, + S_TRANSFER = 2'h2; + + reg [1:0] cur_state, next_state; + reg [CLK_DIV1:0] sclk_d, sclk_q; + reg [7:0] data_d, data_q, data_out_d, data_out_q; + reg dio_out_d; + reg [2:0] ctr_d, ctr_q; + + // output read data if we're reading + assign data = rw ? 8'hZZ : data_out_q; + + // we're busy if we're not idle + assign busy = cur_state != S_IDLE; + + // tick the clock if we're transfering data + assign sclk = ~((~sclk_q[CLK_DIV1]) & (cur_state == S_TRANSFER)); + + always @(*) + begin + sclk_d = sclk_q; + data_d = data_q; + dio_out_d = dio_out; + ctr_d = ctr_q; + data_out_d = data_out_q; + next_state = cur_state; + + case(cur_state) + S_IDLE: begin + sclk_d = 0; + if (data_latch) begin + // if we're reading, set to zero, otherwise latch in + // data to send + data_d = rw ? data : 8'b0; + next_state = S_WAIT; + end + end + + S_WAIT: begin + sclk_d = sclk_q + 1; + // wait till we're halfway into clock pulse + if (sclk_q == {1'b0, {CLK_DIV1{1'b1}}}) begin + sclk_d = 0; + next_state = S_TRANSFER; + end + end + + S_TRANSFER: begin + sclk_d = sclk_q + 1; + if (sclk_q == 0) begin + // start of clock pulse, output MSB + dio_out_d = data_q[0]; + + end else if (sclk_q == {1'b0, {CLK_DIV1{1'b1}}}) begin + // halfway through pulse, read from device + data_d = {dio_in, data_q[7:1]}; + + end else if (&sclk_q) begin + // end of pulse, tick the counter + ctr_d = ctr_q + 1; + + if (&ctr_q) begin + // last bit sent, switch back to idle + // and output any data recieved + next_state = S_IDLE; + data_out_d = data_q; + + dio_out_d = 0; + end + end + end + + default: + next_state = S_IDLE; + endcase + end + + always @(posedge clk) + begin + if (clk_en) + begin + if (rst) + begin + cur_state <= S_IDLE; + sclk_q <= 0; + ctr_q <= 0; + dio_out <= 0; + data_q <= 0; + data_out_q <= 0; + end + else + begin + cur_state <= next_state; + sclk_q <= sclk_d; + ctr_q <= ctr_d; + dio_out <= dio_out_d; + data_q <= data_d; + data_out_q <= data_out_d; + end + end + end +endmodule diff --git a/rtl/pll.qip b/rtl/pll.qip new file mode 100644 index 0000000..afd958b --- /dev/null +++ b/rtl/pll.qip @@ -0,0 +1,4 @@ +set_global_assignment -name IP_TOOL_NAME "ALTPLL" +set_global_assignment -name IP_TOOL_VERSION "13.1" +set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "pll.v"] +set_global_assignment -name MISC_FILE [file join $::quartus(qip_path) "pll.ppf"] diff --git a/rtl/pll.v b/rtl/pll.v new file mode 100644 index 0000000..c352f56 --- /dev/null +++ b/rtl/pll.v @@ -0,0 +1,348 @@ +// megafunction wizard: %ALTPLL% +// GENERATION: STANDARD +// VERSION: WM1.0 +// MODULE: altpll + +// ============================================================ +// File Name: pll.v +// Megafunction Name(s): +// altpll +// +// Simulation Library Files(s): +// altera_mf +// ============================================================ +// ************************************************************ +// THIS IS A WIZARD-GENERATED FILE. DO NOT EDIT THIS FILE! +// +// 13.1.4 Build 182 03/12/2014 SJ Web Edition +// ************************************************************ + + +//Copyright (C) 1991-2014 Altera Corporation +//Your use of Altera Corporation's design tools, logic functions +//and other software and tools, and its AMPP partner logic +//functions, and any output files from any of the foregoing +//(including device programming or simulation files), and any +//associated documentation or information are expressly subject +//to the terms and conditions of the Altera Program License +//Subscription Agreement, Altera MegaCore Function License +//Agreement, or other applicable license agreement, including, +//without limitation, that your use is for the sole purpose of +//programming logic devices manufactured by Altera and sold by +//Altera or its authorized distributors. Please refer to the +//applicable agreement for further details. + + +// synopsys translate_off +`timescale 1 ps / 1 ps +// synopsys translate_on +module pll ( + areset, + inclk0, + c0, + c1, + locked); + + input areset; + input inclk0; + output c0; + output c1; + output locked; +`ifndef ALTERA_RESERVED_QIS +// synopsys translate_off +`endif + tri0 areset; +`ifndef ALTERA_RESERVED_QIS +// synopsys translate_on +`endif + + wire [4:0] sub_wire0; + wire sub_wire2; + wire [0:0] sub_wire6 = 1'h0; + wire [0:0] sub_wire3 = sub_wire0[0:0]; + wire [1:1] sub_wire1 = sub_wire0[1:1]; + wire c1 = sub_wire1; + wire locked = sub_wire2; + wire c0 = sub_wire3; + wire sub_wire4 = inclk0; + wire [1:0] sub_wire5 = {sub_wire6, sub_wire4}; + + altpll altpll_component ( + .areset (areset), + .inclk (sub_wire5), + .clk (sub_wire0), + .locked (sub_wire2), + .activeclock (), + .clkbad (), + .clkena ({6{1'b1}}), + .clkloss (), + .clkswitch (1'b0), + .configupdate (1'b0), + .enable0 (), + .enable1 (), + .extclk (), + .extclkena ({4{1'b1}}), + .fbin (1'b1), + .fbmimicbidir (), + .fbout (), + .fref (), + .icdrclk (), + .pfdena (1'b1), + .phasecounterselect ({4{1'b1}}), + .phasedone (), + .phasestep (1'b1), + .phaseupdown (1'b1), + .pllena (1'b1), + .scanaclr (1'b0), + .scanclk (1'b0), + .scanclkena (1'b1), + .scandata (1'b0), + .scandataout (), + .scandone (), + .scanread (1'b0), + .scanwrite (1'b0), + .sclkout0 (), + .sclkout1 (), + .vcooverrange (), + .vcounderrange ()); + defparam + altpll_component.bandwidth_type = "AUTO", + altpll_component.clk0_divide_by = 675000, + altpll_component.clk0_duty_cycle = 50, + altpll_component.clk0_multiply_by = 715909, + altpll_component.clk0_phase_shift = "0", + altpll_component.clk1_divide_by = 1350000, + altpll_component.clk1_duty_cycle = 50, + altpll_component.clk1_multiply_by = 715909, + altpll_component.clk1_phase_shift = "0", + altpll_component.compensate_clock = "CLK0", + altpll_component.inclk0_input_frequency = 37037, + altpll_component.intended_device_family = "Cyclone III", + altpll_component.lpm_hint = "CBX_MODULE_PREFIX=pll", + altpll_component.lpm_type = "altpll", + altpll_component.operation_mode = "NORMAL", + altpll_component.pll_type = "AUTO", + altpll_component.port_activeclock = "PORT_UNUSED", + altpll_component.port_areset = "PORT_USED", + altpll_component.port_clkbad0 = "PORT_UNUSED", + altpll_component.port_clkbad1 = "PORT_UNUSED", + altpll_component.port_clkloss = "PORT_UNUSED", + altpll_component.port_clkswitch = "PORT_UNUSED", + altpll_component.port_configupdate = "PORT_UNUSED", + altpll_component.port_fbin = "PORT_UNUSED", + altpll_component.port_inclk0 = "PORT_USED", + altpll_component.port_inclk1 = "PORT_UNUSED", + altpll_component.port_locked = "PORT_USED", + altpll_component.port_pfdena = "PORT_UNUSED", + altpll_component.port_phasecounterselect = "PORT_UNUSED", + altpll_component.port_phasedone = "PORT_UNUSED", + altpll_component.port_phasestep = "PORT_UNUSED", + altpll_component.port_phaseupdown = "PORT_UNUSED", + altpll_component.port_pllena = "PORT_UNUSED", + altpll_component.port_scanaclr = "PORT_UNUSED", + altpll_component.port_scanclk = "PORT_UNUSED", + altpll_component.port_scanclkena = "PORT_UNUSED", + altpll_component.port_scandata = "PORT_UNUSED", + altpll_component.port_scandataout = "PORT_UNUSED", + altpll_component.port_scandone = "PORT_UNUSED", + altpll_component.port_scanread = "PORT_UNUSED", + altpll_component.port_scanwrite = "PORT_UNUSED", + altpll_component.port_clk0 = "PORT_USED", + altpll_component.port_clk1 = "PORT_USED", + altpll_component.port_clk2 = "PORT_UNUSED", + altpll_component.port_clk3 = "PORT_UNUSED", + altpll_component.port_clk4 = "PORT_UNUSED", + altpll_component.port_clk5 = "PORT_UNUSED", + altpll_component.port_clkena0 = "PORT_UNUSED", + altpll_component.port_clkena1 = "PORT_UNUSED", + altpll_component.port_clkena2 = "PORT_UNUSED", + altpll_component.port_clkena3 = "PORT_UNUSED", + altpll_component.port_clkena4 = "PORT_UNUSED", + altpll_component.port_clkena5 = "PORT_UNUSED", + altpll_component.port_extclk0 = "PORT_UNUSED", + altpll_component.port_extclk1 = "PORT_UNUSED", + altpll_component.port_extclk2 = "PORT_UNUSED", + altpll_component.port_extclk3 = "PORT_UNUSED", + altpll_component.self_reset_on_loss_lock = "OFF", + altpll_component.width_clock = 5; + + +endmodule + +// ============================================================ +// CNX file retrieval info +// ============================================================ +// Retrieval info: PRIVATE: ACTIVECLK_CHECK STRING "0" +// Retrieval info: PRIVATE: BANDWIDTH STRING "1.000" +// Retrieval info: PRIVATE: BANDWIDTH_FEATURE_ENABLED STRING "1" +// Retrieval info: PRIVATE: BANDWIDTH_FREQ_UNIT STRING "MHz" +// Retrieval info: PRIVATE: BANDWIDTH_PRESET STRING "Low" +// Retrieval info: PRIVATE: BANDWIDTH_USE_AUTO STRING "1" +// Retrieval info: PRIVATE: BANDWIDTH_USE_PRESET STRING "0" +// Retrieval info: PRIVATE: CLKBAD_SWITCHOVER_CHECK STRING "0" +// Retrieval info: PRIVATE: CLKLOSS_CHECK STRING "0" +// Retrieval info: PRIVATE: CLKSWITCH_CHECK STRING "0" +// Retrieval info: PRIVATE: CNX_NO_COMPENSATE_RADIO STRING "0" +// Retrieval info: PRIVATE: CREATE_CLKBAD_CHECK STRING "0" +// Retrieval info: PRIVATE: CREATE_INCLK1_CHECK STRING "0" +// Retrieval info: PRIVATE: CUR_DEDICATED_CLK STRING "c0" +// Retrieval info: PRIVATE: CUR_FBIN_CLK STRING "c0" +// Retrieval info: PRIVATE: DEVICE_SPEED_GRADE STRING "8" +// Retrieval info: PRIVATE: DIV_FACTOR0 NUMERIC "108" +// Retrieval info: PRIVATE: DIV_FACTOR1 NUMERIC "27" +// Retrieval info: PRIVATE: DUTY_CYCLE0 STRING "50.00000000" +// Retrieval info: PRIVATE: DUTY_CYCLE1 STRING "50.00000000" +// Retrieval info: PRIVATE: EFF_OUTPUT_FREQ_VALUE0 STRING "28.636360" +// Retrieval info: PRIVATE: EFF_OUTPUT_FREQ_VALUE1 STRING "14.318180" +// Retrieval info: PRIVATE: EXPLICIT_SWITCHOVER_COUNTER STRING "0" +// Retrieval info: PRIVATE: EXT_FEEDBACK_RADIO STRING "0" +// Retrieval info: PRIVATE: GLOCKED_COUNTER_EDIT_CHANGED STRING "1" +// Retrieval info: PRIVATE: GLOCKED_FEATURE_ENABLED STRING "0" +// Retrieval info: PRIVATE: GLOCKED_MODE_CHECK STRING "0" +// Retrieval info: PRIVATE: GLOCK_COUNTER_EDIT NUMERIC "1048575" +// Retrieval info: PRIVATE: HAS_MANUAL_SWITCHOVER STRING "1" +// Retrieval info: PRIVATE: INCLK0_FREQ_EDIT STRING "27.000" +// Retrieval info: PRIVATE: INCLK0_FREQ_UNIT_COMBO STRING "MHz" +// Retrieval info: PRIVATE: INCLK1_FREQ_EDIT STRING "100.000" +// Retrieval info: PRIVATE: INCLK1_FREQ_EDIT_CHANGED STRING "1" +// Retrieval info: PRIVATE: INCLK1_FREQ_UNIT_CHANGED STRING "1" +// Retrieval info: PRIVATE: INCLK1_FREQ_UNIT_COMBO STRING "MHz" +// Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone III" +// Retrieval info: PRIVATE: INT_FEEDBACK__MODE_RADIO STRING "1" +// Retrieval info: PRIVATE: LOCKED_OUTPUT_CHECK STRING "1" +// Retrieval info: PRIVATE: LONG_SCAN_RADIO STRING "1" +// Retrieval info: PRIVATE: LVDS_MODE_DATA_RATE STRING "Not Available" +// Retrieval info: PRIVATE: LVDS_MODE_DATA_RATE_DIRTY NUMERIC "0" +// Retrieval info: PRIVATE: LVDS_PHASE_SHIFT_UNIT0 STRING "deg" +// Retrieval info: PRIVATE: LVDS_PHASE_SHIFT_UNIT1 STRING "deg" +// Retrieval info: PRIVATE: MIG_DEVICE_SPEED_GRADE STRING "Any" +// Retrieval info: PRIVATE: MIRROR_CLK0 STRING "0" +// Retrieval info: PRIVATE: MIRROR_CLK1 STRING "0" +// Retrieval info: PRIVATE: MULT_FACTOR0 NUMERIC "25" +// Retrieval info: PRIVATE: MULT_FACTOR1 NUMERIC "25" +// Retrieval info: PRIVATE: NORMAL_MODE_RADIO STRING "1" +// Retrieval info: PRIVATE: OUTPUT_FREQ0 STRING "28.63636000" +// Retrieval info: PRIVATE: OUTPUT_FREQ1 STRING "14.31818000" +// Retrieval info: PRIVATE: OUTPUT_FREQ_MODE0 STRING "1" +// Retrieval info: PRIVATE: OUTPUT_FREQ_MODE1 STRING "1" +// Retrieval info: PRIVATE: OUTPUT_FREQ_UNIT0 STRING "MHz" +// Retrieval info: PRIVATE: OUTPUT_FREQ_UNIT1 STRING "MHz" +// Retrieval info: PRIVATE: PHASE_RECONFIG_FEATURE_ENABLED STRING "1" +// Retrieval info: PRIVATE: PHASE_RECONFIG_INPUTS_CHECK STRING "0" +// Retrieval info: PRIVATE: PHASE_SHIFT0 STRING "0.00000000" +// Retrieval info: PRIVATE: PHASE_SHIFT1 STRING "0.00000000" +// Retrieval info: PRIVATE: PHASE_SHIFT_STEP_ENABLED_CHECK STRING "0" +// Retrieval info: PRIVATE: PHASE_SHIFT_UNIT0 STRING "deg" +// Retrieval info: PRIVATE: PHASE_SHIFT_UNIT1 STRING "deg" +// Retrieval info: PRIVATE: PLL_ADVANCED_PARAM_CHECK STRING "0" +// Retrieval info: PRIVATE: PLL_ARESET_CHECK STRING "1" +// Retrieval info: PRIVATE: PLL_AUTOPLL_CHECK NUMERIC "1" +// Retrieval info: PRIVATE: PLL_ENHPLL_CHECK NUMERIC "0" +// Retrieval info: PRIVATE: PLL_FASTPLL_CHECK NUMERIC "0" +// Retrieval info: PRIVATE: PLL_FBMIMIC_CHECK STRING "0" +// Retrieval info: PRIVATE: PLL_LVDS_PLL_CHECK NUMERIC "0" +// Retrieval info: PRIVATE: PLL_PFDENA_CHECK STRING "0" +// Retrieval info: PRIVATE: PLL_TARGET_HARCOPY_CHECK NUMERIC "0" +// Retrieval info: PRIVATE: PRIMARY_CLK_COMBO STRING "inclk0" +// Retrieval info: PRIVATE: RECONFIG_FILE STRING "pll.mif" +// Retrieval info: PRIVATE: SACN_INPUTS_CHECK STRING "0" +// Retrieval info: PRIVATE: SCAN_FEATURE_ENABLED STRING "1" +// Retrieval info: PRIVATE: SELF_RESET_LOCK_LOSS STRING "0" +// Retrieval info: PRIVATE: SHORT_SCAN_RADIO STRING "0" +// Retrieval info: PRIVATE: SPREAD_FEATURE_ENABLED STRING "0" +// Retrieval info: PRIVATE: SPREAD_FREQ STRING "50.000" +// Retrieval info: PRIVATE: SPREAD_FREQ_UNIT STRING "KHz" +// Retrieval info: PRIVATE: SPREAD_PERCENT STRING "0.500" +// Retrieval info: PRIVATE: SPREAD_USE STRING "0" +// Retrieval info: PRIVATE: SRC_SYNCH_COMP_RADIO STRING "0" +// Retrieval info: PRIVATE: STICKY_CLK0 STRING "1" +// Retrieval info: PRIVATE: STICKY_CLK1 STRING "1" +// Retrieval info: PRIVATE: SWITCHOVER_COUNT_EDIT NUMERIC "1" +// Retrieval info: PRIVATE: SWITCHOVER_FEATURE_ENABLED STRING "1" +// Retrieval info: PRIVATE: SYNTH_WRAPPER_GEN_POSTFIX STRING "0" +// Retrieval info: PRIVATE: USE_CLK0 STRING "1" +// Retrieval info: PRIVATE: USE_CLK1 STRING "1" +// Retrieval info: PRIVATE: USE_CLKENA0 STRING "0" +// Retrieval info: PRIVATE: USE_CLKENA1 STRING "0" +// Retrieval info: PRIVATE: USE_MIL_SPEED_GRADE NUMERIC "0" +// Retrieval info: PRIVATE: ZERO_DELAY_RADIO STRING "0" +// Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all +// Retrieval info: CONSTANT: BANDWIDTH_TYPE STRING "AUTO" +// Retrieval info: CONSTANT: CLK0_DIVIDE_BY NUMERIC "675000" +// Retrieval info: CONSTANT: CLK0_DUTY_CYCLE NUMERIC "50" +// Retrieval info: CONSTANT: CLK0_MULTIPLY_BY NUMERIC "715909" +// Retrieval info: CONSTANT: CLK0_PHASE_SHIFT STRING "0" +// Retrieval info: CONSTANT: CLK1_DIVIDE_BY NUMERIC "1350000" +// Retrieval info: CONSTANT: CLK1_DUTY_CYCLE NUMERIC "50" +// Retrieval info: CONSTANT: CLK1_MULTIPLY_BY NUMERIC "715909" +// Retrieval info: CONSTANT: CLK1_PHASE_SHIFT STRING "0" +// Retrieval info: CONSTANT: COMPENSATE_CLOCK STRING "CLK0" +// Retrieval info: CONSTANT: INCLK0_INPUT_FREQUENCY NUMERIC "37037" +// Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone III" +// Retrieval info: CONSTANT: LPM_TYPE STRING "altpll" +// Retrieval info: CONSTANT: OPERATION_MODE STRING "NORMAL" +// Retrieval info: CONSTANT: PLL_TYPE STRING "AUTO" +// Retrieval info: CONSTANT: PORT_ACTIVECLOCK STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_ARESET STRING "PORT_USED" +// Retrieval info: CONSTANT: PORT_CLKBAD0 STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_CLKBAD1 STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_CLKLOSS STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_CLKSWITCH STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_CONFIGUPDATE STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_FBIN STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_INCLK0 STRING "PORT_USED" +// Retrieval info: CONSTANT: PORT_INCLK1 STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_LOCKED STRING "PORT_USED" +// Retrieval info: CONSTANT: PORT_PFDENA STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_PHASECOUNTERSELECT STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_PHASEDONE STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_PHASESTEP STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_PHASEUPDOWN STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_PLLENA STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_SCANACLR STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_SCANCLK STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_SCANCLKENA STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_SCANDATA STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_SCANDATAOUT STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_SCANDONE STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_SCANREAD STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_SCANWRITE STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_clk0 STRING "PORT_USED" +// Retrieval info: CONSTANT: PORT_clk1 STRING "PORT_USED" +// Retrieval info: CONSTANT: PORT_clk2 STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_clk3 STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_clk4 STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_clk5 STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_clkena0 STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_clkena1 STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_clkena2 STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_clkena3 STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_clkena4 STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_clkena5 STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_extclk0 STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_extclk1 STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_extclk2 STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: PORT_extclk3 STRING "PORT_UNUSED" +// Retrieval info: CONSTANT: SELF_RESET_ON_LOSS_LOCK STRING "OFF" +// Retrieval info: CONSTANT: WIDTH_CLOCK NUMERIC "5" +// Retrieval info: USED_PORT: @clk 0 0 5 0 OUTPUT_CLK_EXT VCC "@clk[4..0]" +// Retrieval info: USED_PORT: areset 0 0 0 0 INPUT GND "areset" +// Retrieval info: USED_PORT: c0 0 0 0 0 OUTPUT_CLK_EXT VCC "c0" +// Retrieval info: USED_PORT: c1 0 0 0 0 OUTPUT_CLK_EXT VCC "c1" +// Retrieval info: USED_PORT: inclk0 0 0 0 0 INPUT_CLK_EXT GND "inclk0" +// Retrieval info: USED_PORT: locked 0 0 0 0 OUTPUT GND "locked" +// Retrieval info: CONNECT: @areset 0 0 0 0 areset 0 0 0 0 +// Retrieval info: CONNECT: @inclk 0 0 1 1 GND 0 0 0 0 +// Retrieval info: CONNECT: @inclk 0 0 1 0 inclk0 0 0 0 0 +// Retrieval info: CONNECT: c0 0 0 0 0 @clk 0 0 1 0 +// Retrieval info: CONNECT: c1 0 0 0 0 @clk 0 0 1 1 +// Retrieval info: CONNECT: locked 0 0 0 0 @locked 0 0 0 0 +// Retrieval info: GEN_FILE: TYPE_NORMAL pll.v TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL pll.ppf TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL pll.inc FALSE +// Retrieval info: GEN_FILE: TYPE_NORMAL pll.cmp FALSE +// Retrieval info: GEN_FILE: TYPE_NORMAL pll.bsf FALSE +// Retrieval info: GEN_FILE: TYPE_NORMAL pll_inst.v FALSE +// Retrieval info: GEN_FILE: TYPE_NORMAL pll_bb.v FALSE +// Retrieval info: LIB_FILE: altera_mf +// Retrieval info: CBX_MODULE_PREFIX: ON diff --git a/rtl/ps2keyboard.v b/rtl/ps2keyboard.v new file mode 100644 index 0000000..2d3ff37 --- /dev/null +++ b/rtl/ps2keyboard.v @@ -0,0 +1,339 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Description: PS/2 keyboard interface +// +// Author.....: Niels A. Moseley +// Date.......: 28-1-2018 +// + +module ps2keyboard ( + input clk14, // 25MHz clock + input rst, // active high reset + + // I/O interface to keyboard + input key_clk, // clock input from keyboard / device + input key_din, // data input from keyboard / device + + // I/O interface to computer + input cs, // chip select, active high + input address, // =0 RX buffer, =1 RX status + output reg [7:0] dout // 8-bit output bus. +); + + reg [3:0] rxcnt; // count how many bits have been shift into rxshiftbuf + reg [10:0] rxshiftbuf; // 11 bit shift receive register + reg rx_flag = 0; // this flag is 1 when + // valid data is available in rxshiftbuf + + reg [7:0] rx; // scancode receive buffer + + // wire ps2_clkdb; // debounced PS/2 clock signal + reg prev_ps2_clkdb; // previous clock state (in clk14 domain) + + // keyboard translation signals + reg [7:0] ascii; // ASCII code of received character + reg ascii_rdy; // new ASCII character received + reg shift; // state of the shift key + reg [2:0] cur_state; + reg [2:0] next_state; + + // debounce ps2clk_debounce + // ( + // .clk14(clk14), + // .rst(rst), + // .sig_in(key_clk), + // .sig_out(ps2_clkdb) + // ); + + always @(posedge clk14 or posedge rst) + begin + if (rst) + begin + prev_ps2_clkdb <= 1'b0; + rx_flag <= 1'b0; + end + else + begin + rx_flag <= 1'b0; // reset the new data flag register + + // check for negative edge of PS/2 clock + // and sample the state of the PS/2 data line + if ((prev_ps2_clkdb == 1'b1) && (key_clk == 1'b0))//(ps2_clkdb == 1'b0)) + begin + rxshiftbuf <= {key_din, rxshiftbuf[10:1]}; + rxcnt <= rxcnt + 4'b1; + if (rxcnt == 4'd10) + begin + // 10 bits have been shifted in + // we should have a complete + // scan code here, including + // start, parity and stop bits. + rxcnt <= 0; + + // signal new data is present + // note: this signal will only remain high for one + // clock cycle! + // + // TODO: check parity here? + rx_flag <= 1'b1; + end + end + + // update previous clock state + prev_ps2_clkdb <= key_clk;//ps2_clkdb; + + end + end + +// +// IBM Keyboard code page translation +// state machine for US keyboard layout +// +// http://www.computer-engineering.org/ps2keyboard/scancodes2.html +// + + localparam S_KEYNORMAL = 3'b000; + localparam S_KEYF0 = 3'b001; // regular key release state + localparam S_KEYE0 = 3'b010; // extended key state + localparam S_KEYE0F0 = 3'b011; // extended release state + + always @(posedge clk14 or posedge rst) + begin + if (rst) + begin + rx <= 0; + ascii_rdy <= 0; + shift <= 0; + cur_state <= S_KEYNORMAL; + end + else + begin + // handle I/O from CPU + if (cs == 1'b1) + begin + if (address == 1'b0) + begin + // RX buffer address + dout <= {1'b1, ascii[6:0]}; + ascii_rdy <= 1'b0; + end + else + begin + // RX status register + dout <= {ascii_rdy, 7'b0}; + end + end + + // keyboard translation state machine + if (rx_flag == 1'b1) + begin + // latch data from the serial buffer into + // the rx scancode buffer. + rx <= rxshiftbuf[8:1]; + case(cur_state) + S_KEYNORMAL: + begin + if (rx == 8'hF0) + next_state = S_KEYF0; + else if (rx == 8'hE0) + next_state = S_KEYE0; + else + begin + // check the debounce timer, if this is + // not zero, a new key arrived too quickly + // and we simply discard it. For better + // debouncing, we should check if the key + // is actually the same as the previous received/ + // key, but let's try this first to see if it works + // ok... + + //if (debounce_timer == 16'd0) + //begin + ascii_rdy <= 1'b1; // new key has arrived! + //debounce_timer <= 16'hFFFF; // reset the debounce timer + //end + + // check for a SHIFT key + if ((rx == 8'h59) || (rx == 8'h12)) + begin + shift <= 1'b1; + ascii_rdy <= 1'b0; // shift is not a key! + end + else begin + if (!shift) + case(rx) + 8'h1C: ascii <= "A"; + 8'h32: ascii <= "B"; + 8'h21: ascii <= "C"; + 8'h23: ascii <= "D"; + 8'h24: ascii <= "E"; + 8'h2B: ascii <= "F"; + 8'h34: ascii <= "G"; + 8'h33: ascii <= "H"; + 8'h43: ascii <= "I"; + 8'h3B: ascii <= "J"; + 8'h42: ascii <= "K"; + 8'h4B: ascii <= "L"; + 8'h3A: ascii <= "M"; + 8'h31: ascii <= "N"; + 8'h44: ascii <= "O"; + 8'h4D: ascii <= "P"; + 8'h15: ascii <= "Q"; + 8'h2D: ascii <= "R"; + 8'h1B: ascii <= "S"; + 8'h2C: ascii <= "T"; + 8'h3C: ascii <= "U"; + 8'h2A: ascii <= "V"; + 8'h1D: ascii <= "W"; + 8'h22: ascii <= "X"; + 8'h35: ascii <= "Y"; + 8'h1A: ascii <= "Z"; + + 8'h45: ascii <= "0"; + 8'h16: ascii <= "1"; + 8'h1E: ascii <= "2"; + 8'h26: ascii <= "3"; + 8'h25: ascii <= "4"; + 8'h2E: ascii <= "5"; + 8'h36: ascii <= "6"; + 8'h3D: ascii <= "7"; + 8'h3E: ascii <= "8"; + 8'h46: ascii <= "9"; + + 8'h4E: ascii <= "-"; + 8'h55: ascii <= "="; + 8'h5D: ascii <= 8'h34; // backslash + 8'h66: ascii <= 8'd8; // backspace + 8'h29: ascii <= " "; + + 8'h5A: ascii <= 8'd13; // enter + 8'h54: ascii <= "["; + 8'h5B: ascii <= "]"; + 8'h4C: ascii <= ";"; + 8'h52: ascii <= "'"; + 8'h41: ascii <= ","; + 8'h49: ascii <= "."; + 8'h4A: ascii <= "/"; + default: + // unsupported key! + begin + ascii_rdy <= 1'b0; // shift is not a key! + ascii <= " "; + end + endcase + else + // Here, we're in a shifted state + case(rx) + 8'h1C: ascii <= "A"; + 8'h32: ascii <= "B"; + 8'h21: ascii <= "C"; + 8'h23: ascii <= "D"; + 8'h24: ascii <= "E"; + 8'h2B: ascii <= "F"; + 8'h34: ascii <= "G"; + 8'h33: ascii <= "H"; + 8'h43: ascii <= "I"; + 8'h3B: ascii <= "J"; + 8'h42: ascii <= "K"; + 8'h4B: ascii <= "L"; + 8'h3A: ascii <= "M"; + 8'h31: ascii <= "N"; + 8'h44: ascii <= "O"; + 8'h4D: ascii <= "P"; + 8'h15: ascii <= "Q"; + 8'h2D: ascii <= "R"; + 8'h1B: ascii <= "S"; + 8'h2C: ascii <= "T"; + 8'h3C: ascii <= "U"; + 8'h2A: ascii <= "V"; + 8'h1D: ascii <= "W"; + 8'h22: ascii <= "X"; + 8'h35: ascii <= "Y"; + 8'h1A: ascii <= "Z"; + + 8'h45: ascii <= ")"; + 8'h16: ascii <= "!"; + 8'h1E: ascii <= "@"; + 8'h26: ascii <= "#"; + 8'h25: ascii <= "$"; + 8'h2E: ascii <= "%"; + 8'h36: ascii <= "^"; + 8'h3D: ascii <= "&"; + 8'h3E: ascii <= "*"; + 8'h46: ascii <= "("; + + 8'h4E: ascii <= "_"; + 8'h55: ascii <= "+"; + 8'h5D: ascii <= "|"; + 8'h66: ascii <= 8'd8; // backspace + 8'h29: ascii <= " "; + + 8'h5A: ascii <= 8'd13; // enter + 8'h54: ascii <= "{"; + 8'h5B: ascii <= "}"; + 8'h4C: ascii <= ":"; + 8'h52: ascii <= "\""; + 8'h41: ascii <= "<"; + 8'h49: ascii <= ">"; + 8'h4A: ascii <= "?"; + default: + // unsupported key! + begin + ascii_rdy <= 1'b0; // shift is not a key! + ascii <= " "; + end + endcase + end + end + end + S_KEYF0: + // when we end up here, a 0xF0 byte was received + // which usually means a key release event + begin + if ((rx == 8'h59) || (rx == 8'h12)) + shift <= 1'b0; + next_state = S_KEYNORMAL; + end + S_KEYE0: + begin + if (rx == 8'hF0) + next_state = S_KEYE0F0; + else + next_state = S_KEYNORMAL; + end + S_KEYE0F0: + begin + next_state = S_KEYNORMAL; + end + default: + begin + next_state = S_KEYNORMAL; + end + endcase + end + else + begin + next_state = cur_state; // deliberate blocking assingment! + end + + cur_state <= next_state; + end + end + +endmodule + diff --git a/rtl/pwr_reset.v b/rtl/pwr_reset.v new file mode 100644 index 0000000..1d85c14 --- /dev/null +++ b/rtl/pwr_reset.v @@ -0,0 +1,55 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Description: Clock divider to provide clock enables for +// devices. +// +// Author.....: Alan Garfield +// Niels A. Moseley +// Date.......: 29-1-2018 +// + +module pwr_reset( + input clk14, // 14Mhz master clock + input rst_n, // active low synchronous reset + input enable, // clock enable + output rst // active high synchronous system reset + ); + + reg hard_reset; + reg [5:0] reset_cnt; + wire pwr_up_flag = &reset_cnt; + + always @(posedge clk14) + begin + if (rst_n == 1'b0) + begin + reset_cnt <= 6'b0; + hard_reset <= 1'b0; + end + else if (enable) + begin + if (!pwr_up_flag) + reset_cnt <= reset_cnt + 6'b1; + + hard_reset <= pwr_up_flag; + end + end + + assign rst = ~hard_reset; + +endmodule diff --git a/rtl/ram.v b/rtl/ram.v new file mode 100644 index 0000000..b8d9ad9 --- /dev/null +++ b/rtl/ram.v @@ -0,0 +1,45 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Description: 8KB RAM for system +// +// Author.....: Alan Garfield +// Niels A. Moseley +// Date.......: 26-1-2018 +// + +module ram ( + input clk, // clock signal + input [12:0] address, // address bus + input w_en, // active high write enable strobe + input [7:0] din, // 8-bit data bus (input) + output reg [7:0] dout // 8-bit data bus (output) +); + + reg [7:0] ram_data[0:8191]; + + initial + $readmemh("roms/ram.hex", ram_data, 0, 8191); + + always @(posedge clk) + begin + dout <= ram_data[address]; + if (w_en) ram_data[address] <= din; + end + +endmodule + diff --git a/rtl/rom_basic.v b/rtl/rom_basic.v new file mode 100644 index 0000000..dfb2853 --- /dev/null +++ b/rtl/rom_basic.v @@ -0,0 +1,39 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Description: Wrapper for Apple Integer Basic ROM +// +// Author.....: Alan Garfield +// Niels A. Moseley +// Date.......: 26-1-2018 +// + +module rom_basic ( + input clk, // clock signal + input [11:0] address, // address bus + output reg [7:0] dout // 8-bit data bus (output) +); + + reg [7:0] rom_data[0:4095]; + + initial + $readmemh("roms/basic.hex", rom_data, 0, 4095); + + always @(posedge clk) + dout <= rom_data[address]; + +endmodule diff --git a/rtl/rom_wozmon.v b/rtl/rom_wozmon.v new file mode 100644 index 0000000..71d9093 --- /dev/null +++ b/rtl/rom_wozmon.v @@ -0,0 +1,41 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Description: Wrapper for the Woz Mon ROM +// +// Author.....: Alan Garfield +// Niels A. Moseley +// Date.......: 26-1-2018 +// + +module rom_wozmon ( + input clk, // clock signal + input [7:0] address, // address bus + output reg [7:0] dout // 8-bit data bus (output) +); + + reg [7:0] rom_data[0:255]; + + initial + $readmemh("roms/wozmon.hex", rom_data, 0, 255); + + always @(posedge clk) + dout <= rom_data[address]; + +endmodule + + diff --git a/rtl/roms/basic.hex b/rtl/roms/basic.hex new file mode 100644 index 0000000..4017852 --- /dev/null +++ b/rtl/roms/basic.hex @@ -0,0 +1,4096 @@ +4C +B0 +E2 +AD +11 +D0 +10 +FB +AD +10 +D0 +60 +8A +29 +20 +F0 +23 +A9 +A0 +85 +E4 +4C +C9 +E3 +A9 +20 +C5 +24 +B0 +0C +A9 +8D +A0 +07 +20 +C9 +E3 +A9 +A0 +88 +D0 +F8 +A0 +00 +B1 +E2 +E6 +E2 +D0 +02 +E6 +E3 +60 +20 +15 +E7 +20 +76 +E5 +A5 +E2 +C5 +E6 +A5 +E3 +E5 +E7 +B0 +EF +20 +6D +E0 +4C +3B +E0 +A5 +CA +85 +E2 +A5 +CB +85 +E3 +A5 +4C +85 +E6 +A5 +4D +85 +E7 +D0 +DE +20 +15 +E7 +20 +6D +E5 +A5 +E4 +85 +E2 +A5 +E5 +85 +E3 +B0 +C7 +86 +D8 +A9 +A0 +85 +FA +20 +2A +E0 +98 +85 +E4 +20 +2A +E0 +AA +20 +2A +E0 +20 +1B +E5 +20 +18 +E0 +84 +FA +AA +10 +18 +0A +10 +E9 +A5 +E4 +D0 +03 +20 +11 +E0 +8A +20 +C9 +E3 +A9 +25 +20 +1A +E0 +AA +30 +F5 +85 +E4 +C9 +01 +D0 +05 +A6 +D8 +4C +CD +E3 +48 +84 +CE +A2 +ED +86 +CF +C9 +51 +90 +04 +C6 +CF +E9 +50 +48 +B1 +CE +AA +88 +B1 +CE +10 +FA +E0 +C0 +B0 +04 +E0 +00 +30 +F2 +AA +68 +E9 +01 +D0 +E9 +24 +E4 +30 +03 +20 +F8 +EF +B1 +CE +10 +10 +AA +29 +3F +85 +E4 +18 +69 +A0 +20 +C9 +E3 +88 +E0 +C0 +90 +EC +20 +0C +E0 +68 +C9 +5D +F0 +A4 +C9 +28 +D0 +8A +F0 +9E +20 +18 +E1 +95 +50 +D5 +78 +90 +11 +A0 +2B +4C +E0 +E3 +20 +34 +EE +D5 +50 +90 +F4 +20 +E4 +EF +95 +78 +4C +23 +E8 +20 +34 +EE +F0 +E7 +38 +E9 +01 +60 +20 +18 +E1 +95 +50 +18 +F5 +78 +4C +02 +E1 +A0 +14 +D0 +D6 +20 +18 +E1 +E8 +B5 +50 +85 +DA +65 +CE +48 +A8 +B5 +78 +85 +DB +65 +CF +48 +C4 +CA +E5 +CB +B0 +E3 +A5 +DA +69 +FE +85 +DA +A9 +FF +A8 +65 +DB +85 +DB +C8 +B1 +DA +D9 +CC +00 +D0 +0F +98 +F0 +F5 +68 +91 +DA +99 +CC +00 +88 +10 +F7 +E8 +60 +EA +A0 +80 +D0 +95 +A9 +00 +20 +0A +E7 +A0 +02 +94 +78 +20 +0A +E7 +A9 +BF +20 +C9 +E3 +A0 +00 +20 +9E +E2 +94 +78 +EA +EA +EA +B5 +51 +85 +CE +B5 +79 +85 +CF +E8 +E8 +20 +BC +E1 +B5 +4E +D5 +76 +B0 +15 +F6 +4E +A8 +B1 +CE +B4 +50 +C4 +E4 +90 +04 +A0 +83 +D0 +C1 +91 +DA +F6 +50 +90 +E5 +B4 +50 +8A +91 +DA +E8 +E8 +60 +B5 +51 +85 +DA +38 +E9 +02 +85 +E4 +B5 +79 +85 +DB +E9 +00 +85 +E5 +A0 +00 +B1 +E4 +18 +E5 +DA +85 +E4 +60 +B5 +53 +85 +CE +B5 +7B +85 +CF +B5 +51 +85 +DA +B5 +79 +85 +DB +E8 +E8 +E8 +A0 +00 +94 +78 +94 +A0 +C8 +94 +50 +B5 +4D +D5 +75 +08 +48 +B5 +4F +D5 +77 +90 +07 +68 +28 +B0 +02 +56 +50 +60 +A8 +B1 +CE +85 +E4 +68 +A8 +28 +B0 +F3 +B1 +DA +C5 +E4 +D0 +ED +F6 +4F +F6 +4D +B0 +D7 +20 +D7 +E1 +4C +36 +E7 +20 +54 +E2 +06 +CE +26 +CF +90 +0D +18 +A5 +E6 +65 +DA +85 +E6 +A5 +E7 +65 +DB +85 +E7 +88 +F0 +09 +06 +E6 +26 +E7 +10 +E4 +4C +7E +E7 +A5 +E6 +20 +08 +E7 +A5 +E7 +95 +A0 +06 +E5 +90 +28 +4C +6F +E7 +A9 +55 +85 +E5 +20 +5B +E2 +A5 +CE +85 +DA +A5 +CF +85 +DB +20 +15 +E7 +84 +E6 +84 +E7 +A5 +CF +10 +09 +CA +06 +E5 +20 +6F +E7 +20 +15 +E7 +A0 +10 +60 +20 +6C +EE +F0 +C5 +FF +C9 +84 +D0 +02 +46 +F8 +C9 +DF +F0 +11 +C9 +9B +F0 +06 +99 +00 +02 +C8 +10 +0A +A0 +8B +20 +C4 +E3 +A0 +01 +88 +30 +F6 +20 +03 +E0 +EA +EA +20 +C9 +E3 +C9 +8D +D0 +D6 +A9 +DF +99 +00 +02 +60 +20 +D3 +EF +20 +CD +E3 +46 +D9 +A9 +BE +20 +C9 +E3 +A0 +00 +84 +FA +24 +F8 +10 +0C +A6 +F6 +A5 +F7 +20 +1B +E5 +A9 +A0 +20 +C9 +E3 +A2 +FF +9A +20 +9E +E2 +84 +F1 +8A +85 +C8 +A2 +20 +20 +91 +E4 +A5 +C8 +69 +00 +85 +E0 +A9 +00 +AA +69 +02 +85 +E1 +A1 +E0 +29 +F0 +C9 +B0 +F0 +03 +4C +83 +E8 +A0 +02 +B1 +E0 +99 +CD +00 +88 +D0 +F8 +20 +8A +E3 +A5 +F1 +E5 +C8 +C9 +04 +F0 +A8 +91 +E0 +A5 +CA +F1 +E0 +85 +E4 +A5 +CB +E9 +00 +85 +E5 +A5 +E4 +C5 +CC +A5 +E5 +E5 +CD +90 +45 +A5 +CA +F1 +E0 +85 +E6 +A5 +CB +E9 +00 +85 +E7 +B1 +CA +91 +E6 +E6 +CA +D0 +02 +E6 +CB +A5 +E2 +C5 +CA +A5 +E3 +E5 +CB +B0 +E0 +B5 +E4 +95 +CA +CA +10 +F9 +B1 +E0 +A8 +88 +B1 +E0 +91 +E6 +98 +D0 +F8 +24 +F8 +10 +09 +B5 +F7 +75 +F5 +95 +F7 +E8 +F0 +F7 +10 +7E +00 +00 +00 +00 +A0 +14 +D0 +71 +20 +15 +E7 +A5 +E2 +85 +E6 +A5 +E3 +85 +E7 +20 +75 +E5 +A5 +E2 +85 +E4 +A5 +E3 +85 +E5 +D0 +0E +20 +15 +E7 +20 +6D +E5 +A5 +E6 +85 +E2 +A5 +E7 +85 +E3 +A0 +00 +A5 +CA +C5 +E4 +A5 +CB +E5 +E5 +B0 +16 +A5 +E4 +D0 +02 +C6 +E5 +C6 +E4 +A5 +E6 +D0 +02 +C6 +E7 +C6 +E6 +B1 +E4 +91 +E6 +90 +E0 +A5 +E6 +85 +CA +A5 +E7 +85 +CB +60 +20 +C9 +E3 +C8 +B9 +00 +EB +30 +F7 +C9 +8D +D0 +06 +A9 +00 +85 +24 +A9 +8D +E6 +24 +2C +12 +D0 +30 +FB +8D +12 +D0 +60 +A0 +06 +20 +D3 +EE +24 +D9 +30 +03 +4C +B6 +E2 +4C +9A +EB +2A +69 +A0 +DD +00 +02 +D0 +53 +B1 +FE +0A +30 +06 +88 +B1 +FE +30 +29 +C8 +86 +C8 +98 +48 +A2 +00 +A1 +FE +AA +4A +49 +48 +11 +FE +C9 +C0 +90 +01 +E8 +C8 +D0 +F3 +68 +A8 +8A +4C +C0 +E4 +E6 +F1 +A6 +F1 +F0 +BC +9D +00 +02 +60 +A6 +C8 +A9 +A0 +E8 +DD +00 +02 +B0 +FA +B1 +FE +29 +3F +4A +D0 +B6 +BD +00 +02 +B0 +06 +69 +3F +C9 +1A +90 +6F +69 +4F +C9 +0A +90 +69 +A6 +FD +C8 +B1 +FE +29 +E0 +C9 +20 +F0 +7A +B5 +A8 +85 +C8 +B5 +D1 +85 +F1 +88 +B1 +FE +0A +10 +FA +88 +B0 +38 +0A +30 +35 +B4 +58 +84 +FF +B4 +80 +E8 +10 +DA +F0 +B3 +C9 +7E +B0 +22 +CA +10 +04 +A0 +06 +10 +29 +94 +80 +A4 +FF +94 +58 +A4 +C8 +94 +A8 +A4 +F1 +94 +D1 +29 +1F +A8 +B9 +20 +EC +0A +A8 +A9 +76 +2A +85 +FF +D0 +01 +C8 +C8 +86 +FD +B1 +FE +30 +84 +D0 +05 +A0 +0E +4C +E0 +E3 +C9 +03 +B0 +C3 +4A +A6 +C8 +E8 +BD +00 +02 +90 +04 +C9 +A2 +F0 +0A +C9 +DF +F0 +06 +86 +C8 +20 +1C +E4 +C8 +88 +A6 +FD +B1 +FE +88 +0A +10 +CF +B4 +58 +84 +FF +B4 +80 +E8 +B1 +FE +29 +9F +D0 +ED +85 +F2 +85 +F3 +98 +48 +86 +FD +B4 +D0 +84 +C9 +18 +A9 +0A +85 +F9 +A2 +00 +C8 +B9 +00 +02 +29 +0F +65 +F2 +48 +8A +65 +F3 +30 +1C +AA +68 +C6 +F9 +D0 +F2 +85 +F2 +86 +F3 +C4 +F1 +D0 +DE +A4 +C9 +C8 +84 +F1 +20 +1C +E4 +68 +A8 +A5 +F3 +B0 +A9 +A0 +00 +10 +8B +85 +F3 +86 +F2 +A2 +04 +86 +C9 +A9 +B0 +85 +F9 +A5 +F2 +DD +63 +E5 +A5 +F3 +FD +68 +E5 +90 +0D +85 +F3 +A5 +F2 +FD +63 +E5 +85 +F2 +E6 +F9 +D0 +E7 +A5 +F9 +E8 +CA +F0 +0E +C9 +B0 +F0 +02 +85 +C9 +24 +C9 +30 +04 +A5 +FA +F0 +0B +20 +C9 +E3 +24 +F8 +10 +04 +99 +00 +02 +C8 +CA +10 +C1 +60 +01 +0A +64 +E8 +10 +00 +00 +00 +03 +27 +A5 +CA +85 +E6 +A5 +CB +85 +E7 +E8 +A5 +E7 +85 +E5 +A5 +E6 +85 +E4 +C5 +4C +A5 +E5 +E5 +4D +B0 +26 +A0 +01 +B1 +E4 +E5 +CE +C8 +B1 +E4 +E5 +CF +B0 +19 +A0 +00 +A5 +E6 +71 +E4 +85 +E6 +90 +03 +E6 +E7 +18 +C8 +A5 +CE +F1 +E4 +C8 +A5 +CF +F1 +E4 +B0 +CA +60 +46 +F8 +A5 +4C +85 +CA +A5 +4D +85 +CB +A5 +4A +85 +CC +A5 +4B +85 +CD +A9 +00 +85 +FB +85 +FC +85 +FE +A9 +00 +85 +1D +60 +A5 +D0 +69 +05 +85 +D2 +A5 +D1 +69 +00 +85 +D3 +A5 +D2 +C5 +CA +A5 +D3 +E5 +CB +90 +03 +4C +6B +E3 +A5 +CE +91 +D0 +A5 +CF +C8 +91 +D0 +A5 +D2 +C8 +91 +D0 +A5 +D3 +C8 +91 +D0 +A9 +00 +C8 +91 +D0 +C8 +91 +D0 +A5 +D2 +85 +CC +A5 +D3 +85 +CD +A5 +D0 +90 +43 +85 +CE +84 +CF +20 +FF +E6 +30 +0E +C9 +40 +F0 +0A +4C +28 +E6 +06 +C9 +49 +D0 +07 +A9 +49 +85 +CF +20 +FF +E6 +A5 +4B +85 +D1 +A5 +4A +85 +D0 +C5 +CC +A5 +D1 +E5 +CD +B0 +94 +B1 +D0 +C8 +C5 +CE +D0 +06 +B1 +D0 +C5 +CF +F0 +0E +C8 +B1 +D0 +48 +C8 +B1 +D0 +85 +D1 +68 +A0 +00 +F0 +DB +A5 +D0 +69 +03 +20 +0A +E7 +A5 +D1 +69 +00 +95 +78 +A5 +CF +C9 +40 +D0 +1C +88 +98 +20 +0A +E7 +88 +94 +78 +A0 +03 +F6 +78 +C8 +B1 +D0 +30 +F9 +10 +09 +A9 +00 +85 +D4 +85 +D5 +A2 +20 +48 +A0 +00 +B1 +E0 +10 +18 +0A +30 +81 +20 +FF +E6 +20 +08 +E7 +20 +FF +E6 +95 +A0 +24 +D4 +10 +01 +CA +20 +FF +E6 +B0 +E6 +C9 +28 +D0 +1F +A5 +E0 +20 +0A +E7 +A5 +E1 +95 +78 +24 +D4 +30 +0B +A9 +01 +20 +0A +E7 +A9 +00 +95 +78 +F6 +78 +20 +FF +E6 +30 +F9 +B0 +D3 +24 +D4 +10 +06 +C9 +04 +B0 +D0 +46 +D4 +A8 +85 +D6 +B9 +98 +E9 +29 +55 +0A +85 +D7 +68 +A8 +B9 +98 +E9 +29 +AA +C5 +D7 +B0 +09 +98 +48 +20 +FF +E6 +A5 +D6 +90 +95 +B9 +10 +EA +85 +CE +B9 +88 +EA +85 +CF +20 +FC +E6 +4C +D8 +E6 +6C +CE +00 +E6 +E0 +D0 +02 +E6 +E1 +B1 +E0 +60 +94 +77 +CA +30 +03 +95 +50 +60 +A0 +66 +4C +E0 +E3 +A0 +00 +B5 +50 +85 +CE +B5 +A0 +85 +CF +B5 +78 +F0 +0E +85 +CF +B1 +CE +48 +C8 +B1 +CE +85 +CF +68 +85 +CE +88 +E8 +60 +20 +4A +E7 +20 +15 +E7 +98 +20 +08 +E7 +95 +A0 +C5 +CE +D0 +06 +C5 +CF +D0 +02 +F6 +50 +60 +20 +82 +E7 +20 +59 +E7 +20 +15 +E7 +24 +CF +30 +1B +CA +60 +20 +15 +E7 +A5 +CF +D0 +04 +A5 +CE +F0 +F3 +A9 +FF +20 +08 +E7 +95 +A0 +24 +CF +30 +E9 +20 +15 +E7 +98 +38 +E5 +CE +20 +08 +E7 +98 +E5 +CF +50 +23 +A0 +00 +10 +90 +20 +6F +E7 +20 +15 +E7 +A5 +CE +85 +DA +A5 +CF +85 +DB +20 +15 +E7 +18 +A5 +CE +65 +DA +20 +08 +E7 +A5 +CF +65 +DB +70 +DD +95 +A0 +60 +20 +15 +E7 +A4 +CE +F0 +05 +88 +A5 +CF +F0 +0C +60 +A5 +24 +09 +07 +A8 +C8 +A9 +A0 +20 +C9 +E3 +C4 +24 +B0 +F7 +60 +20 +B1 +E7 +20 +15 +E7 +A5 +CF +10 +0A +A9 +AD +20 +C9 +E3 +20 +72 +E7 +50 +EF +88 +84 +D5 +86 +CF +A6 +CE +20 +1B +E5 +A6 +CF +60 +20 +15 +E7 +A5 +CE +85 +F6 +A5 +CF +85 +F7 +88 +84 +F8 +C8 +A9 +0A +85 +F4 +84 +F5 +60 +20 +15 +E7 +A5 +CE +A4 +CF +10 +F2 +20 +15 +E7 +B5 +50 +85 +DA +B5 +78 +85 +DB +A5 +CE +91 +DA +C8 +A5 +CF +91 +DA +E8 +60 +68 +68 +24 +D5 +10 +05 +20 +CD +E3 +46 +D5 +60 +A0 +FF +84 +D7 +60 +20 +CD +EF +F0 +07 +A9 +25 +85 +D6 +88 +84 +D4 +E8 +60 +A5 +CA +A4 +CB +D0 +5A +A0 +41 +A5 +FC +C9 +08 +B0 +5E +A8 +E6 +FC +A5 +E0 +99 +00 +01 +A5 +E1 +99 +08 +01 +A5 +DC +99 +10 +01 +A5 +DD +99 +18 +01 +20 +15 +E7 +20 +6D +E5 +90 +04 +A0 +37 +D0 +3B +A5 +E4 +A4 +E5 +85 +DC +84 +DD +2C +11 +D0 +30 +4F +18 +69 +03 +90 +01 +C8 +A2 +FF +86 +D9 +9A +85 +E0 +84 +E1 +20 +79 +E6 +24 +D9 +10 +49 +18 +A0 +00 +A5 +DC +71 +DC +A4 +DD +90 +01 +C8 +C5 +4C +D0 +D1 +C4 +4D +D0 +CD +A0 +34 +46 +D9 +4C +E0 +E3 +A0 +4A +A5 +FC +F0 +F7 +C6 +FC +A8 +B9 +0F +01 +85 +DC +B9 +17 +01 +85 +DD +BE +FF +00 +B9 +07 +01 +A8 +8A +4C +7A +E8 +A0 +63 +20 +C4 +E3 +A0 +01 +B1 +DC +AA +C8 +B1 +DC +20 +1B +E5 +4C +B3 +E2 +C6 +FB +A0 +5B +A5 +FB +F0 +C4 +A8 +B5 +50 +D9 +1F +01 +D0 +F0 +B5 +78 +D9 +27 +01 +D0 +E9 +B9 +2F +01 +85 +DA +B9 +37 +01 +85 +DB +20 +15 +E7 +CA +20 +93 +E7 +20 +01 +E8 +CA +A4 +FB +B9 +67 +01 +95 +9F +B9 +5F +01 +A0 +00 +20 +08 +E7 +20 +82 +E7 +20 +59 +E7 +20 +15 +E7 +A4 +FB +A5 +CE +F0 +05 +59 +37 +01 +10 +12 +B9 +3F +01 +85 +DC +B9 +47 +01 +85 +DD +BE +4F +01 +B9 +57 +01 +D0 +87 +C6 +FB +60 +A0 +54 +A5 +FB +C9 +08 +F0 +9A +E6 +FB +A8 +B5 +50 +99 +20 +01 +B5 +78 +99 +28 +01 +60 +20 +15 +E7 +A4 +FB +A5 +CE +99 +5F +01 +A5 +CF +99 +67 +01 +A9 +01 +99 +2F +01 +A9 +00 +99 +37 +01 +A5 +DC +99 +3F +01 +A5 +DD +99 +47 +01 +A5 +E0 +99 +4F +01 +A5 +E1 +99 +57 +01 +60 +20 +15 +E7 +A4 +FB +A5 +CE +99 +2F +01 +A5 +CF +4C +66 +E9 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +AB +03 +03 +03 +03 +03 +03 +03 +03 +03 +03 +03 +03 +03 +03 +3F +3F +C0 +C0 +3C +3C +3C +3C +3C +3C +3C +30 +0F +C0 +CC +FF +55 +00 +AB +AB +03 +03 +FF +FF +55 +FF +FF +55 +CF +CF +CF +CF +CF +FF +55 +C3 +C3 +C3 +55 +F0 +F0 +CF +56 +56 +56 +55 +FF +FF +55 +03 +03 +03 +03 +03 +03 +03 +FF +FF +FF +03 +03 +03 +03 +03 +03 +03 +03 +03 +03 +03 +03 +03 +03 +03 +03 +00 +AB +03 +57 +03 +03 +03 +03 +07 +03 +03 +03 +03 +03 +03 +03 +03 +03 +03 +03 +03 +AA +FF +FF +FF +FF +FF +17 +FF +FF +19 +5D +35 +4B +F2 +EC +87 +6F +AD +B7 +E2 +F8 +54 +80 +96 +85 +82 +22 +10 +33 +4A +13 +06 +0B +4A +01 +40 +47 +7A +00 +FF +23 +09 +5B +16 +B6 +CB +FF +FF +FB +FF +FF +24 +F6 +4E +59 +50 +00 +FF +23 +A3 +6F +36 +23 +D7 +1C +22 +C2 +AE +BA +23 +FF +FF +21 +30 +1E +03 +C4 +20 +00 +C1 +FF +FF +FF +A0 +30 +1E +A4 +D3 +B6 +BC +AA +3A +01 +50 +7E +D8 +D8 +A5 +3C +FF +16 +5B +28 +03 +C4 +1D +00 +0C +4E +00 +3E +00 +A6 +B0 +00 +BC +C6 +57 +8C +01 +27 +FF +FF +FF +FF +FF +E8 +FF +FF +E8 +E0 +E0 +E0 +EF +EF +E3 +E3 +E5 +E5 +E7 +E7 +EE +EF +EF +E7 +E7 +E2 +EF +E7 +E7 +EC +EC +EC +E7 +EC +EC +EC +E2 +00 +FF +E8 +E1 +E8 +E8 +EF +EB +FF +FF +E0 +FF +FF +EF +EE +EF +E7 +E7 +00 +FF +E8 +E7 +E7 +E7 +E8 +E1 +E2 +EE +EE +EE +EE +E8 +FF +FF +E1 +E1 +EF +EE +E7 +E8 +EE +E7 +FF +FF +FF +EE +E1 +EF +E7 +E8 +EF +EF +EB +E9 +E8 +E9 +E9 +E8 +E8 +E8 +E8 +FF +E8 +E8 +E8 +EE +E7 +E8 +EF +EF +EE +EF +EE +EF +EE +EE +EF +EE +EE +EE +E1 +E8 +E8 +FF +FF +FF +FF +FF +BE +B3 +B2 +B7 +B6 +37 +D4 +CF +CF +A0 +CC +CF +CE +47 +D3 +D9 +CE +D4 +C1 +58 +CD +C5 +CD +A0 +C6 +D5 +CC +4C +D4 +CF +CF +A0 +CD +C1 +CE +D9 +A0 +D0 +C1 +D2 +C5 +CE +53 +D3 +D4 +D2 +C9 +CE +47 +CE +CF +A0 +C5 +CE +44 +C2 +C1 +C4 +A0 +C2 +D2 +C1 +CE +C3 +48 +BE +B8 +A0 +C7 +CF +D3 +D5 +C2 +53 +C2 +C1 +C4 +A0 +D2 +C5 +D4 +D5 +D2 +4E +BE +B8 +A0 +C6 +CF +D2 +53 +C2 +C1 +C4 +A0 +CE +C5 +D8 +54 +D3 +D4 +CF +D0 +D0 +C5 +C4 +A0 +C1 +D4 +20 +AA +AA +AA +20 +A0 +C5 +D2 +D2 +0D +BE +B2 +B5 +35 +D2 +C1 +CE +C7 +45 +C4 +C9 +4D +D3 +D4 +D2 +A0 +CF +D6 +C6 +4C +DC +0D +D2 +C5 +D4 +D9 +D0 +C5 +A0 +CC +C9 +CE +C5 +8D +3F +46 +D9 +90 +03 +4C +C3 +E8 +A6 +CF +9A +A6 +CE +A0 +8D +D0 +02 +A0 +99 +20 +C4 +E3 +86 +CE +BA +86 +CF +A0 +FE +84 +D9 +C8 +84 +C8 +20 +99 +E2 +84 +F1 +A2 +20 +A9 +30 +20 +91 +E4 +E6 +D9 +A6 +CE +A4 +C8 +0A +85 +CE +C8 +B9 +00 +02 +C9 +74 +F0 +D2 +49 +B0 +C9 +0A +B0 +F0 +C8 +C8 +84 +C8 +B9 +00 +02 +48 +B9 +FF +01 +A0 +00 +20 +08 +E7 +68 +95 +A0 +A5 +CE +C9 +C7 +D0 +03 +20 +6F +E7 +4C +01 +E8 +FF +FF +FF +50 +20 +13 +EC +D0 +15 +20 +0B +EC +D0 +10 +20 +82 +E7 +20 +6F +E7 +50 +03 +20 +82 +E7 +20 +59 +E7 +56 +50 +4C +36 +E7 +FF +FF +C1 +FF +7F +D1 +CC +C7 +CF +CE +C5 +9A +98 +8B +96 +95 +93 +BF +B2 +32 +2D +2B +BC +B0 +AC +BE +35 +8E +61 +FF +FF +FF +DD +FB +20 +C9 +EF +15 +4F +10 +05 +20 +C9 +EF +35 +4F +95 +50 +10 +CB +4C +C9 +EF +40 +60 +8D +60 +8B +00 +7E +8C +33 +00 +00 +60 +03 +BF +12 +00 +40 +89 +C9 +47 +9D +17 +68 +9D +0A +00 +40 +60 +8D +60 +8B +00 +7E +8C +3C +00 +00 +60 +03 +BF +1B +4B +67 +B4 +A1 +07 +8C +07 +AE +A9 +AC +A8 +67 +8C +07 +B4 +AF +AC +B0 +67 +9D +B2 +AF +AC +AF +A3 +67 +8C +07 +A5 +AB +AF +B0 +F4 +AE +A9 +B2 +B0 +7F +0E +27 +B4 +AE +A9 +B2 +B0 +7F +0E +28 +B4 +AE +A9 +B2 +B0 +64 +07 +A6 +A9 +67 +AF +B4 +AF +A7 +78 +B4 +A5 +AC +78 +7F +02 +AD +A5 +B2 +67 +A2 +B5 +B3 +AF +A7 +EE +B2 +B5 +B4 +A5 +B2 +7E +8C +39 +B4 +B8 +A5 +AE +67 +B0 +A5 +B4 +B3 +27 +AF +B4 +07 +9D +19 +B2 +AF +A6 +7F +05 +37 +B4 +B5 +B0 +AE +A9 +7F +05 +28 +B4 +B5 +B0 +AE +A9 +7F +05 +2A +B4 +B5 +B0 +AE +A9 +E4 +AE +A5 +00 +FF +FF +47 +A2 +A1 +B4 +7F +0D +30 +AD +A9 +A4 +7F +0D +23 +AD +A9 +A4 +67 +AC +AC +A1 +A3 +00 +40 +80 +C0 +C1 +80 +00 +47 +8C +68 +8C +DB +67 +9B +68 +9B +50 +8C +63 +8C +7F +01 +51 +07 +88 +29 +84 +80 +C4 +80 +57 +71 +07 +88 +14 +ED +A5 +AD +AF +AC +ED +A5 +AD +A9 +A8 +F2 +AF +AC +AF +A3 +71 +08 +88 +AE +A5 +AC +68 +83 +08 +68 +9D +08 +71 +07 +88 +60 +76 +B4 +AF +AE +76 +8D +76 +8B +51 +07 +88 +19 +B8 +A4 +AE +B2 +F2 +B3 +B5 +F3 +A2 +A1 +EE +A7 +B3 +E4 +AE +B2 +EB +A5 +A5 +B0 +51 +07 +88 +39 +81 +C1 +4F +7F +0F +2F +00 +51 +06 +88 +29 +C2 +0C +82 +57 +8C +6A +8C +42 +AE +A5 +A8 +B4 +60 +AE +A5 +A8 +B4 +4F +7E +1E +35 +8C +27 +51 +07 +88 +09 +8B +FE +E4 +AF +AD +F2 +AF +E4 +AE +A1 +DC +DE +9C +DD +9C +DE +DD +9E +C3 +DD +CF +CA +CD +CB +00 +47 +9D +AD +A5 +AD +AF +AC +76 +9D +AD +A5 +AD +A9 +A8 +E6 +A6 +AF +60 +8C +20 +AF +B4 +B5 +A1 +F2 +AC +A3 +F2 +A3 +B3 +60 +8C +20 +AC +A5 +A4 +EE +B5 +B2 +60 +AE +B5 +B2 +F4 +B3 +A9 +AC +60 +8C +20 +B4 +B3 +A9 +AC +7A +7E +9A +22 +20 +00 +60 +03 +BF +60 +03 +BF +1F +20 +B1 +E7 +E8 +E8 +B5 +4F +85 +DA +B5 +77 +85 +DB +B4 +4E +98 +D5 +76 +B0 +09 +B1 +DA +20 +C9 +E3 +C8 +4C +0F +EE +A9 +FF +85 +D5 +60 +E8 +A9 +00 +95 +78 +95 +A0 +B5 +77 +38 +F5 +4F +95 +50 +4C +23 +E8 +FF +20 +15 +E7 +A5 +CF +D0 +28 +A5 +CE +60 +20 +34 +EE +A4 +C8 +C9 +30 +B0 +21 +C0 +28 +B0 +1D +60 +EA +EA +20 +34 +EE +60 +EA +8A +A2 +01 +B4 +CE +94 +4C +B4 +48 +94 +CA +CA +F0 +F5 +AA +60 +A0 +77 +4C +E0 +E3 +A0 +7B +D0 +F9 +20 +54 +E2 +A5 +DA +D0 +07 +A5 +DB +D0 +03 +4C +7E +E7 +06 +CE +26 +CF +26 +E6 +26 +E7 +A5 +E6 +C5 +DA +A5 +E7 +E5 +DB +90 +0A +85 +E7 +A5 +E6 +E5 +DA +85 +E6 +E6 +CE +88 +D0 +E1 +60 +FF +FF +FF +FF +FF +FF +20 +15 +E7 +6C +CE +00 +A5 +4C +D0 +02 +C6 +4D +C6 +4C +A5 +48 +D0 +02 +C6 +49 +C6 +48 +A0 +00 +B1 +4C +91 +48 +A5 +CA +C5 +4C +A5 +CB +E5 +4D +90 +E0 +4C +53 +EE +C9 +28 +B0 +9B +A8 +A5 +C8 +60 +EA +EA +98 +AA +A0 +6E +20 +C4 +E3 +8A +A8 +20 +C4 +E3 +A0 +72 +4C +C4 +E3 +20 +15 +E7 +06 +CE +26 +CF +30 +FA +B0 +DC +D0 +04 +C5 +CE +B0 +D6 +60 +20 +15 +E7 +B1 +CE +94 +9F +4C +08 +E7 +20 +34 +EE +A5 +CE +48 +20 +15 +E7 +68 +91 +CE +60 +FF +FF +FF +20 +6C +EE +A5 +CE +85 +E6 +A5 +CF +85 +E7 +4C +44 +E2 +20 +E4 +EE +4C +34 +E1 +20 +E4 +EE +B4 +78 +B5 +50 +69 +FE +B0 +01 +88 +85 +DA +84 +DB +18 +65 +CE +95 +50 +98 +65 +CF +95 +78 +A0 +00 +B5 +50 +D1 +DA +C8 +B5 +78 +F1 +DA +B0 +80 +4C +23 +E8 +20 +15 +E7 +A5 +4E +20 +08 +E7 +A5 +4F +D0 +04 +C5 +4E +69 +00 +29 +7F +85 +4F +95 +A0 +A0 +11 +A5 +4F +0A +18 +69 +40 +0A +26 +4E +26 +4F +88 +D0 +F2 +A5 +CE +20 +08 +E7 +A5 +CF +95 +A0 +4C +7A +E2 +20 +15 +E7 +A4 +CE +C4 +4C +A5 +CF +E5 +4D +90 +1F +84 +48 +A5 +CF +85 +49 +4C +B6 +EE +20 +15 +E7 +A4 +CE +C4 +CA +A5 +CF +E5 +CB +B0 +09 +84 +4A +A5 +CF +85 +4B +4C +B7 +E5 +4C +CB +EE +EA +EA +EA +EA +20 +C9 +EF +20 +71 +E1 +4C +BF +EF +20 +03 +EE +A9 +FF +85 +C8 +A9 +74 +8D +00 +02 +60 +20 +36 +E7 +E8 +20 +36 +E7 +B5 +50 +60 +A9 +00 +85 +4A +85 +4C +A9 +08 +85 +4B +A9 +10 +85 +4D +4C +AD +E5 +D5 +78 +D0 +01 +18 +4C +02 +E1 +20 +B7 +E5 +4C +36 +E8 +20 +B7 +E5 +4C +5B +E8 +E0 +80 +D0 +01 +88 +4C +0C +E0 diff --git a/rtl/roms/ram.hex b/rtl/roms/ram.hex new file mode 100644 index 0000000..b05d5fe --- /dev/null +++ b/rtl/roms/ram.hex @@ -0,0 +1,8192 @@ +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 diff --git a/rtl/roms/vga_font.bin b/rtl/roms/vga_font.bin new file mode 100644 index 0000000..5df4416 --- /dev/null +++ b/rtl/roms/vga_font.bin @@ -0,0 +1,703 @@ +00000000 +00000000 +00111000 +01000100 +01010100 +01011100 +01011000 +01000000 +00111100 +00000000 + +00000000 +00000000 +00010000 +00101000 +01000100 +01000100 +01111100 +01000100 +01000100 +00000000 + +00000000 +00000000 +01111000 +01000100 +01000100 +01111000 +01000100 +01000100 +01111000 +00000000 + +00000000 +00000000 +00111000 +01000100 +01000000 +01000000 +01000000 +01000100 +00111000 +00000000 + +00000000 +00000000 +01111000 +01000100 +01000100 +01000100 +01000100 +01000100 +01111000 +00000000 + +00000000 +00000000 +01111100 +01000000 +01000000 +01111000 +01000000 +01000000 +01111100 +00000000 + +00000000 +00000000 +01111100 +01000000 +01000000 +01111000 +01000000 +01000000 +01000000 +00000000 + +00000000 +00000000 +00111100 +01000000 +01000000 +01000000 +01001100 +01000100 +00111100 +00000000 + +00000000 +00000000 +01000100 +01000100 +01000100 +01111100 +01000100 +01000100 +01000100 +00000000 + +00000000 +00000000 +00111000 +00010000 +00010000 +00010000 +00010000 +00010000 +00111000 +00000000 + +00000000 +00000000 +00000100 +00000100 +00000100 +00000100 +00000100 +01000100 +00111000 +00000000 + +00000000 +00000000 +01000100 +01001000 +01010000 +01100000 +01010000 +01001000 +01000100 +00000000 + +00000000 +00000000 +01000000 +01000000 +01000000 +01000000 +01000000 +01000000 +01111100 +00000000 + +00000000 +00000000 +01000100 +01101100 +01010100 +01010100 +01000100 +01000100 +01000100 +00000000 + +00000000 +00000000 +01000100 +01000100 +01100100 +01010100 +01001100 +01000100 +01000100 +00000000 + +00000000 +00000000 +00111000 +01000100 +01000100 +01000100 +01000100 +01000100 +00111000 +00000000 + +00000000 +00000000 +01111000 +01000100 +01000100 +01111000 +01000000 +01000000 +01000000 +00000000 + +00000000 +00000000 +00111000 +01000100 +01000100 +01000100 +01010100 +01001000 +00110100 +00000000 + +00000000 +00000000 +01111000 +01000100 +01000100 +01111000 +01010000 +01001000 +01000100 +00000000 + +00000000 +00000000 +00111000 +01000100 +01000000 +00111000 +00000100 +01000100 +00111000 +00000000 + +00000000 +00000000 +01111100 +00010000 +00010000 +00010000 +00010000 +00010000 +00010000 +00000000 + +00000000 +00000000 +01000100 +01000100 +01000100 +01000100 +01000100 +01000100 +00111000 +00000000 + +00000000 +00000000 +01000100 +01000100 +01000100 +01000100 +01000100 +00101000 +00010000 +00000000 + +00000000 +00000000 +01000100 +01000100 +01000100 +01010100 +01010100 +01101100 +01000100 +00000000 + +00000000 +00000000 +01000100 +01000100 +00101000 +00010000 +00101000 +01000100 +01000100 +00000000 + +00000000 +00000000 +01000100 +01000100 +00101000 +00010000 +00010000 +00010000 +00010000 +00000000 + +00000000 +00000000 +01111100 +00000100 +00001000 +00010000 +00100000 +01000000 +01111100 +00000000 + +00000000 +00000000 +01111100 +01100000 +01100000 +01100000 +01100000 +01100000 +01111100 +00000000 + +00000000 +00000000 +00000000 +01000000 +00100000 +00010000 +00001000 +00000100 +00000000 +00000000 + +00000000 +00000000 +01111100 +00001100 +00001100 +00001100 +00001100 +00001100 +01111100 +00000000 + +00000000 +00000000 +00000000 +00000000 +00010000 +00101000 +01000100 +00000000 +00000000 +00000000 + +00000000 +00000000 +00000000 +00000000 +00000000 +00000000 +00000000 +00000000 +01111100 +00000000 + +00000000 +00000000 +00000000 +00000000 +00000000 +00000000 +00000000 +00000000 +00000000 +00000000 + +00000000 +00000000 +00010000 +00010000 +00010000 +00010000 +00010000 +00000000 +00010000 +00000000 + +00000000 +00000000 +00101000 +00101000 +00101000 +00000000 +00000000 +00000000 +00000000 +00000000 + +00000000 +00000000 +00101000 +00101000 +01111100 +00101000 +01111100 +00101000 +00101000 +00000000 + +00000000 +00000000 +00010000 +00111100 +01010000 +00111000 +00010100 +01111000 +00010000 +00000000 + +00000000 +00000000 +01100000 +01100100 +00001000 +00010000 +00100000 +01001100 +00001100 +00000000 + +00000000 +00000000 +00100000 +01010000 +01010000 +00100000 +01010100 +01001000 +00110100 +00000000 + +00000000 +00000000 +00010000 +00010000 +00010000 +00000000 +00000000 +00000000 +00000000 +00000000 + +00000000 +00000000 +00010000 +00100000 +01000000 +01000000 +01000000 +00100000 +00010000 +00000000 + +00000000 +00000000 +00010000 +00001000 +00000100 +00000100 +00000100 +00001000 +00010000 +00000000 + +00000000 +00000000 +00010000 +01010100 +00111000 +00010000 +00111000 +01010100 +00010000 +00000000 + +00000000 +00000000 +00000000 +00010000 +00010000 +01111100 +00010000 +00010000 +00000000 +00000000 + +00000000 +00000000 +00000000 +00000000 +00000000 +00000000 +00010000 +00010000 +00100000 +00000000 + +00000000 +00000000 +00000000 +00000000 +00000000 +01111100 +00000000 +00000000 +00000000 +00000000 + +00000000 +00000000 +00000000 +00000000 +00000000 +00000000 +00000000 +00000000 +00010000 +00000000 + +00000000 +00000000 +00000000 +00000100 +00001000 +00010000 +00100000 +01000000 +00000000 +00000000 + +00000000 +00000000 +00111000 +01000100 +01001100 +01010100 +01100100 +01000100 +00111000 +00000000 + +00000000 +00000000 +00010000 +00110000 +00010000 +00010000 +00010000 +00010000 +01111100 +00000000 + +00000000 +00000000 +00111000 +01000100 +00000100 +00011000 +00100000 +01000000 +01111100 +00000000 + +00000000 +00000000 +01111100 +00000100 +00001000 +00011000 +00000100 +01000100 +00111000 +00000000 + +00000000 +00000000 +00001000 +00011000 +00101000 +01001000 +01111100 +00001000 +00001000 +00000000 + +00000000 +00000000 +01111100 +01000000 +01111000 +00000100 +00000100 +01000100 +00111000 +00000000 + +00000000 +00000000 +00011100 +00100000 +01000000 +01111000 +01000100 +01000100 +00111000 +00000000 + +00000000 +00000000 +01111100 +00000100 +00001000 +00010000 +00100000 +00100000 +00100000 +00000000 + +00000000 +00000000 +00111000 +01000100 +01000100 +00111000 +01000100 +01000100 +00111000 +00000000 + +00000000 +00000000 +00111000 +01000100 +01000100 +00111100 +00000100 +00001000 +01110000 +00000000 + +00000000 +00000000 +00000000 +00000000 +00010000 +00000000 +00010000 +00000000 +00000000 +00000000 + +00000000 +00000000 +00000000 +00000000 +00010000 +00000000 +00010000 +00010000 +00100000 +00000000 + +00000000 +00000000 +00001000 +00010000 +00100000 +01000000 +00100000 +00010000 +00001000 +00000000 + +00000000 +00000000 +00000000 +00000000 +01111100 +00000000 +01111100 +00000000 +00000000 +00000000 + +00000000 +00000000 +00100000 +00010000 +00001000 +00000100 +00001000 +00010000 +00100000 +00000000 + +00000000 +00000000 +00111000 +01000100 +00001000 +00010000 +00010000 +00000000 +00010000 +00000000 diff --git a/rtl/roms/vga_font.hex b/rtl/roms/vga_font.hex new file mode 100644 index 0000000..014a804 --- /dev/null +++ b/rtl/roms/vga_font.hex @@ -0,0 +1,640 @@ +00 +00 +38 +44 +54 +5C +58 +40 +3C +00 +00 +00 +10 +28 +44 +44 +7C +44 +44 +00 +00 +00 +78 +44 +44 +78 +44 +44 +78 +00 +00 +00 +38 +44 +40 +40 +40 +44 +38 +00 +00 +00 +78 +44 +44 +44 +44 +44 +78 +00 +00 +00 +7C +40 +40 +78 +40 +40 +7C +00 +00 +00 +7C +40 +40 +78 +40 +40 +40 +00 +00 +00 +3C +40 +40 +40 +4C +44 +3C +00 +00 +00 +44 +44 +44 +7C +44 +44 +44 +00 +00 +00 +38 +10 +10 +10 +10 +10 +38 +00 +00 +00 +04 +04 +04 +04 +04 +44 +38 +00 +00 +00 +44 +48 +50 +60 +50 +48 +44 +00 +00 +00 +40 +40 +40 +40 +40 +40 +7C +00 +00 +00 +44 +6C +54 +54 +44 +44 +44 +00 +00 +00 +44 +44 +64 +54 +4C +44 +44 +00 +00 +00 +38 +44 +44 +44 +44 +44 +38 +00 +00 +00 +78 +44 +44 +78 +40 +40 +40 +00 +00 +00 +38 +44 +44 +44 +54 +48 +34 +00 +00 +00 +78 +44 +44 +78 +50 +48 +44 +00 +00 +00 +38 +44 +40 +38 +04 +44 +38 +00 +00 +00 +7C +10 +10 +10 +10 +10 +10 +00 +00 +00 +44 +44 +44 +44 +44 +44 +38 +00 +00 +00 +44 +44 +44 +44 +44 +28 +10 +00 +00 +00 +44 +44 +44 +54 +54 +6C +44 +00 +00 +00 +44 +44 +28 +10 +28 +44 +44 +00 +00 +00 +44 +44 +28 +10 +10 +10 +10 +00 +00 +00 +7C +04 +08 +10 +20 +40 +7C +00 +00 +00 +7C +60 +60 +60 +60 +60 +7C +00 +00 +00 +00 +40 +20 +10 +08 +04 +00 +00 +00 +00 +7C +0C +0C +0C +0C +0C +7C +00 +00 +00 +00 +00 +10 +28 +44 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +7C +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +10 +10 +10 +10 +10 +00 +10 +00 +00 +00 +28 +28 +28 +00 +00 +00 +00 +00 +00 +00 +28 +28 +7C +28 +7C +28 +28 +00 +00 +00 +10 +3C +50 +38 +14 +78 +10 +00 +00 +00 +60 +64 +08 +10 +20 +4C +0C +00 +00 +00 +20 +50 +50 +20 +54 +48 +34 +00 +00 +00 +10 +10 +10 +00 +00 +00 +00 +00 +00 +00 +10 +20 +40 +40 +40 +20 +10 +00 +00 +00 +10 +08 +04 +04 +04 +08 +10 +00 +00 +00 +10 +54 +38 +10 +38 +54 +10 +00 +00 +00 +00 +10 +10 +7C +10 +10 +00 +00 +00 +00 +00 +00 +00 +00 +10 +10 +20 +00 +00 +00 +00 +00 +00 +7C +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +10 +00 +00 +00 +00 +04 +08 +10 +20 +40 +00 +00 +00 +00 +38 +44 +4C +54 +64 +44 +38 +00 +00 +00 +10 +30 +10 +10 +10 +10 +7C +00 +00 +00 +38 +44 +04 +18 +20 +40 +7C +00 +00 +00 +7C +04 +08 +18 +04 +44 +38 +00 +00 +00 +08 +18 +28 +48 +7C +08 +08 +00 +00 +00 +7C +40 +78 +04 +04 +44 +38 +00 +00 +00 +1C +20 +40 +78 +44 +44 +38 +00 +00 +00 +7C +04 +08 +10 +20 +20 +20 +00 +00 +00 +38 +44 +44 +38 +44 +44 +38 +00 +00 +00 +38 +44 +44 +3C +04 +08 +70 +00 +00 +00 +00 +00 +10 +00 +10 +00 +00 +00 +00 +00 +00 +00 +10 +00 +10 +10 +20 +00 +00 +00 +08 +10 +20 +40 +20 +10 +08 +00 +00 +00 +00 +00 +7C +00 +7C +00 +00 +00 +00 +00 +20 +10 +08 +04 +08 +10 +20 +00 +00 +00 +38 +44 +08 +10 +10 +00 +10 +00 diff --git a/rtl/roms/vga_font_bitreversed.hex b/rtl/roms/vga_font_bitreversed.hex new file mode 100644 index 0000000..b43c367 --- /dev/null +++ b/rtl/roms/vga_font_bitreversed.hex @@ -0,0 +1,1024 @@ +00 +00 +1C +22 +2A +3A +1A +02 +3C +00 +00 +00 +08 +14 +22 +22 +3E +22 +22 +00 +00 +00 +1E +22 +22 +1E +22 +22 +1E +00 +00 +00 +1C +22 +02 +02 +02 +22 +1C +00 +00 +00 +1E +22 +22 +22 +22 +22 +1E +00 +00 +00 +3E +02 +02 +1E +02 +02 +3E +00 +00 +00 +3E +02 +02 +1E +02 +02 +02 +00 +00 +00 +3C +02 +02 +02 +32 +22 +3C +00 +00 +00 +22 +22 +22 +3E +22 +22 +22 +00 +00 +00 +1C +08 +08 +08 +08 +08 +1C +00 +00 +00 +20 +20 +20 +20 +20 +22 +1C +00 +00 +00 +22 +12 +0A +06 +0A +12 +22 +00 +00 +00 +02 +02 +02 +02 +02 +02 +3E +00 +00 +00 +22 +36 +2A +2A +22 +22 +22 +00 +00 +00 +22 +22 +26 +2A +32 +22 +22 +00 +00 +00 +1C +22 +22 +22 +22 +22 +1C +00 +00 +00 +1E +22 +22 +1E +02 +02 +02 +00 +00 +00 +1C +22 +22 +22 +2A +12 +2C +00 +00 +00 +1E +22 +22 +1E +0A +12 +22 +00 +00 +00 +1C +22 +02 +1C +20 +22 +1C +00 +00 +00 +3E +08 +08 +08 +08 +08 +08 +00 +00 +00 +22 +22 +22 +22 +22 +22 +1C +00 +00 +00 +22 +22 +22 +22 +22 +14 +08 +00 +00 +00 +22 +22 +22 +2A +2A +36 +22 +00 +00 +00 +22 +22 +14 +08 +14 +22 +22 +00 +00 +00 +22 +22 +14 +08 +08 +08 +08 +00 +00 +00 +3E +20 +10 +08 +04 +02 +3E +00 +00 +00 +3E +06 +06 +06 +06 +06 +3E +00 +00 +00 +00 +02 +04 +08 +10 +20 +00 +00 +00 +00 +3E +30 +30 +30 +30 +30 +3E +00 +00 +00 +00 +00 +08 +14 +22 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +3E +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +08 +08 +08 +08 +08 +00 +08 +00 +00 +00 +14 +14 +14 +00 +00 +00 +00 +00 +00 +00 +14 +14 +3E +14 +3E +14 +14 +00 +00 +00 +08 +3C +0A +1C +28 +1E +08 +00 +00 +00 +06 +26 +10 +08 +04 +32 +30 +00 +00 +00 +04 +0A +0A +04 +2A +12 +2C +00 +00 +00 +08 +08 +08 +00 +00 +00 +00 +00 +00 +00 +08 +04 +02 +02 +02 +04 +08 +00 +00 +00 +08 +10 +20 +20 +20 +10 +08 +00 +00 +00 +08 +2A +1C +08 +1C +2A +08 +00 +00 +00 +00 +08 +08 +3E +08 +08 +00 +00 +00 +00 +00 +00 +00 +00 +08 +08 +04 +00 +00 +00 +00 +00 +00 +3E +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +08 +00 +00 +00 +00 +20 +10 +08 +04 +02 +00 +00 +00 +00 +1C +22 +32 +2A +26 +22 +1C +00 +00 +00 +08 +0C +08 +08 +08 +08 +3E +00 +00 +00 +1C +22 +20 +18 +04 +02 +3E +00 +00 +00 +3E +20 +10 +18 +20 +22 +1C +00 +00 +00 +10 +18 +14 +12 +3E +10 +10 +00 +00 +00 +3E +02 +1E +20 +20 +22 +1C +00 +00 +00 +38 +04 +02 +1E +22 +22 +1C +00 +00 +00 +3E +20 +10 +08 +04 +04 +04 +00 +00 +00 +1C +22 +22 +1C +22 +22 +1C +00 +00 +00 +1C +22 +22 +3C +20 +10 +0E +00 +00 +00 +00 +00 +08 +00 +08 +00 +00 +00 +00 +00 +00 +00 +08 +00 +08 +08 +04 +00 +00 +00 +10 +08 +04 +02 +04 +08 +10 +00 +00 +00 +00 +00 +3E +00 +3E +00 +00 +00 +00 +00 +04 +08 +10 +20 +10 +08 +04 +00 +00 +00 +1C +22 +10 +08 +08 +00 +08 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 diff --git a/rtl/roms/vga_vram.bin b/rtl/roms/vga_vram.bin new file mode 100644 index 0000000..eace898 --- /dev/null +++ b/rtl/roms/vga_vram.bin @@ -0,0 +1,2048 @@ +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 +100000 diff --git a/rtl/roms/wozmon.hex b/rtl/roms/wozmon.hex new file mode 100644 index 0000000..70a749c --- /dev/null +++ b/rtl/roms/wozmon.hex @@ -0,0 +1,256 @@ +D8 +58 +A0 +7F +8C +12 +D0 +A9 +A7 +8D +11 +D0 +8D +13 +D0 +C9 +DF +F0 +13 +C9 +9B +F0 +03 +C8 +10 +0F +A9 +DC +20 +EF +FF +A9 +8D +20 +EF +FF +A0 +01 +88 +30 +F6 +AD +11 +D0 +10 +FB +AD +10 +D0 +99 +00 +02 +20 +EF +FF +C9 +8D +D0 +D4 +A0 +FF +A9 +00 +AA +0A +85 +2B +C8 +B9 +00 +02 +C9 +8D +F0 +D4 +C9 +AE +90 +F4 +F0 +F0 +C9 +BA +F0 +EB +C9 +D2 +F0 +3B +86 +28 +86 +29 +84 +2A +B9 +00 +02 +49 +B0 +C9 +0A +90 +06 +69 +88 +C9 +FA +90 +11 +0A +0A +0A +0A +A2 +04 +0A +26 +28 +26 +29 +CA +D0 +F8 +C8 +D0 +E0 +C4 +2A +F0 +97 +24 +2B +50 +10 +A5 +28 +81 +26 +E6 +26 +D0 +B5 +E6 +27 +4C +44 +FF +6C +24 +00 +30 +2B +A2 +02 +B5 +27 +95 +25 +95 +23 +CA +D0 +F7 +D0 +14 +A9 +8D +20 +EF +FF +A5 +25 +20 +DC +FF +A5 +24 +20 +DC +FF +A9 +BA +20 +EF +FF +A9 +A0 +20 +EF +FF +A1 +24 +20 +DC +FF +86 +2B +A5 +24 +C5 +28 +A5 +25 +E5 +29 +B0 +C1 +E6 +24 +D0 +02 +E6 +25 +A5 +24 +29 +07 +10 +C8 +48 +4A +4A +4A +4A +20 +E5 +FF +68 +29 +0F +09 +B0 +C9 +BA +90 +02 +69 +06 +2C +12 +D0 +30 +FB +8D +12 +D0 +60 +00 +00 +00 +0F +00 +FF +00 +00 \ No newline at end of file diff --git a/rtl/uart.v b/rtl/uart.v new file mode 100644 index 0000000..07e13e3 --- /dev/null +++ b/rtl/uart.v @@ -0,0 +1,169 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Description: A wrapper for the basic UART from fpga4fun.com +// +// Author.....: Alan Garfield +// Niels A. Moseley +// Date.......: 26-1-2018 +// + +module uart( + input clk, // clock signal + input enable, // clock enable strobe + input rst, // active high reset signal + input [1:0] address, // address bus + input w_en, // active high write enable strobe + input [7:0] din, // 8-bit data bus (input) + output reg [7:0] dout, // 8-bit data bus (output) + + input uart_rx, // asynchronous serial data input from computer + output uart_tx, // asynchronous serial data output to computer + output uart_cts // clear to send flag to computer + ); + + parameter ClkFrequency = 25000000; // 25MHz + parameter Baud = 115200; + parameter Oversampling = 16; + + reg uart_tx_stb, uart_tx_init; + reg [7:0] uart_tx_byte; + wire uart_tx_status; + + async_transmitter #(ClkFrequency, Baud) my_tx ( + .clk(clk), + .rst(rst), + .TxD_start(uart_tx_stb), + .TxD_data(uart_tx_byte), + .TxD(uart_tx), + .TxD_busy(uart_tx_status) + ); + + wire uart_rx_stb, rx_idle, rx_end; + wire [7:0] rx_data; + reg uart_rx_status, uart_rx_ack; + reg [7:0] uart_rx_byte; + + async_receiver #(ClkFrequency, Baud, Oversampling) my_rx( + .clk(clk), + .rst(rst), + .RxD(uart_rx), + .RxD_data_ready(uart_rx_stb), + .RxD_data(rx_data), + .RxD_idle(rx_idle), + .RxD_endofpacket(rx_end) + ); + + always @(posedge clk or posedge rst) + begin + if (rst) + begin + uart_rx_status <= 'b0; + uart_rx_byte <= 8'd0; + end + else + begin + // new byte from RX, check register is clear and CPU has seen + // previous byte, otherwise we ignore the new data + if (uart_rx_stb && ~uart_rx_status) + begin + uart_rx_status <= 'b1; + uart_rx_byte <= rx_data; + end + + // clear the rx status flag on ack from CPU + if (uart_rx_ack) + uart_rx_status <= 'b0; + end + end + + assign uart_cts = ~rx_idle || uart_rx_status; + + localparam UART_RX = 2'b00; + localparam UART_RXCR = 2'b01; + localparam UART_TX = 2'b10; + localparam UART_TXCR = 2'b11; + + // Handle Register + always @(posedge clk or posedge rst) + begin + if (rst) + begin + dout <= 8'd0; + + uart_tx_init <= 0; // flag to ignore the DDR setup from Wozmon PIA call + uart_tx_stb <= 0; + uart_tx_byte <= 8'd0; + uart_rx_ack <= 0; + end + else + begin + uart_tx_stb <= 0; + uart_rx_ack <= 0; + + case (address) + + UART_RX: + begin + // UART RX - 0xD010 + // Bit b7 of KBD is permanently tied to high + dout <= {1'b1, uart_rx_byte[6:0]}; + if (~w_en && ~uart_rx_ack && uart_rx_status && enable) + uart_rx_ack <= 1'b1; + end + + UART_RXCR: + begin + // UART RX CR - 0xD011 + dout <= {uart_rx_status, 7'b0}; + end + + UART_TX: + begin + // UART TX - 0xD012 + dout <= {uart_tx_status, 7'd0}; + + if (w_en) + begin + // Apple 1 terminal only uses 7 bits, MSB indicates + // terminal has ack'd RX + // + // uart_tx_init is a flag to stop the first character + // sent to the UART from being sent. Wozmon initializes + // the PIA which normally isn't sent to the terminal. + // This causes the UART to ignore the very first byte sent. + if (~uart_tx_status && uart_tx_init) + begin + uart_tx_byte <= {1'b0, din[6:0]}; + uart_tx_stb <= 1; + end + else if (~uart_tx_init) + uart_tx_init <= 1 && enable; + end + end + + UART_TXCR: + begin + // UART TX CR - 0xD013 + // Ignore the TX control register + dout <= 8'b0; + end + + endcase + end + end +endmodule diff --git a/rtl/vga.v b/rtl/vga.v new file mode 100644 index 0000000..9ef83eb --- /dev/null +++ b/rtl/vga.v @@ -0,0 +1,299 @@ +module vga ( + input clk14, // clock signal + input enable, // clock enable strobe, + input rst, // active high reset signal + output vga_h_sync, // horizontal VGA sync pulse + output vga_v_sync, // vertical VGA sync pulse + output vga_red, // red VGA signal + output vga_grn, // green VGA signal + output vga_blu, // blue VGA signal + input address, // address bus + input w_en, // active high write enable strobe + input [7:0] din, // 8-bit data bus (input) + input [1:0] mode, // 2-bit mode setting for pixel doubling + input [2:0] fg_colour, // 3 bit background colour + input [2:0] bg_colour, // 3 bit foreground colour + input clr_screen // clear screen button +); + + ////////////////////////////////////////////////////////////////////////// + // Registers and Parameters + + // video structure constants + parameter h_pixels = 910; // horizontal pixels per line + parameter v_lines = 262; // vertical lines per frame + parameter h_pulse = 65; // hsync pulse length (was: 96) + parameter v_pulse = 2; // vsync pulse length + parameter hbp = 144+64; // end of horizontal back porch + parameter hfp = 784+64; // beginning of horizontal front porch + parameter vbp = 10+32; // end of vertical back porch + parameter vfp = 202+32; // beginning of vertical front porch + + // registers for storing the horizontal & vertical counters + reg [9:0] h_cnt; + reg [9:0] v_cnt; + wire [3:0] h_dot; + reg [4:0] v_dot; + + // hardware cursor registers + wire [10:0] cursor; + reg [5:0] h_cursor; + reg [4:0] v_cursor; + + // vram indexing registers + reg [5:0] vram_h_addr; + reg [4:0] vram_v_addr; + reg [4:0] vram_start_addr; + reg [4:0] vram_end_addr; + wire [4:0] vram_clr_addr; + + // vram registers + wire [10:0] vram_r_addr; + reg [10:0] vram_w_addr; + reg vram_w_en; + reg [5:0] vram_din; + wire [5:0] vram_dout; + + // font rom registers + wire [5:0] font_char; + wire [3:0] font_pixel; + wire [4:0] font_line; + wire font_out; + + // cpu control registers + reg char_seen; + + // active region strobes + wire h_active; + wire v_active; + assign h_active = (h_cnt >= hbp && h_cnt < hfp); + assign v_active = (v_cnt >= vbp && v_cnt < vfp); + + ////////////////////////////////////////////////////////////////////////// + // VGA Sync Generation + // + always @(posedge clk14 or posedge rst) + begin + if (rst) + begin + h_cnt <= 10'd0; + v_cnt <= 10'd0; + v_dot <= 5'd0; + end + else + begin + if (h_cnt < h_pixels) + h_cnt <= h_cnt + 1; + + else + begin + // reset horizontal counters + h_cnt <= 0; + + if (v_cnt < v_lines) + begin + v_cnt <= v_cnt + 1; + + // count 20 rows, so 480px / 20 = 24 rows + if (v_active) + begin + v_dot <= v_dot + 1; // +1 + + if (v_dot == 5'd7) // == d19 + v_dot <= 0; + end + end + else + begin + // reset vertical counters + v_cnt <= 0; + v_dot <= 0; + end + end + end + end + + // count 16 pixels, so 640px / 16 = 40 characters + assign h_dot = h_active ? h_cnt[3:0] : 4'd0; + + ////////////////////////////////////////////////////////////////////////// + // Character ROM + + font_rom font_rom( + .clk(clk14), + .mode(mode), + .character(font_char), + .pixel(font_pixel), + .line(font_line), + .out(font_out) + ); + + ////////////////////////////////////////////////////////////////////////// + // Video RAM + + vram vram( + .clk(clk14), + .read_addr(vram_r_addr), + .write_addr(vram_w_addr), + .r_en(h_active), + .w_en(vram_w_en), + .din(vram_din), + .dout(vram_dout) + ); + + ////////////////////////////////////////////////////////////////////////// + // Video Signal Generation + + always @(posedge clk14 or posedge rst) begin + if (rst) begin + vram_h_addr <= 'd0; + vram_v_addr <= 'd0; + end else begin + // start the pipeline for reading vram and font details + // 3 pixel clock cycles early + if (h_dot == 4'hC) + vram_h_addr <= vram_h_addr + 'd1; + + // advance to next row when last display line is reached for row + if (v_dot == 5'd7 && h_cnt == 10'd0) // == d19 + vram_v_addr <= vram_v_addr + 'd1; + + // clear the address registers if we're not in visible area + if (~h_active) + vram_h_addr <= 'd0; + if (~v_active) + vram_v_addr <= vram_start_addr; + end + end + + ////////////////////////////////////////////////////////////////////////// + // Cursor blink + + reg blink; + reg [22:0] blink_div; + always @(posedge clk14 or posedge rst) + begin + if (rst) + blink_div <= 0; + else + begin + blink_div <= blink_div + 1; + + if (blink_div == 23'd0) + blink <= ~blink; + end + end + + ////////////////////////////////////////////////////////////////////////// + // Pipeline and VGA signals + + // vram to font rom to display pipeline assignments + assign cursor = {v_cursor, h_cursor}; + assign vram_r_addr = {vram_v_addr, vram_h_addr}; + + assign font_char = (vram_r_addr != cursor) ? vram_dout : (blink) ? 6'd0 : 6'd32; + assign font_pixel = h_dot + 1; // offset by one to get pixel into right cycle, + // font output one pixel clk behind + assign font_line = v_dot * 2 + 4; + + // vga signals out to monitor + assign vga_red = (h_active & v_active) ? (font_out ? fg_colour[2] : bg_colour[2]) : 1'b0; + assign vga_grn = (h_active & v_active) ? (font_out ? fg_colour[1] : bg_colour[1]) : 1'b0; + assign vga_blu = (h_active & v_active) ? (font_out ? fg_colour[0] : bg_colour[0]) : 1'b0; + + assign vga_h_sync = (h_cnt < h_pulse) ? 0 : 1; + assign vga_v_sync = (v_cnt < v_pulse) ? 0 : 1; + + ////////////////////////////////////////////////////////////////////////// + // CPU control and hardware cursor + + assign vram_clr_addr = vram_end_addr + {3'd0, vram_v_addr[1:0]}; + + always @(posedge clk14 or posedge rst) + begin + if (rst) + begin + h_cursor <= 6'd0; + v_cursor <= 5'd0; + char_seen <= 'b0; + vram_start_addr <= 5'd0; + vram_end_addr <= 5'd24; + end + else + begin + vram_w_en <= 0; + + if (clr_screen) + begin + // return to top of screen + h_cursor <= 6'd0; + v_cursor <= 5'd0; + + vram_start_addr <= 5'd0; + vram_end_addr <= 5'd24; + + // clear the screen + vram_w_addr <= {vram_v_addr, vram_h_addr}; + vram_din <= 6'd32; + vram_w_en <= 1; + end + else + begin + // cursor overflow handling + if (h_cursor == 6'd40) + begin + h_cursor <= 6'd0; + v_cursor <= v_cursor + 'd1; + end + + if (v_cursor == vram_end_addr) + begin + vram_start_addr <= vram_start_addr + 'd1; + vram_end_addr <= vram_end_addr + 'd1; + end + + if (address == 1'b0) // address low == TX register + begin + if (enable & w_en & ~char_seen) + begin + // incoming character + char_seen <= 1; + + case(din) + 8'h0D, + 8'h8D: begin + // handle carriage return + h_cursor <= 0; + v_cursor <= v_cursor + 'd1; + end + + 8'h00, + 8'h0A, + 8'h9B, + 8'h7F: begin + // ignore the escape key + h_cursor <= 0; + end + + default: begin + vram_w_addr <= cursor; + vram_din <= {~din[6], din[4:0]}; + vram_w_en <= 1; + h_cursor <= h_cursor + 1; + end + endcase + end + else if(~enable & ~w_en) + char_seen <= 0; + end + else + begin + vram_w_addr <= {vram_clr_addr, vram_h_addr}; + vram_din <= 6'd32; + vram_w_en <= 1; + end + end + end + end + +endmodule diff --git a/rtl/vram.v b/rtl/vram.v new file mode 100644 index 0000000..4f860ac --- /dev/null +++ b/rtl/vram.v @@ -0,0 +1,46 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Description: Video RAM for system +// +// Author.....: Alan Garfield +// Niels A. Moseley +// Date.......: 26-1-2018 +// + +module vram ( + input clk, // clock signal + input [10:0] read_addr, // read address bus + input [10:0] write_addr, // write address bus + input r_en, // active high read enable strobe + input w_en, // active high write enable strobe + input [5:0] din, // 6-bit data bus (input) + output reg [5:0] dout // 6-bit data bus (output) +); + + reg [5:0] ram_data[0:2047]; + + initial + $readmemb("roms/vga_vram.bin", ram_data, 0, 2047); + + always @(posedge clk) + begin + if (r_en) dout <= ram_data[read_addr]; + if (w_en) ram_data[write_addr] <= din; + end + +endmodule