├── .gitignore ├── LICENSE.txt ├── README.md ├── build.zig ├── flake.lock ├── flake.nix └── src ├── boot.S ├── kernel.zig ├── linker.lds └── uart.zig /.gitignore: -------------------------------------------------------------------------------- 1 | .zig-cache/ 2 | zig-out/ 3 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | BSD Zero Clause License 2 | 3 | Copyright (c) 2024 zig-osdev 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 13 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | PERFORMANCE OF THIS SOFTWARE. 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RISC-V Barebones Template 2 | 3 | This repository contains a barebones template for the RISC-V architecture. 4 | 5 | ## Resources 6 | 7 | - [Stephen Mars' OS Blog](https://osblog.stephenmarz.com/index.html) 8 | - [OS Dev Wiki, RISC-V Bare Bones](https://wiki.osdev.org/RISC-V_Bare_Bones) 9 | - [RISC-V Instruction Green Card](https://rb.gy/o7j3m) 10 | - [RISC-V Unprivileged ISA](https://rb.gy/9pqqg) 11 | - [RISC-V Privileged ISA](https://rb.gy/0r5lc) 12 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn build(b: *std.Build) anyerror!void { 4 | const optimize = b.standardOptimizeOption(.{}); 5 | // The default target is riscv64, but we also support riscv32 6 | const target = b.standardTargetOptions(.{ .default_target = .{ 7 | .cpu_arch = .riscv64, 8 | .os_tag = .freestanding, 9 | .abi = .none, 10 | } }); 11 | 12 | const kernel = b.addExecutable(.{ 13 | .root_source_file = b.path("src/kernel.zig"), 14 | .optimize = optimize, 15 | .target = target, 16 | .name = "kernel", 17 | .code_model = .medium, 18 | }); 19 | 20 | kernel.setLinkerScriptPath(b.path("src/linker.lds")); 21 | // Some of the boot-code changes depending on if we're targeting 32-bit 22 | // or 64-bit, which is why we need the pre-processor to run first. 23 | kernel.addCSourceFiles(.{ 24 | .files = &.{"src/boot.S"}, 25 | .flags = &.{ 26 | "-x", "assembler-with-cpp", 27 | }, 28 | }); 29 | b.installArtifact(kernel); 30 | 31 | const qemu = switch (target.result.cpu.arch) { 32 | .riscv64 => "qemu-system-riscv64", 33 | .riscv32 => "qemu-system-riscv32", 34 | else => unreachable, 35 | }; 36 | 37 | const qemu_cpu = switch (target.result.cpu.arch) { 38 | .riscv64 => "rv64", 39 | .riscv32 => "rv32", 40 | else => unreachable, 41 | }; 42 | 43 | const qemu_cmd = b.addSystemCommand(&.{ 44 | qemu, "-machine", 45 | "virt", "-bios", 46 | "none", "-kernel", 47 | "zig-out/bin/kernel", "-m", 48 | "128M", "-cpu", 49 | qemu_cpu, "-smp", 50 | "4", "-nographic", 51 | "-serial", "mon:stdio", 52 | }); 53 | 54 | qemu_cmd.step.dependOn(b.getInstallStep()); 55 | if (b.args) |args| qemu_cmd.addArgs(args); 56 | const run_step = b.step("run", "Start the kernel in qemu"); 57 | run_step.dependOn(&qemu_cmd.step); 58 | } 59 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-compat": { 4 | "flake": false, 5 | "locked": { 6 | "lastModified": 1696426674, 7 | "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", 8 | "owner": "edolstra", 9 | "repo": "flake-compat", 10 | "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", 11 | "type": "github" 12 | }, 13 | "original": { 14 | "owner": "edolstra", 15 | "repo": "flake-compat", 16 | "type": "github" 17 | } 18 | }, 19 | "flake-compat_2": { 20 | "flake": false, 21 | "locked": { 22 | "lastModified": 1696426674, 23 | "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", 24 | "owner": "edolstra", 25 | "repo": "flake-compat", 26 | "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", 27 | "type": "github" 28 | }, 29 | "original": { 30 | "owner": "edolstra", 31 | "repo": "flake-compat", 32 | "type": "github" 33 | } 34 | }, 35 | "flake-utils": { 36 | "inputs": { 37 | "systems": "systems_2" 38 | }, 39 | "locked": { 40 | "lastModified": 1705309234, 41 | "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", 42 | "owner": "numtide", 43 | "repo": "flake-utils", 44 | "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", 45 | "type": "github" 46 | }, 47 | "original": { 48 | "owner": "numtide", 49 | "repo": "flake-utils", 50 | "type": "github" 51 | } 52 | }, 53 | "flake-utils_2": { 54 | "inputs": { 55 | "systems": "systems_3" 56 | }, 57 | "locked": { 58 | "lastModified": 1710146030, 59 | "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", 60 | "owner": "numtide", 61 | "repo": "flake-utils", 62 | "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", 63 | "type": "github" 64 | }, 65 | "original": { 66 | "owner": "numtide", 67 | "repo": "flake-utils", 68 | "type": "github" 69 | } 70 | }, 71 | "flake-utils_3": { 72 | "inputs": { 73 | "systems": "systems_4" 74 | }, 75 | "locked": { 76 | "lastModified": 1705309234, 77 | "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", 78 | "owner": "numtide", 79 | "repo": "flake-utils", 80 | "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", 81 | "type": "github" 82 | }, 83 | "original": { 84 | "owner": "numtide", 85 | "repo": "flake-utils", 86 | "type": "github" 87 | } 88 | }, 89 | "gitignore": { 90 | "inputs": { 91 | "nixpkgs": [ 92 | "zlspkg", 93 | "nixpkgs" 94 | ] 95 | }, 96 | "locked": { 97 | "lastModified": 1709087332, 98 | "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", 99 | "owner": "hercules-ci", 100 | "repo": "gitignore.nix", 101 | "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", 102 | "type": "github" 103 | }, 104 | "original": { 105 | "owner": "hercules-ci", 106 | "repo": "gitignore.nix", 107 | "type": "github" 108 | } 109 | }, 110 | "langref": { 111 | "flake": false, 112 | "locked": { 113 | "narHash": "sha256-O6p2tiKD8ZMhSX+DeA/o5hhAvcPkU2J9lFys/r11peY=", 114 | "type": "file", 115 | "url": "https://raw.githubusercontent.com/ziglang/zig/0fb2015fd3422fc1df364995f9782dfe7255eccd/doc/langref.html.in" 116 | }, 117 | "original": { 118 | "type": "file", 119 | "url": "https://raw.githubusercontent.com/ziglang/zig/0fb2015fd3422fc1df364995f9782dfe7255eccd/doc/langref.html.in" 120 | } 121 | }, 122 | "nixpkgs": { 123 | "locked": { 124 | "lastModified": 1720149507, 125 | "narHash": "sha256-OFJoD1jjxzFlY1tAsehEWVA88DbU5smzDPQuu9SmnXY=", 126 | "owner": "NixOS", 127 | "repo": "nixpkgs", 128 | "rev": "d9c0b9d611277e42e6db055636ba0409c59db6d2", 129 | "type": "github" 130 | }, 131 | "original": { 132 | "id": "nixpkgs", 133 | "type": "indirect" 134 | } 135 | }, 136 | "nixpkgs_2": { 137 | "locked": { 138 | "lastModified": 1708161998, 139 | "narHash": "sha256-6KnemmUorCvlcAvGziFosAVkrlWZGIc6UNT9GUYr0jQ=", 140 | "owner": "NixOS", 141 | "repo": "nixpkgs", 142 | "rev": "84d981bae8b5e783b3b548de505b22880559515f", 143 | "type": "github" 144 | }, 145 | "original": { 146 | "owner": "NixOS", 147 | "ref": "nixos-23.11", 148 | "repo": "nixpkgs", 149 | "type": "github" 150 | } 151 | }, 152 | "nixpkgs_3": { 153 | "locked": { 154 | "lastModified": 1718208800, 155 | "narHash": "sha256-US1tAChvPxT52RV8GksWZS415tTS7PV42KTc2PNDBmc=", 156 | "owner": "NixOS", 157 | "repo": "nixpkgs", 158 | "rev": "cc54fb41d13736e92229c21627ea4f22199fee6b", 159 | "type": "github" 160 | }, 161 | "original": { 162 | "owner": "NixOS", 163 | "ref": "nixos-24.05", 164 | "repo": "nixpkgs", 165 | "type": "github" 166 | } 167 | }, 168 | "root": { 169 | "inputs": { 170 | "nixpkgs": "nixpkgs", 171 | "utils": "utils", 172 | "zigpkg": "zigpkg", 173 | "zlspkg": "zlspkg" 174 | } 175 | }, 176 | "systems": { 177 | "locked": { 178 | "lastModified": 1681028828, 179 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 180 | "owner": "nix-systems", 181 | "repo": "default", 182 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 183 | "type": "github" 184 | }, 185 | "original": { 186 | "owner": "nix-systems", 187 | "repo": "default", 188 | "type": "github" 189 | } 190 | }, 191 | "systems_2": { 192 | "locked": { 193 | "lastModified": 1681028828, 194 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 195 | "owner": "nix-systems", 196 | "repo": "default", 197 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 198 | "type": "github" 199 | }, 200 | "original": { 201 | "owner": "nix-systems", 202 | "repo": "default", 203 | "type": "github" 204 | } 205 | }, 206 | "systems_3": { 207 | "locked": { 208 | "lastModified": 1681028828, 209 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 210 | "owner": "nix-systems", 211 | "repo": "default", 212 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 213 | "type": "github" 214 | }, 215 | "original": { 216 | "owner": "nix-systems", 217 | "repo": "default", 218 | "type": "github" 219 | } 220 | }, 221 | "systems_4": { 222 | "locked": { 223 | "lastModified": 1681028828, 224 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 225 | "owner": "nix-systems", 226 | "repo": "default", 227 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 228 | "type": "github" 229 | }, 230 | "original": { 231 | "owner": "nix-systems", 232 | "repo": "default", 233 | "type": "github" 234 | } 235 | }, 236 | "utils": { 237 | "inputs": { 238 | "systems": "systems" 239 | }, 240 | "locked": { 241 | "lastModified": 1710146030, 242 | "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", 243 | "owner": "numtide", 244 | "repo": "flake-utils", 245 | "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", 246 | "type": "github" 247 | }, 248 | "original": { 249 | "owner": "numtide", 250 | "repo": "flake-utils", 251 | "type": "github" 252 | } 253 | }, 254 | "zig-overlay": { 255 | "inputs": { 256 | "flake-compat": "flake-compat_2", 257 | "flake-utils": "flake-utils_3", 258 | "nixpkgs": [ 259 | "zlspkg", 260 | "nixpkgs" 261 | ] 262 | }, 263 | "locked": { 264 | "lastModified": 1718539737, 265 | "narHash": "sha256-hvQ900gSqzGnJWMRQwv65TixciIbC44iX0Nh5ENRwCU=", 266 | "owner": "mitchellh", 267 | "repo": "zig-overlay", 268 | "rev": "6eb42ce6f85d247b1aecf854c45d80902821d0ad", 269 | "type": "github" 270 | }, 271 | "original": { 272 | "owner": "mitchellh", 273 | "repo": "zig-overlay", 274 | "type": "github" 275 | } 276 | }, 277 | "zigpkg": { 278 | "inputs": { 279 | "flake-compat": "flake-compat", 280 | "flake-utils": "flake-utils", 281 | "nixpkgs": "nixpkgs_2" 282 | }, 283 | "locked": { 284 | "lastModified": 1720181374, 285 | "narHash": "sha256-tMl+nRGAKZ62VELgCLnaVlFPy25xDy6JDa8dInP2GOA=", 286 | "owner": "mitchellh", 287 | "repo": "zig-overlay", 288 | "rev": "98339f7226cd6310f9be2658e95e81970f83dba5", 289 | "type": "github" 290 | }, 291 | "original": { 292 | "owner": "mitchellh", 293 | "repo": "zig-overlay", 294 | "type": "github" 295 | } 296 | }, 297 | "zlspkg": { 298 | "inputs": { 299 | "flake-utils": "flake-utils_2", 300 | "gitignore": "gitignore", 301 | "langref": "langref", 302 | "nixpkgs": "nixpkgs_3", 303 | "zig-overlay": "zig-overlay" 304 | }, 305 | "locked": { 306 | "lastModified": 1720025017, 307 | "narHash": "sha256-SH/7dyh/MS8umrceIt8KATuG8V/zJGmxrPELzwGGfkw=", 308 | "owner": "zigtools", 309 | "repo": "zls", 310 | "rev": "4408eb218461625fc9ce594dfa4578c53c9d33ca", 311 | "type": "github" 312 | }, 313 | "original": { 314 | "owner": "zigtools", 315 | "repo": "zls", 316 | "type": "github" 317 | } 318 | } 319 | }, 320 | "root": "root", 321 | "version": 7 322 | } 323 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | utils.url = github:numtide/flake-utils; 4 | zigpkg.url = github:mitchellh/zig-overlay; 5 | zlspkg.url = github:zigtools/zls; 6 | }; 7 | 8 | outputs = { self, nixpkgs, zigpkg, zlspkg, utils }: 9 | utils.lib.eachDefaultSystem(system: 10 | let 11 | zig = zigpkg.packages.${system}; 12 | zls = zlspkg.packages.${system}; 13 | pkgs = nixpkgs.legacyPackages.${system}; 14 | in { 15 | devShells.default = pkgs.mkShell { 16 | nativeBuildInputs = [ zig.master zls.default ]; 17 | buildInputs = with pkgs; [ qemu ]; 18 | }; 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /src/boot.S: -------------------------------------------------------------------------------- 1 | .option norvc 2 | .section .data 3 | 4 | .section .text.init 5 | .global _start 6 | _start: 7 | // Get the ID of the currently running hart. 8 | csrr t0, mhartid 9 | // If the hart ID is not 0, then just make the hart spin as we only need one 10 | // hart to execute the boot code. 11 | bnez t0, 3f 12 | // Clear the SATP CSR as we want to work with physical addresses and while booting 13 | // do not want virtual memory enabled. 14 | csrw satp, zero 15 | .option push 16 | .option norelax 17 | la gp, _global_pointer 18 | .option pop 19 | la a0, _bss_start 20 | la a1, _bss_end 21 | // Skip clearing the BSS section if the address of _bss_start is greater than 22 | // the address of _bss_end. 23 | bgeu a0, a1, 2f 24 | 25 | 1: 26 | // Clear the BSS section, where a0 is initially the address of _bss_start. 27 | #if __riscv_xlen == 64 28 | sd zero, (a0) 29 | #elif __riscv_xlen == 32 30 | sw zero, (a0) 31 | #endif 32 | addi a0, a0, 8 33 | bltu a0, a1, 1b 34 | 35 | 2: 36 | // Load the end of the stack into the stack pointer before we jump to kmain. 37 | la sp, _stack 38 | // Here we set the mstatus CSR with the following bits: 39 | // MIE (bit 3) is set to globally enable interrupts 40 | // MPIE (bit 7) holds the value of the interrupt-enable bit active prior to the trap, 41 | // since we keep interrupts on, this stays set. 42 | // MPP (bits 11 and 12) are set to the privilege level we want to return to 43 | // when we execute the mret instruction, in this case we set it to 0b11 44 | // since we want to stay in machine mode (M-mode). 45 | li t0, (0b11 << 11) | (1 << 7) | (1 << 3) 46 | csrw mstatus, t0 47 | // Set the address of kmain function in our Zig code to be the program 48 | // counter when we return (aka when we execute the mret instruction) 49 | la t1, kmain 50 | csrw mepc, t1 51 | // Set the address of the trap function in our Zig code to be the trap-handler 52 | // It will get invoked whenever we get an exception, for example an interrupt 53 | la t2, trap 54 | csrw mtvec, t2 55 | // Set the M-mode interrupt enable bits 56 | // MSIE (bit 3) is set for software interrupts such as IPIs 57 | // (inter-processer interrupts), which will become relevant when using other 58 | // harts. 59 | // MEIE (bit 11) is set to enable external interrupts from hardware devices 60 | // such as an ethernet device or serial UART device. 61 | // Note that MTIE is *not* set as the kernel does not handle timer-interrupts 62 | // and we want to prevent the situation where the trap entry-point is invoked 63 | // before we even get to the kmain entry-point. 64 | li t3, (1 << 3) | (1 << 11) 65 | csrw mie, t3 66 | // Finally, we have the return address (ra) to 67 | la ra, 4f 68 | mret 69 | 70 | 3: 71 | 4: 72 | // Spin the hart 73 | wfi 74 | j 4b 75 | -------------------------------------------------------------------------------- /src/kernel.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const uart = @import("uart.zig"); 3 | 4 | // Here we set up a printf-like writer from the standard library by providing 5 | // a way to output via the UART. 6 | const Writer = std.io.Writer(u32, error{}, uart_put_str); 7 | const uart_writer = Writer { .context = 0 }; 8 | 9 | fn uart_put_str(_: u32, str: []const u8) !usize { 10 | for (str) |ch| { 11 | uart.put_char(ch); 12 | } 13 | return str.len; 14 | } 15 | 16 | pub fn println(comptime fmt: []const u8, args: anytype) void { 17 | uart_writer.print(fmt ++ "\n", args) catch {}; 18 | } 19 | 20 | // This the trap/exception entrypoint, this will be invoked any time 21 | // we get an exception (e.g if something in the kernel goes wrong) or 22 | // an interrupt gets delivered. 23 | export fn trap() align(4) callconv(.C) noreturn { 24 | while (true) {} 25 | } 26 | 27 | // This is the kernel's entrypoint which will be invoked by the booting 28 | // CPU (aka hart) after the boot code has executed. 29 | export fn kmain() callconv(.C) void { 30 | // All we're doing is setting up access to the serial device (UART) 31 | // and printing a simple message to make sure the kernel has started! 32 | uart.init(); 33 | // Who knows, maybe in the future we'll have rv128... 34 | println("Zig is running on barebones RISC-V (rv{})!", .{ @bitSizeOf(usize) }); 35 | } 36 | -------------------------------------------------------------------------------- /src/linker.lds: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH( "riscv" ) 2 | ENTRY( _start ) 3 | 4 | MEMORY 5 | { 6 | ram (wxa) : ORIGIN = 0x80000000, LENGTH = 128M 7 | } 8 | 9 | PHDRS 10 | { 11 | text PT_LOAD; 12 | data PT_LOAD; 13 | bss PT_LOAD; 14 | } 15 | 16 | SECTIONS 17 | { 18 | .text : { 19 | PROVIDE(_text_start = .); 20 | *(.text.init) *(.text .text.*) 21 | PROVIDE(_text_end = .); 22 | } >ram AT>ram :text 23 | 24 | PROVIDE(_global_pointer = .); 25 | 26 | .rodata : { 27 | PROVIDE(_rodata_start = .); 28 | *(.rodata .rodata.*) 29 | PROVIDE(_rodata_end = .); 30 | } >ram AT>ram :text 31 | 32 | .data : { 33 | . = ALIGN(4096); 34 | PROVIDE(_data_start = .); 35 | *(.sdata .sdata.*) *(.data .data.*) 36 | PROVIDE(_data_end = .); 37 | } >ram AT>ram :data 38 | 39 | .bss : { 40 | PROVIDE(_bss_start = .); 41 | *(.sbss .sbss.*) *(.bss .bss.*) 42 | PROVIDE(_bss_end = .); 43 | } >ram AT>ram :bss 44 | 45 | PROVIDE(_memory_start = ORIGIN(ram)); 46 | PROVIDE(_stack = _bss_end + 0x80000); 47 | PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram)); 48 | PROVIDE(_heap_start = _stack); 49 | PROVIDE(_heap_size = _memory_end - _heap_start); 50 | } 51 | -------------------------------------------------------------------------------- /src/uart.zig: -------------------------------------------------------------------------------- 1 | // This is a driver for the NS16550 UART devices. It is based on the OpenSBI NS16550 driver. 2 | 3 | // The default UART serial device is at 0x10000000 on the QEMU RISC-V virt platform 4 | const uart_base: usize = 0x10000000; 5 | 6 | const UART_RBR_OFFSET = 0; // In: Recieve Buffer Register 7 | const UART_DLL_OFFSET = 0; // Out: Divisor Latch Low 8 | const UART_IER_OFFSET = 1; // I/O: Interrupt Enable Register 9 | const UART_DLM_OFFSET = 1; // Out: Divisor Latch High 10 | const UART_FCR_OFFSET = 2; // Out: FIFO Control Register 11 | const UART_LCR_OFFSET = 3; // Out: Line Control Register 12 | const UART_LSR_OFFSET = 5; // In: Line Status Register 13 | const UART_MDR1_OFFSET = 8; // I/O: Mode Register 14 | 15 | const UART_LSR_DR = 0x01; // Receiver data ready 16 | const UART_LSR_THRE = 0x20; // Transmit-hold-register empty 17 | 18 | fn write_reg(offset: usize, value: u8) void { 19 | const ptr: *volatile u8 = @ptrFromInt(uart_base + offset); 20 | ptr.* = value; 21 | } 22 | 23 | fn read_reg(offset: usize) u8 { 24 | const ptr: *volatile u8 = @ptrFromInt(uart_base + offset); 25 | return ptr.*; 26 | } 27 | 28 | pub fn put_char(ch: u8) void { 29 | // Wait for transmission bit to be empty before enqueuing more characters 30 | // to be outputted. 31 | while ((read_reg(UART_LSR_OFFSET) & UART_LSR_THRE) == 0) {} 32 | 33 | write_reg(0, ch); 34 | } 35 | 36 | pub fn get_char() ?u8 { 37 | // Check that we actually have a character to read, if so then we read it 38 | // and return it. 39 | if (read_reg(UART_LSR_OFFSET) & UART_LSR_DR == 1) { 40 | return read_reg(UART_RBR_OFFSET); 41 | } else { 42 | return null; 43 | } 44 | } 45 | 46 | pub fn init() void { 47 | const lcr = (1 << 0) | (1 << 1); 48 | write_reg(UART_LCR_OFFSET, lcr); 49 | write_reg(UART_FCR_OFFSET, (1 << 0)); 50 | write_reg(UART_IER_OFFSET, (1 << 0)); 51 | write_reg(UART_LCR_OFFSET, lcr | (1 << 7)); 52 | 53 | const divisor: u16 = 592; 54 | const divisor_least: u8 = divisor & 0xff; 55 | const divisor_most: u8 = divisor >> 8; 56 | write_reg(UART_DLL_OFFSET, divisor_least); 57 | write_reg(UART_DLM_OFFSET, divisor_most); 58 | 59 | write_reg(UART_LCR_OFFSET, lcr); 60 | } 61 | --------------------------------------------------------------------------------