diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..842e290 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,10 @@ +[build] +target = "m68k-unknown-none-elf" + +[target.m68k-unknown-none-elf] +rustflags = ["-C", "link-arg=-Tkernel.ld"] + +# Note: This target requires nightly and build-std. +# If you are on stable, you must override the target or use a custom JSON target spec (not included). +# [unstable] +# build-std = ["core", "alloc"] diff --git a/.gitignore b/.gitignore index f220079..21e1716 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ Binaries/ disks.rar Analysis/ Binaries/ +/target +/rust_src/target diff --git a/BUILD_RUST.md b/BUILD_RUST.md new file mode 100644 index 0000000..ac8d67c --- /dev/null +++ b/BUILD_RUST.md @@ -0,0 +1,35 @@ +# Building the Rust OS + +The operating system has been converted to a Rust workspace structure. + +## Structure +- `rust_src/mac_types`: Common Macintosh types (OSErr, Rect, etc.) rewritten in Rust. +- `rust_src/os`: Core OS subsystems (MemoryMgr, StartMgr, etc.). +- `rust_src/kernel`: The kernel entry point and main loop (MiniFinder equivalent). + +## Prerequisites +- **Rust Nightly** is **REQUIRED** to build for the `m68k-unknown-none-elf` target because the standard library (`core`) is not pre-compiled for this target. +- `rust-src` component. + +## Building for Macintosh SE/30 (m68k) + +1. Install the nightly toolchain and the rust-src component: + ```bash + rustup toolchain install nightly + rustup component add rust-src --toolchain nightly + ``` + +2. Build using cargo with the nightly toolchain and `build-std`: + ```bash + cargo +nightly build --release -Z build-std=core,alloc + ``` + + *Note: The `.cargo/config.toml` sets the default target to `m68k-unknown-none-elf`. You can uncomment the `[unstable]` section in that file if you are always using nightly.* + +## Verifying on x86_64 Host (Syntax Check) + +If you do not have the nightly toolchain or just want to check the syntax: + +```bash +cargo check --target x86_64-unknown-linux-gnu +``` diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..603bd5d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,22 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "kernel" +version = "0.1.0" +dependencies = [ + "mac_types", + "os", +] + +[[package]] +name = "mac_types" +version = "0.1.0" + +[[package]] +name = "os" +version = "0.1.0" +dependencies = [ + "mac_types", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..93ea69f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[workspace] +members = [ + "rust_src/mac_types", + "rust_src/kernel", + "rust_src/os", +] +resolver = "2" + +[profile.release] +lto = true +opt-level = "s" +panic = "abort" + +[profile.dev] +panic = "abort" diff --git a/kernel.ld b/kernel.ld new file mode 100644 index 0000000..b56c260 --- /dev/null +++ b/kernel.ld @@ -0,0 +1,8 @@ +ENTRY(_start) + +SECTIONS { + . = 0x1000; + .text : { *(.text) } + .data : { *(.data) } + .bss : { *(.bss) } +} diff --git a/cross_compile.sh b/legacy_cross_compile.sh similarity index 100% rename from cross_compile.sh rename to legacy_cross_compile.sh diff --git a/rust_src/kernel/Cargo.toml b/rust_src/kernel/Cargo.toml new file mode 100644 index 0000000..c6d9364 --- /dev/null +++ b/rust_src/kernel/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "kernel" +version = "0.1.0" +edition = "2021" + +[dependencies] +mac_types = { path = "../mac_types" } +os = { path = "../os" } + +[profile.release] +panic = "abort" diff --git a/rust_src/kernel/src/main.rs b/rust_src/kernel/src/main.rs new file mode 100644 index 0000000..ca6c397 --- /dev/null +++ b/rust_src/kernel/src/main.rs @@ -0,0 +1,135 @@ +#![no_std] +#![no_main] + +use core::panic::PanicInfo; +use mac_types::{EventRecord, Rect, Point, Str255, EVERY_EVENT, NULL_EVENT, MOUSE_DOWN, KEY_DOWN}; +use os::memory_mgr::{init_zone, new_handle, dispose_handle}; + +// Mock External Functions (Toolbox) +// In a real implementation, these would be linked to the actual C/Asm implementation +// or rewritten in Rust. For now, we stub them. + +extern "C" { + fn InitGraf(ptr: *mut u8); + fn InitFonts(); + fn InitWindows(); + fn InitMenus(); + fn TEInit(); + fn InitDialogs(ptr: *mut u8); + fn InitCursor(); + fn MaxApplZone(); +} + +#[no_mangle] +#[allow(non_snake_case)] +pub unsafe extern "C" fn FillRect(_rect: *const Rect, _pattern: *const u8) { + // Hardware specific implementation +} + +#[no_mangle] +#[allow(non_snake_case)] +pub unsafe extern "C" fn SetRect(rect: *mut Rect, left: i16, top: i16, right: i16, bottom: i16) { + if rect.is_null() { return; } + (*rect).left = left; + (*rect).top = top; + (*rect).right = right; + (*rect).bottom = bottom; +} + +#[no_mangle] +#[allow(non_snake_case)] +pub unsafe extern "C" fn NewWindow(_w_storage: *mut u8, _bounds_rect: *const Rect, _title: *const Str255, _visible: u8, _proc_id: i16, _behind: *mut u8, _go_away_flag: u8, _ref_con: i32) -> *mut u8 { + // Return a dummy pointer + 0xDEADBEEF as *mut u8 +} + +#[no_mangle] +#[allow(non_snake_case)] +pub unsafe extern "C" fn WaitNextEvent(_event_mask: u16, the_event: *mut EventRecord, _sleep: u32, _mouse_rgn: *mut u8) -> u8 { + // Simulation: just return false for now, or true with a null event + if !the_event.is_null() { + (*the_event).what = NULL_EVENT; + } + 0 +} + +#[no_mangle] +#[allow(non_snake_case)] +pub unsafe extern "C" fn ExitToShell() -> ! { + loop {} +} + +#[no_mangle] +pub extern "C" fn _start() -> ! { + main(); + loop {} +} + +fn main() { + let mut _my_event = EventRecord { + what: 0, + message: 0, + when: 0, + where_: Point { v: 0, h: 0 }, + modifiers: 0, + }; + let mut bounds = Rect::default(); + + // Initialize Memory Manager (Stubbed) + unsafe { + // Assume some memory range 0x10000 to 0x20000 is available for the heap + let heap_start = 0x10000 as *mut u8; + let heap_end = 0x20000 as *mut u8; + let _ = init_zone(heap_start, heap_end); + + let _h = new_handle(1024); + dispose_handle(_h); + } + + // In a real pure Rust OS, we would initialize our own subsystems here. + // For now, we stub the calls to show where they would go. + + // unsafe { + // InitGraf(core::ptr::null_mut()); // Stub + // InitFonts(); + // InitWindows(); + // InitMenus(); + // TEInit(); + // InitDialogs(core::ptr::null_mut()); + // InitCursor(); + // MaxApplZone(); + // } + + unsafe { + // Draw Desktop + FillRect(&bounds, core::ptr::null()); // Mock + + // Create a dummy window to show it's alive + SetRect(&mut bounds, 50, 50, 400, 200); + let title = Str255::from_str("System 7.1 Rust"); + NewWindow(core::ptr::null_mut(), &bounds, &title, 1, 0, core::ptr::null_mut(), 1, 0); + + // Main Event Loop + loop { + if WaitNextEvent(EVERY_EVENT, &mut _my_event, 60, core::ptr::null_mut()) != 0 { + match _my_event.what { + MOUSE_DOWN => { + // Handle clicks + } + KEY_DOWN => { + // Handle keys + // if (_my_event.message & 0xFF) == 'q' as u32 { + // ExitToShell(); + // } + } + _ => {} + } + } + } + } +} + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + loop {} +} diff --git a/rust_src/mac_types/Cargo.toml b/rust_src/mac_types/Cargo.toml new file mode 100644 index 0000000..ad5f732 --- /dev/null +++ b/rust_src/mac_types/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "mac_types" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/rust_src/mac_types/src/gestalt.rs b/rust_src/mac_types/src/gestalt.rs new file mode 100644 index 0000000..54d9277 --- /dev/null +++ b/rust_src/mac_types/src/gestalt.rs @@ -0,0 +1,11 @@ + +use crate::{OSType}; + +pub type SelectorFunctionProcPtr = *mut u8; // pascal OSErr (*)(OSType, long*) + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct GestaltEntry { + pub selector: OSType, + pub handler: SelectorFunctionProcPtr, +} diff --git a/rust_src/mac_types/src/lib.rs b/rust_src/mac_types/src/lib.rs new file mode 100644 index 0000000..249977b --- /dev/null +++ b/rust_src/mac_types/src/lib.rs @@ -0,0 +1,77 @@ +#![no_std] + +pub type OSErr = i16; +pub type OSType = u32; +pub type ResType = OSType; +pub type Boolean = u8; +pub type Size = u32; +pub type Ptr = *mut u8; +pub type Handle = *mut Ptr; +pub type ProcPtr = *mut u8; +pub type GrowZoneProcPtr = *mut u8; + +pub mod start; +pub mod gestalt; +pub mod timer; + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default)] +pub struct Point { + pub v: i16, + pub h: i16, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default)] +pub struct Rect { + pub top: i16, + pub left: i16, + pub bottom: i16, + pub right: i16, +} + +impl Rect { + pub fn new(top: i16, left: i16, bottom: i16, right: i16) -> Self { + Rect { top, left, bottom, right } + } +} + +#[repr(C)] +pub struct EventRecord { + pub what: u16, + pub message: u32, + pub when: u32, + pub where_: Point, + pub modifiers: u16, +} + +pub const NULL_EVENT: u16 = 0; +pub const MOUSE_DOWN: u16 = 1; +pub const MOUSE_UP: u16 = 2; +pub const KEY_DOWN: u16 = 3; +pub const KEY_UP: u16 = 4; +pub const AUTO_KEY: u16 = 5; +pub const UPDATE_EVT: u16 = 6; +pub const DISK_EVT: u16 = 7; +pub const ACTIVATE_EVT: u16 = 8; +pub const OS_EVT: u16 = 15; + +pub const EVERY_EVENT: u16 = 0xFFFF; + +#[repr(C, packed)] +pub struct Str255 { + pub len: u8, + pub data: [u8; 255], +} + +impl Str255 { + pub fn from_str(s: &str) -> Self { + let mut data = [0u8; 255]; + let len = s.len().min(255); + data[..len].copy_from_slice(&s.as_bytes()[..len]); + Str255 { + len: len as u8, + data, + } + } +} diff --git a/rust_src/mac_types/src/start.rs b/rust_src/mac_types/src/start.rs new file mode 100644 index 0000000..7fc91e2 --- /dev/null +++ b/rust_src/mac_types/src/start.rs @@ -0,0 +1,49 @@ +#[repr(C)] +#[derive(Clone, Copy, Debug, Default)] +pub struct SlotDev { + pub sd_ext_dev_id: u8, + pub sd_partition: u8, + pub sd_slot_num: u8, + pub sd_s_rsrc_id: u8, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default)] +pub struct SCSIDev { + pub sd_reserved1: u8, + pub sd_reserved2: u8, + pub sd_ref_num: i16, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union DefStartRec { + pub slot_dev: SlotDev, + pub scsi_dev: SCSIDev, +} + +impl Default for DefStartRec { + fn default() -> Self { + DefStartRec { slot_dev: SlotDev::default() } + } +} + +pub type DefStartPtr = *mut DefStartRec; + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default)] +pub struct DefVideoRec { + pub sd_slot: u8, + pub sds_resource: u8, +} + +pub type DefVideoPtr = *mut DefVideoRec; + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default)] +pub struct DefOSRec { + pub sd_reserved: u8, + pub sd_os_type: u8, +} + +pub type DefOSPtr = *mut DefOSRec; diff --git a/rust_src/mac_types/src/timer.rs b/rust_src/mac_types/src/timer.rs new file mode 100644 index 0000000..2587c3a --- /dev/null +++ b/rust_src/mac_types/src/timer.rs @@ -0,0 +1,29 @@ + +use crate::{ProcPtr}; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct QElem { + pub q_link: *mut QElem, + pub q_type: i16, + pub q_data: [i16; 1], +} + +pub type QElemPtr = *mut QElem; + +pub type TimerUPP = ProcPtr; // pascal void (*)(TMTaskPtr) + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct TMTask { + pub q_link: QElemPtr, + pub q_type: i16, + pub tm_addr: TimerUPP, + pub tm_count: i32, + pub tm_wake_up: i32, + pub tm_reserved: i32, +} + +pub type TMTaskPtr = *mut TMTask; + +pub const K_TMTASK_ACTIVE: i16 = 1 << 15; diff --git a/rust_src/os/Cargo.toml b/rust_src/os/Cargo.toml new file mode 100644 index 0000000..fba6fa0 --- /dev/null +++ b/rust_src/os/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "os" +version = "0.1.0" +edition = "2021" + +[dependencies] +mac_types = { path = "../mac_types" } diff --git a/rust_src/os/src/drivers.rs b/rust_src/os/src/drivers.rs new file mode 100644 index 0000000..26ff8e9 --- /dev/null +++ b/rust_src/os/src/drivers.rs @@ -0,0 +1,7 @@ +pub mod sony; // Floppy +pub mod video; // Display + +pub fn init_drivers() { + // sony::init(); + // video::init(); +} diff --git a/rust_src/os/src/drivers/sony.rs b/rust_src/os/src/drivers/sony.rs new file mode 100644 index 0000000..e56a7d9 --- /dev/null +++ b/rust_src/os/src/drivers/sony.rs @@ -0,0 +1,3 @@ +pub fn init() { + // Initialize Floppy Driver +} diff --git a/rust_src/os/src/drivers/video.rs b/rust_src/os/src/drivers/video.rs new file mode 100644 index 0000000..5e4be98 --- /dev/null +++ b/rust_src/os/src/drivers/video.rs @@ -0,0 +1,3 @@ +pub fn init() { + // Initialize Video Driver +} diff --git a/rust_src/os/src/gestalt.rs b/rust_src/os/src/gestalt.rs new file mode 100644 index 0000000..7554447 --- /dev/null +++ b/rust_src/os/src/gestalt.rs @@ -0,0 +1,26 @@ +use mac_types::{OSErr, OSType}; +use mac_types::gestalt::{SelectorFunctionProcPtr}; + +pub unsafe fn gestalt(_selector: OSType, response: *mut i32) -> OSErr { + // Stub implementation + if response.is_null() { + return -50; // paramErr + } + + // Example: Return 1 for "mach" (Machine Type) selector if checking for it. + // In a real system, we'd check a registered list. + *response = 0; + + // Return noErr + 0 +} + +pub unsafe fn new_gestalt(_selector: OSType, _gestalt_function: SelectorFunctionProcPtr) -> OSErr { + // Stub: Register a new selector + 0 +} + +pub unsafe fn replace_gestalt(_selector: OSType, _gestalt_function: SelectorFunctionProcPtr, _old_gestalt_function: *mut SelectorFunctionProcPtr) -> OSErr { + // Stub + 0 +} diff --git a/rust_src/os/src/lib.rs b/rust_src/os/src/lib.rs new file mode 100644 index 0000000..7b42bf3 --- /dev/null +++ b/rust_src/os/src/lib.rs @@ -0,0 +1,15 @@ +#![no_std] + +pub mod memory_mgr; +pub mod start_mgr; +pub mod trap_dispatcher; +pub mod drivers; +pub mod gestalt; +pub mod timer; + +pub use memory_mgr::*; +pub use start_mgr::*; +pub use trap_dispatcher::*; +pub use drivers::*; +pub use gestalt::*; +pub use timer::*; diff --git a/rust_src/os/src/memory_mgr.rs b/rust_src/os/src/memory_mgr.rs new file mode 100644 index 0000000..f29147f --- /dev/null +++ b/rust_src/os/src/memory_mgr.rs @@ -0,0 +1,60 @@ +use mac_types::{Ptr, OSErr, Handle, Size, GrowZoneProcPtr, ProcPtr}; + +#[repr(C)] +pub struct Zone { + pub bk_lim: Ptr, + pub purge_ptr: Ptr, + pub h_fst_free: Ptr, + pub zcb_free: i32, + pub gz_proc: GrowZoneProcPtr, + pub more_mast: i16, + pub flags: i16, + pub cnt_rel: i16, + pub max_rel: i16, + pub cnt_n_rel: i16, + pub max_n_rel: i16, + pub cnt_empty: i16, + pub cnt_handles: i16, + pub min_cb_free: i32, + pub purge_proc: ProcPtr, + pub spare_ptr: Ptr, + pub alloc_ptr: Ptr, + pub heap_data: i16, +} + +// Global variable pointing to the current zone. +// In a real OS, this would be a fixed address or stored in a global table. +static mut CURRENT_ZONE: *mut Zone = core::ptr::null_mut(); + +pub unsafe fn init_zone(_start_ptr: Ptr, _end_ptr: Ptr) -> OSErr { + // Stub: Initialize a simple zone at the given range + // 1. Create Zone header at start_ptr + // 2. Set bkLim to end_ptr + // 3. Initialize free list + 0 +} + +pub unsafe fn new_handle(_size: Size) -> Handle { + // Stub: Return a mock handle + // In a real implementation, this would: + // 1. Find free space in CURRENT_ZONE + // 2. Allocate Master Pointer + // 3. Link them + + // For now, return NULL or a fake address + core::ptr::null_mut() +} + +pub unsafe fn dispose_handle(h: Handle) { + if !h.is_null() { + // Free memory + } +} + +pub unsafe fn get_zone() -> *mut Zone { + CURRENT_ZONE +} + +pub unsafe fn set_zone(hz: *mut Zone) { + CURRENT_ZONE = hz; +} diff --git a/rust_src/os/src/start_mgr.rs b/rust_src/os/src/start_mgr.rs new file mode 100644 index 0000000..a655e51 --- /dev/null +++ b/rust_src/os/src/start_mgr.rs @@ -0,0 +1,53 @@ +use mac_types::{OSErr}; +use mac_types::start::{DefStartPtr, DefVideoPtr, DefOSPtr}; + +pub fn start_system() -> ! { + // Stub for StartInit + // In a real system, this would initialize the hardware, + // load the system file, and jump to the Finder. + loop {} +} + +pub fn get_boot_drive() -> OSErr { + 0 +} + +pub unsafe fn get_default_startup(param_block: DefStartPtr) { + if !param_block.is_null() { + (*param_block).scsi_dev.sd_ref_num = -1; // Default to no drive + } +} + +pub unsafe fn set_default_startup(_param_block: DefStartPtr) { + // Stub: Write to PRAM +} + +pub unsafe fn get_video_default(param_block: DefVideoPtr) { + if !param_block.is_null() { + (*param_block).sd_slot = 0; + } +} + +pub unsafe fn set_video_default(_param_block: DefVideoPtr) { + // Stub: Write to PRAM +} + +pub unsafe fn get_os_default(param_block: DefOSPtr) { + if !param_block.is_null() { + (*param_block).sd_os_type = 1; // MacOS + } +} + +pub unsafe fn set_os_default(_param_block: DefOSPtr) { + // Stub: Write to PRAM +} + +pub unsafe fn set_timeout(_count: i16) { + // Stub +} + +pub unsafe fn get_timeout(count: *mut i16) { + if !count.is_null() { + *count = 0; + } +} diff --git a/rust_src/os/src/timer.rs b/rust_src/os/src/timer.rs new file mode 100644 index 0000000..4fe46f3 --- /dev/null +++ b/rust_src/os/src/timer.rs @@ -0,0 +1,17 @@ +use mac_types::timer::{QElemPtr}; + +pub unsafe fn ins_time(_tm_task_ptr: QElemPtr) { + // Stub +} + +pub unsafe fn ins_x_time(_tm_task_ptr: QElemPtr) { + // Stub +} + +pub unsafe fn prime_time(_tm_task_ptr: QElemPtr, _count: i32) { + // Stub +} + +pub unsafe fn rmv_time(_tm_task_ptr: QElemPtr) { + // Stub +} diff --git a/rust_src/os/src/trap_dispatcher.rs b/rust_src/os/src/trap_dispatcher.rs new file mode 100644 index 0000000..1ca3376 --- /dev/null +++ b/rust_src/os/src/trap_dispatcher.rs @@ -0,0 +1,45 @@ +// The Trap Dispatcher is the core of the Mac OS syscall mechanism (A-Traps). + +// A minimal trap table representation. +// In 68k Mac OS, the trap table is a list of addresses in low memory. +// There are two tables: OS Traps ($A000-$A0FF) and Toolbox Traps ($A800-$AFFF). + +pub const OS_TRAP_TABLE_BASE: u32 = 0x400; // Typical location +pub const TOOLBOX_TRAP_TABLE_BASE: u32 = 0xE00; // Typical location for Tool Traps (pointers) + +#[repr(C)] +pub struct TrapTable { + // In reality, this is just an array of function pointers. + // For the Rust implementation, we might simulate this or just link it to the hardware vector. + pub table: [u32; 1024], +} + +// Global Trap Table Mock +static mut MOCK_TRAP_TABLE: TrapTable = TrapTable { table: [0; 1024] }; + +pub unsafe fn dispatch_trap(trap_num: u16) { + // Decode trap number + let is_toolbox = (trap_num & 0x0800) != 0; + let _index = (trap_num & 0x03FF) as usize; + + // In a real emulator/OS, we would look up the function and call it. + // Here we just log (or would log if we had a logger). + if is_toolbox { + // Toolbox trap + } else { + // OS Trap + } + + // Example: if trap_num == 0xA07D (GetDefaultStartup) + // We would call start_mgr::get_default_startup(...) +} + +pub unsafe fn set_trap_address(trap_num: u16, addr: u32) { + let index = (trap_num & 0x03FF) as usize; + MOCK_TRAP_TABLE.table[index] = addr; +} + +pub unsafe fn get_trap_address(trap_num: u16) -> u32 { + let index = (trap_num & 0x03FF) as usize; + MOCK_TRAP_TABLE.table[index] +}