Initial commit

This commit is contained in:
pjht 2023-01-29 11:03:53 -06:00
commit 5da40ff553
Signed by: pjht
GPG Key ID: 7B5F6AFBEC7EE78E
28 changed files with 3104 additions and 0 deletions

13
Tupfile Normal file
View File

@ -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

0
Tupfile.ini Normal file
View File

5
Tuprules.tup Normal file
View File

@ -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 |>

6
boot/Tupfile Normal file
View File

@ -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

4
boot/boot.ld Normal file
View File

@ -0,0 +1,4 @@
SECTIONS {
. = 0x0;
.text : { *(.text) }
}

110
boot/boot.z80 Normal file
View File

@ -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

332
init.z80 Normal file
View File

@ -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: */

162
initrd_driver.z80 Normal file
View File

@ -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 */

139
initrd_fs.z80 Normal file
View File

@ -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

46
initrd_maker.rs Normal file
View File

@ -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");
}

5
kernel/Tupfile Normal file
View File

@ -0,0 +1,5 @@
include_rules
LDFLAGS += -T kernel.ld
: foreach *.z80 |> !as |>
: *.o | kernel.ld |> !ld |> kernel.elf

70
kernel/cards.z80 Normal file
View File

@ -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

126
kernel/initrd.z80 Normal file
View File

@ -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"

114
kernel/interrupts.z80 Normal file
View File

@ -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

299
kernel/ipc.z80 Normal file
View File

@ -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

190
kernel/kernel.ld Normal file
View File

@ -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_*) }
}

151
kernel/main.z80 Normal file
View File

@ -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

87
kernel/pmem.c Normal file
View File

@ -0,0 +1,87 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
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);
}
}

233
kernel/pmem.z80 Normal file
View File

@ -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

90
kernel/pmem_2g_wip.c Normal file
View File

@ -0,0 +1,90 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#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);
}
}
}
}

55
kernel/proc_map.z80 Normal file
View File

@ -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

42
kernel/syscalls.z80 Normal file
View File

@ -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

74
kernel/tasking.c Normal file
View File

@ -0,0 +1,74 @@
#include<stdint.h>
#include<stdlib.h>
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();
}
}

508
kernel/tasking.z80 Normal file
View File

@ -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

149
kernel/vmem.z80 Normal file
View File

@ -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

3
libs/Tupfile Normal file
View File

@ -0,0 +1,3 @@
include_rules
: foreach *.z80 |> !as |>

90
libs/syscalls.z80 Normal file
View File

@ -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

1
rom.bin Symbolic link
View File

@ -0,0 +1 @@
../Z80-backplane-computer/emulator/rom/rom.bin