From 2dde34db7a6d81fb31dbe51190daafee5185e88c Mon Sep 17 00:00:00 2001 From: pjht Date: Sun, 29 Jan 2023 11:03:53 -0600 Subject: [PATCH] Initial commit --- Tupfile | 13 ++ Tupfile.ini | 0 Tuprules.tup | 5 + boot/Tupfile | 6 + boot/boot.ld | 4 + boot/boot.z80 | 110 +++++++++ init.z80 | 332 +++++++++++++++++++++++++++ initrd_driver.z80 | 162 ++++++++++++++ initrd_fs.z80 | 139 ++++++++++++ initrd_maker.rs | 46 ++++ kernel/Tupfile | 5 + kernel/cards.z80 | 70 ++++++ kernel/initrd.z80 | 126 +++++++++++ kernel/interrupts.z80 | 114 ++++++++++ kernel/ipc.z80 | 299 +++++++++++++++++++++++++ kernel/kernel.ld | 190 ++++++++++++++++ kernel/main.z80 | 151 +++++++++++++ kernel/pmem.c | 87 ++++++++ kernel/pmem.z80 | 233 +++++++++++++++++++ kernel/pmem_2g_wip.c | 90 ++++++++ kernel/proc_map.z80 | 55 +++++ kernel/syscalls.z80 | 42 ++++ kernel/tasking.c | 74 ++++++ kernel/tasking.z80 | 508 ++++++++++++++++++++++++++++++++++++++++++ kernel/vmem.z80 | 149 +++++++++++++ libs/Tupfile | 3 + libs/syscalls.z80 | 90 ++++++++ rom.bin | 1 + 28 files changed, 3104 insertions(+) create mode 100644 Tupfile create mode 100644 Tupfile.ini create mode 100644 Tuprules.tup create mode 100644 boot/Tupfile create mode 100644 boot/boot.ld create mode 100644 boot/boot.z80 create mode 100644 init.z80 create mode 100644 initrd_driver.z80 create mode 100644 initrd_fs.z80 create mode 100644 initrd_maker.rs create mode 100644 kernel/Tupfile create mode 100644 kernel/cards.z80 create mode 100644 kernel/initrd.z80 create mode 100644 kernel/interrupts.z80 create mode 100644 kernel/ipc.z80 create mode 100644 kernel/kernel.ld create mode 100644 kernel/main.z80 create mode 100644 kernel/pmem.c create mode 100644 kernel/pmem.z80 create mode 100644 kernel/pmem_2g_wip.c create mode 100644 kernel/proc_map.z80 create mode 100644 kernel/syscalls.z80 create mode 100644 kernel/tasking.c create mode 100644 kernel/tasking.z80 create mode 100644 kernel/vmem.z80 create mode 100644 libs/Tupfile create mode 100644 libs/syscalls.z80 create mode 120000 rom.bin diff --git a/Tupfile b/Tupfile new file mode 100644 index 0000000..fa72de8 --- /dev/null +++ b/Tupfile @@ -0,0 +1,13 @@ +include_rules +export RUSTUP_HOME + +RUSTC_FLAGS = -C debuginfo=0 -C opt-level=3 --edition 2021 + +LDLIBS = libs/*.o + +: foreach *.z80 |> !as |> +: foreach *.o | libs/*.o |> !ld |> %B.elf + +: initrd_maker.rs |> rustc $(RUSTC_FLAGS) -o %o %f |> bin/%B +: kernel/kernel.elf *.elf | bin/initrd_maker |> bin/initrd_maker %f |> initrd +: boot/boot.bin initrd |> cat %f > %o |> os.dsk diff --git a/Tupfile.ini b/Tupfile.ini new file mode 100644 index 0000000..e69de29 diff --git a/Tuprules.tup b/Tuprules.tup new file mode 100644 index 0000000..aad321d --- /dev/null +++ b/Tuprules.tup @@ -0,0 +1,5 @@ +.gitignore +LDFLAGS = -n + +!as = |> z80-elf-as $(ASFLAGS) -o %o %f |> %B.o +!ld = |> z80-elf-ld $(LDFLAGS) -o %o $(LDLIBS) %f |> diff --git a/boot/Tupfile b/boot/Tupfile new file mode 100644 index 0000000..a5e5706 --- /dev/null +++ b/boot/Tupfile @@ -0,0 +1,6 @@ +include_rules +LDFLAGS += -T boot.ld + +: foreach *.z80 |> !as |> +: boot.o |> !ld |> boot.elf +: boot.elf |> z80-elf-objcopy -O binary %f %o |> boot.bin diff --git a/boot/boot.ld b/boot/boot.ld new file mode 100644 index 0000000..6f6cb52 --- /dev/null +++ b/boot/boot.ld @@ -0,0 +1,4 @@ +SECTIONS { + . = 0x0; + .text : { *(.text) } +} diff --git a/boot/boot.z80 b/boot/boot.z80 new file mode 100644 index 0000000..71d84af --- /dev/null +++ b/boot/boot.z80 @@ -0,0 +1,110 @@ +_start: +ld sp,0 ; Initialize the stack +ld a,0x4 ; Find a generic storage card +call find_first_card +ld c,0xFF ; Force the top half of the IO address to be the id of the card, +ld a, b ; so the block transfer instructions can be used +out (c), a +; Load the header sectior for the kernel binary to find it's length in sectors +ld a, 1 ; Set the sector number registers to 1 +ld c, 0 +out (c), a +inc c +xor a +out (c), a +inc c +out (c), a +ld b, a ; Setup a transfer of 256 bytes (1 sector) at a time +ld c, 0x3 ; from the storage device's data port +ld h, 0xA0 ; Starting at 0xA000 +ld l, a +inir +ld h, 0xA0 +ld l, 0x0 +ld a, (hl) +add a, 3 +ld c, a +ld b, 0 +add hl, bc +ld d, (hl) +; load the ELF kernel binary off the disk +ld a, 2 ; Set the sector number registers to 2 +ld c, 0 +out (c), a +inc c +xor a +out (c), a +inc c +out (c), a +ld b, a ; Setup a transfer of 256 bytes (1 sector) at a time +ld c, 0x3 ; from the storage device's data port +ld h, 0xA0 ; Starting at 0xA000 +ld l, a +ld a, d ; Transfer 0x23 times +loop: +inir ; Transfer a sector +dec a +jp NZ, loop +ld c,0xFF ; Unforce the top half of the IO address +in a,(c) +ld ix, (0xA01C) ; Load IX with the start of the program headers in memory +ld bc, 0xA000 +add ix, bc +ld a, (0xA02C) ; load B with the number of program headers +ld b, a +phead_loop: +ld a, (ix+0) +cp 1 +jp NZ, next_seg +;Zero out the segemnts place in memory +push bc +ld c, (ix+20) ; Load the byte counter with the number of bytes to zero out +ld b, (ix+21) +ld e, (ix+8) ; Load the destination & source address register with the destination location of the segment in memory +ld d, (ix+9) +ld h, d +ld l, e +ld (hl), 0 ; Zero out the firts byte in the segment ( Acts as a 'seed' for the zeroing ) +ldir +ld c, (ix+16) ; Load the byte counter with the number of bytes to transfer +ld b, (ix+17) +ld l, (ix+4) ; Load the source address register with the memory address if the segment data +ld h, (ix+5) +ld de, 0xA000 +add hl, de +ld e, (ix+8) ; Load the destination address register with the destination location of the segment in memory +ld d, (ix+9) +ldir; Do the transfer +pop bc +next_seg: +ld de, 32 +add ix, de +djnz phead_loop +ld hl, (0xA018) ; Load HL with the program entry point +jp (hl) + + + +find_first_card: +; Finds the first instance of a card type specified in A +; A is zero if no such card was found, and nonzero if a card was found +; If a card was found, the ID of the card is returned in B +; The values in C, D, and E are destroyed +ld d, a +ld e, 0x0 +ld bc, 0x01FF ; BC is the IO address to read. B holds the card ID and C which address to read in the card. +ffc_loop: +in a, (c) ; Get card type +cp d ; Check whether the type of the current card is the type we want +jp Z, done ; If so, return +cp e ; Check whether the card type is 0 (nonexistent card) +jp Z, done ; If so, return +inc b ; Go to the next card +jp ffc_loop +done: +ret + +.if . != 256 +.org 255 +.byte 0 +.endif diff --git a/init.z80 b/init.z80 new file mode 100644 index 0000000..fb20523 --- /dev/null +++ b/init.z80 @@ -0,0 +1,332 @@ +.global _start + +_start: +call find_disk +/* ld hl, test_string */ +/* call print */ +/* call get_free_mailbox ; Initialize a mailbox */ +/* ld (mailbox_num), hl */ +call get_free_frame ; Set up a frame to load programs into at 0x8000 +ld b, c +ld e, l +ld d, h +ld c, 8 +bp_call_sf: +call set_frame +ld iy, initrd_driver_name ; Load and run the initrd driver +call run_file +initrd_driver_loaded: +call yield ; Yield to let the initrd driver initialize +initrd_driver_returned: +ld iy, initrd_fs_name ; Load and run the initrd fs driver +call run_file +initrd_fs_loaded: +call yield ; Yield to let the initrd driver initialize +/* call get_free_frame ; Set up a message frame at 0xA000 */ +/* ld b, c */ +/* push hl */ +/* pop de */ +/* ld c, 0xA */ +/* call set_frame */ +; Set up a read message to the driver starting at 0x80 and reading +; 0x200 bytes +/* ld a, 0x80 */ +/* ld (0xA000), a */ +/* ld a, 0x00 */ +/* ld (0xA001), a */ +/* ld a, 0x00 */ +/* ld (0xA002), a */ +/* ld a, 0x2 */ +/* ld (0xA003), a */ +/* ld hl, (mailbox_num) */ +/* ld (0xA004), hl */ +/* push de */ +/* push bc */ +/* ld de, 0 ; Get the mailbox number for the initrd driver */ +/* call proc_map_get */ +/* pop bc */ +/* pop de */ +/* ld c, b ; Send the message */ +/* ld ix, 0x0000 */ +/* call mb_send */ +/* loop: */ +/* call yield */ +/* ld hl, (mailbox_num) */ +/* call mb_read ; Read a message */ +/* ld a, b ; Loop if there is no message */ +/* cp 0 */ +/* jp nz, loop */ +loop: +call yield +jp loop + +mailbox_num: .ds.b 2 + +initrd_driver_name: .asciz "initrd_driver.elf" +initrd_fs_name: .asciz "initrd_fs.elf" + +; Loads and starts the ELF file with the name pointed to by IY +run_file: +call read_file +ld hl, 0x8000 +push hl +pop iy +ld l, (iy+0x1C) ; Load IX with the start of the program headers in memory +ld h, (iy+0x1D) ; Load IX with the start of the program headers in memory +push hl +pop ix +push iy +pop bc +add ix, bc +ld a, (iy+0x2C) ; load B with the number of program headers +ld b, a +phead_loop: +ld a, (ix+0) +cp 1 +jp NZ, next_seg +push bc +ld c, (ix+20) +ld b, (ix+21) +; b = if c > 0 || b & 0xF > 0 { +; (b & 0xF0) + 0x10 +; } else { +; b & 0xF0 +; } >> 4 +ld a, c +cp 0 +jp z, cmp2 +jp blk1 +cmp2: +ld a, b +and 0xF +cp 0 +jp z, blk2 +blk1: +ld a, b +and 0xF0 +add a, 0x10 +ld b, a +jp end1 +blk2: +ld a, b +and 0xF0 +ld b, a +end1: +srl b +srl b +srl b +srl b +ld c, (ix+9) +srl c +srl c +srl c +srl c +push iy +push ix +push de +ld a, c +sla a +add a, c +ld c, a +ld b, 0 +map_seg_frame: +ld ix, proc_pagemap +add ix, bc +push ix +ld a, 1 +call 0xdffd +pop ix +seg_frame_found: +ld (ix+0), c +ld (ix+1), l +ld (ix+2), h +ld b, c +ld e, l +ld d, h +ld c, 7 +call set_frame +/* ld b, c */ +/* ld d, h */ +/* ld e, l */ +/* pop hl */ +/* ld c, l */ +/* push hl */ +/* call set_frame */ +/* pop bc */ +/* inc c */ +/* djnz page_alloc_loop */ +pop de +pop ix +pop iy +seg_load: +;Zero out the segments place in memory +ld c, (ix+20) ; Load the byte counter with the number of bytes to zero out +ld b, (ix+21) +ld e, (ix+8) ; Load the destination & source address register with the destination location of the segment in memory +ld d, (ix+9) +ld a, d +and 0xF +or 0x70 +ld d, a +ld h, d +ld l, e +ld (hl), 0 ; Zero out the first byte in the segment ( Acts as a 'seed' for the zeroing ) +ldir +ld c, (ix+16) ; Load the byte counter with the number of bytes to transfer +ld b, (ix+17) +ld l, (ix+4) ; Load the source address register with the memory address if the segment data +ld h, (ix+5) +push iy +pop de +add hl, de +ld e, (ix+8) ; Load the destination & source address register with the destination location of the segment in memory +ld d, (ix+9) +ld a, d +and 0xF +or 0x70 +ld d, a +ldir; Do the transfer +pop bc +next_seg: +ld de, 32 +add ix, de +dec b +ld a, b +jp nz, phead_loop +ld l, (iy+0x18) ; Load HL with the program entry point +ld h, (iy+0x19) ; Load HL with the program entry point +ld iy, proc_pagemap +call new_process +ret + + +proc_pagemap: +.ds.b 36 + +strcmp: +strcmp_loop: +ld a, (ix+0) +ld b, (iy+0) +cp b +jp nz, strcmp_different +cp 0 +ret z +inc ix +inc iy +jp strcmp_loop +strcmp_different: +ld a, 1 +ret + + +; Reads a file off the disk to 0x8000 in memory +; Pointer to file name in iy +read_file: +ld c, 0 +ld de, 1 +ri_find_loop: +ld hl, 0x8000 +push bc +call read_sector +ri_find_read_done: +ld ix, 0x8001 +push iy +call strcmp +pop iy +pop bc +jp z, ri_init_found +push bc +ld b, 0 +ld a, (0x8000) +ld c, a +ld hl, 0x8004 +add hl, bc +ld c, (hl) +inc hl +ld b, (hl) +ld l, c +ld h, b +inc hl +add hl, de +ld e, l +ld d, h +pop bc +ld a, c +adc a, 0 +ld c, a +jp ri_find_loop +ri_init_found: +push bc +ld b, 0 +ld a, (0x8000) +ld c, a +ld hl, 0x8004 +add hl, bc +pop bc +ld b, (hl) +ld hl, 1 +add hl, de +ld e, l +ld d, h +ld a, c +adc a, 0 +ld c, a +ld hl, 0x8000 +ri_read_loop: +push bc +call read_sector +pop bc +push hl +ld hl, 1 +add hl, de +ld e, l +ld d, h +ld a, c +adc a, 0 +ld c, a +pop hl +djnz ri_read_loop +ret + + + +; Given a sector number in C:DE and a buffer address in HL, reads the sector off disk. +read_sector: +push bc +ld c,0xFF ; Force the top half of the IO address to be the id of the card, +ld a, (disk_num) ; so the block transfer instructions can be used +out (c), a +pop bc +ld a, c +ld c, 0 +out (c), e +inc c +out (c), d +inc c +out (c), a +ld b, 0x0 ; Setup a transfer of 256 bytes (1 sector) at a time +ld c, 0x3 ; from the storage device's data port +inir +ld c,0xFF ; Unforce the top half of the IO address +in a,(c) +ret + + +.global find_disk +; Clobbers A and HL +find_disk: +ld b,0x4 +ld a, 10 +call 0xdffd +ld a, l +ld (disk_num), a +ret + +disk_num: .ds.b 1 + + +/* initrd: */ +/* .incbin "initrd" */ +/* initrd_end: */ + + diff --git a/initrd_driver.z80 b/initrd_driver.z80 new file mode 100644 index 0000000..610bf4c --- /dev/null +++ b/initrd_driver.z80 @@ -0,0 +1,162 @@ +.global _start + +_start: +call find_disk +ld a, 7 +call 0xdffd +push hl +ld de, 0 +ld a, 8 +call 0xdffd +pop hl +ld (mailbox_num), hl +msg_loop: +call yield +yield_back: +ld hl, (mailbox_num) +ld a, 6 +call 0xdffd +msg_read: +push bc +push de +push hl +ld b, c +ld c, 0xB +call set_frame +ld a, h +or 0xB0 +ld h, a +ld c, (hl) +inc hl +ld b, (hl) +inc hl +ld e, (hl) +inc hl +ld d, (hl) +ld hl, 0xB000 +jp read_bytes +ld hl, (0xB004) ; Load destination mailbox into HL +pop ix +pop de +pop bc +ld a, 5 +call 0xdffd +halt +jp msg_loop +halt + +read_buf: .ds.b 512 + +;Given a starting byte number in BC, a byte count in DE, and a destination address in HL, reads those bytes off the disk +read_bytes: +ld (start_byte), bc +ld (byte_count), de +ld (dest_addr), hl +ld c, 0 +ld d, 0 +ld e, b +ld hl, sec_buf +call read_sector +ld hl, sec_buf +ld b, 0 ; Load BC with lower byte of start_byte (sector offset) +ld a, (start_byte) +ld c, a +add hl, bc ; Add the offset to the buffer address +ld de, (dest_addr) +ld a, (start_byte) ; A = 255 - offset + 1 (256-offset) +ld b, a +ld a, 0xFF +sub b +inc a +ld b, 0 ; Load bc with the value in A +ld c, a +ldir +start_sec_trns_done: +ld (dest_addr), de +ld a, (start_byte) ; A = 255 - offset + 1 (256-offset) +ld b, a +ld a, 0xFF +sub b +inc a +ld b, 0 ; Load bc with the value in A +ld c, a +ld hl, (byte_count) +scf +ccf +sbc hl, bc +ld (rem_bytes), hl +ld b, h +ld c, 0 +ld d, 0 +ld a, (start_byte+1) +inc a +ld e, a +ld hl, (dest_addr) +sector_loop: +push bc +call read_sector +inc de +pop bc +djnz sector_loop +ld (dest_addr), hl +ld hl, (sec_buf) +call read_sector +ld hl, (sec_buf) +ld de, (dest_addr) +ld b, 0 +ld a, (rem_bytes) +ld c, a +ldir +read_bytes_done: +ret + +start_byte: .ds.b 2 +byte_count: .ds.b 2 +dest_addr: .ds.b 2 +rem_bytes: .ds.b 2 +sec_buf: .ds.b 256 + +; Given a sector number in C:DE and a buffer address in HL, reads the sector off disk. +; Clobbers A, BC, HL +read_sector: +push bc +ld c,0xFF ; Force the top half of the IO address to be the id of the card, +ld a, (disk_num) ; so the block transfer instructions can be used +out (c), a +pop bc +ld a, c +ld c, 0 +out (c), e +inc c +out (c), d +inc c +out (c), a +ld b, 0x0 ; Setup a transfer of 256 bytes (1 sector) at a time +ld c, 0x3 ; from the storage device's data port +inir +ld c,0xFF ; Unforce the top half of the IO address +in a,(c) +read_sec_done: +ret + + + +.global find_disk +; Clobbers A and HL +find_disk: +ld b,0x4 +call find_first_card +ld a, l +ld (disk_num), a +ret + +disk_num: .ds.b 1 +mailbox_num: .ds.b 2 + +/* Start sector: Start byte / 256 */ +/* Offset in start sector: Start byte % 256 */ +/* Rem bytes: Byte count - (256 - Offset in start sector) */ +/* Middle sectors start: start sector + 1 */ +/* Middle sectors count: Rem bytes / 256 */ +/* End sector: Middle sectors start + Middle sectors count */ +/* End sector copy length: Rem bytes % 256 */ diff --git a/initrd_fs.z80 b/initrd_fs.z80 new file mode 100644 index 0000000..b3d8bc0 --- /dev/null +++ b/initrd_fs.z80 @@ -0,0 +1,139 @@ +.global _start +_start: +call get_free_mailbox ; Get a mailbox +push hl +ld de, 1 +call get_free_frame ; Set up a frame to send messages at 0x8000 +ld b, c +ld e, l +ld d, h +ld c, 0x8 +call set_frame +call get_free_frame ; Set up a frame for the file mapping at 0xA000 +ld b, c +ld e, l +ld d, h +ld c, 0xA +call set_frame +ld c, 0 +ld de, 1 +ld hl, 0xA000 +ri_find_loop: +push bc +push de +push hl +ld hl, 0x8000 +call read_sector +pop de +push de +ld b, 0 ; Load the filename length in BC +ld a, (0x8000) +ld c, a +inc hl +ldir +ld a, (hl) +ld (de), a +inc hl +pop de +ld a, e +add a, 0x20 +ld e, a +ld a, d +adc a, 0x0 +ld d, a +ld a, (hl) +ld (de), a +inc hl +inc de +ld a, (hl) +ld (de), a +inc hl +inc de +ld l, e +ld h, d +pop de +pop bc +ld (hl), e +inc hl +ld (hl), d +inc hl +ld (hl), c +inc hl +; Advance to the next header +ld b, 0 ; Load the filename length in BC +ld a, (0x8000) +ld c, a +ld hl, 0x8004 +add hl, bc ; Add the filename length to get the pointer to the +; # of blocks in the file in HL +ld c, (hl) ; Read the # of blocks into BC +inc hl +ld b, (hl) +ld l, c ; Move it into HL +ld h, b +inc hl ; Add 1 to account for the header sector +pop bc +add hl, de ; 24-bit add of HL to C:DE +ld e, l +ld d, h +ld a, c +adc a, 0 +ld c, a +jp ri_find_loop +call proc_map_set ; Register ourselves as ID 1 +pop hl +ld (mailbox_num), hl +msg_loop: +call yield +ld hl, (mailbox_num) +call mb_read ; Read a message and place it at 0xB000 +push bc +push de +push hl +ld b, c +ld c, 0xB +call set_frame + +ld hl, (0xB004) ; Load destination mailbox into HL +pop ix +pop de +pop bc +call mb_send +halt +jp msg_loop +halt + +mailbox_num: .ds.b 2 + +; Given a sector number in C:DE, reads the sector off disk into 0x8000 +read_sector: +; Set up a read message to the driver starting at the sector in register E and reading +; 0x100 bytes +ld a, 0x0 +ld (0xA000), a +ld a, e +ld (0xA001), a +ld a, 0x00 +ld (0xA002), a +ld a, 0x1 +ld (0xA003), a +ld hl, (mailbox_num) +ld (0xA004), hl +push de +push bc +ld de, 0 ; Get the mailbox number for the initrd driver +call proc_map_get +pop bc +pop de +ld c, b ; Send the message +ld ix, 0x0000 +call mb_send +loop: +call yield +ld hl, (mailbox_num) +call mb_read ; Read a message +ld a, b ; Loop if there is no message +cp 0 +jp nz, loop +read_sector_done: +ret diff --git a/initrd_maker.rs b/initrd_maker.rs new file mode 100644 index 0000000..78c5b0e --- /dev/null +++ b/initrd_maker.rs @@ -0,0 +1,46 @@ +use std::{ + env::args, + fs::File, + io::{self, Write}, + path::Path, +}; + +const BLOCK_SIZE: u16 = 256; + +fn main() { + let mut archive = File::create("initrd").expect("Could not open initrd"); + for file_path in args().skip(1) { + let file_path = Path::new(&file_path); + let file_name = file_path.file_name().unwrap(); + let file_name_len = + u8::try_from(file_name.len()).expect("File name length greater than 256 bytes"); + if file_name_len > 252 { + panic!("File {:?} has name longer than 252 bytes", file_name); + } + let mut file = File::open(file_path).expect("File did not exist"); + let length = u16::try_from(file.metadata().expect("Could not get file metadata").len()) + .expect("File size greater than 64 KiB"); + let file_num_blocks = (length / BLOCK_SIZE) + if length % BLOCK_SIZE != 0 { 1 } else { 0 }; + let mut header_block = Vec::new(); + header_block.push(file_name_len); + header_block.extend_from_slice(file_name.to_str().unwrap().as_bytes()); + header_block.push(0); + header_block.extend_from_slice(&length.to_le_bytes()); + header_block.extend_from_slice(&file_num_blocks.to_le_bytes()); + header_block.resize(BLOCK_SIZE as usize, 0); + archive + .write_all(&header_block) + .expect("Could not write to initrd"); + let bytes_written = + io::copy(&mut file, &mut archive).expect("Could not read from file/write to initrd"); + if (bytes_written as u16 % BLOCK_SIZE) != 0 { + let bytes_padding = (BLOCK_SIZE) - (bytes_written as u16 % BLOCK_SIZE); + for _ in 0..bytes_padding { + archive.write_all(&[0]).expect("Could not write to initrd"); + } + } + } + archive + .write_all(&[0; 256]) + .expect("Could not write to initrd"); +} diff --git a/kernel/Tupfile b/kernel/Tupfile new file mode 100644 index 0000000..3d4da52 --- /dev/null +++ b/kernel/Tupfile @@ -0,0 +1,5 @@ +include_rules +LDFLAGS += -T kernel.ld + +: foreach *.z80 |> !as |> +: *.o | kernel.ld |> !ld |> kernel.elf diff --git a/kernel/cards.z80 b/kernel/cards.z80 new file mode 100644 index 0000000..f2679bb --- /dev/null +++ b/kernel/cards.z80 @@ -0,0 +1,70 @@ +.global scan_cards +.global find_first_card +.global get_all_cards + +scan_cards: +; Read the types of all the cards in the system +; The values in HL, BC, and A are destroyed +ld hl, card_types+255 +ld bc, 0xFFFF ; Start at card 255 +scan_loop: ; Read the types of card 255 through to card 1 +in a, (c) +ld (hl), a +dec hl +djnz scan_loop +in a, (c) ; Read card 0's type +ld (hl), a +ret + +get_all_cards: +; Gets the indexes of the cards with the specified type +; IX is a pointer to the buffer to store the results in +; B is the length of the buffer (0 is a 256 byte length) +; C is the type of card to find +; The zero flag is set if the buffer is full +; The values in A, B, HL, and IX are destroyed +ld hl, card_types+1 +gac_loop: +ld a, (hl) ; Get card type +cp c ; Check whether the type of the current card is the type we want +jp nz, gac_next ; Skip the card if it is not +ld (ix), l +inc ix +dec b +gac_next: +inc hl ; Go to the next card +ld a, l ; Check if we have gone through all the cards +or a ; (or a == cp 0) +jp Z, gac_done ; If so, return +ld a, b ; Check if we have filled the buffer +or a ; (or a == cp 0) +jp Z, gac_full ; If so, return with the zero flag set +jp gac_loop +gac_done: +or 1 ; Reset zero flag to provide correct "buffer filed" indication +gac_full: +ret + +find_first_card: +; Finds the first instance of a card type specified in B +; A is zero if no such card was found, and nonzero if a card was found +; If a card was found, the ID of the card is returned in L +; If a card was not found, 0 is returned in L +; The values in A and H are destroyed +ld hl, card_types+1 +ffc_loop: +ld a, (hl) ; Get card type +cp b ; Check whether the type of the current card is the type we want +jp Z, ffc_done ; If so, return the type in L +or a ; (or a == cp 0) Check whether the card type is 0 (nonexistent card) +jp Z, ffc_notfound ; If so, return 0 in L +inc hl ; Go to the next card +jp ffc_loop +ffc_notfound: +ld l, 0 +ffc_done: +ret + +.bss +.balign 256 +card_types: .ds.b 256 diff --git a/kernel/initrd.z80 b/kernel/initrd.z80 new file mode 100644 index 0000000..2f5cba1 --- /dev/null +++ b/kernel/initrd.z80 @@ -0,0 +1,126 @@ + +strcmp: +strcmp_loop: +ld a, (ix+0) +ld b, (iy+0) +cp b +jp nz, strcmp_different +cp 0 +ret z +inc ix +inc iy +jp strcmp_loop +strcmp_different: +ld a, 1 +ret + + +.global read_init +; Reads init.elf off the disk to 0x8000 in memory +read_init: +ld c, 0 +ld de, 1 +ri_find_loop: +ld hl, 0x8000 +push bc +call read_sector +ld ix, 0x8001 +ld iy, init_str +call strcmp +pop bc +jp z, ri_init_found +push bc +ld b, 0 +ld a, (0x8000) +ld c, a +ld hl, 0x8004 +add hl, bc +ld c, (hl) +inc hl +ld b, (hl) +ld l, c +ld h, b +inc hl +add hl, de +ld e, l +ld d, h +pop bc +ld a, c +adc a, 0 +ld c, a +jp ri_find_loop +ri_init_found: +push bc +ld b, 0 +ld a, (0x8000) +ld c, a +ld hl, 0x8004 +add hl, bc +pop bc +ld b, (hl) +ld hl, 1 +add hl, de +ld e, l +ld d, h +ld a, c +adc a, 0 +ld c, a +ld hl, 0x8000 +ri_read_loop: +push bc +call read_sector +pop bc +push hl +ld hl, 1 +add hl, de +ld e, l +ld d, h +ld a, c +adc a, 0 +ld c, a +pop hl +djnz ri_read_loop +ret + + + +; Given a sector number in C:DE and a buffer address in HL, reads the sector off disk. +read_sector: +push bc +ld c,0xFF ; Force the top half of the IO address to be the id of the card, +ld a, (disk_num) ; so the block transfer instructions can be used +out (c), a +pop bc +ld a, c +ld c, 0 +out (c), e +inc c +out (c), d +inc c +out (c), a +ld b, 0x0 ; Setup a transfer of 256 bytes (1 sector) at a time +ld c, 0x3 ; from the storage device's data port +inir +ld c,0xFF ; Unforce the top half of the IO address +in a,(c) +ret + + +.global find_disk +; Clobbers A and HL +find_disk: +ld b,0x4 +call find_first_card +ld a, l +ld (disk_num), a +ret + +disk_num: .ds.b 1 + + +/* initrd: */ +/* .incbin "initrd" */ +/* initrd_end: */ + + +init_str: .asciz "init.elf" diff --git a/kernel/interrupts.z80 b/kernel/interrupts.z80 new file mode 100644 index 0000000..188fbf3 --- /dev/null +++ b/kernel/interrupts.z80 @@ -0,0 +1,114 @@ +init: +di +im 2 +ld hl, idt +ld a, h +ld i, a +ei +ret + + +hdlr: +reti + +hdlr0: +ld a, 0 +jp hdlr +hdlr1: +ld a, 1 +jp hdlr +hdlr2: +ld a, 2 +jp hdlr +hdlr3: +ld a, 3 +jp hdlr +hdlr4: +ld a, 4 +jp hdlr +hdlr5: +ld a, 5 +jp hdlr +hdlr6: +ld a, 6 +jp hdlr +hdlr7: +ld a, 7 +jp hdlr +hdlr8: +ld a, 8 +jp hdlr +hdlr9: +ld a, 9 +jp hdlr +hdlr10: +ld a, 10 +jp hdlr +hdlr11: +ld a, 11 +jp hdlr +hdlr12: +ld a, 12 +jp hdlr +hdlr13: +ld a, 13 +jp hdlr +hdlr14: +ld a, 14 +jp hdlr +hdlr15: +ld a, 15 +jp hdlr +hdlr16: +ld a, 16 +jp hdlr +hdlr17: +ld a, 17 +jp hdlr +hdlr18: +ld a, 18 +jp hdlr +hdlr19: +ld a, 19 +jp hdlr +hdlr20: +ld a, 20 +jp hdlr +hdlr21: +ld a, 21 +jp hdlr +hdlr22: +ld a, 22 +jp hdlr +hdlr23: +ld a, 23 +jp hdlr +hdlr24: +ld a, 24 +jp hdlr +hdlr25: +ld a, 25 +jp hdlr +hdlr26: +ld a, 26 +jp hdlr +hdlr27: +ld a, 27 +jp hdlr +hdlr28: +ld a, 28 +jp hdlr +hdlr29: +ld a, 29 +jp hdlr +hdlr30: +ld a, 30 +jp hdlr +hdlr31: +ld a, 31 +jp hdlr + + +.align 8 +idt: +.dc.w hdlr0, hdlr1, hdlr2, hdlr3, hdlr4, hdlr5, hdlr6, hdlr7, hdlr8, hdlr9, hdlr10, hdlr11, hdlr12, hdlr13, hdlr14, hdlr15, hdlr16, hdlr17, hdlr18, hdlr19, hdlr20, hdlr21, hdlr22, hdlr23, hdlr24, hdlr25, hdlr26, hdlr27, hdlr28, hdlr29, hdlr30, hdlr31 diff --git a/kernel/ipc.z80 b/kernel/ipc.z80 new file mode 100644 index 0000000..cbefb2b --- /dev/null +++ b/kernel/ipc.z80 @@ -0,0 +1,299 @@ +.global mb_send +; Sends a message to the MB whose ID is in HL, +; with the message pointer in C:DE:IX +; Returns 0 in B if it sent the mesage, of 1 if the box was full +mb_send: +push de +push bc +push ix +call get_mb_ptr +pop ix +pop bc +pop de +push ix +push hl +pop ix +pop hl +ld ixl, 0xFC +ld a, (ix+0) +inc a +cp 42 +jp nz, no_wrap +ld a, 0 +no_wrap: +ld b, (ix+1) +cp b +jp nz, mb_not_full +ld b, 1 +ret +mb_not_full: +ld a, (ix+0) +ld ixl, 0 +ld b, a +sla a +sla a +sla b +add a, b +ld ixl, a +ld (ix+0), c +ld (ix+1), e +ld (ix+2), d +ld (ix+3), l +ld (ix+4), h +ld ixl, 0xFC +ld a, (ix+0) +inc a +cp 42 +jp nz, mbs_no_wrap +ld a, 0 +mbs_no_wrap: +ld (ix+0), a +ld b, 0 +ret + +.global mb_read +;Reads a meddage from the MB specified in HL, +; and returns the pointer in C:DE:HL. +; Returns 0 in B if the mailbox had a message, and 1 if it was full +mb_read: +call get_mb_ptr +mb_read_gmbp_done: +push hl +pop ix +ld ixl, 0xFC +ld a, (ix+1) +ld b, (ix+0) +ld ixl, 0x0 +cp b +jp nz, mb_not_empty +ld b, 1 +ret +mb_not_empty: +ld b, a +sla a +sla a +sla b +add a, b +ld ixl, a +ld c, (ix+0) +ld e, (ix+1) +ld d, (ix+2) +ld l, (ix+3) +ld h, (ix+4) +ld ixl, 0xFC +ld a, (ix+1) +inc a +cp 42 +jp nz, mbr_no_wrap +ld a, 0 +mbr_no_wrap: +ld (ix+1), a +ld b, 0 +ret + + + + + +.global get_free_mailbox +; Gets a free mailbox +; ID is returned in HL +get_free_mailbox: +ld a, (mb_free_list) +cp 0 +jp nz, gfm_list_not_empty +ld a, (mb_free_list+1) +cp 0 +jp nz, gfm_list_not_empty +gfm_list_empty: +ld hl, (mb_next_frame_num) +add hl, hl +add hl, hl +add hl, hl +add hl, hl +push hl +ld bc, (mb_next_frame_num) +inc bc +ld (mb_next_frame_num), bc +call get_mb_ptr +push hl +pop ix +push hl +pop iy +link_loop: +inc iyh +ld a, iyh +cp 0xf0 +jp nz, iy_ok +ld (ix + 0), 0 +ld (ix + 1), 0 +jp link_loop_done +iy_ok: +push iy +pop bc +ld (ix + 0), c +ld (ix + 1), b +inc ixh +jp link_loop +link_loop_done: +pop hl +ld (mb_free_list), hl +ld bc, (mb_free_list) +inc bc +ld (mb_free_list), bc +ret +gfm_list_not_empty: +ld a, (mb_free_list) +ld (temp), a +ld a, (mb_free_list + 1) +ld (temp + 1), a +ld hl, (mb_free_list) +call get_mb_ptr +push hl +pop ix +ld a, (ix + 0) +ld (mb_free_list), a +ld a, (ix + 1) +ld (mb_free_list), a +ld hl, (temp) +ret +; Clobbers A, BC, DE, and IX +get_mb_ptr: +ld a, h +srl a +srl a +srl a +srl a +srl a +srl a +ld b, a +sla a +add a, b +ld ix, mb_frame_list_frames +ld b, 0 +ld c, a +add ix, bc +ld b, (ix + 0) +ld a, b +cp 0 +jp z, gmbp_no_frame_list_frame +ld b, (ix + 0) +ld e, (ix + 1) +ld d, (ix + 2) +ld c, 0xE +push ix +call set_frame +pop ix +gmbp_has_frame_list_frame: +push hl +ld a, h +and 0x3F +ld h, a +srl h +rr l +srl h +rr l +ld a, l +and 0xFC +ld l, a +ld a, h +or 0xE0 +ld h, a +ld b, (hl) +ld a, b +cp 0 +jp z, gmbp_no_frame +inc hl +ld e, (hl) +inc hl +ld d, (hl) +ld c, 0xE +call set_frame +gmbp_has_frame: +pop hl +ld a, l +and 0xF +or 0xE0 +ld h, a +ld l, 0x0 +gmbp_done: +ret +gmbp_no_frame_list_frame: +push hl +push ix +call get_free_frame +pop ix +push hl +pop de +ld (ix + 0), c +ld (ix + 1), e +ld (ix + 2), d +pop hl +ld b, c +ld c, 0xE +push ix +call set_frame +pop ix +push bc +push de +push hl +ld bc, 0x1000 +ld de, 0xE000 +ld h, d +ld l, e +ld (hl), 0 ; Zero out the first byte in the segment ( Acts as a 'seed' for the zeroing ) +ldir +gbmp_no_flf_zeroed: +pop hl +pop de +pop bc +jp gmbp_has_frame_list_frame +gmbp_no_frame: +push hl +push ix +call get_free_frame +pop ix +push hl +pop de +pop hl +push bc +push de +ld b, (ix + 0) +ld e, (ix + 1) +ld d, (ix + 2) +ld c, 0xE +call set_frame +pop de +pop bc +ld (hl), c +inc hl +ld (hl), e +inc hl +ld (hl), d +dec hl +dec hl +ld b, c +ld c, 0xE +push ix +call set_frame +pop ix +push bc +push de +push hl +ld bc, 0x1000 +ld de, 0xE000 +ld h, d +ld l, e +ld (hl), 0 ; Zero out the first byte in the segment ( Acts as a 'seed' for the zeroing ) +ldir +gbmp_no_f_zeroed: +pop hl +pop de +pop bc +jp gmbp_has_frame + +temp: .ds.b 5 +mb_free_list: .ds.b 2 + +mb_frame_list_frames: .ds.b 3*4 +mb_next_frame_num: .ds.b 2 diff --git a/kernel/kernel.ld b/kernel/kernel.ld new file mode 100644 index 0000000..d8c6c75 --- /dev/null +++ b/kernel/kernel.ld @@ -0,0 +1,190 @@ +/* Default linker script, for normal executables */ +/* Copyright (C) 2014-2021 Free Software Foundation, Inc. + Copying and distribution of this script, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. */ +OUTPUT_FORMAT("elf32-z80", "elf32-z80", + "elf32-z80") +OUTPUT_ARCH(z80) +ENTRY(_start) +SEARCH_DIR("=/usr/z80-elf/lib"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + PROVIDE (__executable_start = SEGMENT_START("text-segment", 0xc000)); . = SEGMENT_START("text-segment", 0xc000); + .interp : { *(.interp) } + .note.gnu.build-id : { *(.note.gnu.build-id) } + .init : + { + KEEP (*(SORT_NONE(.init))) + } + .plt : { *(.plt) } + .iplt : { *(.iplt) } + .text : + { + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(SORT(.text.sorted.*)) + *(.text .stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf.em. */ + *(.gnu.warning) + } + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } + /* These sections are generated by the Sun/Oracle C++ compiler. */ + .exception_ranges : ONLY_IF_RO { *(.exception_ranges*) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = ALIGN(1) + (. & (1 - 1)); + /* Exception handling */ + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gnu_extab : ONLY_IF_RW { *(.gnu_extab) } + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } + .exception_ranges : ONLY_IF_RW { *(.exception_ranges*) } + /* Thread Local Storage sections */ + .tdata : + { + PROVIDE_HIDDEN (__tdata_start = .); + *(.tdata .tdata.* .gnu.linkonce.td.*) + } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + } + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + .jcr : { KEEP (*(.jcr)) } + .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } + .dynamic : { *(.dynamic) } + .got : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) } + .data : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + _edata = .; PROVIDE (edata = .); + . = .; + __bss_start = .; + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. + FIXME: Why do we need it? When there is no .bss section, we do not + pad the .data section. */ + . = ALIGN(. != 0 ? 1 : 1); + } + . = ALIGN(1); + . = SEGMENT_START("ldata-segment", .); + . = ALIGN(1); + _end = .; PROVIDE (end = .); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1. */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions. */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2. */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2. */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions. */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3. */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + /* DWARF 5. */ + .debug_addr 0 : { *(.debug_addr) } + .debug_line_str 0 : { *(.debug_line_str) } + .debug_loclists 0 : { *(.debug_loclists) } + .debug_macro 0 : { *(.debug_macro) } + .debug_names 0 : { *(.debug_names) } + .debug_rnglists 0 : { *(.debug_rnglists) } + .debug_str_offsets 0 : { *(.debug_str_offsets) } + .debug_sup 0 : { *(.debug_sup) } + .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } + /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } +} diff --git a/kernel/main.z80 b/kernel/main.z80 new file mode 100644 index 0000000..4040eca --- /dev/null +++ b/kernel/main.z80 @@ -0,0 +1,151 @@ +.global _start + +_start: +ld hl, syscall +ld a, 0xc3 +ld (0xdffd), a +ld (0xdffe), hl +call scan_cards +call vmem_init +call pmem_init +call proc_map_init +ld c, 0 +call get_frame +push bc +ld ix, empty_pagemap +call pmap_load +pop bc +ld c, b +ld b, 4 +init_page_map_loop: +ld a, b +add a, 7 +push bc +ld b, c +ld c, a +ld e, a +ld d, 0 +call set_frame +pop bc +djnz init_page_map_loop +call tasking_init +call find_disk +ld hl, init_str +call read_init +ld hl, 0x8000 +after_file_pos: +push hl +pop iy +ld l, (iy+0x1C) ; Load IX with the start of the program headers in memory +ld h, (iy+0x1D) ; Load IX with the start of the program headers in memory +push hl +pop ix +push iy +pop bc +add ix, bc +ld a, (iy+0x2C) ; load B with the number of program headers +ld b, a +phead_loop: +ld a, (ix+0) +cp 1 +jp NZ, next_seg +push bc +ld c, (ix+20) +ld b, (ix+21) +; b = if c > 0 || b & 0xF > 0 { +; (b & 0xF0) + 0x10 +; } else { +; b & 0xF0 +; } >> 4 +ld a, c +cp 0 +jp z, cmp2 +jp blk1 +cmp2: +ld a, b +and 0xF +cp 0 +jp z, blk2 +blk1: +ld a, b +and 0xF0 +add a, 0x10 +ld b, a +jp end1 +blk2: +ld a, b +and 0xF0 +ld b, a +end1: +srl b +srl b +srl b +srl b +ld c, (ix+9) +srl c +srl c +srl c +srl c +push iy +push ix +push de +page_alloc_loop: +push bc +call get_free_frame +ld b, c +ld d, h +ld e, l +pop hl +ld c, l +push hl +call set_frame +pop bc +inc c +djnz page_alloc_loop +pop de +pop ix +pop iy +;Zero out the segments place in memory +ld c, (ix+20) ; Load the byte counter with the number of bytes to zero out +ld b, (ix+21) +ld e, (ix+8) ; Load the destination & source address register with the destination location of the segment in memory +ld d, (ix+9) +ld h, d +ld l, e +ld (hl), 0 ; Zero out the firts byte in the segment ( Acts as a 'seed' for the zeroing ) +ldir +ld c, (ix+16) ; Load the byte counter with the number of bytes to transfer +ld b, (ix+17) +ld l, (ix+4) ; Load the source address register with the memory address if the segment data +ld h, (ix+5) +push iy +pop de +add hl, de +ld e, (ix+8) ; Load the destination address register with the destination location of the segment in memory +ld d, (ix+9) +ldir; Do the transfer +pop bc +next_seg: +ld de, 32 +add ix, de +dec b +ld a, b +jp nz, phead_loop +ld l, (iy+0x18) ; Load HL with the program entry point +ld h, (iy+0x19) ; Load HL with the program entry point +ld ix, init_pagemap +call pmap_set +ld ix, empty_pagemap +call pmap_load +ld iy, init_pagemap +call new_process +call yield +halt + +init_str: .asciz "init.elf" + +empty_pagemap: +.ds.b 36 +init_pagemap: +.ds.b 36 + diff --git a/kernel/pmem.c b/kernel/pmem.c new file mode 100644 index 0000000..928fcd6 --- /dev/null +++ b/kernel/pmem.c @@ -0,0 +1,87 @@ +#include +#include +#include + +typedef int uint24_t; // shut the linter up + +#define RAM_TYPE 4 +#define SHARED_DATA_PAGE 2 +#define BMAP_PAGE_512K 3 +#define RAM_SIZE_LOW_PORT 0xFD +#define RAM_SIZE_HIGH_PORT 0xFE + +// Actual external functions +extern uint8_t get_first_card(uint8_t type); +extern void get_all_cards(uint8_t* buffer, size_t len,uint8_t type); + +// In assembly, these are Z80 opcodes that C can't do. +extern void write_io(uint16_t addr, uint8_t val); +extern uint8_t read_io(uint16_t addr); +extern void set_bit(uint8_t* byte, uint16_t bit); +extern uint8_t get_bit(uint8_t byte, uint16_t bit); + +static uint8_t* ram_nums = (uint8_t*)0xF000; +static uint8_t first_ram_card = 0; + +void set_bank(uint8_t card, uint8_t page, uint16_t bank) { + write_io(card << 8 | bank, (page & 0xFF)); + write_io(card << 8 | 0x20 | bank, (page >> 8) | 0x80); +} + + +inline uint16_t get_ram_size(uint8_t card_idx) { + uint16_t this_size = read_io(card_idx << 8 | RAM_SIZE_LOW_PORT); + return this_size | read_io(card_idx << 8 | RAM_SIZE_HIGH_PORT) << 8; +} + +uint8_t* get_ram_bmap(uint8_t card_idx) { + set_bank(first_ram_card, 0xF, SHARED_DATA_PAGE); + if (get_ram_size(card_idx) == 128) { + set_bank(first_ram_card, 0xF, BMAP_PAGE_512K); + return (uint8_t*)(0xF000 + card_idx * 16); + } else { + set_bank(card_idx, 0xF, 0); + return (uint8_t*)0xF000; + } +} + +uint24_t get_free_page() { + set_bank(first_ram_card, 0xF, SHARED_DATA_PAGE); + for (uint8_t i = 16; i != 0; i--) { + if (ram_nums[i] == 0) continue; + uint8_t* bmap = get_ram_bmap(i); + for(uint8_t byte = get_ram_size(i) >> 3; byte != 0; byte--) { + for(uint8_t bit = 8; bit != 0; bit--) { + if (get_bit(bmap[byte], bit)) { + set_bit(&bmap[byte], bit); + return i<<16 | bit; + } + } + } + } + return 0; +} + + +void pmem_init() { + first_ram_card = get_first_card(RAM_TYPE); + set_bank(first_ram_card, 0xF, SHARED_DATA_PAGE); + get_all_cards(ram_nums, 16, RAM_TYPE); + for (int i = 16; i != 0; i--) { + uint8_t card_idx = ram_nums[i]; + if (card_idx == 0); continue; + uint16_t this_size = read_io(card_idx << 8 | RAM_SIZE_LOW_PORT); + this_size = this_size | read_io(card_idx << 8 | RAM_SIZE_HIGH_PORT) << 8; + uint8_t* bmap = get_ram_bmap(card_idx); + memset(bmap, 0, this_size); + if (this_size != 128) { + set_bit(bmap, 0); + } + if (card_idx == first_ram_card) { + for (int j = 4; i != 0; i--) { + set_bit(bmap, j - 1); + } + } + set_bank(first_ram_card, 0xF, SHARED_DATA_PAGE); + } +} diff --git a/kernel/pmem.z80 b/kernel/pmem.z80 new file mode 100644 index 0000000..8ff44d6 --- /dev/null +++ b/kernel/pmem.z80 @@ -0,0 +1,233 @@ +.global set_frame +.global get_free_frame +.global pmem_init + +RAM_TYPE equ 2 +SHARED_DATA_PAGE equ 2 +BMAP_PAGE_512K equ 3 +RAM_SIZE_LOW_PORT equ 0xFD +RAM_SIZE_HIGH_PORT equ 0xFE +BANKING_PAGE equ 0xE + +ram_nums equ BANKING_PAGE << 12 +first_ram_card: .db 0 + +; Get the pointer to the specified RAM card's bitmap. +; Also maps the right frame in +; A contains the RAM card to use +; HL returns the pointer to the bitmap +; The values in A, BC, DE, and IX are destroyed. +get_ram_bmap: + ex af, af' + ld a, (first_ram_card) + ld b, a + ld c, BANKING_PAGE + ld d, 0 + ld e, SHARED_DATA_PAGE + call set_frame + ex af, af' + ld b, a + ld c, 0xFD + in l, (c) + inc c + in h, (c) + ex af, af' + ld a, h + cp 0 + jp nz, grb_large_card + ld a, l + cp 128 + jp nz, grb_large_card + ; grb_512k_card + ld l, a + ld a, (first_ram_card) + ld b, a + ld c, BANKING_PAGE + ld d, 0 + ld e, BMAP_PAGE_512K + call set_frame + ex af, af' + ld h, 0 + ld l, a + add hl, hl + add hl, hl + add hl, hl + add hl, hl + ld a, h + or 0xE0 + ld h, a + ret + grb_large_card: + ex af, af' + ld b, a + ld c, 0xF + ld d, 0 + ld e, d + call set_frame + ld hl, 0xE000 + ret + +; Get a free frame of RAM +; On return, C contains the card that the frame is in, and +; HL contains the frame number +; The values in A, B, D, E, IX, and IY are destroyed. +get_free_frame: +push bc +ld a, (first_ram_card) +ld b, a +ld c, BANKING_PAGE +ld d, 0 +ld e, SHARED_DATA_PAGE +call set_frame +pop bc +gff_loop: +ld ix, ram_nums +ld a, (ix+0) +cp 0 +jp z, gff_done +push af +push ix +call get_ram_bmap +pop ix +push hl +pop iy +pop af +push iy +ld b, a +ld c, 0xFD +in l, (c) +inc c +in h, (c) +srl h +rr l +srl h +rr l +srl h +rr l +ld b, h +ld c, l +byte_loop: +push bc +ld b, 8 +ld c, a +bit_loop: +ld a, b +sub 1 +sla a +sla a +sla a +or 0x47 +ld (bit_ins+1), a +or 0x80 +ld (gff_set_ins+1), a +ld a, (iy+0) +bit_ins: bit 0, a +jp nz, used_frame +pop de +pop de +push iy +pop hl +ld a, (hl) +gff_set_ins: set 0, a +ld (hl), a +scf +ccf +sbc hl, de +add hl, hl +add hl, hl +add hl, hl +ld a, b +sub 1 +add a, l +ld l, a +ret +used_frame: +djnz bit_loop +ld d, c +pop bc +dec bc +inc iy +ld a,b +or c +ld a, d +jp nz,byte_loop +inc ix +pop iy +jp gff_loop +gff_done: +exx +ld c, 0 +ld hl, 0 +ret + +pmem_init: +ld b, RAM_TYPE +call find_first_card +ld a, l +ld (first_ram_card), a +ex af, af' +ld c, BANKING_PAGE +ld b, l +ld d, 0 +ld e, SHARED_DATA_PAGE +call set_frame +ld ix, ram_nums +ld b, 16 +ld c, RAM_TYPE +call get_all_cards +ld ix, ram_nums +init_loop: +ld a, (ix+0) +inc ix +cp 0 +jp z, init_done +ld b, a +ld c, RAM_SIZE_LOW_PORT +in l, (c) +ld c, RAM_SIZE_HIGH_PORT +in h, (c) +push hl +pop iy +ld d, a +push af +ld a, d +call get_ram_bmap +pmem_init_grb_done: +pop af +push hl +push iy +pop bc +ld d, h +ld e, l +ld (hl), 0 ; Zero out the first byte ( Acts as a 'seed' for the zeroing ) +ldir +pop hl +ld b, a +push iy +pop de +ld a, d +cp 0 +jp nz, init_if1 +ld a, e +cp 128 +jp nz, init_if1 +jp init_if1_end +init_if1: +ld (hl), 0x1 +init_if1_end: +/* ld a, (first_ram_card) */ +/* ld c, a */ +/* ld a, b */ +/* cp c */ +jp nz, init_if2_end +ld (hl), 0xFF +init_if2_end: +ld a, (first_ram_card) +ld b, a +ld c, BANKING_PAGE +ld d, 0 +ld e, SHARED_DATA_PAGE +call set_frame +jp init_loop +init_done: +ret diff --git a/kernel/pmem_2g_wip.c b/kernel/pmem_2g_wip.c new file mode 100644 index 0000000..9289f56 --- /dev/null +++ b/kernel/pmem_2g_wip.c @@ -0,0 +1,90 @@ +#include +#include +#include + +#define RAM_TYPE 4 +#define INFO_PAGE 0 +#define BMAP_START 2 +#define RAM_SIZE_LOW_PORT 0xFD +#define RAM_SIZE_HIGH_PORT 0xFE + +// Actual external functions +extern uint8_t get_first_card(uint8_t type); +extern void get_all_cards(uint8_t* buffer, size_t len,uint8_t type); + +// In assembly, these are Z80 opcodes that C can't do. +extern void write_io(uint16_t addr, uint8_t val); +extern uint8_t read_io(uint16_t addr); +extern void set_bit(uint8_t* bmap, uint16_t bit); +extern uint8_t get_bit(uint8_t* bmap, uint16_t bit); + +static uint8_t* ram_nums = (uint8_t*)0xF000; +static uint8_t** bmap_ptrs = (uint8_t**)0xF010; +static uint16_t* ram_sizes = (uint8_t**)0xF030; +static uint8_t first_ram_card = 0; + +void set_bank(uint8_t card, uint8_t page, uint8_t frame) { + write_io(card << 8 | page, frame & 0x80); +} + +uint8_t* get_ram_bmap(uint8_t card_idx) { + set_bank(first_ram_card, 0xF, INFO_PAGE + BMAP_START); + return bmap_ptrs[card_idx]; +} + +// TODO: bmap is not a regular pointer and needs to be treated specially +uint16_t get_free_page() { + for (uint8_t i = 16; i != 0; i--) { + if (ram_nums[i] == 0) continue; + uint8_t* bmap = get_ram_bmap(i); + for(uint8_t bit = 0; bit < ram_sizes[i]; bit++) { + if (get_bit(bmap, bit)) { + set_bit(bmap, bit); + return ; + } + } + } + return 0; +} + +void pmem_init() { + first_ram_card = get_first_card(RAM_TYPE); + set_bank(first_ram_card, 0xF, 2); + get_all_cards(ram_nums, 16, RAM_TYPE); + uint16_t num_used_frames = 0; + uint16_t num_pages = 1; + uint16_t next_in_info = 0x50; + for (int i = 16; i != 0; i--) { + if (ram_nums[i] == 0); continue; + uint16_t this_size = read_io(ram_nums[i] << 8 | RAM_SIZE_LOW_PORT); + ram_sizes[i] = this_size; + this_size = this_size | read_io(ram_nums[i] << 8 | RAM_SIZE_HIGH_PORT) << 8; + if (this_size == 32768) { + if (num_used_frames != 0) num_pages+=1; + bmap_ptrs[i] = (uint8_t*)(num_pages << 24); + num_pages += 1; + } else if (next_in_info < 0x1000 && next_in_info + this_size/8 <= 0x1000) { + bmap_ptrs[i] = (uint8_t*)next_in_info; + next_in_info += this_size; + } else if (this_size/8 + num_used_frames/8 > 0x1000) { + num_pages += 1; + bmap_ptrs[i] = (uint8_t*)(num_pages << 24); + } else { + bmap_ptrs[i] = (uint8_t*)(num_pages << 24 || num_used_frames/8); + num_used_frames += this_size; + if (num_used_frames == 0xFFFF) { + num_pages += 1; + num_used_frames = 0; + } + } + // TODO: bmap_ptrs[i] is not a regular pointer and needs to be treated specially + memset(bmap_ptrs[i], 0, this_size); + if (ram_nums[i] == first_ram_card) { + set_bit(bmap_ptrs[i], 0); + set_bit(bmap_ptrs[i], 1); + for (int j = num_pages; i != 0; i--) { + set_bit(bmap_ptrs[i], BMAP_START + j); + } + } + } +} diff --git a/kernel/proc_map.z80 b/kernel/proc_map.z80 new file mode 100644 index 0000000..390d6a7 --- /dev/null +++ b/kernel/proc_map.z80 @@ -0,0 +1,55 @@ +.global proc_map_init +.global proc_map_set +.global proc_map_load + +proc_map_init: + +call get_free_frame +ld a, c +ld (map_frame), a +ld (map_frame+1), hl +ret + +; Clobbers A, BC, HL, IX +; Sets the type id in DE to HL +proc_map_set: +push de +push hl +ld c, 0xE +ld a, (map_frame) +ld b, a +ld de, (map_frame+1) +call set_frame +pop de +pop hl +add hl, hl +ld a, h +or 0xE +ld h, a +ld (hl), e +inc hl +ld (hl), d +ret + +; Clobbers A, BC, HL, IX, DE +; Gets the value of the type id in DE and puts it in HL +proc_map_load: +push de +ld c, 0xE +ld a, (map_frame) +ld b, a +ld de, (map_frame+1) +call set_frame +pop hl +add hl, hl +ld a, h +or 0xE +ld h, a +ld e, (hl) +inc hl +ld d, (hl) +push de +pop hl +ret + +map_frame: .ds.b 3 diff --git a/kernel/syscalls.z80 b/kernel/syscalls.z80 new file mode 100644 index 0000000..460bfb5 --- /dev/null +++ b/kernel/syscalls.z80 @@ -0,0 +1,42 @@ +.global syscall + +syscall: +push af +push ix +sla a +ld ix, syscall_table +add a, ixl +ld ixl, a +ld a, (ix+0) +ld (syscall_jump+1), a +ld a, (ix+1) +ld (syscall_jump+2), a +pop ix +pop af +syscall_jump: jp 0x0 + +print: +ld a, (hl) +cp 0 +ret z +ld bc, 0x200 +out (c), a +inc hl +jp print + +gff: + + +.align 8 +syscall_table: +.dc.w print ; 0 +.dc.w get_free_frame ; 1 +.dc.w set_frame ; 2 +.dc.w new_process ; 3 +.dc.w yield ; 4 +.dc.w mb_send ; 5 +.dc.w mb_read ; 6 +.dc.w get_free_mailbox ; 7 +.dc.w proc_map_set ; 8 +.dc.w proc_map_load ; 9 +.dc.w find_first_card ; 10 diff --git a/kernel/tasking.c b/kernel/tasking.c new file mode 100644 index 0000000..c056313 --- /dev/null +++ b/kernel/tasking.c @@ -0,0 +1,74 @@ +#include +#include +typedef int uint24_t; +typedef struct Process { + uint16_t stack_pointer; + uint24_t stack_pointer_frame; + struct Process* next_process; + struct Process* prev_process; + uint16_t pid; + uint24_t page_mappings[16]; +} Process; + + + +static Process* ready_to_run_head = NULL; +static Process* ready_to_run_tail = NULL; +static Process* current_process = NULL; +static uint16_t next_pid = 0; + +Process processes[4]; + +extern uint24_t get_free_frame(); +extern void switch_to_process_asm(); +extern Process* switch_to_process_asm_arg; + +void new_process(uint24_t* page_mappings, uint16_t entry_point) { + uint16_t pid = next_pid; + next_pid++; + processes[pid].pid = pid; + processes[pid].stack_pointer_frame = get_free_frame(); + set_frame(0xE, processes[pid].stack_pointer_frame); + processes[pid].stack_pointer = 0xFFEA; + *((uint16_t*)(0xEFFE)) = entry_point; + for (int i = 0; i<16; i++) { + processes[pid].page_mappings[i] = page_mappings[i]; + } + processes[pid].next_process = NULL; + processes[pid].prev_process = NULL; + if (ready_to_run_head) { + ready_to_run_head->prev_process = &processes[pid]; + processes[pid].next_process = ready_to_run_head; + ready_to_run_head = &processes[pid]; + } else if (current_process) { + ready_to_run_head = &processes[pid]; + ready_to_run_tail = &processes[pid]; + } else { + current_process = &processes[pid]; + } +} + +void yield() { + if (ready_to_run_head) { + Process* process = ready_to_run_head; + ready_to_run_head = ready_to_run_head->next_process; + if (ready_to_run_head==NULL) { + ready_to_run_head = current_process; + ready_to_run_tail = current_process; + } else { + ready_to_run_head->prev_process = current_process; + current_process->next_process = ready_to_run_head; + ready_to_run_head = current_process; + } + process->prev_process=NULL; + process->next_process=NULL; + for (int i=0; i<16; i++) { + current_process->page_mappings[i] = get_frame(i); + } + for (int i=0; i<16; i++) { + set_frame(i, process->page_mappings[i]); + } + switch_to_process_asm_arg = process; + switch_to_process_asm(); + } +} diff --git a/kernel/tasking.z80 b/kernel/tasking.z80 new file mode 100644 index 0000000..2b178d9 --- /dev/null +++ b/kernel/tasking.z80 @@ -0,0 +1,508 @@ + .global tasking_init +tasking_init: + ld bc, (next_pid) + push bc + pop de + inc de + ld (next_pid), de + push bc + call allocate_process + push bc + pop ix + pop bc + push bc + ld (ix+2), 0x3 + ld (ix+3), 0xF + ld (ix+4), 0x0 + ld (ix+9), c + ld (ix+10), b + push ix + pop iy + ld bc, 11 + add iy, bc + ld c, 0 + task_init_pmap_set_loop: + call get_frame + ld (iy+0), b + ld (iy+1), d + ld (iy+2), e + inc iy + inc iy + inc iy + inc c + cp 12 + jp nz, task_init_pmap_set_loop + pop bc + jp schedule_process + + .global new_process + ; Creates a new process + ; Entry point in HL, page mapping pointer in IY +new_process: + ld bc, (next_pid) + push bc + pop de + inc de + ld (next_pid), de + push bc + push hl + call allocate_process + pop hl + push bc + pop ix + pop bc + push bc + ld (ix+9), c + ld (ix+10), b + ld bc, 0 + ld (ix+5), c + ld (ix+6), b + /* ld (ix+7), e */ + /* ld (ix+8), d */ + pop bc + push hl + push bc + push iy + pop hl + push ix + pop iy + ld de, 11 + add iy, de + ld b, 12 + new_p_pmap_set_loop: + ld a, (hl) + ld (iy+0), a + inc hl + ld a, (hl) + ld (iy+1), a + inc hl + ld a, (hl) + ld (iy+2), a + inc hl + inc iy + inc iy + inc iy + djnz new_p_pmap_set_loop + new_p_sp_set: + push ix + call get_free_frame +new_p_after_gff: + pop ix + pop de + push bc + push de + pop bc + push hl + push de + call get_process_ptr +new_p_after_gpp: + pop de + push bc + pop ix + pop hl + pop bc + ld (ix+2), c + ld (ix+3), l + ld (ix+4), h + ld (ix+0), 0xEA + ld (ix+1), 0xFF + ld b, c + ld c, 0xE + push de + push hl + pop de + push ix + call set_frame + pop ix + new_p_ep_set: + pop bc + pop hl + ld (0xEFFE), hl + ; Schedules the process with the pid in BC +schedule_process: + ld hl, (ready_to_run_head) + ld a, h + cp 0 + jp nz, block1 + ld a, l + cp 0 + jp nz, block1 + jp end1 +block1: + push bc + push hl + pop bc + call get_process_ptr + push bc + pop iy + pop bc + push bc + /* ld (iy+7), c */ + /* ld (iy+8), b */ + call get_process_ptr + push bc + pop ix + pop bc + ld (ix+5), l + ld (ix+6), h + ld (ready_to_run_head), bc + ret +end1: + ld hl, (current_process) + ld a, h + cp 0 + jp nz, block2 + ld a, l + cp 0 + jp nz, block2 + jp block3 +block2: + ld (ready_to_run_head), bc + ret +block3: + ld (current_process), bc + ret + +.global yield +yield: + push ix + ld ix, (ready_to_run_head) + ld a, ixh + cp 0 + jp nz, end3 + ld a, ixl + cp 0 + jp nz, end3 + pop ix + ret +end3: + pop ix + ; Save old process context + push af + push bc + push de + push hl + exx + ex af, af' + push af + push bc + push de + push hl + exx + ex af, af' + push ix + push iy + ld bc, (current_process) + call get_process_ptr + push bc + pop ix + ld de, 11 + add ix, de + call pmap_set + ld bc, (current_process) + call get_process_ptr + push bc + pop iy + ld hl, 0 + add hl, sp + ld (iy+0), l + ld (iy+1), h +yield_list_manip: + ; Get next process into ix, unlink it from the + ; ready to run list, and link the current process in + ; Logic: + ; next_process = ready_to_run_head -> pid + ; if (current_process->pid == 1) { + ; ready_to_run_head = 0 + ; } else { + ; current_process->next = ready_to_run_head->next + ; ready_to_run_head = current_process + ; } + ; current_process = next_process + ld ix, (ready_to_run_head) + push ix + pop bc + call get_process_ptr +ylm_gpp_done: + push bc + pop ix + ld e, (ix+9) + ld d, (ix+10) + ld l, (ix+5) + ld h, (ix+6) + push hl + push de + push ix + ld bc, (current_process) + call get_process_ptr +ylm_gpp2_done: + pop ix + pop de + pop hl + push bc + pop iy + ld a, (iy + 9) + cp 1 + jp nz, else4 + xor a + ld (ready_to_run_head), a + ld (ready_to_run_head+1), a + jp end4 +else4: + ld (iy+5), l + ld (iy+6), h + ld bc, (current_process) + ld (ready_to_run_head), bc +end4: + ld (current_process), de +yield_sp_page_set: + push ix + ld de, 11 + add ix, de + call pmap_load +ysps_pl_done: + pop ix + ; Load next process context + ld b, (ix+2) + ld c, 0xF + ld e, (ix+3) + ld d, (ix+4) + ld iy, vmem_mapping_card + ld (iy+0xF), b + ld iy, vmem_mapping_bank_low + ld (iy+0xF), e + ld iy, vmem_mapping_bank_high + ld (iy+0xF), d + ld a, e + out (c), a + ld a, c + or 0x20 + ld c, a + ld a, d + or 0x80 + out (c), a + ld bc, (current_process) + call get_process_ptr + push bc + pop ix +yield_sp_load: + ld l, (ix+0) + ld h, (ix+1) + ld sp, hl + pop iy + pop ix + pop hl + pop de + pop bc + pop af + exx + ex af, af' + pop hl + pop de + pop bc + pop af + ret + +ready_to_run_head: .ds.b 2 +current_process: .ds.b 2 +next_pid: .dc.w 1 +process_allocation_bitmap: .ds.b 128 +process_heap_pages: .ds.b 48 + +; Given a pid BC, return the pointer to the process or 0 if it's nonexistent, mapping the right page in if necessary +; Clobbers A, BC, DE, HL, and IX +get_process_ptr: +push bc +ld a, c +and 0x7 +sla a +sla a +sla a +or 0xC7 +ld (set_ins+1), a +; Shift bc right 3 times to get the byte number in c +srl b +rr c +srl b +rr c +srl b +rr c +ld hl, process_allocation_bitmap +add hl, bc +ld a, (hl) +set_ins: set 0, a +ld (hl), a +pop bc +push bc +; Shift bc right 6 times to get the page number in c +push bc +pop hl +xor a +add hl, hl +rla +add hl, hl +rla +ld l, h +ld h, a +push hl +pop bc +; Multiply the page number by 3 to get the start offset in the table +add hl, bc +add hl, bc +push hl +pop bc +ld hl, process_heap_pages +add hl, bc +ld a, (hl) +cp 0 +jp z, not_allocated +ld c, 0xE +ld b, (hl) +inc hl +ld e, (hl) +inc hl +ld d, (hl) +call set_frame +pop bc +ld a, c +and 0x3F +ld c, a +ld b, 0 +; Shift BC right 6 bits (BC=BC*64) +push bc +pop hl +xor a +srl h +rr l +rra +srl h +rr l +rra +ld h, l +ld l, a +push hl +pop bc +ld a, b +add a, 0xE0 +ld b, a +ret +not_allocated: +pop bc +ld bc, 0 +ret + + +; Given a pid BC, allocate the space for that process and return a pointer to it in BC +; Clobbers A, DE, HL, and IX +allocate_process: +push bc +ld a, c +and 0x7 +sla a +sla a +sla a +or 0xC7 +ld (set_ins_2+1), a +; Shift bc right 3 times to get the byte number in c +srl b +rr c +srl b +rr c +srl b +rr c +ld hl, process_allocation_bitmap +add hl, bc +ld a, (hl) +set_ins_2: set 0, a +ld (hl), a +pop bc +push bc +; Shift bc right 6 times to get the page number in c +push bc +pop hl +xor a +add hl, hl +rla +add hl, hl +rla +ld l, h +ld h, a +push hl +pop bc +; Multiply the page number by 3 to get the start offset in the table +add hl, bc +add hl, bc +push hl +pop bc +ld hl, process_heap_pages +add hl, bc +ld a, (hl) +cp 0 +jp nz, return_ptr +push hl +call get_free_frame +pop de +ld a, c +ld (de), a +inc de +ld a, l +ld (de), a +inc de +ld a, h +ld (de), a +dec de +dec de +push de +pop hl +return_ptr: +ld c, 0xE +ld b, (hl) +inc hl +ld e, (hl) +inc hl +ld d, (hl) +call set_frame +pop bc +ld a, c +and 0x3F +ld c, a +ld b, 0 +; Shift BC left 6 bits (BC=BC*64) +xor a +srl b +rr c +rra +srl b +rr c +rra +ld b, c +ld c, a +ld a, b +add a, 0xE0 +ld b, a +ret + +; Given a pid in BC, free the processes memory +; Clobber A, BC, and HL +free_process: +ld a, c +and 0x7 +sla a +sla a +sla a +or 0x87 +ld (res_ins+1), a +; Shift bc right 3 times to get the byte number in c +srl b +rr c +srl b +rr c +srl b +rr c +ld hl, process_allocation_bitmap +add hl, bc +ld a, (hl) +res_ins: res 0, a +ld (hl), a +ret + diff --git a/kernel/vmem.z80 b/kernel/vmem.z80 new file mode 100644 index 0000000..5922cae --- /dev/null +++ b/kernel/vmem.z80 @@ -0,0 +1,149 @@ +.global set_frame +.global get_frame +.global clear_frame +.global vmem_init +.global vmem_mapping_card +.global vmem_mapping_bank_low +.global vmem_mapping_bank_high +.global pmap_set +.global pmap_load + +; map a RAM frame into memory +; C contains the page to map +; B contains the card # to use +; DE contains the bank # in the card +; The contents of A, IX and C are destroyed +set_frame: + ld a, c + ld (card_set+2), a + ld (low_set+2), a + ld (high_set+2), a + ld ix, vmem_mapping_card + card_set: ld (ix+0), b + ld ix, vmem_mapping_bank_low + low_set: ld (ix+0), e + ld ix, vmem_mapping_bank_high + high_set: ld (ix+0), d + ld a, e + out (c), a + ld a, c + or 0x20 + ld c, a + ld a, d + or 0x80 + out (c), a + ret + +; Get the RAM frame a page is mapped to +; C contains the page to get +; B will contain the card # +; DE will contain the bank # in the card +; The contents of A, IX are destroyed +; If the page is unmapped, C will be 0 and DE will contain garbage +get_frame: + ld a, c + ld (vgf_card_get+2), a + ld (vgf_low_get+2), a + ld (vgf_high_get+2), a + ld ix, vmem_mapping_card + vgf_card_get: ld b, (ix+0) + ld ix, vmem_mapping_bank_low + vgf_low_get: ld e, (ix+0) + ld ix, vmem_mapping_bank_high + vgf_high_get: ld d, (ix+0) + ret + +; Unmaps a memory page +; C contains the page to unmap +; The contents of A, BC, DE, and IX are destroyed +clear_frame: + call get_frame + ld a, c + ld (vcf_card_set+2), a + ld ix, vmem_mapping_card + vcf_card_set: ld (ix+0), 0 + xor a + out (c), a + ld a, c + or 0x20 + ld c, a + xor a + out (c), a + ret + +; Loads a pagemap pointed to by IX +; The contents of A, BC, DE, and IX are destroyed +pmap_load: +ld c, 0 +pmap_load_loop: +push bc +push ix +call clear_frame +pop ix +pop bc +ld b, (ix+0) +ld e, (ix+1) +ld d, (ix+2) +push bc +push ix +call set_frame +pop ix +pop bc +inc ix +inc ix +inc ix +inc c +ld a, c +cp 12 +jp nz, pmap_load_loop +ret + +; Stores the pagemap in the memory pointed to by IX +; The contents of A, BC, DE, and IX are destroyed +pmap_set: +ld c, 0 +pmap_set_loop: +push ix +call get_frame +pop ix +ld (ix+0), b +ld (ix+1), e +ld (ix+2), d +inc ix +inc ix +inc ix +inc c +ld a, c +cp 12 +jp nz, pmap_set_loop +ret + +RAM_TYPE equ 2 + +; Initializes the virtual memory manager +vmem_init: +ld b, RAM_TYPE +call find_first_card +ld a, l +ld c, 15 +ld b, 0 +ld de, vmem_mapping_card +ld h, d +ld l, e +inc de +ld (hl), a +ldir +ld ix, vmem_mapping_bank_low+15 +ld b, 15 +vmem_init_loop: +ld (ix+0), b +dec ix +djnz vmem_init_loop +ret + + + +vmem_mapping_card: .ds.b 16, 0 +vmem_mapping_bank_low: .ds.b 16, 0 +vmem_mapping_bank_high: .ds.b 16, 0 + diff --git a/libs/Tupfile b/libs/Tupfile new file mode 100644 index 0000000..a3b1c94 --- /dev/null +++ b/libs/Tupfile @@ -0,0 +1,3 @@ +include_rules + +: foreach *.z80 |> !as |> diff --git a/libs/syscalls.z80 b/libs/syscalls.z80 new file mode 100644 index 0000000..f02b757 --- /dev/null +++ b/libs/syscalls.z80 @@ -0,0 +1,90 @@ +; Prints a string +; Arguments: Takes a pointer in HL to a null-terminated string +; Clobbers: A, BC, HL +.global print +print: +ld a, 0 +jp 0xdffd + +; Gets a free frame of memory +; Returns: Frame card in C, frame number in HL +; Clobbers: A, B, DE, IX, IY +.global get_free_frame +get_free_frame: +ld a, 1 +jp 0xdffd + +; Maps a RAM frame into memory +; Arguments: Page to map in C, card # in B, bank # in DE +; Clobbers: A, IX, and C +.global set_frame +set_frame: +ld a, 2 +jp 0xdffd + +; Creates a new process +; Arguments: Entry point in HL, page mapping in IY +; Clobbers: unknown +.global new_process +new_process: +ld a, 3 +jp 0xdffd + +; Yields to the next process +.global yield +yield: +ld a, 4 +jp 0xdffd + +; Sends a message to the given mailbox +; Arguments: Mailbox ID in HL, message pointer in C:DE:IX +; Returns: 0 in B if message was sent, or 1 if the box was full +.global mb_send +mb_send: +ld a, 5 +jp 0xdffd + +; Reads a message from the given mailbox +; Arguments: Mailbox ID in HL +; Returns: 0 in B if there was a message, or 1 in B if there wasn't +; message pointer in C:DE:HL +.global mb_read +mb_read: +ld a, 6 +jp 0xdffd + +; Gets a free mailbox +; Returns: ID in HL +.global get_free_mailbox +get_free_mailbox: +ld a, 7 +jp 0xdffd + + +; Sets the given type ID to the given PID +; Arguments: Type ID in DE, PID in HL +.global proc_map_set +proc_map_set: +ld a, 8 +jp 0xdffd + +; Gets the value of the given type ID +; Arguments: Type ID in DE +; Returns: PID in HL +.global proc_map_get +proc_map_get: +ld a, 9 +jp 0xdffd + +; Finds the first card with a given type +; Arguments: Card type in B +; Returns: Zero in A if a card was found, or nonzero otherwise +; The ID of the card in L if a card was found, +; or zero otherwise +; Clobbers: A and H +.global find_first_card +find_first_card: +ld a, 10 +jp 0xdffd + + diff --git a/rom.bin b/rom.bin new file mode 120000 index 0000000..9fe9f5c --- /dev/null +++ b/rom.bin @@ -0,0 +1 @@ +../Z80-backplane-computer/emulator/rom/rom.bin \ No newline at end of file