├── .gitignore ├── LICENSE ├── NVBoard.jpg ├── README.md ├── board └── N4 ├── example ├── .gitignore ├── Makefile ├── README.md ├── constr │ └── top.nxdc ├── csrc │ └── main.cpp ├── resource │ └── picture.hex └── vsrc │ ├── led.v │ ├── ps2_keyboard.v │ ├── seg.v │ ├── top.v │ ├── uart.v │ └── vga_ctrl.v ├── include ├── .gitignore ├── component.h ├── configs.h ├── font.h ├── keyboard.h ├── macro.h ├── nvboard.h ├── pins.h ├── render.h ├── term.h ├── uart.h └── vga.h ├── resources ├── font │ └── FreeMono.ttf └── pic │ ├── .gitignore │ ├── vbtn_off.png │ ├── vbtn_on.png │ ├── vsw_off.png │ └── vsw_on.png ├── scripts ├── .gitignore ├── auto_pin_bind.py └── nvboard.mk ├── src ├── .gitignore ├── at_scancode.h ├── button.cpp ├── component.cpp ├── event.cpp ├── font.cpp ├── keyboard.cpp ├── led.cpp ├── nvboard.cpp ├── render.cpp ├── segs7.cpp ├── switch.cpp ├── term.cpp ├── timer.cpp ├── uart.cpp └── vga.cpp └── usr └── include ├── nvboard.h └── pins.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.* 2 | * 3 | !*/ 4 | !.gitignore 5 | !Makefile 6 | !makefile 7 | !LICENSE 8 | !README.md 9 | !README.en.md 10 | !board/* 11 | !*.v 12 | !*.cpp 13 | build/ 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 木兰宽松许可证, 第2版 2 | 3 | 木兰宽松许可证, 第2版 4 | 2020年1月 http://license.coscl.org.cn/MulanPSL2 5 | 6 | 7 | 您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束: 8 | 9 | 0. 定义 10 | 11 | “软件”是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。 12 | 13 | “贡献”是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。 14 | 15 | “贡献者”是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。 16 | 17 | “法人实体”是指提交贡献的机构及其“关联实体”。 18 | 19 | “关联实体”是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。 20 | 21 | 1. 授予版权许可 22 | 23 | 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。 24 | 25 | 2. 授予专利许可 26 | 27 | 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。 28 | 29 | 3. 无商标许可 30 | 31 | “本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。 32 | 33 | 4. 分发限制 34 | 35 | 您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。 36 | 37 | 5. 免责声明与责任限制 38 | 39 | “软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。 40 | 41 | 6. 语言 42 | “本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。 43 | 44 | 条款结束 45 | 46 | 如何将木兰宽松许可证,第2版,应用到您的软件 47 | 48 | 如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步: 49 | 50 | 1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字; 51 | 52 | 2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中; 53 | 54 | 3, 请将如下声明文本放入每个源文件的头部注释中。 55 | 56 | Copyright (c) [Year] [name of copyright holder] 57 | [Software Name] is licensed under Mulan PSL v2. 58 | You can use this software according to the terms and conditions of the Mulan PSL v2. 59 | You may obtain a copy of Mulan PSL v2 at: 60 | http://license.coscl.org.cn/MulanPSL2 61 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 62 | See the Mulan PSL v2 for more details. 63 | 64 | 65 | Mulan Permissive Software License,Version 2 66 | 67 | Mulan Permissive Software License,Version 2 (Mulan PSL v2) 68 | January 2020 http://license.coscl.org.cn/MulanPSL2 69 | 70 | Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions: 71 | 72 | 0. Definition 73 | 74 | Software means the program and related documents which are licensed under this License and comprise all Contribution(s). 75 | 76 | Contribution means the copyrightable work licensed by a particular Contributor under this License. 77 | 78 | Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License. 79 | 80 | Legal Entity means the entity making a Contribution and all its Affiliates. 81 | 82 | Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity. 83 | 84 | 1. Grant of Copyright License 85 | 86 | Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not. 87 | 88 | 2. Grant of Patent License 89 | 90 | Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken. 91 | 92 | 3. No Trademark License 93 | 94 | No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in Section 4. 95 | 96 | 4. Distribution Restriction 97 | 98 | You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software. 99 | 100 | 5. Disclaimer of Warranty and Limitation of Liability 101 | 102 | THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 103 | 104 | 6. Language 105 | 106 | THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL. 107 | 108 | END OF THE TERMS AND CONDITIONS 109 | 110 | How to Apply the Mulan Permissive Software License,Version 2 (Mulan PSL v2) to Your Software 111 | 112 | To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps: 113 | 114 | i Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner; 115 | 116 | ii Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package; 117 | 118 | iii Attach the statement to the appropriate annotated syntax at the beginning of each source file. 119 | 120 | 121 | Copyright (c) [Year] [name of copyright holder] 122 | [Software Name] is licensed under Mulan PSL v2. 123 | You can use this software according to the terms and conditions of the Mulan PSL v2. 124 | You may obtain a copy of Mulan PSL v2 at: 125 | http://license.coscl.org.cn/MulanPSL2 126 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 127 | See the Mulan PSL v2 for more details. 128 | -------------------------------------------------------------------------------- /NVBoard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NJU-ProjectN/nvboard/fd19d9b07af12e48d442f6057f65a75114cd9755/NVBoard.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NVBoard 2 | 3 | ![image](./NVBoard.jpg) 4 | 5 | ## 介绍 6 | 7 | NVBoard(NJU Virtual Board)是基于SDL开发的虚拟FPGA开发板,可以在Verilator仿真环境中模拟FPGA,支持LED,七段数码管,拨码开关,UART,键盘和VGA。 8 | 9 | ## 项目文件说明 10 | 11 | ``` 12 | . 13 | ├── board # 引脚说明文件 14 | │ └── ... 15 | ├── example # 示例项目 16 | │ └── ... 17 | ├── include # 用于NVboard项目内部包含的头文件 18 | │   ├── component.h 19 | │   ├── configs.h 20 | │   ├── font.h 21 | │   ├── keyboard.h 22 | │   ├── macro.h 23 | │   ├── nvboard.h 24 | │   ├── pins.h 25 | │   ├── render.h 26 | │   ├── term.h 27 | │   ├── uart.h 28 | │   └── vga.h 29 | ├── resources 30 | │   ├── font # 字体资源 31 | │ └── pic # 图片资源 32 | ├── scripts 33 | │   ├── auto_pin_bind.py # 生成引脚绑定代码的脚本 34 | │   └── nvboard.mk # NVBoard构建规则 35 | ├── src # NVBoard源码 36 | │   ├── at_scancode.h 37 | │ ├── button.cpp 38 | │ ├── component.cpp 39 | │ ├── event.cpp 40 | │ ├── font.cpp 41 | │ ├── keyboard.cpp 42 | │ ├── led.cpp 43 | │ ├── nvboard.cpp 44 | │ ├── render.cpp 45 | │ ├── segs7.cpp 46 | │ ├── switch.cpp 47 | │ ├── term.cpp 48 | │ ├── timer.cpp 49 | │ ├── uart.cpp 50 | │ └── vga.cpp 51 | ├── usr 52 | │ └── include # 用于给外部项目包含的头文件 53 | │   ├── nvboard.h 54 | │   └── pins.h 55 | ├── LICENSE 56 | └── README.md 57 | ``` 58 | 59 | ## 安装教程 60 | 61 | 1. 将项目拷贝到本地,`git clone https://github.com/NJU-ProjectN/nvboard.git` 62 | 2. 通过`apt-get install libsdl2-dev libsdl2-image-dev libsdl2-ttf-dev` / `yum install SDL2-devel SDL2_image-devel SDL2_ttf-devel`安装SDL2,SDL2-image和SDL2-ttf;对于`macOS`,可以通过`brew install sdl2 sdl2_image sdl2_ttf`安装 63 | 3. 把本项目的目录设置成环境变量`NVBOARD_HOME` 64 | 65 | ## 示例 66 | 67 | `example`目录下包含一个示例项目,在该目录下通过 `make run` 命令可运行该项目。 68 | 69 | ## 接入verilator步骤 70 | 71 | ### API说明 72 | 73 | NVBoard提供了以下几组API 74 | 75 | - `void nvboard_init()`: 初始化NVBoard 76 | - `void nvboard_quit()`: 退出NVBoard 77 | - `void nvboard_bind_pin(void *signal, int len, ...)`: 将HDL的信号signal连接到NVBoard里的引脚上,具体地 78 | - `len`为信号的长度,大于1时为向量信号 79 | - 可变参数列表`...`为引脚编号列表,编号为整数;绑定向量信号时,引脚编号列表从MSB到LSB排列 80 | - `void nvboard_update()`: 更新NVBoard中各组件的状态,每当电路状态发生改变时都需要调用该函数 81 | 82 | ### 引脚绑定 83 | 84 | 手动调用`nvboard_bind_pin()`来绑定所有引脚较为繁琐。 85 | 为了方便进行信号的绑定,NVBoard项目提供了一套从自定义约束文件生成绑定代码的流程。具体地 86 | 1. 编写一个自定义格式的约束文件,其格式为 87 | ``` 88 | top=top_name 89 | 90 | # Line comment inside nxdc 91 | signal pin 92 | signal (pin1, pin2, ..., pink) 93 | ``` 94 | 在约束文件的第一行,需要指定顶层模块名(上文中为`top_name`)。 95 | 约束文件支持两种信号绑定方式,`signal pin`表示将顶层模块的`signal`端口信号绑定到引脚`pin`上, 96 | `signal (pin1, pin2, ..., pink)`表示将顶层模块的`signal`信号的每一位从高到低依次绑定到`pin1, pin2, ..., pink`上。 97 | 约束文件支持空行与行注释。 98 | 99 | 2. 通过命令`python $(NVBOARD_HOME)/scripts/auto_pin_bind.py nxdc约束文件路径 auto_bind.cpp输出路径`来生成C++文件。 100 | 101 | 调用该文件中的`nvboard_bind_all_pins(dut)`函数即可完成所有信号的绑定。 102 | 103 | 注意,该脚本的错误处理并不完善,若自定义约束文件中存在错误,则可能无法生成正确的报错信息与C++文件。 104 | ~~如果发现脚本中的错误也可以尝试修复一下然后丢pr~~ 105 | 106 | 可以在`board`目录下的引脚说明文件中查看引脚信息。 107 | 其中`output`表示该信号方向为输出方向(从RTL代码到NVBoard),`input`为输入方向(从NVBoard到RTL代码)。 108 | 109 | 目前引脚列表中不包含复位引脚`RST`,因为NVBoard在cpp文件中包含一些内部状态,仅复位RTL设计部分会使其与NVBoard状态不一致。 110 | 一个实现全系统复位效果的简单方法是退出NVBoard并重新运行。RTL设计的复位工作由verilator的wrapper完成,具体见`example/csrc/main.cpp`。 111 | 112 | ### 调用API 113 | 114 | 在C++仿真代码中调用NVBoard提供的API 115 | ```c++ 116 | #include 117 | 118 | // ... 119 | nvboard_bind_all_pins(&dut); 120 | nvboard_init(); 121 | 122 | while (1) { 123 | // ... 124 | nvboard_update(); 125 | } 126 | 127 | nvboard_quit(); 128 | ``` 129 | 具体地 130 | * 在进入verilator仿真的循环前,先对引脚进行绑定,然后对NVBoard进行初始化 131 | * 在verilator仿真的循环中更新NVBoard各组件的状态 132 | * 退出verilator仿真的循环后,销毁NVBoard的相关资源 133 | 134 | ### 编译链接 135 | 136 | 在Makefile中 137 | * 将生成的上述引脚绑定的C++文件加入源文件列表 138 | * 将NVBoard的构建脚本包含进来 139 | ``` 140 | include $(NVBOARD_HOME)/scripts/nvboard.mk 141 | ``` 142 | * 通过`make nvboard-archive`生成NVBoard的库文件 143 | * 在生成verilator仿真可执行文件(即`$(NVBOARD_ARCHIVE)`)将这个库文件加入链接过程,并添加链接选项`-lSDL2 -lSDL2_image` 144 | 145 | 可以参考示例项目中的Makefile文件,即`example/Makefile` 146 | -------------------------------------------------------------------------------- /board/N4: -------------------------------------------------------------------------------- 1 | input BTNC 2 | input BTNU 3 | input BTND 4 | input BTNL 5 | input BTNR 6 | 7 | input SW0 8 | input SW1 9 | input SW2 10 | input SW3 11 | input SW4 12 | input SW5 13 | input SW6 14 | input SW7 15 | input SW8 16 | input SW9 17 | input SW10 18 | input SW11 19 | input SW12 20 | input SW13 21 | input SW14 22 | input SW15 23 | 24 | output LD0 25 | output LD1 26 | output LD2 27 | output LD3 28 | output LD4 29 | output LD5 30 | output LD6 31 | output LD7 32 | output LD8 33 | output LD9 34 | output LD10 35 | output LD11 36 | output LD12 37 | output LD13 38 | output LD14 39 | output LD15 40 | 41 | output R16 42 | output G16 43 | output B16 44 | output R17 45 | output G17 46 | output B17 47 | 48 | output SEG0A 49 | output SEG0B 50 | output SEG0C 51 | output SEG0D 52 | output SEG0E 53 | output SEG0F 54 | output SEG0G 55 | output DEC0P 56 | 57 | output SEG1A 58 | output SEG1B 59 | output SEG1C 60 | output SEG1D 61 | output SEG1E 62 | output SEG1F 63 | output SEG1G 64 | output DEC1P 65 | 66 | output SEG2A 67 | output SEG2B 68 | output SEG2C 69 | output SEG2D 70 | output SEG2E 71 | output SEG2F 72 | output SEG2G 73 | output DEC2P 74 | 75 | output SEG3A 76 | output SEG3B 77 | output SEG3C 78 | output SEG3D 79 | output SEG3E 80 | output SEG3F 81 | output SEG3G 82 | output DEC3P 83 | 84 | output SEG4A 85 | output SEG4B 86 | output SEG4C 87 | output SEG4D 88 | output SEG4E 89 | output SEG4F 90 | output SEG4G 91 | output DEC4P 92 | 93 | output SEG5A 94 | output SEG5B 95 | output SEG5C 96 | output SEG5D 97 | output SEG5E 98 | output SEG5F 99 | output SEG5G 100 | output DEC5P 101 | 102 | output SEG6A 103 | output SEG6B 104 | output SEG6C 105 | output SEG6D 106 | output SEG6E 107 | output SEG6F 108 | output SEG6G 109 | output DEC6P 110 | 111 | output SEG7A 112 | output SEG7B 113 | output SEG7C 114 | output SEG7D 115 | output SEG7E 116 | output SEG7F 117 | output SEG7G 118 | output DEC7P 119 | 120 | output VGA_VSYNC 121 | output VGA_HSYNC 122 | output VGA_BLANK_N 123 | output VGA_R0 124 | output VGA_R1 125 | output VGA_R2 126 | output VGA_R3 127 | output VGA_R4 128 | output VGA_R5 129 | output VGA_R6 130 | output VGA_R7 131 | output VGA_G0 132 | output VGA_G1 133 | output VGA_G2 134 | output VGA_G3 135 | output VGA_G4 136 | output VGA_G5 137 | output VGA_G6 138 | output VGA_G7 139 | output VGA_B0 140 | output VGA_B1 141 | output VGA_B2 142 | output VGA_B3 143 | output VGA_B4 144 | output VGA_B5 145 | output VGA_B6 146 | output VGA_B7 147 | 148 | input PS2_CLK 149 | input PS2_DAT 150 | 151 | output UART_TX 152 | input UART_RX 153 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | !resource/*.hex 2 | !constr/*.nxdc 3 | -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | TOPNAME = top 2 | NXDC_FILES = constr/top.nxdc 3 | INC_PATH ?= 4 | 5 | VERILATOR = verilator 6 | VERILATOR_CFLAGS += -MMD --build -cc \ 7 | -O3 --x-assign fast --x-initial fast --noassert 8 | 9 | BUILD_DIR = ./build 10 | OBJ_DIR = $(BUILD_DIR)/obj_dir 11 | BIN = $(BUILD_DIR)/$(TOPNAME) 12 | 13 | default: $(BIN) 14 | 15 | $(shell mkdir -p $(BUILD_DIR)) 16 | 17 | # constraint file 18 | SRC_AUTO_BIND = $(abspath $(BUILD_DIR)/auto_bind.cpp) 19 | $(SRC_AUTO_BIND): $(NXDC_FILES) 20 | python3 $(NVBOARD_HOME)/scripts/auto_pin_bind.py $^ $@ 21 | 22 | # project source 23 | VSRCS = $(shell find $(abspath ./vsrc) -name "*.v") 24 | CSRCS = $(shell find $(abspath ./csrc) -name "*.c" -or -name "*.cc" -or -name "*.cpp") 25 | CSRCS += $(SRC_AUTO_BIND) 26 | 27 | # rules for NVBoard 28 | include $(NVBOARD_HOME)/scripts/nvboard.mk 29 | 30 | # rules for verilator 31 | INCFLAGS = $(addprefix -I, $(INC_PATH)) 32 | CXXFLAGS += $(INCFLAGS) -DTOP_NAME="\"V$(TOPNAME)\"" 33 | 34 | $(BIN): $(VSRCS) $(CSRCS) $(NVBOARD_ARCHIVE) 35 | @rm -rf $(OBJ_DIR) 36 | $(VERILATOR) $(VERILATOR_CFLAGS) \ 37 | --top-module $(TOPNAME) $^ \ 38 | $(addprefix -CFLAGS , $(CXXFLAGS)) $(addprefix -LDFLAGS , $(LDFLAGS)) \ 39 | --Mdir $(OBJ_DIR) --exe -o $(abspath $(BIN)) 40 | 41 | all: default 42 | 43 | run: $(BIN) 44 | @$^ 45 | 46 | clean: 47 | rm -rf $(BUILD_DIR) 48 | 49 | .PHONY: default all clean run 50 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # 示例工程 2 | 3 | 先设置环境变量`NVBOARD_HOME`为NVBoard项目的路径, 然后执行`make run`. 4 | 5 | 该示例的演示效果如下: 6 | 1. 左边8个LED为流水灯效果 7 | 1. 拨动右边的8个拨码开关, 可控制对应LED的亮灭 8 | 1. 按下按钮可将8~12号中对应的LED亮灭效果取反 9 | 1. 8个数码管流水显示数字0-7 10 | 1. 窗口左下角为VGA输出, 将会展示一张图片 11 | 1. 敲击键盘, 终端将会输出按键的扫描码 12 | 1. 窗口右侧中部为UART终端 13 | * 鼠标选中后光标变为粉红色, 此时的按键输入将被UART RX端捕捉 14 | * UART RX端发送的输入将通过回环连接从UART TX端输出, 并在UART终端上显示 15 | * 鼠标点击UART终端以外的其他位置, 可取消选中UART终端, 光标变为黑色, 后续按键输入将被PS/2键盘捕捉 16 | -------------------------------------------------------------------------------- /example/constr/top.nxdc: -------------------------------------------------------------------------------- 1 | top=top 2 | 3 | # VGA_CLK is set to clk in this example 4 | VGA_VSYNC VGA_VSYNC 5 | VGA_HSYNC VGA_HSYNC 6 | VGA_BLANK_N VGA_BLANK_N 7 | VGA_R (VGA_R7, VGA_R6, VGA_R5, VGA_R4, VGA_R3, VGA_R2, VGA_R1, VGA_R0) 8 | VGA_G (VGA_G7, VGA_G6, VGA_G5, VGA_G4, VGA_G3, VGA_G2, VGA_G1, VGA_G0) 9 | VGA_B (VGA_B7, VGA_B6, VGA_B5, VGA_B4, VGA_B3, VGA_B2, VGA_B1, VGA_B0) 10 | 11 | ledr (LD15, LD14, LD13, LD12, LD11, LD10, LD9, LD8, LD7, LD6, LD5, LD4, LD3, LD2, LD1, LD0) 12 | sw (SW7, SW6, SW5, SW4, SW3, SW2, SW1, SW0) 13 | btn (BTNL, BTNU, BTNC, BTND, BTNR) 14 | seg0 (SEG0A, SEG0B, SEG0C, SEG0D, SEG0E, SEG0F, SEG0G, DEC0P) 15 | seg1 (SEG1A, SEG1B, SEG1C, SEG1D, SEG1E, SEG1F, SEG1G, DEC1P) 16 | seg2 (SEG2A, SEG2B, SEG2C, SEG2D, SEG2E, SEG2F, SEG2G, DEC2P) 17 | seg3 (SEG3A, SEG3B, SEG3C, SEG3D, SEG3E, SEG3F, SEG3G, DEC3P) 18 | seg4 (SEG4A, SEG4B, SEG4C, SEG4D, SEG4E, SEG4F, SEG4G, DEC4P) 19 | seg5 (SEG5A, SEG5B, SEG5C, SEG5D, SEG5E, SEG5F, SEG5G, DEC5P) 20 | seg6 (SEG6A, SEG6B, SEG6C, SEG6D, SEG6E, SEG6F, SEG6G, DEC6P) 21 | seg7 (SEG7A, SEG7B, SEG7C, SEG7D, SEG7E, SEG7F, SEG7G, DEC7P) 22 | 23 | ps2_clk PS2_CLK 24 | ps2_data PS2_DAT 25 | 26 | uart_tx UART_TX 27 | uart_rx UART_RX 28 | -------------------------------------------------------------------------------- /example/csrc/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static TOP_NAME dut; 5 | 6 | void nvboard_bind_all_pins(TOP_NAME* top); 7 | 8 | static void single_cycle() { 9 | dut.clk = 0; dut.eval(); 10 | dut.clk = 1; dut.eval(); 11 | } 12 | 13 | static void reset(int n) { 14 | dut.rst = 1; 15 | while (n -- > 0) single_cycle(); 16 | dut.rst = 0; 17 | } 18 | 19 | int main() { 20 | nvboard_bind_all_pins(&dut); 21 | nvboard_init(); 22 | 23 | reset(10); 24 | 25 | while(1) { 26 | nvboard_update(); 27 | single_cycle(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /example/vsrc/led.v: -------------------------------------------------------------------------------- 1 | module led( 2 | input clk, 3 | input rst, 4 | input [4:0] btn, 5 | input [7:0] sw, 6 | output [15:0] ledr 7 | ); 8 | reg [31:0] count; 9 | reg [7:0] led; 10 | always @(posedge clk) begin 11 | if (rst) begin led <= 1; count <= 0; end 12 | else begin 13 | if (count == 0) led <= {led[6:0], led[7]}; 14 | count <= (count >= 5000000 ? 32'b0 : count + 1); 15 | end 16 | end 17 | 18 | assign ledr = {led[7:5], led[4:0] ^ btn, sw}; 19 | endmodule 20 | -------------------------------------------------------------------------------- /example/vsrc/ps2_keyboard.v: -------------------------------------------------------------------------------- 1 | module ps2_keyboard(clk,resetn,ps2_clk,ps2_data); 2 | input clk,resetn,ps2_clk,ps2_data; 3 | 4 | reg [9:0] buffer; // ps2_data bits 5 | reg [3:0] count; // count ps2_data bits 6 | reg [2:0] ps2_clk_sync; 7 | 8 | always @(posedge clk) begin 9 | ps2_clk_sync <= {ps2_clk_sync[1:0],ps2_clk}; 10 | end 11 | 12 | wire sampling = ps2_clk_sync[2] & ~ps2_clk_sync[1]; 13 | 14 | always @(posedge clk) begin 15 | if (resetn == 0) begin // reset 16 | count <= 0; 17 | end 18 | else begin 19 | if (sampling) begin 20 | if (count == 4'd10) begin 21 | if ((buffer[0] == 0) && // start bit 22 | (ps2_data) && // stop bit 23 | (^buffer[9:1])) begin // odd parity 24 | $display("receive %x", buffer[8:1]); 25 | end 26 | count <= 0; // for next 27 | end else begin 28 | buffer[count] <= ps2_data; // store ps2_data 29 | count <= count + 3'b1; 30 | end 31 | end 32 | end 33 | end 34 | 35 | endmodule -------------------------------------------------------------------------------- /example/vsrc/seg.v: -------------------------------------------------------------------------------- 1 | module seg( 2 | input clk, 3 | input rst, 4 | output [7:0] o_seg0, 5 | output [7:0] o_seg1, 6 | output [7:0] o_seg2, 7 | output [7:0] o_seg3, 8 | output [7:0] o_seg4, 9 | output [7:0] o_seg5, 10 | output [7:0] o_seg6, 11 | output [7:0] o_seg7 12 | ); 13 | 14 | wire [7:0] segs [7:0]; 15 | assign segs[0] = 8'b11111101; 16 | assign segs[1] = 8'b01100000; 17 | assign segs[2] = 8'b11011010; 18 | assign segs[3] = 8'b11110010; 19 | assign segs[4] = 8'b01100110; 20 | assign segs[5] = 8'b10110110; 21 | assign segs[6] = 8'b10111110; 22 | assign segs[7] = 8'b11100000; 23 | 24 | parameter CLK_NUM = 5000000; 25 | 26 | reg [31:0] count; 27 | reg [2:0] offset; 28 | 29 | always @(posedge clk) begin 30 | if(rst) begin count <= 0; offset <= 0; end 31 | else begin 32 | if(count == CLK_NUM) begin offset <= offset + 1; end 33 | count <= (count == CLK_NUM) ? 0 : count + 1; 34 | end 35 | end 36 | 37 | assign o_seg0 = ~segs[offset + 3'd0]; 38 | assign o_seg1 = ~segs[offset + 3'd1]; 39 | assign o_seg2 = ~segs[offset + 3'd2]; 40 | assign o_seg3 = ~segs[offset + 3'd3]; 41 | assign o_seg4 = ~segs[offset + 3'd4]; 42 | assign o_seg5 = ~segs[offset + 3'd5]; 43 | assign o_seg6 = ~segs[offset + 3'd6]; 44 | assign o_seg7 = ~segs[offset + 3'd7]; 45 | 46 | endmodule 47 | -------------------------------------------------------------------------------- /example/vsrc/top.v: -------------------------------------------------------------------------------- 1 | module top( 2 | input clk, 3 | input rst, 4 | input [4:0] btn, 5 | input [7:0] sw, 6 | input ps2_clk, 7 | input ps2_data, 8 | input uart_rx, 9 | output uart_tx, 10 | output [15:0] ledr, 11 | output VGA_CLK, 12 | output VGA_HSYNC, 13 | output VGA_VSYNC, 14 | output VGA_BLANK_N, 15 | output [7:0] VGA_R, 16 | output [7:0] VGA_G, 17 | output [7:0] VGA_B, 18 | output [7:0] seg0, 19 | output [7:0] seg1, 20 | output [7:0] seg2, 21 | output [7:0] seg3, 22 | output [7:0] seg4, 23 | output [7:0] seg5, 24 | output [7:0] seg6, 25 | output [7:0] seg7 26 | ); 27 | 28 | led my_led( 29 | .clk(clk), 30 | .rst(rst), 31 | .btn(btn), 32 | .sw(sw), 33 | .ledr(ledr) 34 | ); 35 | 36 | assign VGA_CLK = clk; 37 | 38 | wire [9:0] h_addr; 39 | wire [9:0] v_addr; 40 | wire [23:0] vga_data; 41 | 42 | vga_ctrl my_vga_ctrl( 43 | .pclk(clk), 44 | .reset(rst), 45 | .vga_data(vga_data), 46 | .h_addr(h_addr), 47 | .v_addr(v_addr), 48 | .hsync(VGA_HSYNC), 49 | .vsync(VGA_VSYNC), 50 | .valid(VGA_BLANK_N), 51 | .vga_r(VGA_R), 52 | .vga_g(VGA_G), 53 | .vga_b(VGA_B) 54 | ); 55 | 56 | ps2_keyboard my_keyboard( 57 | .clk(clk), 58 | .resetn(~rst), 59 | .ps2_clk(ps2_clk), 60 | .ps2_data(ps2_data) 61 | ); 62 | 63 | seg my_seg( 64 | .clk(clk), 65 | .rst(rst), 66 | .o_seg0(seg0), 67 | .o_seg1(seg1), 68 | .o_seg2(seg2), 69 | .o_seg3(seg3), 70 | .o_seg4(seg4), 71 | .o_seg5(seg5), 72 | .o_seg6(seg6), 73 | .o_seg7(seg7) 74 | ); 75 | 76 | vmem my_vmem( 77 | .h_addr(h_addr), 78 | .v_addr(v_addr[8:0]), 79 | .vga_data(vga_data) 80 | ); 81 | 82 | uart my_uart( 83 | .tx(uart_tx), 84 | .rx(uart_rx) 85 | ); 86 | 87 | endmodule 88 | 89 | module vmem( 90 | input [9:0] h_addr, 91 | input [8:0] v_addr, 92 | output [23:0] vga_data 93 | ); 94 | 95 | reg [23:0] vga_mem [524287:0]; 96 | 97 | initial begin 98 | $readmemh("resource/picture.hex", vga_mem); 99 | end 100 | 101 | assign vga_data = vga_mem[{h_addr, v_addr}]; 102 | 103 | endmodule 104 | -------------------------------------------------------------------------------- /example/vsrc/uart.v: -------------------------------------------------------------------------------- 1 | module uart ( 2 | output tx, 3 | input rx 4 | ); 5 | assign tx = rx; 6 | endmodule 7 | -------------------------------------------------------------------------------- /example/vsrc/vga_ctrl.v: -------------------------------------------------------------------------------- 1 | module vga_ctrl ( 2 | input pclk, 3 | input reset, 4 | input [23:0] vga_data, 5 | output [9:0] h_addr, 6 | output [9:0] v_addr, 7 | output hsync, 8 | output vsync, 9 | output valid, 10 | output [7:0] vga_r, 11 | output [7:0] vga_g, 12 | output [7:0] vga_b 13 | ); 14 | 15 | parameter h_frontporch = 96; 16 | parameter h_active = 144; 17 | parameter h_backporch = 784; 18 | parameter h_total = 800; 19 | 20 | parameter v_frontporch = 2; 21 | parameter v_active = 35; 22 | parameter v_backporch = 515; 23 | parameter v_total = 525; 24 | 25 | reg [9:0] x_cnt; 26 | reg [9:0] y_cnt; 27 | wire h_valid; 28 | wire v_valid; 29 | 30 | always @(posedge pclk) begin 31 | if(reset == 1'b1) begin 32 | x_cnt <= 1; 33 | y_cnt <= 1; 34 | end 35 | else begin 36 | if(x_cnt == h_total)begin 37 | x_cnt <= 1; 38 | if(y_cnt == v_total) y_cnt <= 1; 39 | else y_cnt <= y_cnt + 1; 40 | end 41 | else x_cnt <= x_cnt + 1; 42 | end 43 | end 44 | 45 | //生成同步信号 46 | assign hsync = (x_cnt > h_frontporch); 47 | assign vsync = (y_cnt > v_frontporch); 48 | //生成消隐信号 49 | assign h_valid = (x_cnt > h_active) & (x_cnt <= h_backporch); 50 | assign v_valid = (y_cnt > v_active) & (y_cnt <= v_backporch); 51 | assign valid = h_valid & v_valid; 52 | //计算当前有效像素坐标 53 | assign h_addr = h_valid ? (x_cnt - 10'd145) : 10'd0; 54 | assign v_addr = v_valid ? (y_cnt - 10'd36) : 10'd0; 55 | //设置输出的颜色值 56 | assign {vga_r, vga_g, vga_b} = vga_data; 57 | 58 | endmodule 59 | -------------------------------------------------------------------------------- /include/.gitignore: -------------------------------------------------------------------------------- 1 | !*.h 2 | !*.hpp -------------------------------------------------------------------------------- /include/component.h: -------------------------------------------------------------------------------- 1 | #ifndef __COMPONENT_H__ 2 | #define __COMPONENT_H__ 3 | 4 | #include 5 | #include 6 | 7 | // component type 8 | enum { 9 | BUTTON_TYPE = 1, SWITCH_TYPE, NAIVE_LED_TYPE, RGB_LED_TYPE, SEGS7_TYPE, 10 | VGA_TYPE, KEYBOARD_TYPE, UART_TYPE 11 | }; 12 | 13 | // logic type 14 | enum { 15 | COMB_TYPE = 1, SEQ_TYPE = 2 16 | }; 17 | 18 | class Component{ 19 | private: 20 | SDL_Renderer *m_renderer; 21 | int m_component_type; 22 | std::vector m_rects; 23 | std::vector m_textures; 24 | int m_state; 25 | std::vector pins; 26 | 27 | public: 28 | Component(SDL_Renderer *rend, int cnt, int init_val, int ct); 29 | 30 | bool in_rect(int x, int y) const; 31 | SDL_Renderer *get_renderer() const; 32 | int get_component_type() const; 33 | SDL_Rect *get_rect(int idx) const; 34 | SDL_Texture *get_texture(int idx) const; 35 | int get_state() const; 36 | uint16_t get_pin(int idx = 0) const; 37 | 38 | void set_rect(SDL_Rect *rect, int val); 39 | void set_texture(SDL_Texture *texture, int val); 40 | void set_state(int val); 41 | void add_pin(const uint16_t pin); 42 | virtual void update_gui(); 43 | virtual void update_state(); 44 | void remove(); 45 | 46 | friend void delete_components(); 47 | }; 48 | 49 | #if 0 50 | class RGB_LED : public Component{ 51 | public: 52 | RGB_LED(SDL_Renderer *rend, int cnt, int init_val, int ct); 53 | virtual void update_gui(); 54 | virtual void update_state(); 55 | }; 56 | #endif 57 | 58 | class SEGS7 : public Component{ 59 | private: 60 | bool is_len8; 61 | public: 62 | SEGS7(SDL_Renderer *rend, int cnt, int init_val, int ct, bool is_len8); 63 | virtual void update_gui(); 64 | virtual void update_state(); 65 | }; 66 | 67 | void init_components(SDL_Renderer *renderer); 68 | void init_gui(SDL_Renderer *renderer); 69 | 70 | void add_component(Component *c); 71 | void update_components(SDL_Renderer *renderer); 72 | void delete_components(); 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /include/configs.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONFIGS_H__ 2 | #define __CONFIGS_H__ 3 | 4 | // 1. Mapping 5 | // You can replace pictures used in GUI 6 | 7 | // png 8 | #define VBTN_ON_PATH "vbtn_on.png" 9 | #define VBTN_OFF_PATH "vbtn_off.png" 10 | 11 | // png 12 | #define VSW_ON_PATH "vsw_on.png" 13 | #define VSW_OFF_PATH "vsw_off.png" 14 | 15 | // 2. Hardware options 16 | 17 | #define VGA_ENA 18 | 19 | // 3. Experimental Function 20 | 21 | //#define HARDWARE_ACC 22 | 23 | //#define VSYNC 24 | 25 | // 4. Windows options 26 | 27 | #define WINDOW_WIDTH (640 * 2) 28 | #define WINDOW_HEIGHT (480 * 2) 29 | 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /include/font.h: -------------------------------------------------------------------------------- 1 | #ifndef __FONT_H__ 2 | #define __FONT_H__ 3 | 4 | #include 5 | 6 | #define CH_WIDTH 10 7 | #define CH_HEIGHT 16 8 | 9 | SDL_Surface* str2surface(const char *str, uint32_t fg); 10 | SDL_Surface* str2surface(const char *str, uint32_t fg, uint32_t bg); 11 | SDL_Texture* str2texture(SDL_Renderer *renderer, const char *str, uint32_t fg); 12 | SDL_Texture* str2texture(SDL_Renderer *renderer, const char *str, uint32_t fg, uint32_t bg); 13 | SDL_Surface* ch2surface(uint8_t ch, uint32_t fg); 14 | SDL_Surface* ch2surface(uint8_t ch, uint32_t fg, uint32_t bg); 15 | SDL_Texture* ch2texture(SDL_Renderer *renderer, uint8_t ch, uint32_t fg); 16 | SDL_Texture* ch2texture(SDL_Renderer *renderer, uint8_t ch, uint32_t fg, uint32_t bg); 17 | SDL_Texture* ch2texture_term(uint8_t ch); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /include/keyboard.h: -------------------------------------------------------------------------------- 1 | #ifndef __KEYBOARD_H__ 2 | #define __KEYBOARD_H__ 3 | 4 | #include 5 | #include 6 | 7 | #define NOT_A_KEY -1 8 | #define CLK_NUM 10 9 | #define PS2_START 0 10 | #define PS2_DATA_0 1 11 | #define PS2_DATA_7 8 12 | #define PS2_PARTIAL 9 13 | #define PS2_STOP 10 14 | 15 | #define UINT2_XOR(a) (((a) >> 1) ^ ((a) & 0b1)) 16 | #define UINT4_XOR(a) UINT2_XOR(((a) >> 2) ^ ((a) & 0b11)) 17 | #define UINT8_XOR(a) UINT4_XOR(((a) >> 4) ^ ((a) & 0b1111)) 18 | 19 | class KEYBOARD : public Component{ 20 | private: 21 | std::queue all_keys; 22 | int data_idx; 23 | int left_clk; 24 | int cur_key; 25 | 26 | public: 27 | KEYBOARD(SDL_Renderer *rend, int cnt, int init_val, int ct); 28 | ~KEYBOARD(); 29 | void push_key(uint8_t scancode, bool is_keydown); 30 | virtual void update_state(); 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /include/macro.h: -------------------------------------------------------------------------------- 1 | // macro concatenation 2 | #define concat_temp(x, y) x ## y 3 | #define concat(x, y) concat_temp(x, y) 4 | 5 | // vga macros 6 | #define VGA_POS_EDGE(constr) (concat(vga_, constr) == 1 && concat(vga_pre_, constr) == 0) 7 | #define VGA_NEG_EDGE(constr) (concat(vga_, constr) == 0 && concat(vga_pre_, constr) == 1) 8 | 9 | #if !defined(likely) 10 | #define likely(cond) __builtin_expect(cond, 1) 11 | #define unlikely(cond) __builtin_expect(cond, 0) 12 | #endif 13 | -------------------------------------------------------------------------------- /include/nvboard.h: -------------------------------------------------------------------------------- 1 | #ifndef __NVBOARD_H__ 2 | #define __NVBOARD_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define VERSION_STR "v1.0 (2024.01.10)" 14 | 15 | void set_redraw(); 16 | uint64_t nvboard_get_time(); 17 | 18 | void init_render(SDL_Renderer *renderer); 19 | SDL_Texture* load_pic_texture(SDL_Renderer *renderer, std::string path); 20 | SDL_Texture* new_texture(SDL_Renderer *renderer, int w, int h, int r, int g, int b); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /include/pins.h: -------------------------------------------------------------------------------- 1 | #ifndef __PINS_H__ 2 | #define __PINS_H__ 3 | 4 | #include <../usr/include/pins.h> 5 | #include 6 | #include 7 | 8 | typedef struct PinNode { 9 | void *ptr; 10 | uint8_t data; 11 | uint8_t vector_len; 12 | uint8_t bit_offset; 13 | } PinNode; 14 | extern PinNode pin_array[]; 15 | 16 | static inline uint8_t pin_peek(int pin) { 17 | PinNode *p = &pin_array[pin]; 18 | if (p->vector_len == 1) { 19 | return *(uint8_t *)p->ptr & 1; 20 | } else { 21 | uint64_t v = *(uint64_t *)p->ptr; 22 | return (v >> p->bit_offset) & 1; 23 | } 24 | } 25 | 26 | static inline uint8_t pin_peek8(int pin) { 27 | PinNode *p = &pin_array[pin]; 28 | assert(p->vector_len == 8); 29 | return *(uint8_t *)p->ptr; 30 | } 31 | 32 | static inline void pin_poke(int pin, uint64_t v) { 33 | PinNode *p = &pin_array[pin]; 34 | if (p->vector_len == 1) { 35 | *(uint8_t *)p->ptr = v & 1; 36 | } else { 37 | uint64_t x = *(uint64_t *)p->ptr; 38 | uint64_t mask = 1 << p->bit_offset; 39 | *(uint64_t *)p->ptr = (x & ~mask) | ((v & 1) << p->bit_offset); 40 | } 41 | } 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /include/render.h: -------------------------------------------------------------------------------- 1 | #ifndef __RENDER_H__ 2 | #define __RENDER_H__ 3 | 4 | #include 5 | 6 | #define BOARD_BG_COLOR 0x00008060 7 | 8 | static inline SDL_Point Point(int x, int y) { 9 | return (SDL_Point){ .x = x, .y = y }; 10 | } 11 | 12 | static inline SDL_Point operator+(const SDL_Point &A, const SDL_Point &B) { 13 | return Point(A.x + B.x, A.y + B.y); 14 | } 15 | 16 | static inline SDL_Point operator-(const SDL_Point &A, const SDL_Point &B) { 17 | return Point(A.x - B.x, A.y - B.y); 18 | } 19 | 20 | static inline SDL_Rect Rect(int x, int y, int w, int h) { 21 | return (SDL_Rect){ .x = x, .y = y, .w = w, .h = h }; 22 | } 23 | 24 | static inline SDL_Rect Rect(const SDL_Point &top_left, int w, int h) { 25 | return Rect(top_left.x, top_left.y, w, h); 26 | } 27 | 28 | static inline SDL_Rect Rect(const SDL_Point &top_left, const SDL_Point &size) { 29 | return Rect(top_left, size.x, size.y); 30 | } 31 | 32 | static inline SDL_Rect operator+(const SDL_Rect &A, const SDL_Rect &B) { 33 | return Rect(A.x + B.x, A.y + B.y, A.w + B.w, A.h + B.h); 34 | } 35 | 36 | void draw_thicker_line(SDL_Renderer *renderer, const SDL_Point *point, int n); 37 | void draw_surrounding_line(SDL_Renderer *renderer, SDL_Rect r, int gap); 38 | void draw_str(SDL_Renderer *renderer, const char *str, int x, int y, uint32_t fg); 39 | void draw_str(SDL_Renderer *renderer, const char *str, int x, int y, uint32_t fg, uint32_t bg); 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /include/term.h: -------------------------------------------------------------------------------- 1 | #ifndef __TERM_H__ 2 | #define __TERM_H__ 3 | 4 | class Term { 5 | private: 6 | SDL_Renderer *renderer; 7 | SDL_Rect region; 8 | int w_in_char, h_in_char; 9 | std::vector lines; 10 | int cursor_x, cursor_y; // cursor_y start with all history when scrolling is supported 11 | bool is_cursor_visible; 12 | bool is_focus; 13 | SDL_Texture *cursor_texture; 14 | SDL_Texture *get_focus_cursor_texture; 15 | int screen_y; 16 | bool dirty_screen; 17 | bool *dirty_line; 18 | bool *dirty_char; 19 | 20 | void clear_screen(); 21 | void newline(); 22 | void _return(); 23 | uint8_t *add_line(); 24 | void draw_cursor(); 25 | bool is_cursor_on_screen(); 26 | void set_dirty_char(int y, int x); 27 | void init_dirty(bool val); 28 | 29 | public: 30 | Term(SDL_Renderer *r, int x, int y, int w, int h); 31 | ~Term(); 32 | void feed_ch(uint8_t ch); 33 | void backspace(bool is_input); 34 | void feed_str(const char *s); 35 | void clear(); 36 | void set_cursor_visibility(bool v); 37 | void set_focus(bool v); 38 | void update_gui(); 39 | }; 40 | 41 | #endif 42 | 43 | -------------------------------------------------------------------------------- /include/uart.h: -------------------------------------------------------------------------------- 1 | #ifndef __UART_H__ 2 | #define __UART_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class UART : public Component{ 9 | private: 10 | Term *term; 11 | int tx_state, rx_state; 12 | uint16_t divisor; 13 | uint8_t tx_data, rx_data; 14 | std::string rx_sending_str; 15 | bool need_update_gui; 16 | uint8_t *p_tx; 17 | public: 18 | UART(SDL_Renderer *rend, int cnt, int init_val, int ct, int x, int y, int w, int h); 19 | ~UART(); 20 | void set_divisor(uint16_t d); 21 | 22 | virtual void update_gui(); 23 | virtual void update_state(); 24 | void tx_receive(); 25 | void rx_send(); 26 | void rx_getchar(uint8_t ch); 27 | void term_focus(bool v); 28 | }; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /include/vga.h: -------------------------------------------------------------------------------- 1 | #ifndef __VGA_H__ 2 | #define __VGA_H__ 3 | 4 | #include 5 | 6 | #define VGA_DEFAULT_WIDTH 640 7 | #define VGA_DEFAULT_HEIGHT 480 8 | 9 | enum { //VGA_MOD_ID 10 | VGA_MODE_640_480, NR_VGA_MODE 11 | }; 12 | 13 | struct VGA_MODE{ 14 | int h_frontporch, h_active, h_backporch, h_total; 15 | int v_frontporch, v_active, v_backporch, v_total; 16 | }; 17 | 18 | class VGA : public Component{ 19 | private: 20 | int vga_screen_width, vga_screen_height; 21 | uint32_t *pixels; 22 | int vga_clk_cnt; 23 | uint32_t *p_pixel; 24 | uint32_t *p_pixel_end; 25 | uint8_t *p_r, *p_g, *p_b; 26 | bool is_r_len8, is_g_len8, is_b_len8; 27 | bool is_all_len8; 28 | bool is_pixels_same; 29 | 30 | uint32_t get_pixel_color_slowpath(); 31 | void finish_one_frame(); 32 | 33 | public: 34 | VGA(SDL_Renderer *rend, int cnt, int init_val, int ct); 35 | ~VGA(); 36 | 37 | virtual void update_gui(); 38 | virtual void update_state(); 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /resources/font/FreeMono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NJU-ProjectN/nvboard/fd19d9b07af12e48d442f6057f65a75114cd9755/resources/font/FreeMono.ttf -------------------------------------------------------------------------------- /resources/pic/.gitignore: -------------------------------------------------------------------------------- 1 | !*.png 2 | !*.jpg 3 | !*.jpeg 4 | !*.gif 5 | -------------------------------------------------------------------------------- /resources/pic/vbtn_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NJU-ProjectN/nvboard/fd19d9b07af12e48d442f6057f65a75114cd9755/resources/pic/vbtn_off.png -------------------------------------------------------------------------------- /resources/pic/vbtn_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NJU-ProjectN/nvboard/fd19d9b07af12e48d442f6057f65a75114cd9755/resources/pic/vbtn_on.png -------------------------------------------------------------------------------- /resources/pic/vsw_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NJU-ProjectN/nvboard/fd19d9b07af12e48d442f6057f65a75114cd9755/resources/pic/vsw_off.png -------------------------------------------------------------------------------- /resources/pic/vsw_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NJU-ProjectN/nvboard/fd19d9b07af12e48d442f6057f65a75114cd9755/resources/pic/vsw_on.png -------------------------------------------------------------------------------- /scripts/.gitignore: -------------------------------------------------------------------------------- 1 | !*.mk 2 | !*.py -------------------------------------------------------------------------------- /scripts/auto_pin_bind.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | import os 4 | 5 | class BoardDescParser(): 6 | def __init__(self): 7 | self.pins = {} 8 | 9 | def parseLine(self, lid, lineseg): 10 | direction = lineseg[0].strip() 11 | pinname = lineseg[1].strip() 12 | 13 | if direction != "input" and direction != "output": 14 | print(f"Board Line {lid}: Error: Invalid pin direction \"{direction}\"") 15 | exit(-1) 16 | 17 | self.pins[pinname] = 1 18 | 19 | def parseFile(self, path): 20 | self.pins = {} 21 | with open(path, "r") as f: 22 | for i, line in enumerate(f): 23 | # Remove comment 24 | if '#' in line: 25 | line = line[:line.find('#')] 26 | line = line.strip() 27 | 28 | # Skip empty lines 29 | if line == '': 30 | continue 31 | 32 | lineseg = line.split() 33 | if len(lineseg) == 2: 34 | self.parseLine(i, lineseg) 35 | else: 36 | print(f"Board Line {i}: Error: Invalid line \"{line}\"") 37 | exit(-1) 38 | 39 | def checkPinValid(self, pin): 40 | return pin in self.pins 41 | 42 | 43 | class NxdcParser(): 44 | def __init__(self) -> None: 45 | self.toplevel = "" 46 | self.binds = [] 47 | 48 | def parseToplevel(self, topline): 49 | sptopline = topline.split('=') 50 | if not '=' in topline or sptopline[0].strip() != 'top' or len(sptopline) != 2: 51 | print("Line 1: Error: Invalid top module specification") 52 | print(" Usage: in line 1: top=module_name") 53 | exit(1) 54 | 55 | self.toplevel = sptopline[1].strip(' \n') 56 | 57 | def parseVec(self, lid, line): 58 | # Stupid method to check missing bracelet 59 | if ' (' in line and line.endswith(')'): 60 | signal = line[:line.find('(')].strip() 61 | pins = [u.strip() for u in line[line.find('(')+1:-1].split(',') if u.strip() != ''] 62 | 63 | self.binds.append( (signal, pins) ) 64 | else: 65 | print(f"Line {lid+1}: Error: Invalid multi-pin assignment") 66 | exit(1) 67 | 68 | def parsePin(self, lid, line): 69 | spline = line.split(' ') 70 | if len(spline) == 2: 71 | signal = spline[0].strip() 72 | pin = spline[1].strip() 73 | 74 | self.binds.append( (signal, pin) ) 75 | else: 76 | print(f"Line {lid+1}: Error: Invalid line") 77 | exit(1) 78 | 79 | def parseFile(self, path): 80 | cons_f = open(path, "r") 81 | 82 | self.parseToplevel(cons_f.readline()) 83 | 84 | for i, line in enumerate(cons_f): 85 | # Remove comment 86 | if '#' in line: 87 | line = line[:line.find('#')] 88 | line = line.strip() 89 | 90 | # Skip empty lines 91 | if line == '': 92 | continue 93 | 94 | # Multi-pin assignment 95 | if ' (' in line or ')' in line: 96 | self.parseVec(i, line) 97 | # Single pin assignment 98 | elif ' ' in line: 99 | self.parsePin(i, line) 100 | else: 101 | # Single word line, ignore to comply with previous version 102 | pass 103 | 104 | 105 | class IndentWriter(): 106 | def __init__(self): 107 | self.fp = None 108 | self.indent_level = 0 109 | self.newline = True 110 | self.inside_comment = False 111 | 112 | def open(self, filename): 113 | self.fp = open(filename, 'w') 114 | self.indent_level = 0 115 | self.newline = True 116 | 117 | def close(self): 118 | self.fp.close() 119 | self.fp = None 120 | 121 | def writeline(self, wrline): 122 | if len(wrline) == 0: 123 | return 124 | 125 | if '//' in wrline: 126 | # Check indent before comment and proceed 127 | cmtInd = wrline.find('//') 128 | self.writeline(wrline[:cmtInd]) 129 | self.inside_comment = True 130 | wrline = wrline[cmtInd:] 131 | 132 | if not self.inside_comment: 133 | if '{' in wrline: 134 | indclose = wrline[:wrline.find('{')].count('}') 135 | else: 136 | indclose = wrline.count('}') 137 | 138 | if indclose > self.indent_level: 139 | print("Internal Error: Indent level closed beyond 0") 140 | exit(-1) 141 | 142 | if self.newline: 143 | self.fp.write('\t'*(self.indent_level-indclose) ) 144 | 145 | self.fp.write(wrline) 146 | 147 | if not self.inside_comment: 148 | self.indent_level += wrline.count('{') - wrline.count('}') 149 | 150 | self.newline = wrline[-1] == '\n' 151 | if self.newline: 152 | self.inside_comment = False 153 | 154 | def write(self, wrstr): 155 | wrlines = wrstr.split('\n') 156 | for line in wrlines[:-1]: 157 | self.writeline(line+'\n') 158 | self.writeline(wrlines[-1]) 159 | 160 | 161 | class AutoBindWriter(): 162 | def __init__(self, iwriter, board): 163 | self.iw = iwriter 164 | self.board = board 165 | 166 | def bindPin(self, signal, pin): 167 | if not self.board.checkPinValid(pin): 168 | print(f"Error: Invalid pin {pin}") 169 | exit(1) 170 | self.iw.write(f"nvboard_bind_pin( &top->{signal}, 1, {pin});\n") 171 | 172 | def bindVec(self, signal, pins): 173 | for pin in pins: 174 | if not self.board.checkPinValid(pin): 175 | print(f"Error: Invalid pin {pin}") 176 | exit(1) 177 | self.iw.write(f"nvboard_bind_pin( &top->{signal}, {len(pins)}") 178 | for pin in pins: 179 | self.iw.write(f", {pin}") 180 | self.iw.write(");\n") 181 | 182 | def bind(self, signal, pin): 183 | if type(pin) is list: 184 | self.bindVec(signal, pin) 185 | elif type(pin) is str: 186 | self.bindPin(signal, pin) 187 | else: 188 | print(f"Error: Invalid bind from {signal} to {pin}") 189 | exit(-1) 190 | 191 | def writeHead(self, top): 192 | self.iw.write( ( 193 | "#include \n" 194 | f'#include "V{top}.h"\n' 195 | "\n" 196 | f"void nvboard_bind_all_pins(V{top}* top) {{\n" 197 | ) ) 198 | 199 | def writeTail(self): 200 | self.iw.write("}\n") 201 | 202 | def print_usage(): 203 | print("Usage: python3 auto_pin_bind.py nxdc_constraint_file_path auto_bind_c_output_file_path") 204 | 205 | if __name__ == "__main__": 206 | if len(sys.argv) != 3: 207 | print("Error: Bad command line arguments") 208 | print_usage() 209 | exit(-1) 210 | 211 | nvboard_path = os.environ.get('NVBOARD_HOME') 212 | if nvboard_path is None: 213 | print("Error: NVBOARD_HOME is not set") 214 | exit(-1) 215 | 216 | cons_path = sys.argv[1] 217 | output_path = sys.argv[2] 218 | boardfile_path = os.path.join(nvboard_path, "board/N4") 219 | 220 | if not os.path.exists(cons_path): 221 | print(f"Error: Constraint file doesn't exist:") 222 | print(f" {cons_path}") 223 | exit(-1) 224 | 225 | if not os.path.exists(boardfile_path): 226 | print(f"Error: Board file doesn't exist:") 227 | print(f" {boardfile_path}") 228 | exit(-1) 229 | 230 | # Parse board descriptor for pin list 231 | board = BoardDescParser() 232 | board.parseFile(boardfile_path) 233 | 234 | # Parse nxdc constraint file 235 | constr = NxdcParser() 236 | constr.parseFile(cons_path) 237 | # Write bind file with indent 238 | bind_wr = IndentWriter() 239 | bind_wr.open(output_path) 240 | 241 | abw = AutoBindWriter(bind_wr, board) 242 | 243 | abw.writeHead(constr.toplevel) 244 | for signal, pin in constr.binds: 245 | abw.bind(signal, pin) 246 | abw.writeTail() 247 | 248 | bind_wr.close() 249 | -------------------------------------------------------------------------------- /scripts/nvboard.mk: -------------------------------------------------------------------------------- 1 | # files of NVBoard 2 | NVBOARD_SRC = $(NVBOARD_HOME)/src 3 | NVBOARD_SRCS := $(shell find $(NVBOARD_SRC) -name "*.cpp") 4 | NVBOARD_INC = $(NVBOARD_HOME)/include 5 | NVBOARD_USR_INC = $(NVBOARD_HOME)/usr/include 6 | INC_PATH += $(NVBOARD_USR_INC) 7 | 8 | NVBOARD_BUILD_DIR = $(NVBOARD_HOME)/build 9 | NVBOARD_OBJS := $(addprefix $(NVBOARD_BUILD_DIR)/, $(addsuffix .o, $(basename $(notdir $(NVBOARD_SRCS))))) 10 | 11 | # The archive of NVBoard 12 | NVBOARD_ARCHIVE = $(NVBOARD_BUILD_DIR)/nvboard.a 13 | CXXFLAGS += -MMD -O3 $(shell sdl2-config --cflags) 14 | 15 | $(NVBOARD_BUILD_DIR)/%.o: $(NVBOARD_SRC)/%.cpp 16 | @echo + CXX "->" NVBOARD_HOME/$(shell realpath $< --relative-to $(NVBOARD_HOME)) 17 | @mkdir -p $(dir $@) 18 | @$(CXX) -I$(NVBOARD_INC) $(CXXFLAGS) -c -o $@ $< 19 | 20 | # Build the archive of NVBoard 21 | $(NVBOARD_ARCHIVE): $(NVBOARD_OBJS) 22 | @echo + AR "->" $(shell realpath $@ --relative-to $(NVBOARD_HOME)) 23 | @ar rcs $(NVBOARD_ARCHIVE) $(NVBOARD_OBJS) 24 | 25 | # Rule (`#include` dependencies): paste in `.d` files generated by gcc on `-MMD` 26 | -include $(NVBOARD_OBJS:.o=.d) 27 | 28 | # Link flags for examples 29 | LDFLAGS += $(shell sdl2-config --libs) -lSDL2_image -lSDL2_ttf 30 | 31 | .PHONY: nvboard-archive nvboard-clean 32 | 33 | nvboard-archive: $(NVBOARD_ARCHIVE) 34 | 35 | nvboard-clean: 36 | rm -rf $(NVBOARD_BUILD_DIR) 37 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | !*.c 2 | !*.cc 3 | !*.cpp 4 | -------------------------------------------------------------------------------- /src/at_scancode.h: -------------------------------------------------------------------------------- 1 | #ifndef _VFPGA_AT_SCANCODE_H 2 | #define _VFPGA_AT_SCANCODE_H 3 | 4 | #include 5 | 6 | #define AT_SCANCODE_A 0x1C 7 | #define AT_SCANCODE_B 0x32 8 | #define AT_SCANCODE_C 0x21 9 | #define AT_SCANCODE_D 0x23 10 | #define AT_SCANCODE_E 0x24 11 | #define AT_SCANCODE_F 0x2B 12 | #define AT_SCANCODE_G 0x34 13 | #define AT_SCANCODE_H 0x33 14 | #define AT_SCANCODE_I 0x43 15 | #define AT_SCANCODE_J 0x3B 16 | #define AT_SCANCODE_K 0x42 17 | #define AT_SCANCODE_L 0x4B 18 | #define AT_SCANCODE_M 0x3A 19 | #define AT_SCANCODE_N 0x31 20 | #define AT_SCANCODE_O 0x44 21 | #define AT_SCANCODE_P 0x4D 22 | #define AT_SCANCODE_Q 0x15 23 | #define AT_SCANCODE_R 0x2D 24 | #define AT_SCANCODE_S 0x1B 25 | #define AT_SCANCODE_T 0x2C 26 | #define AT_SCANCODE_U 0x3C 27 | #define AT_SCANCODE_V 0x2A 28 | #define AT_SCANCODE_W 0x1D 29 | #define AT_SCANCODE_X 0x22 30 | #define AT_SCANCODE_Y 0x35 31 | #define AT_SCANCODE_Z 0x1A 32 | #define AT_SCANCODE_0 0x45 33 | #define AT_SCANCODE_1 0x16 34 | #define AT_SCANCODE_2 0x1E 35 | #define AT_SCANCODE_3 0x26 36 | #define AT_SCANCODE_4 0x25 37 | #define AT_SCANCODE_5 0x2E 38 | #define AT_SCANCODE_6 0x36 39 | #define AT_SCANCODE_7 0x3D 40 | #define AT_SCANCODE_8 0x3E 41 | #define AT_SCANCODE_9 0x46 42 | 43 | #define AT_SCANCODE_GRAVE 0x0E 44 | #define AT_SCANCODE_MINUS 0x4E 45 | #define AT_SCANCODE_EQUALS 0x55 46 | #define AT_SCANCODE_BACKSLASH 0x5D 47 | #define AT_SCANCODE_BACKSPACE 0x66 48 | #define AT_SCANCODE_SPACE 0x29 49 | #define AT_SCANCODE_TAB 0x0D 50 | #define AT_SCANCODE_CAPSLOCK 0x58 51 | #define AT_SCANCODE_LSHIFT 0x12 52 | #define AT_SCANCODE_LCTRL 0x14 53 | #define AT_SCANCODE_LGUI 0xE0,0x1F 54 | #define AT_SCANCODE_LALT 0x11 55 | #define AT_SCANCODE_RSHIFT 0x59 56 | #define AT_SCANCODE_RCTRL 0xE0,0x14 57 | #define AT_SCANCODE_RGUI 0xE0,0x27 58 | #define AT_SCANCODE_RALT 0xE0,0x11 59 | #define AT_SCANCODE_APPLICATION 0xE0,0x2F 60 | #define AT_SCANCODE_RETURN 0x5A 61 | #define AT_SCANCODE_ESCAPE 0x76 62 | #define AT_SCANCODE_F1 0x5 63 | #define AT_SCANCODE_F2 0x6 64 | #define AT_SCANCODE_F3 0x4 65 | #define AT_SCANCODE_F4 0x0C 66 | #define AT_SCANCODE_F5 0x3 67 | #define AT_SCANCODE_F6 0x0B 68 | #define AT_SCANCODE_F7 0x83 69 | #define AT_SCANCODE_F8 0x0A 70 | #define AT_SCANCODE_F9 0x1 71 | #define AT_SCANCODE_F10 0x9 72 | #define AT_SCANCODE_F11 0x78 73 | #define AT_SCANCODE_F12 0x7 74 | #define AT_SCANCODE_PRINTSCREEN 0xE0,0x12 75 | // SCRN E0,7C (?) 76 | 77 | /*SDL_SCANCODE_PAUSE E1,14,77, E1,F0,14, F0,77 not supported */ 78 | 79 | #define AT_SCANCODE_SCROLLLOCK 0x7E 80 | #define AT_SCANCODE_LEFTBRACKET 0x54 81 | #define AT_SCANCODE_INSERT 0xE0,0x70 82 | #define AT_SCANCODE_HOME 0xE0,0x6C 83 | #define AT_SCANCODE_PAGEUP 0xE0,0x7D 84 | #define AT_SCANCODE_DELETE 0xE0,0x71 85 | #define AT_SCANCODE_END 0xE0,0x69 86 | #define AT_SCANCODE_PAGEDOWN 0xE0,0x7A 87 | #define AT_SCANCODE_UP 0xE0,0x75 88 | #define AT_SCANCODE_LEFT 0xE0,0x6B 89 | #define AT_SCANCODE_DOWN 0xE0,0x72 90 | #define AT_SCANCODE_RIGHT 0xE0,0x74 91 | #define AT_SCANCODE_NUMLOCKCLEAR 0x77 92 | #define AT_SCANCODE_KP_DIVIDE 0xE0,0x4A 93 | #define AT_SCANCODE_KP_MULTIPLY 0x7C 94 | #define AT_SCANCODE_KP_MINUS 0x7B 95 | #define AT_SCANCODE_KP_PLUS 0x79 96 | #define AT_SCANCODE_KP_ENTER 0xE0,0x5A 97 | #define AT_SCANCODE_KP_PERIOD 0x71 98 | #define AT_SCANCODE_KP_0 0x70 99 | #define AT_SCANCODE_KP_1 0x69 100 | #define AT_SCANCODE_KP_2 0x72 101 | #define AT_SCANCODE_KP_3 0x7A 102 | #define AT_SCANCODE_KP_4 0x6B 103 | #define AT_SCANCODE_KP_5 0x73 104 | #define AT_SCANCODE_KP_6 0x74 105 | #define AT_SCANCODE_KP_7 0x6C 106 | #define AT_SCANCODE_KP_8 0x75 107 | #define AT_SCANCODE_KP_9 0x7D 108 | #define AT_SCANCODE_RIGHTBRACKET 0x5B 109 | #define AT_SCANCODE_SEMICOLON 0x4C 110 | #define AT_SCANCODE_APOSTROPHE 0x52 111 | #define AT_SCANCODE_COMMA 0x41 112 | #define AT_SCANCODE_PERIOD 0x49 113 | #define AT_SCANCODE_SLASH 0x4A 114 | 115 | #define MAP(c, f) c(f) 116 | #define AT_PREFIX(a) concat(AT_SCANCODE, a) 117 | #define SDL_PREFIX(a) concat(SDL_SCANCODE, a) 118 | 119 | 120 | // count arguments 121 | #define NARGS(...) NARGS_(__VA_ARGS__, 2, 2, 2, 1, 0) 122 | #define NARGS_(_4, _3, _2, _1, N, ...) N 123 | #define GET_FIRST(...) GET_ELEM(1, _, __VA_ARGS__) 124 | #define GET_SECOND(...) GET_ELEM(NARGS(__VA_ARGS__), _, __VA_ARGS__ ,,,,,,,,,,,) 125 | 126 | #define GET_ELEM(N, ...) concat(GET_ELEM_, N)(__VA_ARGS__) 127 | #define GET_ELEM_0(_0, ...) _0 128 | #define GET_ELEM_1(_0, _1, ...) _1 129 | #define GET_ELEM_2(_0, _1, _2, ...) _2 130 | #define GET_ELEM_3(_0, _1, _2, _3, ...) _3 131 | #define GET_ELEM_4(_0, _1, _2, _3, _4, ...) _4 132 | 133 | #define SCANCODE_LIST(f) \ 134 | f(_A) f(_B) f(_C) f(_D) f(_E) f(_F) f(_G) f(_H) f(_I) f(_J) f(_K) f(_L) f(_M) \ 135 | f(_N) f(_O) f(_P) f(_Q) f(_R) f(_S) f(_T) f(_U) f(_V) f(_W) f(_X) f(_Y) f(_Z) \ 136 | f(_0) f(_1) f(_2) f(_3) f(_4) f(_5) f(_6) f(_7) f(_8) f(_9) \ 137 | f(_GRAVE) f(_MINUS) f(_EQUALS) f(_BACKSLASH) f(_BACKSPACE) f(_SPACE) f(_TAB) f(_CAPSLOCK) \ 138 | f(_LSHIFT) f(_LCTRL) f(_LGUI) f(_LALT) f(_RSHIFT) f(_RCTRL) f(_RGUI) f(_RALT) \ 139 | f(_APPLICATION) f(_RETURN) f(_ESCAPE) \ 140 | f(_F1) f(_F2) f(_F3) f(_F4) f(_F5) f(_F6) f(_F7) f(_F8) f(_F9) f(_F10) f(_F11) f(_F12) \ 141 | f(_PRINTSCREEN) f(_SCROLLLOCK) f(_LEFTBRACKET) f(_INSERT) f(_HOME) f(_PAGEUP) f(_DELETE) f(_END) \ 142 | f(_PAGEDOWN) f(_UP) f(_LEFT) f(_DOWN) f(_RIGHT) f(_NUMLOCKCLEAR) \ 143 | f(_KP_DIVIDE) f(_KP_MULTIPLY) f(_KP_MINUS) f(_KP_PLUS) f(_KP_ENTER) f(_KP_PERIOD) \ 144 | f(_KP_0) f(_KP_1) f(_KP_2) f(_KP_3) f(_KP_4) f(_KP_5) f(_KP_6) f(_KP_7) f(_KP_8) f(_KP_9) \ 145 | f(_RIGHTBRACKET) f(_SEMICOLON) f(_APOSTROPHE) f(_COMMA) f(_PERIOD) f(_SLASH) 146 | 147 | 148 | #endif -------------------------------------------------------------------------------- /src/button.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define BTNC_X 520 4 | #define BTNC_Y 250 5 | #define BTNC_SEP 20 6 | #define BTNC_WIDTH 30 7 | #define BTNC_HEIGHT 30 8 | 9 | static const SDL_Rect btn_rects[6] = { 10 | {BTNC_X, BTNC_Y, BTNC_WIDTH, BTNC_HEIGHT }, // C 11 | {BTNC_X, BTNC_Y - BTNC_HEIGHT - BTNC_SEP, BTNC_WIDTH, BTNC_HEIGHT }, // U 12 | {BTNC_X, BTNC_Y + BTNC_HEIGHT + BTNC_SEP, BTNC_WIDTH, BTNC_HEIGHT }, // D 13 | {BTNC_X - BTNC_WIDTH - BTNC_SEP, BTNC_Y, BTNC_WIDTH, BTNC_HEIGHT }, // L 14 | {BTNC_X + BTNC_WIDTH + BTNC_SEP, BTNC_Y, BTNC_WIDTH, BTNC_HEIGHT }, // R 15 | {BTNC_X + BTNC_WIDTH + BTNC_SEP/2, BTNC_Y - 2 * (BTNC_HEIGHT + BTNC_SEP), BTNC_WIDTH, BTNC_HEIGHT } // RST, but not draw 16 | }; 17 | 18 | static void init_render_local(SDL_Renderer *renderer) { 19 | // draw surrounding lines 20 | const int gap = 8; 21 | int w = (BTNC_WIDTH + BTNC_SEP) * 3 - BTNC_SEP; 22 | draw_surrounding_line(renderer, Rect(btn_rects[3].x, btn_rects[1].y, w, w), gap); 23 | 24 | // draw indices for each button 25 | const char *str = "CUDLR"; 26 | char buf[2] = "?"; 27 | for (int i = 0; i < 5; i ++) { 28 | SDL_Point p = Point(btn_rects[i].x, btn_rects[i].y) + Point(BTNC_WIDTH + 2, BTNC_HEIGHT / 2) 29 | - Point(0, CH_HEIGHT / 2); 30 | buf[0] = str[i]; 31 | draw_str(renderer, buf, p.x, p.y, 0xffffff, BOARD_BG_COLOR); 32 | } 33 | 34 | // draw label 35 | str = "Button Pad"; 36 | SDL_Point p = Point(btn_rects[3].x, btn_rects[1].y) - Point(0, gap) - Point(0, CH_HEIGHT / 2) 37 | + Point(w / 2, 0) - Point(CH_WIDTH * strlen(str) / 2, 0); 38 | draw_str(renderer, str, p.x, p.y, 0xffffff, BOARD_BG_COLOR); 39 | } 40 | 41 | void init_button(SDL_Renderer *renderer) { 42 | SDL_Texture *tbutton_on = load_pic_texture(renderer, VBTN_ON_PATH); 43 | SDL_Texture *tbutton_off = load_pic_texture(renderer, VBTN_OFF_PATH); 44 | init_render_local(renderer); 45 | 46 | for (int i = 0; i < 5; ++i) { 47 | Component *ptr = new Component(renderer, 2, 0, BUTTON_TYPE); 48 | 49 | // off 50 | SDL_Rect *rect_ptr = new SDL_Rect; 51 | *rect_ptr = btn_rects[i]; 52 | ptr->set_rect(rect_ptr, 0); 53 | ptr->set_texture(tbutton_off, 0); 54 | 55 | // on 56 | rect_ptr = new SDL_Rect; 57 | *rect_ptr = btn_rects[i]; 58 | ptr->set_rect(rect_ptr, 1); 59 | ptr->set_texture(tbutton_on, 1); 60 | 61 | ptr->add_pin(BTNC + i); 62 | add_component(ptr); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/component.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | Component::Component(SDL_Renderer *rend, int cnt, int init_val, int ct) { 5 | m_renderer = rend; 6 | m_component_type = ct; 7 | m_rects.resize(cnt); 8 | m_textures.resize(cnt); 9 | m_state = init_val; 10 | } 11 | 12 | bool Component::in_rect(int x, int y) const{ 13 | SDL_Rect *temp = m_rects[0]; 14 | return x >= temp->x && y >= temp->y 15 | && x < temp->x + temp->w 16 | && y < temp->y + temp->h; 17 | } 18 | 19 | SDL_Renderer *Component::get_renderer() const{ 20 | return m_renderer; 21 | } 22 | 23 | int Component::get_component_type() const{ 24 | return m_component_type; 25 | } 26 | 27 | SDL_Rect *Component::get_rect(int idx) const{ 28 | return m_rects[idx]; 29 | } 30 | 31 | SDL_Texture *Component::get_texture(int idx) const{ 32 | return m_textures[idx]; 33 | } 34 | 35 | int Component::get_state() const{ 36 | return m_state; 37 | } 38 | 39 | uint16_t Component::get_pin(int idx) const{ 40 | return pins[idx]; 41 | } 42 | 43 | void Component::set_rect(SDL_Rect *rect, int val) { 44 | m_rects[val] = rect; 45 | } 46 | 47 | void Component::set_texture(SDL_Texture *texture, int val) { 48 | m_textures[val] = texture; 49 | } 50 | 51 | void Component::set_state(int val) { 52 | m_state = val; 53 | } 54 | 55 | void Component::add_pin(const uint16_t pin) { 56 | pins.push_back(pin); 57 | } 58 | 59 | void Component::update_gui() { 60 | SDL_RenderCopy(m_renderer, m_textures[m_state], NULL, m_rects[m_state]); 61 | set_redraw(); 62 | } 63 | 64 | void Component::update_state() { 65 | uint16_t pin = *(pins.begin()); 66 | int newval = pin_peek(pin); 67 | if (newval != m_state) { 68 | set_state(newval); 69 | update_gui(); 70 | } 71 | } 72 | 73 | void Component::remove() { 74 | for (auto rect_ptr : m_rects) { delete rect_ptr; } 75 | } 76 | 77 | #if 0 78 | RGB_LED::RGB_LED(SDL_Renderer *rend, int cnt, int init_val, int ct) 79 | : Component(rend, cnt, init_val, ct){} 80 | 81 | void RGB_LED::update_gui() { 82 | SDL_RenderCopy(get_renderer(), get_texture(get_state()), NULL, get_rect(get_state())); 83 | set_redraw(); 84 | } 85 | 86 | void RGB_LED::update_state() { 87 | int newval = 0; 88 | for (int i = 0; i < 3; ++i) { 89 | newval = (newval << 1) | pin_peek(get_pin(i)); 90 | } 91 | if (newval != get_state()) { 92 | set_state(newval); 93 | update_gui(); 94 | } 95 | } 96 | #endif 97 | 98 | void init_components(SDL_Renderer *renderer) { 99 | #define COMPONENT_LIST(f) f(led) f(switch) f(button) f(segs7) f(keyboard) f(vga) f(uart) 100 | #define INIT_FN(c) { void concat(init_, c)(SDL_Renderer *); concat(init_, c)(renderer); } 101 | COMPONENT_LIST(INIT_FN); 102 | } 103 | 104 | std::vector components; 105 | 106 | void add_component(Component *c) { 107 | components.push_back(c); 108 | } 109 | 110 | void delete_components() { 111 | for (auto comp_ptr : components) { 112 | comp_ptr->remove(); 113 | delete comp_ptr; 114 | } 115 | components.clear(); 116 | } 117 | 118 | // render buttons, switches, leds and 7-segs 119 | void init_gui(SDL_Renderer *renderer) { 120 | for (auto ptr : components) { ptr->update_gui(); } 121 | } 122 | 123 | void update_components(SDL_Renderer *renderer) { 124 | for (auto ptr : components) { ptr->update_state(); } 125 | } 126 | -------------------------------------------------------------------------------- /src/event.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | extern std::vector components; 6 | void uart_rx_getchar(uint8_t ch); 7 | void uart_term_focus(bool v); 8 | void kb_push_key(uint8_t scancode, bool is_keydown); 9 | static bool uart_term_get_focus = false; 10 | 11 | static void mousedown_handler(const SDL_Event &ev) { 12 | int x_pos = ev.button.x; 13 | int y_pos = ev.button.y; 14 | bool click_uart_term = false; 15 | for (auto i : components) { 16 | if (i->in_rect(x_pos, y_pos)) { 17 | switch (i->get_component_type()) { 18 | case BUTTON_TYPE: pin_poke(i->get_pin(), 1); break; 19 | case SWITCH_TYPE: pin_poke(i->get_pin(), i->get_state() ^ 1); break; 20 | case UART_TYPE: click_uart_term = true; break; 21 | } 22 | } 23 | } 24 | if (uart_term_get_focus ^ click_uart_term) { 25 | if (click_uart_term) SDL_StartTextInput(); 26 | else SDL_StopTextInput(); 27 | uart_term_get_focus = click_uart_term; 28 | uart_term_focus(uart_term_get_focus); 29 | } 30 | } 31 | 32 | static void mouseup_handler(const SDL_Event &ev) { 33 | int x_pos = ev.button.x; 34 | int y_pos = ev.button.y; 35 | for (auto i : components) { 36 | if (i->in_rect(x_pos, y_pos)) { 37 | switch (i->get_component_type()) { 38 | case BUTTON_TYPE: pin_poke(i->get_pin(), 0); break; 39 | } 40 | } 41 | } 42 | } 43 | 44 | void read_event() { 45 | SDL_Event ev; 46 | while (SDL_PollEvent(&ev)) { 47 | switch (ev.type) { 48 | case SDL_QUIT: exit(0); 49 | case SDL_WINDOWEVENT: 50 | if (ev.window.event == SDL_WINDOWEVENT_CLOSE) { exit(0); } 51 | break; 52 | case SDL_MOUSEBUTTONDOWN: mousedown_handler(ev); break; 53 | case SDL_MOUSEBUTTONUP: mouseup_handler(ev); break; 54 | case SDL_KEYDOWN: 55 | if (uart_term_get_focus) { 56 | switch (ev.key.keysym.sym) { 57 | case SDLK_RETURN: uart_rx_getchar('\n'); break; 58 | case SDLK_BACKSPACE: uart_rx_getchar('\b'); break; 59 | } 60 | } 61 | case SDL_KEYUP: 62 | if (!uart_term_get_focus) kb_push_key(ev.key.keysym.scancode, ev.key.type == SDL_KEYDOWN); 63 | break; 64 | case SDL_TEXTINPUT: if (uart_term_get_focus) uart_rx_getchar(ev.text.text[0]); break; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/font.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static TTF_Font *font = NULL; 5 | static SDL_Texture* font_texture_term[128] = { NULL }; 6 | SDL_Texture* surface2texture(SDL_Renderer *renderer, SDL_Surface *s); 7 | SDL_Texture *nvboard_texture = NULL; 8 | 9 | void init_font(SDL_Renderer *renderer) { 10 | int ret = TTF_Init(); 11 | assert(ret != -1); 12 | std::string nvboard_home = getenv("NVBOARD_HOME"); 13 | 14 | font = TTF_OpenFont((nvboard_home + "/resources/font/" + "FreeMono.ttf").c_str(), 48); 15 | assert(font != NULL); 16 | TTF_SetFontHinting(font, TTF_HINTING_MONO); 17 | TTF_SetFontStyle(font, TTF_STYLE_BOLD); 18 | nvboard_texture = str2texture(renderer, "NVBoard", 0xffffff, BOARD_BG_COLOR); 19 | 20 | TTF_SetFontSize(font, CH_HEIGHT); 21 | SDL_Color fg = {.r = 0x00, .g = 0x00, .b = 0x00 }; 22 | SDL_Color bg = {.r = 0xff, .g = 0xff, .b = 0xff }; 23 | for (int i = 1; i < 128; i ++) { 24 | SDL_Surface *s = TTF_RenderGlyph_Shaded(font, i, fg, bg); 25 | assert(s->w == CH_WIDTH); 26 | assert(s->h == CH_HEIGHT); 27 | font_texture_term[i] = surface2texture(renderer, s); 28 | } 29 | } 30 | 31 | SDL_Surface* str2surface(const char *str, uint32_t fg) { 32 | SDL_Color c_fg = {.r = (uint8_t)(fg >> 16), .g = (uint8_t)(fg >> 8), .b = (uint8_t)fg }; 33 | SDL_Surface *s = TTF_RenderText_Blended_Wrapped(font, str, c_fg, 0); 34 | assert(s != NULL); 35 | return s; 36 | } 37 | 38 | SDL_Surface* str2surface(const char *str, uint32_t fg, uint32_t bg) { 39 | SDL_Color c_fg = {.r = (uint8_t)(fg >> 16), .g = (uint8_t)(fg >> 8), .b = (uint8_t)fg }; 40 | SDL_Color c_bg = {.r = (uint8_t)(bg >> 16), .g = (uint8_t)(bg >> 8), .b = (uint8_t)bg }; 41 | SDL_Surface *s = TTF_RenderText_Shaded_Wrapped(font, str, c_fg, c_bg, 0); 42 | assert(s != NULL); 43 | return s; 44 | } 45 | 46 | SDL_Texture* str2texture(SDL_Renderer *renderer, const char *str, uint32_t fg) { 47 | return surface2texture(renderer, str2surface(str, fg)); 48 | } 49 | 50 | SDL_Texture* str2texture(SDL_Renderer *renderer, const char *str, uint32_t fg, uint32_t bg) { 51 | return surface2texture(renderer, str2surface(str, fg, bg)); 52 | } 53 | 54 | SDL_Surface* ch2surface(uint8_t ch, uint32_t fg) { 55 | SDL_Color c_fg = {.r = (uint8_t)(fg >> 16), .g = (uint8_t)(fg >> 8), .b = (uint8_t)fg }; 56 | SDL_Surface *s = TTF_RenderGlyph_Blended(font, ch, c_fg); 57 | assert(s != NULL); 58 | return s; 59 | } 60 | 61 | SDL_Surface* ch2surface(uint8_t ch, uint32_t fg, uint32_t bg) { 62 | SDL_Color c_fg = {.r = (uint8_t)(fg >> 16), .g = (uint8_t)(fg >> 8), .b = (uint8_t)fg }; 63 | SDL_Color c_bg = {.r = (uint8_t)(bg >> 16), .g = (uint8_t)(bg >> 8), .b = (uint8_t)bg }; 64 | SDL_Surface *s = TTF_RenderGlyph_Shaded(font, ch, c_fg, c_bg); 65 | assert(s != NULL); 66 | return s; 67 | } 68 | 69 | SDL_Texture* ch2texture(SDL_Renderer *renderer, uint8_t ch, uint32_t fg) { 70 | return surface2texture(renderer, ch2surface(ch, fg)); 71 | } 72 | 73 | SDL_Texture* ch2texture(SDL_Renderer *renderer, uint8_t ch, uint32_t fg, uint32_t bg) { 74 | return surface2texture(renderer, ch2surface(ch, fg, bg)); 75 | } 76 | 77 | SDL_Texture* ch2texture_term(uint8_t ch) { 78 | assert(ch < 128); 79 | return font_texture_term[ch == 0 ? ' ' : ch]; 80 | } 81 | 82 | void close_font() { 83 | TTF_CloseFont(font); 84 | TTF_Quit(); 85 | } 86 | -------------------------------------------------------------------------------- /src/keyboard.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "at_scancode.h" 4 | 5 | typedef struct { 6 | SDL_Texture *t_up, *t_down; 7 | SDL_Rect rect; 8 | uint8_t map0, map1; 9 | bool pressing; 10 | } Key; 11 | 12 | static KEYBOARD* kb = NULL; 13 | bool is_kb_idle = true; 14 | static Key keys[256] = {}; 15 | 16 | KEYBOARD::KEYBOARD(SDL_Renderer *rend, int cnt, int init_val, int ct): 17 | Component(rend, cnt, init_val, ct), 18 | data_idx(0), left_clk(0), cur_key(NOT_A_KEY) { } 19 | 20 | 21 | void KEYBOARD::push_key(uint8_t sdl_key, bool is_keydown){ 22 | Key *e = &keys[sdl_key]; 23 | uint8_t at_key = e->map0; 24 | if(at_key == 0xe0){ 25 | all_keys.push(0xe0); 26 | at_key = e->map1; 27 | } 28 | if(!is_keydown) all_keys.push(0xf0); 29 | all_keys.push(at_key); 30 | is_kb_idle = false; 31 | 32 | if (e->pressing != is_keydown) { 33 | e->pressing = is_keydown; 34 | SDL_RenderCopy(get_renderer(), (is_keydown ? e->t_down : e->t_up), NULL, &e->rect); 35 | set_redraw(); 36 | } 37 | } 38 | 39 | void KEYBOARD::update_state(){ 40 | if(cur_key == NOT_A_KEY){ 41 | if(all_keys.empty()) { 42 | is_kb_idle = true; 43 | return; 44 | } 45 | cur_key = all_keys.front(); 46 | assert(data_idx == 0); 47 | left_clk = CLK_NUM; 48 | } 49 | 50 | if(left_clk == 0){ 51 | uint8_t ps2_clk = pin_peek(PS2_CLK); 52 | ps2_clk = !ps2_clk; 53 | pin_poke(PS2_CLK, ps2_clk); 54 | left_clk = CLK_NUM; 55 | if(ps2_clk){ 56 | assert(!all_keys.empty()); 57 | uint8_t ps2_dat = (data_idx == PS2_PARTIAL) ? !UINT8_XOR(all_keys.front()) : \ 58 | (data_idx == PS2_STOP) ? 1 : \ 59 | ((data_idx >= PS2_DATA_0) && (data_idx <= PS2_DATA_7)) ? (cur_key & 1) : 0; 60 | pin_poke(PS2_DAT, ps2_dat); 61 | if((data_idx >= PS2_DATA_0) && (data_idx <= PS2_DATA_7)) cur_key >>= 1; 62 | data_idx ++; 63 | } else if(data_idx == 11){ 64 | data_idx = 0; 65 | cur_key = NOT_A_KEY; 66 | all_keys.pop(); 67 | } 68 | } 69 | else{ 70 | left_clk --; 71 | } 72 | } 73 | 74 | static SDL_Surface* new_key_shape(int w, int h) { 75 | SDL_Surface *s = SDL_CreateRGBSurface(0, w, h, 32, 0xff0000, 0x00ff00, 0x0000ff, 0xff000000); 76 | uint32_t black = SDL_MapRGBA(s->format, 0, 0, 0, 0xff); 77 | SDL_Rect r; 78 | r = (SDL_Rect){.x = 0, .y = 0, .w = 1, .h = h}; SDL_FillRect(s, &r, black); 79 | r = (SDL_Rect){.x = 0, .y = 0, .w = w, .h = 1}; SDL_FillRect(s, &r, black); 80 | r = (SDL_Rect){.x = w - 1, .y = 0, .w = 1, .h = h}; SDL_FillRect(s, &r, black); 81 | r = (SDL_Rect){.x = 0, .y = h - 1, .w = w, .h = 1}; SDL_FillRect(s, &r, black); 82 | return s; 83 | } 84 | 85 | static SDL_Surface* surdup(SDL_Surface *src, uint32_t bg) { 86 | SDL_PixelFormat *f = src->format; 87 | SDL_Surface *s = SDL_CreateRGBSurface(0, src->w, src->h, 88 | f->BitsPerPixel, f->Rmask, f->Gmask, f->Bmask, f->Amask); 89 | SDL_FillRect(s, NULL, bg); 90 | SDL_BlitSurface(src, NULL, s, NULL); 91 | return s; 92 | } 93 | 94 | static SDL_Texture* gen_key_texture(SDL_Renderer *renderer, const char *desc1, 95 | const char *desc2, SDL_Surface *key_shape, bool is_down) { 96 | std::string desc = std::string(desc1) + '\n' + desc2; 97 | uint32_t color_up = SDL_MapRGBA(key_shape->format, 0xf0, 0xf0, 0xf0, 0xff); 98 | uint32_t color_dn = SDL_MapRGBA(key_shape->format, 0xc0, 0xc0, 0xc0, 0xff); 99 | SDL_Surface *s = surdup(key_shape, is_down ? color_dn : color_up); 100 | SDL_Surface *s_desc = str2surface(desc.c_str(), 0); 101 | SDL_Rect r = (SDL_Rect) { .x = 1, .y = 1 }; 102 | SDL_BlitSurface(s_desc, NULL, s, &r); 103 | SDL_Texture *t = SDL_CreateTextureFromSurface(renderer, s); 104 | SDL_FreeSurface(s_desc); 105 | SDL_FreeSurface(s); 106 | return t; 107 | } 108 | 109 | static void init_key_texture(SDL_Renderer *renderer, uint8_t sdl_key, 110 | const char *desc1, const char *desc2, SDL_Surface *key_shape, int x, int y) { 111 | Key *e = &keys[sdl_key]; 112 | e->t_up = gen_key_texture(renderer, desc1, desc2, key_shape, false); 113 | e->t_down = gen_key_texture(renderer, desc1, desc2, key_shape, true); 114 | e->rect = (SDL_Rect){ .x = x, .y = y, .w = key_shape->w, .h = key_shape->h }; 115 | } 116 | 117 | static void init_render_local(SDL_Renderer *renderer) { 118 | const int key_unit_width = 34; 119 | const int key_gap = key_unit_width / 14; 120 | const int h_keyboard = key_unit_width * 6 + key_gap * 5 + key_unit_width / 2; 121 | const int x_top_left = WINDOW_WIDTH / 2 + 1; 122 | const int y_below_uart = WINDOW_HEIGHT / 2; 123 | const int y_top_left = y_below_uart + (WINDOW_HEIGHT - y_below_uart - h_keyboard) / 2; 124 | const int key_gap_before_extend_keys = key_unit_width / 3; 125 | 126 | SDL_Surface *s_1p0 = new_key_shape(key_unit_width, key_unit_width); 127 | SDL_Surface *s_1p5 = new_key_shape(key_unit_width + key_gap + key_unit_width / 2, key_unit_width); 128 | SDL_Surface *s_2p0 = new_key_shape(key_unit_width * 2 + key_gap, key_unit_width); 129 | SDL_Surface *s_2p25= new_key_shape(key_unit_width * 2 + key_gap * 2 + key_unit_width / 4, key_unit_width); 130 | SDL_Surface *s_6p0 = new_key_shape(key_unit_width * 6 + key_gap * 5, key_unit_width); 131 | 132 | int x = x_top_left, y = y_top_left; 133 | 134 | #define _entry(k, desc1, desc2, width) do { \ 135 | int idx = SDL_PREFIX(concat(_, k)); \ 136 | init_key_texture(renderer, idx, desc1, desc2, concat(s_, width), x, y); \ 137 | x += concat(s_, width)->w + key_gap; \ 138 | SDL_RenderCopy(renderer, keys[idx].t_up, NULL, &keys[idx].rect); \ 139 | } while (0) 140 | 141 | #define nextline() do { x = x_top_left; y += key_unit_width + key_gap; } while (0) 142 | 143 | _entry(ESCAPE, "ESC", "", 1p0); 144 | x += key_unit_width / 2 + key_gap; 145 | _entry(F1, "F1", "", 1p0); 146 | _entry(F2, "F2", "", 1p0); 147 | _entry(F3, "F3", "", 1p0); 148 | _entry(F4, "F4", "", 1p0); 149 | x += key_unit_width / 2; 150 | _entry(F5, "F5", "", 1p0); 151 | _entry(F6, "F6", "", 1p0); 152 | _entry(F7, "F7", "", 1p0); 153 | _entry(F8, "F8", "", 1p0); 154 | x += key_unit_width / 2; 155 | _entry(F9, "F9", "", 1p0); 156 | _entry(F10, "F10", "", 1p0); 157 | _entry(F11, "F11", "", 1p0); 158 | _entry(F12, "F12", "", 1p0); 159 | x += key_gap_before_extend_keys; 160 | _entry(PRINTSCREEN, "Prt", "Scr", 1p0); 161 | _entry(SCROLLLOCK, "Scr", "Lck", 1p0); 162 | _entry(PAUSE, "Pa-", "use", 1p0); 163 | 164 | nextline(); 165 | y += key_unit_width / 2; // more space between the 1st and 2nd line 166 | _entry(GRAVE, "~", "`", 1p0); 167 | _entry(1, "!", "1", 1p0); 168 | _entry(2, "@", "2", 1p0); 169 | _entry(3, "#", "3", 1p0); 170 | _entry(4, "$", "4", 1p0); 171 | _entry(5, "%", "5", 1p0); 172 | _entry(6, "^", "6", 1p0); 173 | _entry(7, "&", "7", 1p0); 174 | _entry(8, "*", "8", 1p0); 175 | _entry(9, "(", "9", 1p0); 176 | _entry(0, ")", "0", 1p0); 177 | _entry(MINUS, "_", "-", 1p0); 178 | _entry(EQUALS, "+", "=", 1p0); 179 | _entry(BACKSPACE, "Back", "Space", 1p5); 180 | x += key_gap_before_extend_keys - key_gap; 181 | _entry(INSERT, "Ins", "", 1p0); 182 | _entry(HOME, "Ho-", "me", 1p0); 183 | _entry(PAGEUP, "Pg", "Up", 1p0); 184 | 185 | nextline(); 186 | _entry(TAB, "Tab", "", 1p5); 187 | _entry(Q, "Q", "", 1p0); 188 | _entry(W, "W", "", 1p0); 189 | _entry(E, "E", "", 1p0); 190 | _entry(R, "R", "", 1p0); 191 | _entry(T, "T", "", 1p0); 192 | _entry(Y, "Y", "", 1p0); 193 | _entry(U, "U", "", 1p0); 194 | _entry(I, "I", "", 1p0); 195 | _entry(O, "O", "", 1p0); 196 | _entry(P, "P", "", 1p0); 197 | _entry(LEFTBRACKET, "{", "[", 1p0); 198 | _entry(RIGHTBRACKET, "}", "]", 1p0); 199 | _entry(BACKSLASH, "|", "\\", 1p0); 200 | x += key_gap_before_extend_keys - key_gap; 201 | _entry(DELETE, "Del", "", 1p0); 202 | _entry(END, "End", "", 1p0); 203 | _entry(PAGEDOWN, "Pg", "Dn", 1p0); 204 | 205 | nextline(); 206 | _entry(CAPSLOCK, "Caps", "Lock", 2p0); 207 | _entry(A, "A", "", 1p0); 208 | _entry(S, "S", "", 1p0); 209 | _entry(D, "D", "", 1p0); 210 | _entry(F, "F", "", 1p0); 211 | _entry(G, "G", "", 1p0); 212 | _entry(H, "H", "", 1p0); 213 | _entry(J, "J", "", 1p0); 214 | _entry(K, "K", "", 1p0); 215 | _entry(L, "L", "", 1p0); 216 | _entry(SEMICOLON, ":", ";", 1p0); 217 | _entry(APOSTROPHE,"\"", "'", 1p0); 218 | _entry(RETURN, "Enter", "", 1p5); 219 | 220 | nextline(); 221 | _entry(LSHIFT, "Shift", "", 2p25); 222 | _entry(Z, "Z", "", 1p0); 223 | _entry(X, "X", "", 1p0); 224 | _entry(C, "C", "", 1p0); 225 | _entry(V, "V", "", 1p0); 226 | _entry(B, "B", "", 1p0); 227 | _entry(N, "N", "", 1p0); 228 | _entry(M, "M", "", 1p0); 229 | _entry(COMMA, "<", ",", 1p0); 230 | _entry(PERIOD, ">", ".", 1p0); 231 | _entry(SLASH, "?", "/", 1p0); 232 | _entry(RSHIFT, "Shift", "", 2p25); 233 | x += key_gap_before_extend_keys + key_unit_width; 234 | _entry(UP, " ^", " |", 1p0); 235 | 236 | nextline(); 237 | _entry(LCTRL, "Ctrl", "", 1p5); 238 | x += key_unit_width + key_gap + key_unit_width / 4; 239 | _entry(LALT, "Alt", "", 1p5); 240 | _entry(SPACE, "Space", "", 6p0); 241 | _entry(RALT, "Alt", "", 1p5); 242 | x += key_unit_width + key_gap + key_unit_width / 4; 243 | _entry(RCTRL, "Ctrl", "", 1p5); 244 | x += key_gap_before_extend_keys - key_gap; 245 | _entry(LEFT, "<-", "", 1p0); 246 | _entry(DOWN, " |", " V", 1p0); 247 | _entry(RIGHT, "->", "", 1p0); 248 | 249 | SDL_FreeSurface(s_1p0); 250 | SDL_FreeSurface(s_1p5); 251 | SDL_FreeSurface(s_2p0); 252 | SDL_FreeSurface(s_2p25); 253 | SDL_FreeSurface(s_6p0); 254 | 255 | 256 | // draw line 257 | SDL_SetRenderDrawColor(renderer, 0xff, 0xff, 0xff, 0); 258 | SDL_Point p[3]; 259 | p[0] = Point(WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2); 260 | p[1] = p[0] - Point(16, 16); 261 | p[2] = p[1] - Point(20, 0); 262 | draw_thicker_line(renderer, p, 3); 263 | 264 | // draw label 265 | const char *str = "PS/2 Keyboard"; 266 | draw_str(renderer, str, p[2].x - strlen(str) * CH_WIDTH, p[2].y - CH_HEIGHT / 2, 0xffffff); 267 | } 268 | 269 | void init_keyboard(SDL_Renderer *renderer) { 270 | init_render_local(renderer); 271 | kb = new KEYBOARD(renderer, 0, 0, KEYBOARD_TYPE); 272 | for (int p = PS2_CLK; p <= PS2_DAT; p ++) { 273 | kb->add_pin(p); 274 | } 275 | #define FILL_KEYMAP0(a) keys[SDL_PREFIX(a)].map0 = GET_FIRST(AT_PREFIX(a)); 276 | #define FILL_KEYMAP1(a) keys[SDL_PREFIX(a)].map1 = GET_SECOND(AT_PREFIX(a)); 277 | MAP(SCANCODE_LIST, FILL_KEYMAP0) 278 | MAP(SCANCODE_LIST, FILL_KEYMAP1) 279 | } 280 | 281 | void kb_update() { 282 | kb->update_state(); 283 | } 284 | 285 | void kb_push_key(uint8_t scancode, bool is_keydown){ 286 | kb->push_key(scancode, is_keydown); 287 | } 288 | -------------------------------------------------------------------------------- /src/led.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define LED_X 60 + (4/2) 4 | #define LED_Y 360 5 | #define LED_SEP 14 6 | #define LED_WIDTH 16 7 | #define LED_HEIGHT 8 8 | 9 | static void init_render_local(SDL_Renderer *renderer) { 10 | // draw rectangle region 11 | const int gap = 12; 12 | int w = (LED_WIDTH + LED_SEP) * 16 - LED_SEP; 13 | SDL_Rect r = Rect(Point(LED_X, LED_Y) - Point(gap, gap), w + gap * 2, 76 + gap * 2); 14 | SDL_SetRenderDrawColor(renderer, 21, 153, 120, 0); 15 | SDL_RenderFillRect(renderer, &r); 16 | 17 | // draw line 18 | SDL_SetRenderDrawColor(renderer, 0xff, 0xff, 0xff, 0); 19 | SDL_Point p[2]; 20 | const int gap2 = gap + 4; 21 | p[0] = Point(LED_X, LED_Y) + Point(-gap2, LED_HEIGHT / 2); 22 | p[1] = p[0] + Point(w + gap2 * 2, 0); 23 | draw_thicker_line(renderer, p, 2); 24 | 25 | int w_group4 = (LED_WIDTH + LED_SEP) * 4; 26 | p[0] = Point(LED_X, LED_Y) + Point(w_group4, 0) - Point(LED_SEP / 2, 0) 27 | + Point(0, LED_HEIGHT / 2) + Point(0, -LED_SEP / 2); 28 | p[1] = p[0] + Point(0, LED_SEP); 29 | for (int i = 0; i < 3; i ++) { 30 | draw_thicker_line(renderer, p, 2); 31 | p[0] = p[0] + Point(w_group4, 0); 32 | p[1] = p[1] + Point(w_group4, 0); 33 | } 34 | 35 | // draw indices for each LED 36 | SDL_Point p0 = Point(LED_X, LED_Y) + Point(w, 0) - Point(LED_WIDTH / 2, 0) 37 | + Point(0, LED_HEIGHT * 2); 38 | for (int i = 0; i < 16; i ++) { 39 | char buf[8]; 40 | int n = snprintf(buf, 8, "%d", i); 41 | draw_str(renderer, buf, p0.x - CH_WIDTH * n / 2, p0.y, 0xffffff); 42 | p0.x -= LED_WIDTH + LED_SEP; 43 | } 44 | 45 | // draw label 46 | const char *str = "LED"; 47 | p0 = Point(LED_X, LED_Y) - Point(gap2 + 4, 0) + Point(0, LED_HEIGHT / 2) 48 | - Point(0, CH_HEIGHT / 2) - Point(CH_WIDTH * strlen(str), 0); 49 | draw_str(renderer, str, p0.x, p0.y, 0xffffff); 50 | } 51 | 52 | void init_led(SDL_Renderer *renderer) { 53 | SDL_Texture *tled_off = new_texture(renderer, LED_WIDTH, LED_HEIGHT, 0x7f, 0x7f, 0x7f); 54 | SDL_Texture *tled_g = new_texture(renderer, LED_WIDTH, LED_HEIGHT, 0x00, 0xff, 0x00); 55 | #if 0 56 | SDL_Texture* tled_r = new_texture(renderer, LED_WIDTH, LED_HEIGHT, 0xff, 0x00, 0x00); 57 | SDL_Texture* tled_b = new_texture(renderer, LED_WIDTH, LED_HEIGHT, 0x00, 0x00, 0xff); 58 | SDL_Texture* tled_rg = new_texture(renderer, LED_WIDTH, LED_HEIGHT, 0xff, 0xff, 0x00); 59 | SDL_Texture* tled_rb = new_texture(renderer, LED_WIDTH, LED_HEIGHT, 0xff, 0x00, 0xff); 60 | SDL_Texture* tled_gb = new_texture(renderer, LED_WIDTH, LED_HEIGHT, 0x00, 0xff, 0xff); 61 | SDL_Texture* tled_rgb = new_texture(renderer, LED_WIDTH, LED_HEIGHT, 0xff, 0xff, 0xff); 62 | #endif 63 | init_render_local(renderer); 64 | 65 | for (int i = 0; i < 16; ++i) { 66 | Component *ptr = new Component(renderer, 2, 0, NAIVE_LED_TYPE); 67 | 68 | // off 69 | SDL_Rect *rect_ptr = new SDL_Rect; 70 | *rect_ptr = (SDL_Rect){LED_X + (15 - i) * (LED_WIDTH + LED_SEP), LED_Y, LED_WIDTH, LED_HEIGHT}; 71 | ptr->set_rect(rect_ptr, 0); 72 | ptr->set_texture(tled_off, 0); 73 | 74 | // on 75 | rect_ptr = new SDL_Rect; 76 | *rect_ptr = (SDL_Rect){LED_X + (15 - i) * (LED_WIDTH + LED_SEP), LED_Y, LED_WIDTH, LED_HEIGHT}; 77 | ptr->set_rect(rect_ptr, 1); 78 | ptr->set_texture(tled_g, 1); 79 | 80 | ptr->add_pin(LD0 + i); 81 | add_component(ptr); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/nvboard.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define FPS 60 7 | 8 | static SDL_Window *main_window = nullptr; 9 | static SDL_Renderer *main_renderer = nullptr; 10 | PinNode pin_array[NR_PINS]; 11 | 12 | static bool need_redraw = true; 13 | void set_redraw() { need_redraw = true; } 14 | 15 | void vga_update(); 16 | void kb_update(); 17 | void uart_tx_receive(); 18 | void uart_rx_send(); 19 | 20 | void nvboard_update() { 21 | extern uint8_t *vga_blank_n_ptr; 22 | if (*vga_blank_n_ptr) vga_update(); 23 | 24 | extern bool is_kb_idle; 25 | if (unlikely(!is_kb_idle)) kb_update(); 26 | 27 | extern int16_t uart_divisor_cnt; 28 | extern bool is_uart_rx_idle; 29 | if (unlikely((-- uart_divisor_cnt) < 0)) { 30 | uart_tx_receive(); 31 | if (unlikely(!is_uart_rx_idle)) uart_rx_send(); 32 | } 33 | 34 | static uint64_t last = 0; 35 | static int cpf = 1; // count per frame 36 | static int cnt = 0; 37 | if (unlikely((-- cnt) < 0)) { 38 | uint64_t now = nvboard_get_time(); 39 | uint64_t diff = now - last; 40 | if (diff == 0) return; 41 | int cpf_new = ((uint64_t)cpf * 1000000) / ((uint64_t)diff * FPS); // adjust cpf 42 | cnt += cpf_new - cpf; 43 | cpf = cpf_new; 44 | if (diff > 1000000 / FPS) { 45 | last = now; 46 | cnt = cpf; 47 | 48 | void read_event(); 49 | read_event(); 50 | update_components(main_renderer); 51 | if (need_redraw) { 52 | SDL_RenderPresent(main_renderer); 53 | need_redraw = false; 54 | } 55 | } 56 | } 57 | } 58 | 59 | void nvboard_init(int vga_clk_cycle) { 60 | // init SDL and SDL_image 61 | SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_EVENTS); 62 | IMG_Init(IMG_INIT_PNG); 63 | 64 | main_window = SDL_CreateWindow("NVBoard " VERSION_STR, SDL_WINDOWPOS_CENTERED, 65 | SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN); 66 | main_renderer = SDL_CreateRenderer(main_window, -1, 67 | #ifdef VSYNC 68 | SDL_RENDERER_PRESENTVSYNC | 69 | #endif 70 | #ifdef HARDWARE_ACC 71 | SDL_RENDERER_ACCELERATED | 72 | #else 73 | SDL_RENDERER_SOFTWARE | 74 | #endif 75 | 0 76 | ); 77 | SDL_SetRenderDrawColor(main_renderer, 0xff, 0xff, 0xff, 0); 78 | SDL_RenderFillRect(main_renderer, NULL); 79 | 80 | for (int i = 0; i < NR_PINS; i ++) { 81 | if (pin_array[i].ptr == NULL) pin_array[i].ptr = &pin_array[i].data; 82 | } 83 | 84 | void init_font(SDL_Renderer *renderer); 85 | init_font(main_renderer); 86 | init_render(main_renderer); 87 | init_components(main_renderer); 88 | init_gui(main_renderer); 89 | 90 | void init_nvboard_timer(); 91 | init_nvboard_timer(); 92 | 93 | update_components(main_renderer); 94 | 95 | extern void vga_set_clk_cycle(int cycle); 96 | vga_set_clk_cycle(vga_clk_cycle); 97 | } 98 | 99 | void nvboard_quit(){ 100 | delete_components(); 101 | SDL_DestroyWindow(main_window); 102 | SDL_DestroyRenderer(main_renderer); 103 | IMG_Quit(); 104 | SDL_Quit(); 105 | } 106 | 107 | void nvboard_bind_pin(void *signal, int len, ...) { 108 | assert(len < 64); 109 | va_list ap; 110 | va_start(ap, len); 111 | for (int i = 0; i < len; i ++) { 112 | uint16_t pin = va_arg(ap, int); 113 | pin_array[pin].ptr = signal; 114 | pin_array[pin].vector_len = len; 115 | pin_array[pin].bit_offset = len - 1 - i; 116 | } 117 | va_end(ap); 118 | } 119 | -------------------------------------------------------------------------------- /src/render.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static std::string nvboard_home; 4 | static SDL_Texture *tfpga_background; 5 | 6 | static SDL_Texture* load_texture(SDL_Renderer *renderer, std::string path) { 7 | SDL_Texture *t = IMG_LoadTexture(renderer, (nvboard_home + path).c_str()); 8 | assert(t != NULL); 9 | return t; 10 | } 11 | 12 | SDL_Texture* load_pic_texture(SDL_Renderer *renderer, std::string path) { 13 | return load_texture(renderer, "/resources/pic/" + path); 14 | } 15 | 16 | SDL_Texture* surface2texture(SDL_Renderer *renderer, SDL_Surface *s) { 17 | assert(s != NULL); 18 | SDL_Texture *t = SDL_CreateTextureFromSurface(renderer, s); 19 | assert(t != NULL); 20 | SDL_FreeSurface(s); 21 | return t; 22 | } 23 | 24 | SDL_Texture* new_texture(SDL_Renderer *renderer, int w, int h, int r, int g, int b) { 25 | SDL_Surface *s = SDL_CreateRGBSurface(0, w, h, 32, 0, 0, 0, 0); 26 | SDL_FillRect(s, NULL, SDL_MapRGB(s->format, r, g, b)); 27 | return surface2texture(renderer, s); 28 | } 29 | 30 | void draw_thicker_line(SDL_Renderer *renderer, const SDL_Point *point, int n) { 31 | SDL_RenderDrawLines(renderer, point, n); 32 | 33 | SDL_Point *p = new SDL_Point[n]; 34 | for (int i = 0; i < n ; i ++) { 35 | p[i] = point[i] + Point(-1, -1); 36 | } 37 | SDL_RenderDrawLines(renderer, &p[0], n); 38 | delete [] p; 39 | } 40 | 41 | void draw_surrounding_line(SDL_Renderer *renderer, SDL_Rect r, 42 | int gap) { // gap between surrounding lines and component 43 | SDL_Point top_left = Point(r.x, r.y) + Point(-gap, -gap); 44 | const int w = r.w + gap * 2; 45 | const int h = r.h + gap * 2; 46 | const int d = 12; 47 | SDL_Point p[9]; 48 | p[0] = top_left + Point(d, 0); 49 | p[1] = p[0] + Point(w - 2 * d, 0); 50 | p[2] = p[1] + Point(d, d); 51 | p[3] = p[2] + Point(0, h - 2 * d); 52 | p[4] = p[3] + Point(-d, d); 53 | p[5] = p[4] - Point(w - 2 * d, 0); 54 | p[6] = p[5] - Point(d, d); 55 | p[7] = p[6] - Point(0, h - 2 * d); 56 | p[8] = p[0]; 57 | draw_thicker_line(renderer, p, 9); 58 | } 59 | 60 | static void draw_str_internal(SDL_Renderer *renderer, SDL_Texture *t, const char *str, int x, int y) { 61 | SDL_Rect r = Rect(x, y, CH_WIDTH * strlen(str), CH_HEIGHT); 62 | SDL_RenderCopy(renderer, t, NULL, &r); 63 | SDL_DestroyTexture(t); 64 | } 65 | 66 | void draw_str(SDL_Renderer *renderer, const char *str, int x, int y, uint32_t fg) { 67 | SDL_Texture *t = str2texture(renderer, str, fg); 68 | draw_str_internal(renderer, t, str, x, y); 69 | } 70 | 71 | void draw_str(SDL_Renderer *renderer, const char *str, int x, int y, uint32_t fg, uint32_t bg) { 72 | SDL_Texture *t = str2texture(renderer, str, fg, bg); 73 | draw_str_internal(renderer, t, str, x, y); 74 | } 75 | 76 | 77 | void init_render(SDL_Renderer *renderer) { 78 | nvboard_home = getenv("NVBOARD_HOME"); 79 | 80 | SDL_Rect rect_bg = {0, 0, WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2}; 81 | SDL_SetRenderDrawColor(renderer, 82 | (BOARD_BG_COLOR >> 16) & 0xff, (BOARD_BG_COLOR >> 8) & 0xff, BOARD_BG_COLOR & 0xff, 0); 83 | SDL_RenderFillRect(renderer, &rect_bg); 84 | 85 | extern SDL_Texture *nvboard_texture; 86 | int w, h; 87 | SDL_QueryTexture(nvboard_texture, NULL, NULL, &w, &h); 88 | SDL_Rect r = Rect(60, 140, w, h); 89 | SDL_RenderCopy(renderer, nvboard_texture, NULL, &r); 90 | SDL_DestroyTexture(nvboard_texture); 91 | 92 | SDL_SetRenderDrawColor(renderer, 0xff, 0xff, 0xff, 0); 93 | SDL_Point p[2]; 94 | p[0] = Point(60, 140 + h + 4); 95 | p[1] = Point(WINDOW_WIDTH / 2 / 2 + 120, p[0].y); 96 | draw_thicker_line(renderer, p, 2); 97 | p[0].y += 4, p[1].y += 4; 98 | draw_thicker_line(renderer, p, 2); 99 | 100 | draw_str(renderer, VERSION_STR, 60 + w + CH_WIDTH, 140 + h - CH_HEIGHT, 0xffffff); 101 | } 102 | -------------------------------------------------------------------------------- /src/segs7.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define SEG_X 60 4 | #define SEG_Y 225 5 | 6 | #define SEG_VER_WIDTH 3 7 | #define SEG_VER_HEIGHT 30 8 | #define SEG_HOR_WIDTH 30 9 | #define SEG_HOR_HEIGHT 3 10 | #define SEG_DOT_WIDTH 4 11 | #define SEG_DOT_HEIGHT 4 12 | 13 | #define SEG_SEP 3 14 | 15 | #define SEG_TOT_WIDTH (SEG_SEP * 18 + SEG_VER_WIDTH * 16 + SEG_DOT_WIDTH * 8 + SEG_HOR_WIDTH * 8) 16 | #define SEG_TOT_HEIGHT (SEG_SEP * 4 + SEG_VER_HEIGHT * 2 + SEG_HOR_HEIGHT * 3) 17 | 18 | #define GET_SEGA(i) (SEG0A + 8 * i) 19 | #define GET_DECP(i) (SEG0A + 8 * i + 7) 20 | 21 | static SDL_Texture *tsegled_ver_off, *tsegled_ver_on, 22 | *tsegled_hor_off, *tsegled_hor_on, 23 | *tsegled_dot_off, *tsegled_dot_on; 24 | 25 | static SDL_Rect segs_rect[8] = { 26 | {SEG_SEP + SEG_VER_WIDTH, SEG_SEP , SEG_HOR_WIDTH, SEG_HOR_HEIGHT }, 27 | {SEG_SEP + SEG_VER_WIDTH + SEG_HOR_WIDTH, SEG_SEP + SEG_HOR_HEIGHT , SEG_VER_WIDTH, SEG_VER_HEIGHT }, 28 | {SEG_SEP + SEG_VER_WIDTH + SEG_HOR_WIDTH, SEG_SEP + 2*SEG_HOR_HEIGHT + SEG_VER_HEIGHT , SEG_VER_WIDTH, SEG_VER_HEIGHT }, 29 | {SEG_SEP + SEG_VER_WIDTH, SEG_SEP + 2*SEG_HOR_HEIGHT + 2*SEG_VER_HEIGHT , SEG_HOR_WIDTH, SEG_HOR_HEIGHT }, 30 | {SEG_SEP, SEG_SEP + 2*SEG_HOR_HEIGHT + SEG_VER_HEIGHT , SEG_VER_WIDTH, SEG_VER_HEIGHT }, 31 | {SEG_SEP, SEG_SEP + SEG_HOR_HEIGHT , SEG_VER_WIDTH, SEG_VER_HEIGHT }, 32 | {SEG_SEP + SEG_VER_WIDTH, SEG_SEP + SEG_HOR_HEIGHT + SEG_VER_HEIGHT , SEG_HOR_WIDTH, SEG_HOR_HEIGHT }, 33 | {2*SEG_SEP + 2*SEG_VER_WIDTH + SEG_HOR_WIDTH, SEG_SEP + 3*SEG_HOR_HEIGHT + 2*SEG_VER_HEIGHT - SEG_DOT_HEIGHT , SEG_DOT_WIDTH, SEG_DOT_HEIGHT } 34 | }; 35 | 36 | static SDL_Texture *segs_texture(int index, int val) { 37 | switch (index) { 38 | case 0: case 3: case 6: return val ? tsegled_hor_on : tsegled_hor_off; 39 | case 1: case 2: case 4: case 5: return val ? tsegled_ver_on : tsegled_ver_off; 40 | case 7: return val ? tsegled_dot_on : tsegled_dot_off; 41 | default: assert(0); return nullptr; 42 | } 43 | } 44 | 45 | SEGS7::SEGS7(SDL_Renderer *rend, int cnt, int init_val, int ct, bool is_len8) 46 | : Component(rend, cnt, init_val, ct), is_len8(is_len8) {} 47 | 48 | void SEGS7::update_gui() { 49 | int newval = get_state(); 50 | for (int i = 0; i < 8; ++i) { 51 | int texture_idx = (7 - i) * 2 + (((newval >> i) & 1) ? 0 : 1); 52 | SDL_RenderCopy(get_renderer(), get_texture(texture_idx), NULL, get_rect(texture_idx)); 53 | } 54 | set_redraw(); 55 | } 56 | 57 | void SEGS7::update_state() { 58 | int newval = 0; 59 | if (is_len8) { 60 | newval = pin_peek8(get_pin()); 61 | } else { 62 | for (int i = 0; i < 8; ++i) { 63 | newval |= (pin_peek(get_pin(7 - i)) << i); 64 | } 65 | } 66 | if (newval != get_state()) { 67 | set_state(newval); 68 | update_gui(); 69 | } 70 | } 71 | 72 | static void init_render_local(SDL_Renderer *renderer) { 73 | // vertical 74 | tsegled_ver_on = new_texture(renderer, SEG_VER_WIDTH, SEG_VER_HEIGHT, 0xff, 0x00, 0x00); 75 | tsegled_ver_off = new_texture(renderer, SEG_VER_WIDTH, SEG_VER_HEIGHT, 0x2b, 0x2b, 0x2b); 76 | 77 | // horizontal 78 | tsegled_hor_on = new_texture(renderer, SEG_HOR_WIDTH, SEG_HOR_HEIGHT, 0xff, 0x00, 0x00); 79 | tsegled_hor_off = new_texture(renderer, SEG_HOR_WIDTH, SEG_HOR_HEIGHT, 0x2b, 0x2b, 0x2b); 80 | 81 | // dot 82 | tsegled_dot_on = new_texture(renderer, SEG_DOT_WIDTH, SEG_DOT_HEIGHT, 0xff, 0x00, 0x00); 83 | tsegled_dot_off = new_texture(renderer, SEG_DOT_WIDTH, SEG_DOT_HEIGHT, 0x2b, 0x2b, 0x2b); 84 | 85 | SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); 86 | SDL_Rect rect_seg7 = {SEG_X, SEG_Y, SEG_TOT_WIDTH, SEG_TOT_HEIGHT}; 87 | SDL_RenderFillRect(renderer, &rect_seg7); 88 | SDL_SetRenderDrawColor(renderer, 0xff, 0xff, 0xff, 0); 89 | 90 | // draw surrounding lines 91 | const int gap = 14; 92 | draw_surrounding_line(renderer, Rect(SEG_X, SEG_Y, SEG_TOT_WIDTH, SEG_TOT_HEIGHT), gap); 93 | 94 | // draw indices of each 7-seg display 95 | SDL_Point p = Point(SEG_X, SEG_Y) + Point(SEG_TOT_WIDTH, SEG_TOT_HEIGHT) + Point(0, gap) 96 | - Point(SEG_TOT_WIDTH / 8 / 2, 0) - Point(CH_WIDTH / 2, CH_HEIGHT / 2); 97 | char buf[2] = "?"; 98 | for (int i = 0; i < 8; i ++) { 99 | buf[0] = '0' + i; 100 | draw_str(renderer, buf, p.x, p.y, 0xffffff, BOARD_BG_COLOR); 101 | p = p - Point(SEG_TOT_WIDTH / 8, 0); 102 | } 103 | 104 | // draw label 105 | const char *str = "Seven Segment Display"; 106 | p = Point(SEG_X, SEG_Y) - Point(0, gap) - Point(0, CH_HEIGHT / 2) 107 | + Point(SEG_TOT_WIDTH / 2, 0) - Point(CH_WIDTH * strlen(str) / 2, 0); 108 | draw_str(renderer, str, p.x, p.y, 0xffffff, BOARD_BG_COLOR); 109 | } 110 | 111 | void init_segs7(SDL_Renderer *renderer) { 112 | init_render_local(renderer); 113 | for (int i = 0; i < 8; ++i) { 114 | SDL_Rect mv = {SEG_X + SEG_SEP + (7 - i) * (SEG_HOR_WIDTH + SEG_DOT_WIDTH + SEG_VER_WIDTH * 2 + SEG_SEP * 2), SEG_Y + SEG_SEP, 0, 0}; 115 | bool is_len8 = (pin_array[GET_SEGA(i)].vector_len == 8); 116 | Component *ptr = new SEGS7(renderer, 16, 0x5555, SEGS7_TYPE, is_len8); 117 | for (int j = 0; j < 8; ++j) { 118 | SDL_Rect *rect_ptr = new SDL_Rect; 119 | *rect_ptr = mv + segs_rect[j]; 120 | ptr->set_texture(segs_texture(j, 0), j << 1 | 0); 121 | ptr->set_rect(rect_ptr, j << 1 | 0); 122 | rect_ptr = new SDL_Rect; 123 | *rect_ptr = mv + segs_rect[j]; 124 | ptr->set_texture(segs_texture(j, 1), j << 1 | 1); 125 | ptr->set_rect(rect_ptr, j << 1 | 1); 126 | } 127 | 128 | for (int p = GET_SEGA(i); p <= GET_DECP(i); p ++) { 129 | ptr->add_pin(p); 130 | } 131 | add_component(ptr); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/switch.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define SWITCH_X 60 4 | #define SWITCH_Y 400 5 | #define SWITCH_SEP 10 6 | #define SWITCH_WIDTH 20 7 | #define SWITCH_HEIGHT 40 8 | 9 | static void init_render_local(SDL_Renderer *renderer) { 10 | // draw line 11 | SDL_SetRenderDrawColor(renderer, 0xff, 0xff, 0xff, 0); 12 | SDL_Point p[2]; 13 | const int gap2 = 16; 14 | int w = (SWITCH_WIDTH + SWITCH_SEP) * 16 - SWITCH_SEP; 15 | p[0] = Point(SWITCH_X, SWITCH_Y) + Point(-gap2, SWITCH_HEIGHT / 2); 16 | p[1] = p[0] + Point(w + gap2 * 2, 0); 17 | draw_thicker_line(renderer, p, 2); 18 | 19 | int w_group4 = (SWITCH_WIDTH + SWITCH_SEP) * 4; 20 | p[0] = Point(SWITCH_X, SWITCH_Y) + Point(w_group4, 0) - Point(SWITCH_SEP / 2, 0) 21 | + Point(0, SWITCH_HEIGHT / 2) + Point(0, -SWITCH_SEP / 2); 22 | p[1] = p[0] + Point(0, SWITCH_SEP); 23 | for (int i = 0; i < 3; i ++) { 24 | draw_thicker_line(renderer, p, 2); 25 | p[0] = p[0] + Point(w_group4, 0); 26 | p[1] = p[1] + Point(w_group4, 0); 27 | } 28 | 29 | // draw label 30 | const char *str = "SW"; 31 | SDL_Point p0 = Point(SWITCH_X, SWITCH_Y) - Point(gap2 + 4, 0) + Point(0, SWITCH_HEIGHT / 2) 32 | - Point(0, CH_HEIGHT / 2) - Point(CH_WIDTH * strlen(str), 0); 33 | draw_str(renderer, str, p0.x, p0.y, 0xffffff); 34 | } 35 | 36 | void init_switch(SDL_Renderer *renderer) { 37 | SDL_Texture *tswitch_on = load_pic_texture(renderer, VSW_ON_PATH); 38 | SDL_Texture *tswitch_off = load_pic_texture(renderer, VSW_OFF_PATH); 39 | init_render_local(renderer); 40 | for (int i = 0; i < 16; ++i) { 41 | Component *ptr = new Component(renderer, 2, 0, SWITCH_TYPE); 42 | 43 | // off 44 | SDL_Rect *rect_ptr = new SDL_Rect; 45 | *rect_ptr = (SDL_Rect){SWITCH_X + (15 - i) * (SWITCH_WIDTH + SWITCH_SEP), SWITCH_Y, SWITCH_WIDTH, SWITCH_HEIGHT}; 46 | ptr->set_rect(rect_ptr, 0); 47 | ptr->set_texture(tswitch_off, 0); 48 | 49 | // on 50 | rect_ptr = new SDL_Rect; 51 | *rect_ptr = (SDL_Rect){SWITCH_X + (15 - i) * (SWITCH_WIDTH + SWITCH_SEP), SWITCH_Y, SWITCH_WIDTH, SWITCH_HEIGHT}; 52 | ptr->set_rect(rect_ptr, 1); 53 | ptr->set_texture(tswitch_on, 1); 54 | 55 | ptr->add_pin(SW0 + i); 56 | add_component(ptr); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/term.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | Term::Term(SDL_Renderer *r, int x, int y, int w, int h): 5 | renderer(r), cursor_x(0), cursor_y(0), screen_y(0) { 6 | region = (SDL_Rect){ .x = x, .y = y, .w = w, .h = h }; 7 | w_in_char = region.w / CH_WIDTH; 8 | h_in_char = region.h / CH_HEIGHT; 9 | uint8_t *l = add_line(); 10 | cursor_texture = new_texture(r, CH_WIDTH, CH_HEIGHT, 0x10, 0x10, 0x10); 11 | get_focus_cursor_texture = new_texture(r, CH_WIDTH, CH_HEIGHT, 0xff, 0x00, 0xff); 12 | is_cursor_visible = true; 13 | is_focus = false; 14 | clear_screen(); 15 | draw_cursor(); 16 | dirty_line = new bool[h_in_char]; 17 | dirty_char = new bool[w_in_char * h_in_char]; 18 | init_dirty(false); 19 | } 20 | 21 | void Term::init_dirty(bool val) { 22 | dirty_screen = val; 23 | memset(dirty_line, val, h_in_char); 24 | memset(dirty_char, val, w_in_char * h_in_char); 25 | } 26 | 27 | Term::~Term() { 28 | SDL_DestroyTexture(cursor_texture); 29 | } 30 | 31 | void Term::clear_screen() { 32 | SDL_RenderFillRect(renderer, ®ion); 33 | } 34 | 35 | void Term::set_cursor_visibility(bool v) { 36 | bool update_cursor_gui = is_cursor_visible ^ v; 37 | is_cursor_visible = v; 38 | if (update_cursor_gui) draw_cursor(); 39 | } 40 | 41 | void Term::set_focus(bool v) { 42 | bool update_cursor_gui = is_focus ^ v; 43 | is_focus = v; 44 | if (update_cursor_gui) draw_cursor(); 45 | } 46 | 47 | void Term::clear() { 48 | while (lines.size() > 1) { 49 | delete [] lines.back(); 50 | lines.pop_back(); 51 | } 52 | memset(lines[0], ' ', w_in_char); 53 | cursor_x = cursor_y = screen_y = 0; 54 | clear_screen(); 55 | init_dirty(false); 56 | set_redraw(); 57 | } 58 | 59 | void Term::newline() { 60 | cursor_x = 0; 61 | cursor_y ++; 62 | if (cursor_y >= lines.size()) add_line(); 63 | if (cursor_y == screen_y + h_in_char) { // scroll down one line 64 | screen_y ++; // TODO: only set dirty if screen_y changes between two draws 65 | init_dirty(true); 66 | } 67 | } 68 | 69 | void Term::_return() { 70 | cursor_x = 0; 71 | } 72 | 73 | uint8_t* Term::add_line() { 74 | uint8_t *l = new uint8_t[w_in_char]; 75 | memset(l, ' ', w_in_char); 76 | lines.push_back(l); 77 | return l; 78 | } 79 | 80 | void Term::set_dirty_char(int y, int x) { 81 | dirty_char[y * w_in_char + x] = true; 82 | dirty_line[y] = true; 83 | dirty_screen = true; 84 | } 85 | 86 | void Term::backspace(bool is_input) { 87 | if (cursor_x == 0) { 88 | if (!is_input) return; 89 | if (cursor_y == 0) return; 90 | delete [] lines[cursor_y]; 91 | lines.erase(lines.begin() + cursor_y); 92 | cursor_y --; 93 | cursor_x = w_in_char - 1; 94 | if (cursor_y < screen_y) { // scroll up one line 95 | screen_y --; 96 | init_dirty(true); 97 | } 98 | } else { 99 | if (is_cursor_on_screen()) set_dirty_char(cursor_y - screen_y, cursor_x); 100 | cursor_x --; 101 | } 102 | lines[cursor_y][cursor_x] = ' '; 103 | if (is_cursor_on_screen()) set_dirty_char(cursor_y - screen_y, cursor_x); 104 | } 105 | 106 | void Term::feed_ch(uint8_t ch) { 107 | assert(ch < 128); 108 | if (is_cursor_on_screen()) set_dirty_char(cursor_y - screen_y, cursor_x); 109 | int y = cursor_y; 110 | assert(y < lines.size()); 111 | if (ch == '\n') { newline(); return; } 112 | else if (ch == '\r') { _return(); return; } 113 | else if (ch == '\b') { backspace(false); return; } 114 | lines[y][cursor_x] = ch; 115 | cursor_x ++; 116 | if (cursor_x == w_in_char) newline(); 117 | } 118 | 119 | void Term::feed_str(const char *s) { 120 | while (*s != '\0') feed_ch(*(s ++)); 121 | } 122 | 123 | bool Term::is_cursor_on_screen() { 124 | return cursor_y >= screen_y && cursor_y < screen_y + h_in_char; 125 | } 126 | 127 | void Term::draw_cursor() { 128 | if (is_cursor_on_screen()) { 129 | int y = cursor_y - screen_y; 130 | int x = cursor_x; 131 | SDL_Rect rect = region; 132 | rect.w = CH_WIDTH, rect.h = CH_HEIGHT; 133 | rect.y += CH_HEIGHT * y; 134 | rect.x += CH_WIDTH * x; 135 | SDL_Texture *t = is_cursor_visible ? (is_focus ? get_focus_cursor_texture : 136 | cursor_texture) : ch2texture_term(' '); 137 | SDL_RenderCopy(renderer, t, NULL, &rect); 138 | set_redraw(); 139 | } 140 | } 141 | 142 | void Term::update_gui() { 143 | if (!dirty_screen) return; 144 | SDL_Rect rect = region; 145 | int x_start = rect.x; 146 | rect.w = CH_WIDTH, rect.h = CH_HEIGHT; 147 | for (int y = 0; y < h_in_char; y ++) { 148 | if (screen_y + y >= lines.size()) break; 149 | if (!dirty_line[y]) continue; 150 | 151 | uint8_t *l = lines[screen_y + y]; 152 | rect.y = region.y + rect.h * y; 153 | bool *dirty = &dirty_char[y * w_in_char]; 154 | for (int x = 0; x < w_in_char; x ++) { 155 | if (!dirty[x]) continue; 156 | 157 | uint8_t ch = l[x]; 158 | rect.x = region.x + rect.w * x; 159 | SDL_Texture *t = ch2texture_term(ch); 160 | SDL_RenderCopy(renderer, t, NULL, &rect); 161 | set_redraw(); 162 | } 163 | } 164 | draw_cursor(); 165 | init_dirty(false); 166 | } 167 | -------------------------------------------------------------------------------- /src/timer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static uint64_t boot_time = 0; 5 | 6 | static uint64_t get_time_internal() { 7 | struct timeval now; 8 | gettimeofday(&now, NULL); 9 | uint64_t us = now.tv_sec * 1000000 + now.tv_usec; 10 | return us; 11 | } 12 | 13 | uint64_t nvboard_get_time() { 14 | uint64_t now = get_time_internal(); 15 | return now - boot_time; 16 | } 17 | 18 | void init_nvboard_timer() { 19 | boot_time = get_time_internal(); 20 | } 21 | -------------------------------------------------------------------------------- /src/uart.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // There is no need to update TX too frequently 5 | #define UART_TX_FPS 5 6 | 7 | static UART* uart = NULL; 8 | int16_t uart_divisor_cnt = 0; 9 | bool is_uart_rx_idle = true; 10 | 11 | UART::UART(SDL_Renderer *rend, int cnt, int init_val, int ct, int x, int y, int w, int h): 12 | Component(rend, cnt, init_val, ct), 13 | tx_state(0), rx_state(0), divisor(16), need_update_gui(false) { 14 | term = new Term(rend, x, y, w, h); 15 | 16 | SDL_Rect *rect_ptr = new SDL_Rect; 17 | *rect_ptr = (SDL_Rect){x, y, w, h}; 18 | set_rect(rect_ptr, 0); 19 | 20 | uart_divisor_cnt = divisor - 1; 21 | int len = pin_array[UART_TX].vector_len; 22 | assert(len == 0 || len == 1); // either unbound or bound to 1 bit signal 23 | p_tx = (uint8_t *)pin_array[UART_TX].ptr; 24 | 25 | SDL_SetRenderDrawColor(rend, 0x00, 0x00, 0x00, 0); 26 | SDL_RenderDrawLine(rend, x, y + h, x + w, y + h); 27 | SDL_SetRenderDrawColor(rend, 0xff, 0xff, 0xff, 0); 28 | 29 | rx_sending_str = ""; 30 | pin_poke(UART_RX, 1); 31 | } 32 | 33 | UART::~UART() { 34 | SDL_DestroyTexture(get_texture(0)); 35 | } 36 | 37 | void UART::update_gui() { // everything is done in update_state() 38 | } 39 | 40 | void UART::tx_receive() { 41 | uart_divisor_cnt = divisor - 1; 42 | 43 | uint8_t tx = *p_tx; 44 | if (tx_state == 0) { // idle 45 | if (!tx) { // start bit 46 | tx_data = 0; 47 | tx_state ++; 48 | } 49 | } else if (tx_state >= 1 && tx_state <= 8) { // data 50 | tx_data = (tx << 7) | (tx_data >> 1); // data bit 51 | tx_state ++; 52 | } else if (tx_state == 9) { 53 | if (tx) { // stop bit 54 | tx_state = 0; 55 | term->feed_ch(tx_data); 56 | need_update_gui = true; 57 | } 58 | } 59 | } 60 | 61 | void UART::rx_send() { 62 | // the uart_divisor_cnt is maintained in tx_receive() 63 | if (rx_state == 0) { // idle 64 | rx_data = rx_sending_str[0]; 65 | if (rx_data == '\0') { 66 | is_uart_rx_idle = true; 67 | return; 68 | } 69 | rx_sending_str.erase(0, 1); 70 | pin_poke(UART_RX, 0); // start bit 71 | rx_state ++; 72 | } else if (rx_state >= 1 && rx_state <= 8) { // data 73 | pin_poke(UART_RX, rx_data & 1); // data bit 74 | rx_data >>= 1; 75 | rx_state ++; 76 | } else if (rx_state == 9) { 77 | pin_poke(UART_RX, 1); // stop bit 78 | rx_state = 0; 79 | } 80 | } 81 | 82 | void UART::rx_getchar(uint8_t ch) { 83 | rx_sending_str += ch; 84 | is_uart_rx_idle = false; 85 | } 86 | 87 | void UART::update_state() { 88 | if (need_update_gui) { 89 | static uint64_t last = 0; 90 | uint64_t now = nvboard_get_time(); 91 | if (now - last > 1000000 / UART_TX_FPS) { 92 | last = now; 93 | need_update_gui = false; 94 | term->update_gui(); 95 | } 96 | } 97 | } 98 | 99 | void UART::set_divisor(uint16_t d) { 100 | divisor = d; 101 | } 102 | 103 | void UART::term_focus(bool v) { 104 | term->set_focus(v); 105 | } 106 | 107 | static void init_render_local(SDL_Renderer *renderer) { 108 | SDL_SetRenderDrawColor(renderer, 0xff, 0xff, 0xff, 0); 109 | SDL_Point p[2]; 110 | p[0] = Point(WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2) - Point(0, 30); 111 | p[1] = p[0] - Point(10, 0); 112 | const char* label[2] = { "RX", "TX" }; 113 | for (int i = 0; i < 2; i ++) { 114 | draw_thicker_line(renderer, p, 2); 115 | draw_str(renderer, label[i], p[1].x - 2 * CH_WIDTH, p[1].y - CH_HEIGHT / 2, 0xffffff); 116 | p[0].y -= CH_HEIGHT; p[1].y -= CH_HEIGHT; 117 | } 118 | draw_str(renderer, "UART-[", p[1].x - 8 * CH_WIDTH, p[1].y + CH_HEIGHT, 0xffffff); 119 | } 120 | 121 | void init_uart(SDL_Renderer *renderer) { 122 | init_render_local(renderer); 123 | int x = WINDOW_WIDTH / 2, y = 0, w = WINDOW_WIDTH / 2, h = WINDOW_HEIGHT / 2; 124 | uart = new UART(renderer, 1, 0, UART_TYPE, x, y, w, h); 125 | uart->add_pin(UART_TX); 126 | uart->add_pin(UART_RX); 127 | add_component(uart); 128 | } 129 | 130 | void uart_tx_receive() { 131 | uart->tx_receive(); 132 | } 133 | 134 | void uart_rx_send() { 135 | uart->rx_send(); 136 | } 137 | 138 | void uart_rx_getchar(uint8_t ch) { 139 | uart->rx_getchar(ch); 140 | } 141 | 142 | void uart_term_focus(bool v) { 143 | uart->term_focus(v); 144 | } 145 | -------------------------------------------------------------------------------- /src/vga.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static VGA* vga = NULL; 6 | 7 | VGA_MODE vga_mod_accepted[NR_VGA_MODE] = { 8 | [VGA_MODE_640_480] = { 9 | .h_frontporch = 96, 10 | .h_active = 144, 11 | .h_backporch = 784, 12 | .h_total = 800, 13 | .v_frontporch = 2, 14 | .v_active = 35, 15 | .v_backporch = 515, 16 | .v_total = 525, 17 | }, 18 | }; 19 | 20 | static int vga_clk_cycle_minus_1 = 0; 21 | uint8_t *vga_blank_n_ptr = NULL; 22 | 23 | VGA::VGA(SDL_Renderer *rend, int cnt, int init_val, int ct): 24 | Component(rend, cnt, init_val, ct), 25 | vga_screen_width(VGA_DEFAULT_WIDTH), vga_screen_height(VGA_DEFAULT_HEIGHT), 26 | vga_clk_cnt(1) { 27 | SDL_Texture *vga_texture = SDL_CreateTexture(rend, SDL_PIXELFORMAT_ARGB8888, 28 | SDL_TEXTUREACCESS_STREAMING, vga_screen_width, vga_screen_height); 29 | set_texture(vga_texture, 0); 30 | pixels = new uint32_t[vga_screen_width * vga_screen_height]; 31 | memset(pixels, 0, vga_screen_width * vga_screen_height * sizeof(uint32_t)); 32 | 33 | SDL_Rect *rect_ptr = new SDL_Rect; 34 | *rect_ptr = (SDL_Rect){0, WINDOW_HEIGHT / 2, VGA_DEFAULT_WIDTH, VGA_DEFAULT_HEIGHT}; 35 | set_rect(rect_ptr, 0); 36 | SDL_UpdateTexture(vga_texture, NULL, pixels, vga_screen_width * sizeof(uint32_t)); 37 | SDL_RenderCopy(rend, vga_texture, NULL, rect_ptr); 38 | 39 | is_r_len8 = pin_array[VGA_R0].vector_len == 8; 40 | is_g_len8 = pin_array[VGA_G0].vector_len == 8; 41 | is_b_len8 = pin_array[VGA_B0].vector_len == 8; 42 | is_all_len8 = is_r_len8 && is_g_len8 && is_b_len8; 43 | if (is_r_len8) p_r = (uint8_t *)pin_array[VGA_R0].ptr; 44 | if (is_g_len8) p_g = (uint8_t *)pin_array[VGA_G0].ptr; 45 | if (is_b_len8) p_b = (uint8_t *)pin_array[VGA_B0].ptr; 46 | int vga_blank_n_len = pin_array[VGA_BLANK_N].vector_len; 47 | assert(vga_blank_n_len == 1 || vga_blank_n_len == 0); 48 | vga_blank_n_ptr = (uint8_t *)pin_array[VGA_BLANK_N].ptr; 49 | p_pixel = pixels; 50 | p_pixel_end = pixels + vga_screen_width * vga_screen_height; 51 | } 52 | 53 | VGA::~VGA() { 54 | SDL_DestroyTexture(get_texture(0)); 55 | delete []pixels; 56 | } 57 | 58 | void VGA::update_gui() { 59 | #ifdef DEBUG 60 | static int frames = 0; 61 | frames ++; 62 | printf("%d frames\n", frames); 63 | #endif 64 | SDL_Texture *vga_texture = get_texture(0); 65 | SDL_UpdateTexture(vga_texture, NULL, pixels, vga_screen_width * sizeof(uint32_t)); 66 | SDL_RenderCopy(get_renderer(), vga_texture, NULL, get_rect(0)); 67 | set_redraw(); 68 | } 69 | 70 | uint32_t VGA::get_pixel_color_slowpath() { 71 | #define concat3(a, b, c) concat(concat(a, b), c) 72 | #define MAP2(c, f, x) c(f, x) 73 | #define GET_COLOR_BIT(color, n) (pin_peek(concat3(VGA_, color, n)) << n) 74 | #define BITS(f, color) f(color, 0) f(color, 1) f(color, 2) f(color, 3) \ 75 | f(color, 4) f(color, 5) f(color, 6) f(color, 7) 76 | #define GET_COLOR_BIT_REDUCE(color, n) GET_COLOR_BIT(color, n) | 77 | #define GET_COLOR(color) MAP2(BITS, GET_COLOR_BIT_REDUCE, color) 0 78 | int r = is_r_len8 ? *p_r : GET_COLOR(R); 79 | int g = is_g_len8 ? *p_g : GET_COLOR(G); 80 | int b = is_b_len8 ? *p_b : GET_COLOR(B); 81 | uint32_t color = (r << 16) | (g << 8) | b; 82 | return color; 83 | } 84 | 85 | __attribute__((noinline)) void VGA::finish_one_frame() { 86 | p_pixel = pixels; 87 | if (!is_pixels_same) { 88 | update_gui(); 89 | is_pixels_same = true; 90 | } 91 | } 92 | 93 | void VGA::update_state() { 94 | if (unlikely(vga_clk_cycle_minus_1 > 0)) { 95 | if (vga_clk_cnt > 0) { vga_clk_cnt --; return; } 96 | vga_clk_cnt = vga_clk_cycle_minus_1; 97 | } 98 | 99 | uint32_t color = 0; 100 | if (likely(is_all_len8)) color = ((*p_r) << 16) | ((*p_g) << 8) | (*p_b); 101 | else color = get_pixel_color_slowpath(); 102 | bool is_same = (*p_pixel == color); 103 | is_pixels_same &= is_same; 104 | *p_pixel = color; 105 | p_pixel ++; 106 | if (unlikely(p_pixel == p_pixel_end)) { 107 | finish_one_frame(); 108 | } 109 | } 110 | 111 | void vga_set_clk_cycle(int cycle) { 112 | vga_clk_cycle_minus_1 = cycle - 1; 113 | } 114 | 115 | static void init_render_local(SDL_Renderer *renderer) { 116 | // draw line 117 | SDL_SetRenderDrawColor(renderer, 0xff, 0xff, 0xff, 0); 118 | SDL_Point p[3]; 119 | p[0] = Point(0, WINDOW_HEIGHT / 2) + Point(30, 0) - Point(0, CH_HEIGHT); 120 | p[1] = p[0] - Point(16, 0); 121 | p[2] = Point(p[1].x, WINDOW_HEIGHT / 2); 122 | draw_thicker_line(renderer, p, 3); 123 | 124 | // draw label 125 | draw_str(renderer, "VGA", p[0].x + 4, p[0].y - CH_HEIGHT / 2, 0xffffff); 126 | } 127 | 128 | void init_vga(SDL_Renderer *renderer) { 129 | init_render_local(renderer); 130 | vga = new VGA(renderer, 1, 0, VGA_TYPE); 131 | for (int p = VGA_VSYNC; p <= VGA_B7; p ++) { 132 | vga->add_pin(p); 133 | } 134 | } 135 | 136 | void vga_update() { 137 | vga->update_state(); 138 | } 139 | -------------------------------------------------------------------------------- /usr/include/nvboard.h: -------------------------------------------------------------------------------- 1 | #ifndef __NVBOARD_H__ 2 | #define __NVBOARD_H__ 3 | 4 | #include 5 | 6 | void nvboard_init(int vga_clk_cycle = 1); 7 | void nvboard_quit(); 8 | void nvboard_bind_pin(void *signal, int len, ...); 9 | void nvboard_update(); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /usr/include/pins.h: -------------------------------------------------------------------------------- 1 | #ifndef _NVBOARD_PINS_H 2 | #define _NVBOARD_PINS_H 3 | 4 | // RST is defined but not used. NVBoard contains some internal states in *.cpp 5 | // file. Only resetting the RTL design in *.v file may make the RTL design and 6 | // NVBoard inconsistent. The easy way to reset the whole system is to exit 7 | // NVBoard and re-run. The reset work for RTL design is done by the verilator 8 | // wrapper. See `example/csrc/main.cpp` for more details. 9 | #define BTN_INPUT BTNC, BTNU, BTND, BTNL, BTNR, RST 10 | 11 | #define SW_INPUT SW0, SW1, SW2, SW3, \ 12 | SW4, SW5, SW6, SW7, \ 13 | SW8, SW9, SW10, SW11, \ 14 | SW12, SW13, SW14, SW15 15 | 16 | #define NAIVE_LEDS_OUTPUT LD0, LD1, LD2, LD3, \ 17 | LD4, LD5, LD6, LD7, \ 18 | LD8, LD9, LD10, LD11, \ 19 | LD12, LD13, LD14, LD15 20 | 21 | #define RGB_LEDS_OUTPUT R16, G16, B16, R17, G17, B17 22 | 23 | 24 | #define SEG7_SEGS_OUTPUT SEG0A, SEG0B, SEG0C, SEG0D, SEG0E, SEG0F, SEG0G, DEC0P, \ 25 | SEG1A, SEG1B, SEG1C, SEG1D, SEG1E, SEG1F, SEG1G, DEC1P, \ 26 | SEG2A, SEG2B, SEG2C, SEG2D, SEG2E, SEG2F, SEG2G, DEC2P, \ 27 | SEG3A, SEG3B, SEG3C, SEG3D, SEG3E, SEG3F, SEG3G, DEC3P, \ 28 | SEG4A, SEG4B, SEG4C, SEG4D, SEG4E, SEG4F, SEG4G, DEC4P, \ 29 | SEG5A, SEG5B, SEG5C, SEG5D, SEG5E, SEG5F, SEG5G, DEC5P, \ 30 | SEG6A, SEG6B, SEG6C, SEG6D, SEG6E, SEG6F, SEG6G, DEC6P, \ 31 | SEG7A, SEG7B, SEG7C, SEG7D, SEG7E, SEG7F, SEG7G, DEC7P 32 | 33 | #define UART_OUTPUT UART_TX 34 | #define UART_INPUT UART_RX 35 | 36 | #define VGA_OUTPUT VGA_VSYNC, VGA_HSYNC, VGA_BLANK_N, \ 37 | VGA_R0, VGA_R1, VGA_R2, VGA_R3, VGA_R4, VGA_R5, VGA_R6, VGA_R7, \ 38 | VGA_G0, VGA_G1, VGA_G2, VGA_G3, VGA_G4, VGA_G5, VGA_G6, VGA_G7, \ 39 | VGA_B0, VGA_B1, VGA_B2, VGA_B3, VGA_B4, VGA_B5, VGA_B6, VGA_B7 40 | 41 | #define KEYBOARD_INPUT PS2_CLK, PS2_DAT 42 | 43 | enum { 44 | BTN_INPUT, 45 | SW_INPUT, 46 | KEYBOARD_INPUT, 47 | UART_INPUT, 48 | 49 | NAIVE_LEDS_OUTPUT, 50 | RGB_LEDS_OUTPUT, 51 | SEG7_SEGS_OUTPUT, 52 | VGA_OUTPUT, 53 | UART_OUTPUT, 54 | NR_PINS, 55 | }; 56 | 57 | #endif 58 | --------------------------------------------------------------------------------