Initial commit
This commit is contained in:
commit
2dde34db7a
13
Tupfile
Normal file
13
Tupfile
Normal 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
0
Tupfile.ini
Normal file
5
Tuprules.tup
Normal file
5
Tuprules.tup
Normal 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
6
boot/Tupfile
Normal 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
4
boot/boot.ld
Normal file
@ -0,0 +1,4 @@
|
||||
SECTIONS {
|
||||
. = 0x0;
|
||||
.text : { *(.text) }
|
||||
}
|
110
boot/boot.z80
Normal file
110
boot/boot.z80
Normal 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
332
init.z80
Normal 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
162
initrd_driver.z80
Normal 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
139
initrd_fs.z80
Normal 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
46
initrd_maker.rs
Normal 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
5
kernel/Tupfile
Normal 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
70
kernel/cards.z80
Normal 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
126
kernel/initrd.z80
Normal 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
114
kernel/interrupts.z80
Normal 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
299
kernel/ipc.z80
Normal 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
190
kernel/kernel.ld
Normal 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
151
kernel/main.z80
Normal 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
87
kernel/pmem.c
Normal 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
233
kernel/pmem.z80
Normal 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
90
kernel/pmem_2g_wip.c
Normal 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
55
kernel/proc_map.z80
Normal 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
42
kernel/syscalls.z80
Normal 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
74
kernel/tasking.c
Normal 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
508
kernel/tasking.z80
Normal 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
149
kernel/vmem.z80
Normal 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
3
libs/Tupfile
Normal file
@ -0,0 +1,3 @@
|
||||
include_rules
|
||||
|
||||
: foreach *.z80 |> !as |>
|
90
libs/syscalls.z80
Normal file
90
libs/syscalls.z80
Normal 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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user