Reorganize
This commit is contained in:
parent
622f6d1791
commit
b8cd346b0a
97
Makefile
97
Makefile
@ -8,24 +8,91 @@ default: all
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -drf $(BUILD)
|
||||
cd bootloader; make clean
|
||||
cd tetros; cargo clean
|
||||
|
||||
# Make everything
|
||||
# (but don't run qemu)
|
||||
.PHONY: all
|
||||
all: img
|
||||
|
||||
# Make bios bootloader
|
||||
# dd if=./bootloader/build/stage2.bin of=$@ conv=notrunc bs=512 seek=40
|
||||
#
|
||||
# MARK: boot
|
||||
#
|
||||
|
||||
# Compile tetros as library
|
||||
LIB_SRC = ./tetros/Cargo.toml ./tetros/Cargo.lock $(shell find ./tetros/src -type f)
|
||||
$(BUILD)/tetros.lib: $(LIB_SRC)
|
||||
@mkdir -p $(BUILD)
|
||||
cd tetros && \
|
||||
env RUSTFLAGS="-C soft-float" \
|
||||
cargo rustc \
|
||||
--manifest-path="./Cargo.toml" \
|
||||
-Z build-std=core \
|
||||
-Z build-std-features=compiler-builtins-mem \
|
||||
--target "./targets/x86-unknown-none.json" \
|
||||
--lib \
|
||||
--release \
|
||||
-- \
|
||||
--emit link="$(CURDIR)/$@"
|
||||
|
||||
# Link tetros
|
||||
BIOS_LD = ./tetros/linkers/x86-unknown-none.ld
|
||||
$(BUILD)/tetros.elf: $(BUILD)/tetros.lib $(BIOS_LD)
|
||||
ld \
|
||||
-m elf_i386 \
|
||||
--gc-sections \
|
||||
-z max-page-size=0x1000 \
|
||||
-T "$(BIOS_LD)" \
|
||||
-o "$@" \
|
||||
"$<"
|
||||
|
||||
objcopy --only-keep-debug "$@" "$@.sym"
|
||||
objcopy --strip-debug "$@"
|
||||
|
||||
# Wrap tetros in three-stage BIOS loader
|
||||
# Parameters:
|
||||
# - BIOS_SRC: source directory of bios assembly
|
||||
# - STAGE2_SECTOR: the index of the first sector of the stage 2 binary on the disk
|
||||
BIOS_SRC = ./bios
|
||||
STAGE2_SECTOR = 1
|
||||
$(BUILD)/bios.bin: $(wildcard $(BIOS_SRC)/*.asm) $(BUILD)/tetros.elf
|
||||
@mkdir -p "$(BUILD)"
|
||||
nasm \
|
||||
-f bin \
|
||||
-D STAGE3=$(BUILD)/tetros.elf \
|
||||
-D STAGE2_SECTOR=$(STAGE2_SECTOR) \
|
||||
-o "$@" \
|
||||
-l "$@.lst" \
|
||||
-i "$(BIOS_SRC)" \
|
||||
"$(BIOS_SRC)/main.asm"
|
||||
|
||||
# Extract full mbr (first 512 bytes)
|
||||
$(BUILD)/mbr.bin: $(BUILD)/bios.bin
|
||||
@mkdir -p "$(BUILD)"
|
||||
@echo ""
|
||||
dd if="$<" bs=512 count=1 of="$@"
|
||||
|
||||
# Extract stage 2 (rest of file)
|
||||
$(BUILD)/stage2.bin: $(BUILD)/bios.bin
|
||||
@mkdir -p "$(BUILD)"
|
||||
@echo ""
|
||||
dd if="$<" bs=512 skip=1 of="$@"
|
||||
|
||||
#
|
||||
# MARK: bundle
|
||||
#
|
||||
|
||||
# Make full disk image
|
||||
.PHONY: img
|
||||
img: $(BUILD)/disk.img
|
||||
$(BUILD)/disk.img:
|
||||
mkdir -p $(BUILD)
|
||||
cd bootloader; make
|
||||
$(BUILD)/disk.img: $(BUILD)/mbr.bin $(BUILD)/stage2.bin
|
||||
@mkdir -p $(BUILD)
|
||||
@echo ""
|
||||
dd if=/dev/zero of=$@ bs=512 count=32
|
||||
dd if=./bootloader/build/512.bin of=$@ conv=notrunc bs=512
|
||||
dd if=./bootloader/build/stage2.bin of=$@ conv=notrunc seek=5 bs=512
|
||||
dd if="$(BUILD)/mbr.bin" of=$@ conv=notrunc bs=512
|
||||
dd if="$(BUILD)/stage2.bin" of=$@ conv=notrunc seek=$(STAGE2_SECTOR) bs=512
|
||||
|
||||
.PHONY: qemu
|
||||
qemu: $(BUILD)/disk.img
|
||||
qemu-system-i386 \
|
||||
-d cpu_reset \
|
||||
@ -35,6 +102,20 @@ qemu: $(BUILD)/disk.img
|
||||
-net none \
|
||||
-serial stdio \
|
||||
-fda "$<"
|
||||
|
||||
# Same as qemu, but with no dependency.
|
||||
# Used for remote dev, where build box != run box.
|
||||
.PHONY: qemu-remote
|
||||
qemu-remote:
|
||||
qemu-system-i386 \
|
||||
-d cpu_reset \
|
||||
-no-reboot \
|
||||
-smp 1 -m 2048 \
|
||||
-machine q35 \
|
||||
-net none \
|
||||
-serial stdio \
|
||||
-fda "$(BUILD)/disk.img"
|
||||
|
||||
# -gdb tcp::26000 \
|
||||
# -S
|
||||
# -enable-kvm \
|
||||
|
5
README.md
Normal file
5
README.md
Normal file
@ -0,0 +1,5 @@
|
||||
- fix asm loader
|
||||
- clock
|
||||
- input
|
||||
- music
|
||||
- document everything
|
@ -64,27 +64,6 @@ gdt:
|
||||
.null equ $ - gdt
|
||||
dq 0
|
||||
|
||||
.lm64_code equ $ - gdt
|
||||
istruc GDTEntry
|
||||
at GDTEntry.limitl, dw 0
|
||||
at GDTEntry.basel, dw 0
|
||||
at GDTEntry.basem, db 0
|
||||
at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.code
|
||||
at GDTEntry.flags__limith, db gdt_flag.long_mode
|
||||
at GDTEntry.baseh, db 0
|
||||
iend
|
||||
|
||||
.lm64_data equ $ - gdt
|
||||
istruc GDTEntry
|
||||
at GDTEntry.limitl, dw 0
|
||||
at GDTEntry.basel, dw 0
|
||||
at GDTEntry.basem, db 0
|
||||
; AMD System Programming Manual states that the writeable bit is ignored in long mode, but ss can not be set to this descriptor without it
|
||||
at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.writable
|
||||
at GDTEntry.flags__limith, db 0
|
||||
at GDTEntry.baseh, db 0
|
||||
iend
|
||||
|
||||
; All GTD addresses are multiples of 8,
|
||||
; and thus end in three zero bits.
|
||||
;
|
1
bootloader/.gitignore
vendored
1
bootloader/.gitignore
vendored
@ -1 +0,0 @@
|
||||
build
|
@ -1,83 +0,0 @@
|
||||
# This compiles our bootloader as a static library,
|
||||
# and wraps it in a multistage loader.
|
||||
|
||||
|
||||
BUILD = ./build
|
||||
|
||||
|
||||
.PHONY: all
|
||||
all: $(BUILD)/mbr.bin $(BUILD)/512.bin $(BUILD)/stage2.bin
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -drf $(BUILD)
|
||||
cd bootloader; cargo clean
|
||||
|
||||
# Compile bootloader as library
|
||||
LIB_SRC = ./bootloader/Cargo.toml ./bootloader/Cargo.lock $(shell find ./bootloader/src -type f)
|
||||
$(BUILD)/bootloader.lib: $(LIB_SRC)
|
||||
@mkdir -p $(BUILD)
|
||||
cd bootloader && \
|
||||
env RUSTFLAGS="-C soft-float" \
|
||||
cargo rustc \
|
||||
--manifest-path="./Cargo.toml" \
|
||||
-Z build-std=core,alloc \
|
||||
-Z build-std-features=compiler-builtins-mem \
|
||||
--target "./targets/x86-unknown-none.json" \
|
||||
--lib \
|
||||
--release \
|
||||
-- \
|
||||
--emit link="$(CURDIR)/$@"
|
||||
|
||||
# Link bootloader
|
||||
BIOS_LD = ./bootloader/linkers/x86-unknown-none.ld
|
||||
$(BUILD)/bootloader.elf: $(BUILD)/bootloader.lib $(BIOS_LD)
|
||||
ld \
|
||||
-m elf_i386 \
|
||||
--gc-sections \
|
||||
-z max-page-size=0x1000 \
|
||||
-T "$(BIOS_LD)" \
|
||||
-o "$@" \
|
||||
"$<"
|
||||
|
||||
objcopy --only-keep-debug "$@" "$@.sym"
|
||||
objcopy --strip-debug "$@"
|
||||
|
||||
# Wrap bootloader in three-stage BIOS loader
|
||||
# Parameters:
|
||||
# - BIOS_SRC: source directory of bios assembly
|
||||
# - STAGE3: path to linked stage 3 binary
|
||||
# - STAGE2_SECTOR: the index of the first sector of the stage 2 binary on the disk
|
||||
BIOS_SRC = ./bios
|
||||
STAGE2_SECTOR = 5
|
||||
STAGE3 = $(BUILD)/bootloader.elf
|
||||
$(BUILD)/bios.bin: $(wildcard $(BIOS_SRC)/*.asm) $(STAGE3)
|
||||
@mkdir -p "$(BUILD)"
|
||||
nasm \
|
||||
-f bin \
|
||||
-D STAGE3=$(STAGE3) \
|
||||
-D STAGE2_SECTOR=$(STAGE2_SECTOR) \
|
||||
-o "$@" \
|
||||
-l "$@.lst" \
|
||||
-i "$(BIOS_SRC)" \
|
||||
"$(BIOS_SRC)/main.asm"
|
||||
|
||||
# Extract MBR code (first 440 bytes)
|
||||
# This can be used to embed this mbr in gpt-partitioned disks
|
||||
$(BUILD)/mbr.bin: $(BUILD)/bios.bin
|
||||
@mkdir -p "$(BUILD)"
|
||||
@echo ""
|
||||
dd if="$<" bs=440 count=1 of="$@"
|
||||
|
||||
# Extract full mbr (first 512 bytes)
|
||||
# This can be used to make raw boot disks
|
||||
$(BUILD)/512.bin: $(BUILD)/bios.bin
|
||||
@mkdir -p "$(BUILD)"
|
||||
@echo ""
|
||||
dd if="$<" bs=512 count=1 of="$@"
|
||||
|
||||
# Extract stage 2 (rest of file)
|
||||
$(BUILD)/stage2.bin: $(BUILD)/bios.bin
|
||||
@mkdir -p "$(BUILD)"
|
||||
@echo ""
|
||||
dd if="$<" bs=512 skip=1 of="$@"
|
@ -1,73 +0,0 @@
|
||||
#![no_std]
|
||||
#![feature(alloc_error_handler)]
|
||||
#![feature(int_roundings)]
|
||||
#![feature(lang_items)]
|
||||
#![allow(internal_features)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use self::os::{OsMemoryEntry, OsMemoryKind};
|
||||
|
||||
#[macro_use]
|
||||
mod os;
|
||||
|
||||
#[macro_use]
|
||||
mod serial;
|
||||
|
||||
//TODO: allocate this in a more reasonable manner
|
||||
static mut AREAS: [OsMemoryEntry; 1024] = [OsMemoryEntry {
|
||||
base: 0,
|
||||
size: 0,
|
||||
kind: OsMemoryKind::Null,
|
||||
}; 1024];
|
||||
static mut AREAS_LEN: usize = 0;
|
||||
|
||||
pub fn area_add(area: OsMemoryEntry) {
|
||||
unsafe {
|
||||
for existing_area in &mut AREAS[0..AREAS_LEN] {
|
||||
if existing_area.kind == area.kind {
|
||||
if existing_area.base.unchecked_add(existing_area.size) == area.base {
|
||||
existing_area.size += area.size;
|
||||
return;
|
||||
}
|
||||
if area.base.unchecked_add(area.size) == existing_area.base {
|
||||
existing_area.base = area.base;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
*AREAS.get_mut(AREAS_LEN).expect("AREAS overflowed!") = area;
|
||||
AREAS_LEN += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub static mut LIVE_OPT: Option<(u64, &'static [u8])> = None;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
#[repr(C, packed(8))]
|
||||
pub struct KernelArgs {
|
||||
kernel_base: u64,
|
||||
kernel_size: u64,
|
||||
stack_base: u64,
|
||||
stack_size: u64,
|
||||
env_base: u64,
|
||||
env_size: u64,
|
||||
|
||||
/// The base pointer to the saved RSDP.
|
||||
///
|
||||
/// This field can be NULL, and if so, the system has not booted with UEFI or in some other way
|
||||
/// retrieved the RSDPs. The kernel or a userspace driver will thus try searching the BIOS
|
||||
/// memory instead. On UEFI systems, searching is not guaranteed to actually work though.
|
||||
acpi_rsdp_base: u64,
|
||||
/// The size of the RSDP region.
|
||||
acpi_rsdp_size: u64,
|
||||
|
||||
areas_base: u64,
|
||||
areas_size: u64,
|
||||
|
||||
bootstrap_base: u64,
|
||||
bootstrap_size: u64,
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -1,84 +0,0 @@
|
||||
use core::{cmp, mem, ptr};
|
||||
|
||||
use crate::area_add;
|
||||
use crate::os::{OsMemoryEntry, OsMemoryKind};
|
||||
|
||||
use super::{thunk::ThunkData, MEMORY_MAP_ADDR};
|
||||
|
||||
#[repr(C, packed)]
|
||||
struct MemoryMapEntry {
|
||||
pub base: u64,
|
||||
pub size: u64,
|
||||
pub kind: u32,
|
||||
}
|
||||
|
||||
pub struct MemoryMapIter {
|
||||
thunk15: extern "C" fn(),
|
||||
data: ThunkData,
|
||||
first: bool,
|
||||
}
|
||||
|
||||
impl MemoryMapIter {
|
||||
pub fn new(thunk15: extern "C" fn()) -> Self {
|
||||
Self {
|
||||
thunk15,
|
||||
data: ThunkData::new(),
|
||||
first: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for MemoryMapIter {
|
||||
type Item = OsMemoryEntry;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.first {
|
||||
self.first = false;
|
||||
} else if self.data.ebx == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.data.eax = 0xE820;
|
||||
self.data.ecx = mem::size_of::<MemoryMapEntry>() as u32;
|
||||
self.data.edx = 0x534D4150;
|
||||
self.data.edi = MEMORY_MAP_ADDR as u32;
|
||||
|
||||
unsafe {
|
||||
self.data.with(self.thunk15);
|
||||
}
|
||||
|
||||
//TODO: return error?
|
||||
assert_eq!({ self.data.eax }, 0x534D4150);
|
||||
assert_eq!({ self.data.ecx }, mem::size_of::<MemoryMapEntry>() as u32);
|
||||
|
||||
let entry = unsafe { ptr::read(MEMORY_MAP_ADDR as *const MemoryMapEntry) };
|
||||
Some(Self::Item {
|
||||
base: entry.base,
|
||||
size: entry.size,
|
||||
kind: match entry.kind {
|
||||
0 => OsMemoryKind::Null,
|
||||
1 => OsMemoryKind::Free,
|
||||
3 => OsMemoryKind::Reclaim,
|
||||
_ => OsMemoryKind::Reserved,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn memory_map(thunk15: extern "C" fn()) -> Option<(usize, usize)> {
|
||||
let mut heap_limits = None;
|
||||
for entry in MemoryMapIter::new(thunk15) {
|
||||
let heap_start = 1 * 1024 * 1024;
|
||||
if { entry.kind } == OsMemoryKind::Free
|
||||
&& entry.base <= heap_start as u64
|
||||
&& (entry.base + entry.size) >= heap_start as u64
|
||||
{
|
||||
let heap_end = cmp::min(entry.base + entry.size, usize::MAX as u64) as usize;
|
||||
if heap_end >= heap_start {
|
||||
heap_limits = Some((heap_start, heap_end - heap_start));
|
||||
}
|
||||
}
|
||||
|
||||
area_add(entry);
|
||||
}
|
||||
heap_limits
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
use core::{fmt, slice};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C, packed)]
|
||||
pub struct VgaTextBlock {
|
||||
pub char: u8,
|
||||
pub color: u8,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(u8)]
|
||||
pub enum VgaTextColor {
|
||||
Black = 0,
|
||||
Blue = 1,
|
||||
Green = 2,
|
||||
Cyan = 3,
|
||||
Red = 4,
|
||||
Purple = 5,
|
||||
Brown = 6,
|
||||
Gray = 7,
|
||||
DarkGray = 8,
|
||||
LightBlue = 9,
|
||||
LightGreen = 10,
|
||||
LightCyan = 11,
|
||||
LightRed = 12,
|
||||
LightPurple = 13,
|
||||
Yellow = 14,
|
||||
White = 15,
|
||||
}
|
||||
|
||||
pub struct Vga {
|
||||
pub base: usize,
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
pub x: usize,
|
||||
pub y: usize,
|
||||
pub bg: VgaTextColor,
|
||||
pub fg: VgaTextColor,
|
||||
}
|
||||
|
||||
impl Vga {
|
||||
pub const unsafe fn new(base: usize, width: usize, height: usize) -> Self {
|
||||
Self {
|
||||
base,
|
||||
width,
|
||||
height,
|
||||
x: 0,
|
||||
y: 0,
|
||||
bg: VgaTextColor::Black,
|
||||
fg: VgaTextColor::Gray,
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn blocks(&mut self) -> &'static mut [VgaTextBlock] {
|
||||
slice::from_raw_parts_mut(self.base as *mut VgaTextBlock, self.width * self.height)
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.x = 0;
|
||||
self.y = 0;
|
||||
let blocks = unsafe { self.blocks() };
|
||||
for i in 0..blocks.len() {
|
||||
blocks[i] = VgaTextBlock {
|
||||
char: 0,
|
||||
color: ((self.bg as u8) << 4) | (self.fg as u8),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for Vga {
|
||||
fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
|
||||
let blocks = unsafe { self.blocks() };
|
||||
for c in s.chars() {
|
||||
if self.x >= self.width {
|
||||
self.x = 0;
|
||||
self.y += 1;
|
||||
}
|
||||
while self.y >= self.height {
|
||||
for y in 1..self.height {
|
||||
for x in 0..self.width {
|
||||
let i = y * self.width + x;
|
||||
let j = i - self.width;
|
||||
blocks[j] = blocks[i];
|
||||
if y + 1 == self.height {
|
||||
blocks[i].char = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.y -= 1;
|
||||
}
|
||||
match c {
|
||||
'\x08' => {
|
||||
if self.x > 0 {
|
||||
self.x -= 1;
|
||||
}
|
||||
}
|
||||
'\r' => {
|
||||
self.x = 0;
|
||||
}
|
||||
'\n' => {
|
||||
self.x = 0;
|
||||
self.y += 1;
|
||||
}
|
||||
_ => {
|
||||
let i = self.y * self.width + self.x;
|
||||
if let Some(block) = blocks.get_mut(i) {
|
||||
block.char = c as u8;
|
||||
block.color = ((self.bg as u8) << 4) | (self.fg as u8);
|
||||
}
|
||||
self.x += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
pub use self::bios::*;
|
||||
#[macro_use]
|
||||
mod bios;
|
||||
|
||||
// Keep synced with BootloaderMemoryKind in kernel
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[repr(u64)]
|
||||
pub enum OsMemoryKind {
|
||||
Null = 0,
|
||||
Free = 1,
|
||||
Reclaim = 2,
|
||||
Reserved = 3,
|
||||
}
|
||||
|
||||
// Keep synced with BootloaderMemoryEntry in kernel
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C, packed(8))]
|
||||
pub struct OsMemoryEntry {
|
||||
pub base: u64,
|
||||
pub size: u64,
|
||||
pub kind: OsMemoryKind,
|
||||
}
|
19
bootloader/bootloader/Cargo.lock → tetros/Cargo.lock
generated
19
bootloader/bootloader/Cargo.lock → tetros/Cargo.lock
generated
@ -35,15 +35,6 @@ dependencies = [
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked_list_allocator"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286"
|
||||
dependencies = [
|
||||
"spinning_top",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
@ -84,21 +75,11 @@ dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spinning_top"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b9eb1a2f4c41445a3a0ff9abc5221c5fcd28e1f13cd7c0397706f9ac938ddb0"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tetros"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"linked_list_allocator",
|
||||
"spin",
|
||||
"uart_16550",
|
||||
]
|
@ -6,7 +6,7 @@ publish = false
|
||||
|
||||
[lib]
|
||||
name = "tetros"
|
||||
path = "src/main.rs"
|
||||
path = "src/lib.rs"
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
|
||||
@ -32,7 +32,6 @@ missing_docs = "allow"
|
||||
needless_return = "allow"
|
||||
new_without_default = "allow"
|
||||
tabs_in_doc_comments = "allow"
|
||||
expect_used = "deny"
|
||||
dbg_macro = "deny"
|
||||
allow_attributes = "deny"
|
||||
create_dir = "deny"
|
||||
@ -55,8 +54,9 @@ match_on_vec_items = "deny"
|
||||
wildcard_dependencies = "deny"
|
||||
negative_feature_names = "deny"
|
||||
redundant_feature_names = "deny"
|
||||
multiple_crate_versions = "deny"
|
||||
|
||||
multiple_crate_versions = "allow"
|
||||
missing_safety_doc = "allow"
|
||||
identity_op = "allow"
|
||||
|
||||
#
|
||||
# MARK: dependencies
|
||||
@ -64,8 +64,7 @@ multiple_crate_versions = "deny"
|
||||
|
||||
|
||||
[dependencies]
|
||||
linked_list_allocator = "0.10.5"
|
||||
spin = "0.9.5"
|
||||
spin = "0.9.8"
|
||||
uart_16550 = "0.3.2"
|
||||
|
||||
[dependencies.lazy_static]
|
4
tetros/src/drivers/mod.rs
Normal file
4
tetros/src/drivers/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
#[macro_use]
|
||||
pub mod serial;
|
||||
|
||||
pub mod vga;
|
@ -42,7 +42,7 @@ pub fn _print(args: core::fmt::Arguments<'_>) {
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => {
|
||||
$crate::serial::_print(format_args!($($arg)*));
|
||||
$crate::drivers::serial::_print(format_args!($($arg)*));
|
||||
};
|
||||
}
|
||||
|
@ -1,26 +1,18 @@
|
||||
use linked_list_allocator::LockedHeap;
|
||||
#![no_std]
|
||||
#![feature(int_roundings)]
|
||||
#![feature(lang_items)]
|
||||
#![allow(internal_features)]
|
||||
|
||||
use drivers::vga::Vga13h;
|
||||
use os::thunk::ThunkData;
|
||||
use spin::Mutex;
|
||||
use tetris::TetrisBoard;
|
||||
use vga::Vga13h;
|
||||
use tetrisboard::TetrisBoard;
|
||||
|
||||
use self::memory_map::memory_map;
|
||||
use self::thunk::ThunkData;
|
||||
mod os;
|
||||
mod tetrisboard;
|
||||
|
||||
use crate::println;
|
||||
|
||||
mod memory_map;
|
||||
mod panic;
|
||||
mod tetris;
|
||||
mod thunk;
|
||||
mod vga;
|
||||
|
||||
// Real mode memory allocation, for use with thunk
|
||||
// 0x500 to 0x7BFF is free
|
||||
const MEMORY_MAP_ADDR: usize = 0x1380; // 24 bytes, ends at 0x1397
|
||||
const THUNK_STACK_ADDR: usize = 0x7C00; // Grows downwards
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOCATOR: LockedHeap = LockedHeap::empty();
|
||||
#[macro_use]
|
||||
mod drivers;
|
||||
|
||||
pub(crate) static VGA: Mutex<Vga13h> = Mutex::new(unsafe { Vga13h::new() });
|
||||
|
||||
@ -67,9 +59,5 @@ pub unsafe extern "C" fn start(
|
||||
let t = TetrisBoard::new();
|
||||
t.draw(&mut v);
|
||||
|
||||
let (heap_start, heap_size) = memory_map(thunk15).expect("No memory for heap");
|
||||
|
||||
ALLOCATOR.lock().init(heap_start as *mut u8, heap_size);
|
||||
|
||||
panic!("kernel");
|
||||
}
|
6
tetros/src/os/mod.rs
Normal file
6
tetros/src/os/mod.rs
Normal file
@ -0,0 +1,6 @@
|
||||
pub mod thunk;
|
||||
|
||||
#[macro_use]
|
||||
pub mod panic;
|
||||
|
||||
const THUNK_STACK_ADDR: usize = 0x7C00; // Grows downwards
|
@ -1,6 +1,5 @@
|
||||
//! Intrinsics for panic handling
|
||||
|
||||
use core::alloc::Layout;
|
||||
use core::arch::asm;
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
@ -22,13 +21,6 @@ pub fn rust_begin_unwind(info: &PanicInfo<'_>) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
#[alloc_error_handler]
|
||||
#[no_mangle]
|
||||
#[allow(improper_ctypes_definitions)] // Layout is not repr(C)
|
||||
pub extern "C" fn rust_oom(_layout: Layout) -> ! {
|
||||
panic!("memory allocation failed");
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
/// Required to handle panics
|
31
tetros/src/tetrisboard/cell.rs
Normal file
31
tetros/src/tetrisboard/cell.rs
Normal file
@ -0,0 +1,31 @@
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum TetrisCell {
|
||||
Empty,
|
||||
Blue,
|
||||
Cyan,
|
||||
Orange,
|
||||
Red,
|
||||
Green,
|
||||
Purple,
|
||||
Yellow,
|
||||
}
|
||||
|
||||
impl TetrisCell {
|
||||
pub fn vga_color(&self) -> u8 {
|
||||
match self {
|
||||
Self::Empty => 0b0000_0000,
|
||||
Self::Blue => 0b0000_0001,
|
||||
Self::Cyan => 0b0000_0011,
|
||||
Self::Orange => 0b0000_0110,
|
||||
Self::Red => 0b0000_0100,
|
||||
Self::Green => 0b0000_0010,
|
||||
Self::Purple => 0b0000_0101,
|
||||
Self::Yellow => 0b1100_0000,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
matches!(self, Self::Empty)
|
||||
}
|
||||
}
|
@ -1,70 +1,10 @@
|
||||
use super::vga::Vga13h;
|
||||
use crate::drivers::vga::Vga13h;
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum TetrisCell {
|
||||
Empty,
|
||||
Blue,
|
||||
Cyan,
|
||||
Orange,
|
||||
Red,
|
||||
Green,
|
||||
Purple,
|
||||
Yellow,
|
||||
}
|
||||
|
||||
impl TetrisCell {
|
||||
pub fn to_vga_color(&self) -> u8 {
|
||||
match self {
|
||||
Self::Empty => 0b000_00000,
|
||||
Self::Blue => 0b000_00001,
|
||||
Self::Cyan => 0b000_00011,
|
||||
Self::Orange => 0b000_00110,
|
||||
Self::Red => 0b000_00100,
|
||||
Self::Green => 0b000_00010,
|
||||
Self::Purple => 0b000_00101,
|
||||
Self::Yellow => 0b110_000_00,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
matches!(self, Self::Empty)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TetrisBoard {
|
||||
board: [TetrisCell; TetrisBoard::BOARD_WIDTH * TetrisBoard::BOARD_HEIGHT],
|
||||
}
|
||||
use super::{TetrisBoard, TetrisCell};
|
||||
|
||||
impl TetrisBoard {
|
||||
const BOARD_WIDTH: usize = 10;
|
||||
const BOARD_HEIGHT: usize = 20;
|
||||
|
||||
const CELL_SIZE: usize = 9;
|
||||
|
||||
pub fn new() -> Self {
|
||||
let mut x = Self {
|
||||
board: [TetrisCell::Empty; TetrisBoard::BOARD_WIDTH * TetrisBoard::BOARD_HEIGHT],
|
||||
};
|
||||
|
||||
x.board[0] = TetrisCell::Blue;
|
||||
x.board[1] = TetrisCell::Cyan;
|
||||
x.board[2] = TetrisCell::Orange;
|
||||
x.board[3] = TetrisCell::Red;
|
||||
x.board[4] = TetrisCell::Green;
|
||||
x.board[5] = TetrisCell::Purple;
|
||||
x.board[6] = TetrisCell::Yellow;
|
||||
|
||||
x.board[9] = TetrisCell::Blue;
|
||||
x.board[8] = TetrisCell::Blue;
|
||||
x.board[10] = TetrisCell::Blue;
|
||||
x.board[12] = TetrisCell::Blue;
|
||||
|
||||
x
|
||||
}
|
||||
|
||||
fn draw_cell(&self, fb: &mut [u8], state: TetrisCell, x: usize, y: usize) {
|
||||
let color = state.to_vga_color();
|
||||
let color = state.vga_color();
|
||||
for yo in 0..TetrisBoard::CELL_SIZE {
|
||||
let left = Vga13h::pix_idx(x, y + yo);
|
||||
let right = Vga13h::pix_idx(x + TetrisBoard::CELL_SIZE, y + yo);
|
||||
@ -73,7 +13,7 @@ impl TetrisBoard {
|
||||
}
|
||||
|
||||
fn draw_frame(&self, fb: &mut [u8], x: usize, y: usize) {
|
||||
let color = 0b111_000_00;
|
||||
let color = 0b1110_0000;
|
||||
for yo in 0..TetrisBoard::CELL_SIZE {
|
||||
let left = Vga13h::pix_idx(x, y + yo);
|
||||
let right = Vga13h::pix_idx(x + TetrisBoard::CELL_SIZE, y + yo);
|
35
tetros/src/tetrisboard/mod.rs
Normal file
35
tetros/src/tetrisboard/mod.rs
Normal file
@ -0,0 +1,35 @@
|
||||
mod cell;
|
||||
mod draw;
|
||||
pub use cell::*;
|
||||
|
||||
pub struct TetrisBoard {
|
||||
board: [TetrisCell; TetrisBoard::BOARD_WIDTH * TetrisBoard::BOARD_HEIGHT],
|
||||
}
|
||||
|
||||
impl TetrisBoard {
|
||||
const BOARD_WIDTH: usize = 10;
|
||||
const BOARD_HEIGHT: usize = 20;
|
||||
|
||||
const CELL_SIZE: usize = 9;
|
||||
|
||||
pub fn new() -> Self {
|
||||
let mut x = Self {
|
||||
board: [TetrisCell::Empty; TetrisBoard::BOARD_WIDTH * TetrisBoard::BOARD_HEIGHT],
|
||||
};
|
||||
|
||||
x.board[0] = TetrisCell::Blue;
|
||||
x.board[1] = TetrisCell::Cyan;
|
||||
x.board[2] = TetrisCell::Orange;
|
||||
x.board[3] = TetrisCell::Red;
|
||||
x.board[4] = TetrisCell::Green;
|
||||
x.board[5] = TetrisCell::Purple;
|
||||
x.board[6] = TetrisCell::Yellow;
|
||||
|
||||
x.board[9] = TetrisCell::Blue;
|
||||
x.board[8] = TetrisCell::Blue;
|
||||
x.board[10] = TetrisCell::Blue;
|
||||
x.board[12] = TetrisCell::Blue;
|
||||
|
||||
x
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user