1
0

Compare commits

...

4 Commits

Author SHA1 Message Date
cbf6c48b22
Write README
All checks were successful
CI / Typos (push) Successful in 10s
CI / Clippy (push) Successful in 31s
CI / Build (push) Successful in 1m17s
2025-03-02 21:00:16 -08:00
1b401b46b2
Dead code 2025-03-02 20:36:01 -08:00
f5ede33bc8
Remove cpuid checks 2025-03-02 20:35:26 -08:00
de3ff45f1b
Quick drop and lose condition 2025-03-02 20:35:23 -08:00
5 changed files with 52 additions and 193 deletions

View File

@ -1,5 +1,36 @@
- quick drop
- faster timer
- fix asm loader
- document everything
- prettier pictures
# TetrOS: bare-metal tetris
## TODO:
- Fix stage 1 loader
## Features
- Compiles to a standalone disk image
- Written from scratch using only Nasm and Rust
- Custom BIOS bootloader
- 32-bit x86 OS
- 🌟 Detailed comments. Read the [makefile](./Makefile), then start in [`./bios/main.asm`](./bios/main.asm).
## Non-Features
- Never tested on real hardware
- Minimal gameplay and graphics. These features aren't hard to implement, but also don't present any interesting challenges. I have other things to do.
## 🚀 Building and Running
- All scripts are in the [makefile](./Makefile).
- To build and run, use `make qemu`.
- Dependencies: `nasm`, `cargo`, GNU `binutils`, `qemu`
- This will NOT work on MacOS. BSD `ld` does not work like GNU `ld`.
- Alternatively, a compiled disk image for the latest commit is [here](https://git.betalupi.com/api/packages/Mark/generic/tetros/latest/disk.img). This is the same file produced by `make`.
- Download it and run `qemu-system-i386 -d cpu_reset -no-reboot -smp 1 -m 2048 -machine q35 -net none -serial stdio -fda "disk.img"`
## 📜 Resources
**Used directly:**
- [jdh's video](https://www.youtube.com/watch?v=FaILnmUYS_U)
- [The OSDev wiki](https://wiki.osdev.org/Main_Page)
- [RedoxOS bootloader](https://gitlab.redox-os.org/redox-os/bootloader)
**Useful background knowledge:**
- [Writing an OS in Rust](https://os.phil-opp.com)
- [Operating Systems: From 0 to 1](https://github.com/tuhdo/os01)

View File

@ -1,176 +0,0 @@
SECTION .text
USE16
cpuid_required_features:
.edx equ cpuid_edx.fpu | cpuid_edx.pse | cpuid_edx.pge | cpuid_edx.fxsr
.ecx equ 0
cpuid_check:
; If bit 21 of EFLAGS can be changed, then CPUID is supported
pushfd ;Save EFLAGS
pushfd ;Store EFLAGS
xor dword [esp],0x00200000 ;Invert the ID bit in stored EFLAGS
popfd ;Load stored EFLAGS (with ID bit inverted)
pushfd ;Store EFLAGS again (ID bit may or may not be inverted)
pop eax ;eax = modified EFLAGS (ID bit may or may not be inverted)
xor eax,[esp] ;eax = whichever bits were changed
popfd ;Restore original EFLAGS
test eax,0x00200000 ;eax = zero if ID bit can't be changed, else non-zero
jz .no_cpuid
mov eax, 1
cpuid
and edx, cpuid_required_features.edx
cmp edx, cpuid_required_features.edx
jne .error
and ecx, cpuid_required_features.ecx
cmp ecx, cpuid_required_features.ecx
jne .error
ret
.no_cpuid:
mov si, .msg_cpuid
call print
mov si, .msg_line
call print
jmp .halt
.error:
push ecx
push edx
mov si, .msg_features
call print
mov si, .msg_line
call print
mov si, .msg_edx
call print
pop ebx
push ebx
shr ebx, 16
call print_hex
pop ebx
call print_hex
mov si, .msg_must_contain
call print
mov ebx, cpuid_required_features.edx
shr ebx, 16
call print_hex
mov ebx, cpuid_required_features.edx
call print_hex
mov si, .msg_line
call print
mov si, .msg_ecx
call print
pop ebx
push ebx
shr ebx, 16
call print_hex
pop ebx
call print_hex
mov si, .msg_must_contain
call print
mov ebx, cpuid_required_features.ecx
shr ebx, 16
call print_hex
mov ebx, cpuid_required_features.ecx
call print_hex
mov si, .msg_line
call print
.halt:
cli
hlt
jmp .halt
.msg_cpuid: db "CPUID not supported",0
.msg_features: db "Required CPU features are not present",0
.msg_line: db 13,10,0
.msg_edx: db "EDX ",0
.msg_ecx: db "ECX ",0
.msg_must_contain: db " must contain ",0
cpuid_edx:
.fpu equ 1 << 0
.vme equ 1 << 1
.de equ 1 << 2
.pse equ 1 << 3
.tsc equ 1 << 4
.msr equ 1 << 5
.pae equ 1 << 6
.mce equ 1 << 7
.cx8 equ 1 << 8
.apic equ 1 << 9
.sep equ 1 << 11
.mtrr equ 1 << 12
.pge equ 1 << 13
.mca equ 1 << 14
.cmov equ 1 << 15
.pat equ 1 << 16
.pse_36 equ 1 << 17
.psn equ 1 << 18
.clfsh equ 1 << 19
.ds equ 1 << 21
.acpi equ 1 << 22
.mmx equ 1 << 23
.fxsr equ 1 << 24
.sse equ 1 << 25
.sse2 equ 1 << 26
.ss equ 1 << 27
.htt equ 1 << 28
.tm equ 1 << 29
.ia64 equ 1 << 30
.pbe equ 1 << 31
cpuid_ecx:
.sse3 equ 1 << 0
.pclmulqdq equ 1 << 1
.dtes64 equ 1 << 2
.monitor equ 1 << 3
.ds_cpl equ 1 << 4
.vmx equ 1 << 5
.smx equ 1 << 6
.est equ 1 << 7
.tm2 equ 1 << 8
.ssse3 equ 1 << 9
.cnxt_id equ 1 << 10
.sdbg equ 1 << 11
.fma equ 1 << 12
.cmpxchg16b equ 1 << 13
.xtpr equ 1 << 14
.pdcm equ 1 << 15
.pcid equ 1 << 17
.dca equ 1 << 18
.sse4_1 equ 1 << 19
.sse4_2 equ 1 << 20
.x2apic equ 1 << 21
.movbe equ 1 << 22
.popcnt equ 1 << 23
.tsc_deadline equ 1 << 24
.aes equ 1 << 25
.xsave equ 1 << 26
.osxsave equ 1 << 27
.avx equ 1 << 28
.f16c equ 1 << 29
.rdrand equ 1 << 30
.hypervisor equ 1 << 31

View File

@ -8,10 +8,6 @@ stage2.entry:
call print_char
call print_line
; check for required features
call cpuid_check
; enable A20-Line via IO-Port 92, might not work on all motherboards
in al, 0x92
or al, 2
@ -20,7 +16,6 @@ stage2.entry:
mov dword [protected_mode.func], stage3.entry
jmp protected_mode.entry
%include "cpuid.asm"
%include "gdt.asm"
%include "protected_mode.asm"
%include "thunk.asm"

View File

@ -62,6 +62,7 @@ impl Direction {
}
}
/*
pub fn rot_ccw(self) -> Self {
match self {
Self::North => Self::West,
@ -70,6 +71,7 @@ impl Direction {
Self::East => Self::North,
}
}
*/
}
#[derive(Debug, Clone)]
@ -124,9 +126,11 @@ impl FallingTetromino {
self.direction = self.direction.rot_cw()
}
/*
pub fn rotate_ccw(&mut self) {
self.direction = self.direction.rot_ccw()
}
*/
/// Returns the positions of this falling tetromino's tiles.
pub fn tiles(&self) -> [(usize, usize); 4] {

View File

@ -205,12 +205,14 @@ pub unsafe extern "C" fn start(thunk10: extern "C" fn()) -> ! {
};
}
Some(InputKey::Down) => {
fall_test.rotate_ccw();
Some(InputKey::Down) => loop {
fall_test.translate(0, 1);
if board.tetromino_valid(&fall_test) {
fall.rotate_ccw()
};
}
fall.translate(0, 1);
} else {
break;
}
},
Some(InputKey::Left) => {
fall_test.translate(-1, 0);
@ -244,7 +246,7 @@ pub unsafe extern "C" fn start(thunk10: extern "C" fn()) -> ! {
board.draw(&mut v, fall.as_ref());
if let Some(fall_inner) = fall.as_mut() {
if t % 5 == 0 {
if t % 4 == 0 {
let mut fall_test = fall_inner.clone();
fall_test.translate(0, 1);
@ -258,7 +260,10 @@ pub unsafe extern "C" fn start(thunk10: extern "C" fn()) -> ! {
}
}
} else {
*fall = Some(FallingTetromino::random(5, 1))
*fall = Some(FallingTetromino::random(5, 1));
if !board.tetromino_valid(fall.as_ref().unwrap()) {
panic!("\nGAME OVER");
}
}
})
}