Compare commits
44 Commits
hardware-t
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
5fa8e0efa3 | ||
|
e688617286 | ||
|
d0a5eb4c6f | ||
|
5fe565b6f7 | ||
|
1b2184fe52 | ||
|
e9c4e993f4 | ||
|
36e66600f5 | ||
|
0953fe31ec | ||
|
cec3b93c83 | ||
|
168816cb1e | ||
|
1ac79d005b | ||
|
8c5247c317 | ||
|
d1024d7793 | ||
|
89c0f95219 | ||
|
094e278212 | ||
|
c7b69675bb | ||
|
7799813a30 | ||
|
6a4c1908bb | ||
|
d1f1bfa974 | ||
|
de2edf1404 | ||
|
7f0a94352d | ||
|
0e3ae9c4e3 | ||
|
6e66cb9bbe | ||
|
5729c6c893 | ||
|
b41d65bfce | ||
|
d63430bb4d | ||
|
d0fbc7df56 | ||
|
7107c0ef8b | ||
|
ddadeed70c | ||
|
964cbcd68d | ||
|
2114741766 | ||
|
750b1edc16 | ||
|
7937de6ef0 | ||
|
ce771b19bb | ||
|
02f03d2224 | ||
|
679eb8cf57 | ||
|
0faa2eb553 | ||
|
afaf5e1a03 | ||
|
43e902e83c | ||
|
9aa56cdce2 | ||
|
9216b3359a | ||
|
6ebee28032 | ||
|
8c309c6d9f | ||
|
606fc37e37 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,4 +1,6 @@
|
|||||||
*.o
|
*.o
|
||||||
*.bin
|
*.bin
|
||||||
|
*.img
|
||||||
*.lock
|
*.lock
|
||||||
|
*.com
|
||||||
bx_enh_dbg.ini
|
bx_enh_dbg.ini
|
||||||
|
60
Makefile
60
Makefile
@ -1,19 +1,57 @@
|
|||||||
objects = entry.o kernel.o handler.o interrupt.o v86.o print.o
|
objects = entry.o kernel.o task.o handler.o interrupt.o v86.o print.o tss.o gdt.o\
|
||||||
CFLAGS = -target "i686-elf" -m32 -mgeneral-regs-only -ffreestanding -march=pentium-m -fno-stack-protector -nostdlib -c
|
paging.o fault.o tests.o kbd.o helper.o disk.o file.o fs.o dosfs/dosfs.o fs_dos.o\
|
||||||
|
progs.o hexedit.o textedit.o
|
||||||
|
CFLAGS = -target "i686-elf" -m32 -mgeneral-regs-only -ffreestanding\
|
||||||
|
-march=i686 -fno-stack-protector -Wno-int-conversion -nostdlib -c
|
||||||
|
LFLAGS = -Wl,--gc-sections -Wl,--print-gc-sections -m32 -nostartfiles -nostdlib
|
||||||
|
|
||||||
|
ifeq ($(OUTFILE),)
|
||||||
|
OUTFILE = virtdisk.bin
|
||||||
|
endif
|
||||||
|
ifeq ($(PARTSTART),)
|
||||||
|
PARTSTART = 2048
|
||||||
|
endif
|
||||||
|
|
||||||
|
PARTVBR=$(shell echo "$(PARTSTART) * (2^9)" | bc)
|
||||||
|
PARTVBRBOOT=$(shell echo "$(PARTVBR) + 90" | bc)
|
||||||
|
KERNSEC=$(shell echo "$(PARTSTART) + 4" | bc)
|
||||||
|
|
||||||
|
.PHONY: $(OUTFILE)
|
||||||
|
|
||||||
|
all: $(OUTFILE)
|
||||||
|
|
||||||
|
$(OUTFILE): boot.bin boot_partition.bin kernel.bin
|
||||||
|
# Copy system bootloader to boot sector, don't overwrite MBR
|
||||||
|
dd bs=400 count=1 conv=notrunc if=boot.bin of=$@
|
||||||
|
# Ensure partition VBR contains EB 5A
|
||||||
|
echo -n -e '\xeb\x5a' | dd bs=1 seek=$(PARTVBR) count=2 conv=notrunc of=$@
|
||||||
|
# Copy kernel bootloader to partition VBR
|
||||||
|
dd bs=1 count=420 seek=$(PARTVBRBOOT) conv=notrunc if=boot_partition.bin of=$@
|
||||||
|
# TODO Check that disk has enough reserved sectors,
|
||||||
|
# currently this will overwrite the disk if too few
|
||||||
|
# Write kernel beyond boot sector, maximum 64K (128 sectors)
|
||||||
|
dd bs=512 count=128 seek=$(KERNSEC) conv=notrunc if=kernel.bin of=$@
|
||||||
|
|
||||||
|
kernel.bin: out.o link.ld usermode.o
|
||||||
|
clang $(LFLAGS) -Wl,-M -Tlink.ld -ffreestanding -o $@ out.o usermode.o
|
||||||
|
|
||||||
|
usermode.o: usermode.bin
|
||||||
|
objcopy -I binary -O elf32-i386 -B i386 $< $@
|
||||||
|
|
||||||
|
%.bin: %.nasm
|
||||||
|
nasm -o $@ $<
|
||||||
|
|
||||||
|
out.o: $(objects)
|
||||||
|
clang $(LFLAGS) -e entry -r -o $@ $^
|
||||||
|
|
||||||
%.o: %.nasm
|
%.o: %.nasm
|
||||||
nasm -f elf32 -o $@ $<
|
nasm -f elf32 -o $@ $<
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
clang $(CFLAGS) -O2 $<
|
clang $(CFLAGS) -ffunction-sections -fdata-sections -Os -o $@ $<
|
||||||
|
|
||||||
all: $(objects)
|
|
||||||
nasm boot.nasm -o boot.bin
|
|
||||||
gcc -Tlink.ld -m32 -ffreestanding -nostartfiles -nostdlib -o kernel.bin\
|
|
||||||
$(objects)
|
|
||||||
dd bs=256 count=1 conv=notrunc if=boot.bin of=virtdisk.bin
|
|
||||||
dd bs=512 seek=1 conv=notrunc if=kernel.bin of=virtdisk.bin
|
|
||||||
virtdisk:
|
virtdisk:
|
||||||
dd bs=1M count=32 if=/dev/zero of=virtdisk.bin
|
cp virtdisk.bin.ex virtdisk.bin
|
||||||
echo -n -e '\x55\xaa' | dd bs=1 seek=510 conv=notrunc of=virtdisk.bin
|
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(objects) out.o kernel.bin boot.bin usermode.bin
|
||||||
|
5
bochsrc
5
bochsrc
@ -1,6 +1,5 @@
|
|||||||
# configuration file generated by Bochs
|
# configuration file generated by Bochs
|
||||||
display_library: x, options="gui_debug"
|
display_library: x, options="gui_debug"
|
||||||
#magic_break: enabled=1
|
|
||||||
plugin_ctrl: unmapped=true, biosdev=true, speaker=true, extfpuirq=true, parallel=true, serial=true, iodebug=true, pcidev=false, usb_uhci=false
|
plugin_ctrl: unmapped=true, biosdev=true, speaker=true, extfpuirq=true, parallel=true, serial=true, iodebug=true, pcidev=false, usb_uhci=false
|
||||||
config_interface: textconfig
|
config_interface: textconfig
|
||||||
display_library: x
|
display_library: x
|
||||||
@ -12,7 +11,7 @@ floppy_bootsig_check: disabled=0
|
|||||||
floppya: type=none
|
floppya: type=none
|
||||||
# no floppyb
|
# no floppyb
|
||||||
ata0: enabled=true, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
|
ata0: enabled=true, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
|
||||||
ata0-master: type=disk, path="virtdisk.bin", mode=flat, cylinders=2, heads=16, spt=63, sect_size=512, model="Generic 1234", biosdetect=auto, translation=auto
|
ata0-master: type=disk, path="virtdisk.bin", mode=flat, cylinders=8, heads=16, spt=63, sect_size=512, model="Generic 1234", biosdetect=auto, translation=auto
|
||||||
ata0-slave: type=none
|
ata0-slave: type=none
|
||||||
ata1: enabled=true, ioaddr1=0x170, ioaddr2=0x370, irq=15
|
ata1: enabled=true, ioaddr1=0x170, ioaddr2=0x370, irq=15
|
||||||
ata1-master: type=none
|
ata1-master: type=none
|
||||||
@ -32,7 +31,7 @@ vga: extension=vbe, update_freq=5, realtime=1, ddc=builtin
|
|||||||
cpu: count=1:1:1, ips=4000000, quantum=16, model=core_duo_t2400_yonah, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0
|
cpu: count=1:1:1, ips=4000000, quantum=16, model=core_duo_t2400_yonah, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0
|
||||||
print_timestamps: enabled=0
|
print_timestamps: enabled=0
|
||||||
debugger_log: -
|
debugger_log: -
|
||||||
magic_break: enabled=0
|
magic_break: enabled=1
|
||||||
port_e9_hack: enabled=0
|
port_e9_hack: enabled=0
|
||||||
private_colormap: enabled=0
|
private_colormap: enabled=0
|
||||||
clock: sync=none, time0=local, rtc_sync=0
|
clock: sync=none, time0=local, rtc_sync=0
|
||||||
|
70
boot.nasm
70
boot.nasm
@ -1,18 +1,67 @@
|
|||||||
[ORG 0x7c00]
|
; This boots the active partition.
|
||||||
|
; Relocates self to 0x7E00, loads the
|
||||||
|
; first sector of active partition
|
||||||
|
; to 0x7C00 and jumps
|
||||||
|
[ORG 0x7C00]
|
||||||
[BITS 16]
|
[BITS 16]
|
||||||
|
|
||||||
xor ax, ax
|
xor ax, ax
|
||||||
mov ds, ax
|
mov ds, ax
|
||||||
mov es, ax
|
mov es, ax
|
||||||
|
mov ax, 0x8000
|
||||||
|
mov ss, ax
|
||||||
|
mov sp, 0xFF00
|
||||||
|
; Relocate self
|
||||||
|
mov di, 0x7E00
|
||||||
|
mov si, 0x7C00
|
||||||
|
mov cx, 512
|
||||||
|
rep movsw
|
||||||
|
jmp 0:relocated
|
||||||
|
; TODO Make this calculated, somehow
|
||||||
|
[SECTION RELOC vstart=0x7E20]
|
||||||
|
relocated:
|
||||||
|
mov ah, 0x41 ; int 13 extensions check
|
||||||
|
mov bx, 0x55AA
|
||||||
|
int 0x13
|
||||||
|
jc no_int13
|
||||||
|
; Find active partition
|
||||||
|
xor cx, cx
|
||||||
|
mov si, 0x7E00+0x1BE
|
||||||
|
.find_active:
|
||||||
|
lodsb
|
||||||
|
bt ax, 7
|
||||||
|
jc read
|
||||||
|
add si, 15
|
||||||
|
inc cl
|
||||||
|
cmp cl, 4
|
||||||
|
jl .find_active
|
||||||
|
jmp err
|
||||||
|
read:
|
||||||
|
; Put partition start LBA in disk address packet
|
||||||
|
add si, 7
|
||||||
|
mov di, addr_packet_start_block
|
||||||
|
movsw
|
||||||
|
movsw
|
||||||
|
; Load the first sector of the partition
|
||||||
|
xor ax, ax
|
||||||
mov ah, 0x42
|
mov ah, 0x42
|
||||||
mov si, addr_packet
|
mov si, addr_packet
|
||||||
int 0x13
|
int 0x13
|
||||||
jnc 0x8000
|
jc err
|
||||||
|
; Jump to partition boot
|
||||||
|
jmp 0:0x7C00
|
||||||
|
|
||||||
|
no_int13:
|
||||||
|
mov si, no_exten_str
|
||||||
|
mov cx, no_exten_str_end - no_exten_str
|
||||||
|
jmp print
|
||||||
|
err:
|
||||||
|
mov bx, ax
|
||||||
|
mov si, string
|
||||||
|
mov cx, string_end - string
|
||||||
|
print:
|
||||||
push 0xb800
|
push 0xb800
|
||||||
pop es
|
pop es
|
||||||
xor di, di
|
xor di, di
|
||||||
mov si, string
|
|
||||||
mov cx, 10
|
|
||||||
mov ah, 0x7
|
mov ah, 0x7
|
||||||
err_print:
|
err_print:
|
||||||
lodsb
|
lodsb
|
||||||
@ -21,11 +70,14 @@ loop err_print
|
|||||||
hlt_loop:
|
hlt_loop:
|
||||||
hlt
|
hlt
|
||||||
jmp hlt_loop
|
jmp hlt_loop
|
||||||
|
|
||||||
string: db 'DISK ERROR'
|
string: db 'DISK ERROR'
|
||||||
|
string_end:
|
||||||
|
no_exten_str: db 'NO INT13 EXTEN'
|
||||||
|
no_exten_str_end:
|
||||||
|
|
||||||
addr_packet:
|
addr_packet:
|
||||||
db 0x10, 0x00 ; size, reserved
|
db 0x10, 0x00 ; size, reserved
|
||||||
dw 0x20 ; blocks
|
dw 1 ; blocks
|
||||||
dd 0x8000 ; transfer buffer
|
addr_packet_transfer_buff_off: dw 0x7C00 ; transfer buffer offset
|
||||||
dq 1 ; start block
|
addr_packet_transfer_buff_seg: dw 0x0000 ; transfer buffer segment
|
||||||
|
addr_packet_start_block: dq 0 ; start block
|
||||||
|
136
boot_partition.nasm
Normal file
136
boot_partition.nasm
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
; This boots *from a FAT partition*
|
||||||
|
; So it starts at 0x7C00+90, the offset of
|
||||||
|
; the boot code in a FAT VBR
|
||||||
|
secreadcnt equ 0x38
|
||||||
|
kernelreads equ 0x10000/(512*secreadcnt) ; 64K / Sector Size FIXME This underestimates when kernel size is not divisible by bytes per read
|
||||||
|
[ORG 0x7c00+90]
|
||||||
|
[BITS 16]
|
||||||
|
xor ax, ax
|
||||||
|
mov ds, ax
|
||||||
|
mov es, ax
|
||||||
|
mov ax, 0x8000
|
||||||
|
mov ss, ax
|
||||||
|
mov sp, 0xFF00
|
||||||
|
; FIXME Assumes INT 13 Extension support
|
||||||
|
mov ah, 0x42
|
||||||
|
mov si, addr_packet
|
||||||
|
int 0x13
|
||||||
|
jc err
|
||||||
|
xor cx, cx
|
||||||
|
mov si, 0x7E00+0x1BE
|
||||||
|
.find_active:
|
||||||
|
lodsb
|
||||||
|
bt ax, 7
|
||||||
|
jc read
|
||||||
|
add si, 15
|
||||||
|
inc cl
|
||||||
|
cmp cl, 4
|
||||||
|
jl .find_active
|
||||||
|
jmp err
|
||||||
|
read:
|
||||||
|
mov dh, cl ; Store active partition
|
||||||
|
add si, 7
|
||||||
|
mov di, addr_packet_start_block
|
||||||
|
lodsw
|
||||||
|
; Offset of kernel in partition
|
||||||
|
add ax, 4
|
||||||
|
stosw
|
||||||
|
lodsw
|
||||||
|
adc ax, 0
|
||||||
|
stosw
|
||||||
|
mov cx, kernelreads
|
||||||
|
read_loop:
|
||||||
|
xor ax, ax
|
||||||
|
mov ah, 0x42
|
||||||
|
mov si, addr_packet
|
||||||
|
int 0x13
|
||||||
|
jc err
|
||||||
|
add word [addr_packet_transfer_buff_seg], (secreadcnt*512)/16
|
||||||
|
add word [addr_packet_start_block], secreadcnt
|
||||||
|
loop read_loop
|
||||||
|
entry:
|
||||||
|
cli ; no interrupts
|
||||||
|
xor ax,ax
|
||||||
|
mov ds, ax
|
||||||
|
lgdt [gdt_desc] ; load gdt register
|
||||||
|
mov eax, cr0 ; set pmode bit
|
||||||
|
or al, 1
|
||||||
|
mov cr0, eax
|
||||||
|
jmp 08h:Pmode
|
||||||
|
err:
|
||||||
|
mov si, string
|
||||||
|
mov cx, string_end - string
|
||||||
|
print:
|
||||||
|
push 0xb800
|
||||||
|
pop es
|
||||||
|
xor di, di
|
||||||
|
mov ah, 0x7
|
||||||
|
err_print:
|
||||||
|
lodsb
|
||||||
|
stosw
|
||||||
|
loop err_print
|
||||||
|
hlt_loop:
|
||||||
|
hlt
|
||||||
|
jmp hlt_loop
|
||||||
|
|
||||||
|
[BITS 32]
|
||||||
|
Pmode:
|
||||||
|
mov eax, 0x10
|
||||||
|
mov ds, ax
|
||||||
|
mov es, ax
|
||||||
|
mov fs, ax
|
||||||
|
mov gs, ax
|
||||||
|
mov ss, ax
|
||||||
|
; check A20
|
||||||
|
.a20:
|
||||||
|
mov edi, 0x107C00
|
||||||
|
mov esi, 0x007C00
|
||||||
|
mov [esi], esi
|
||||||
|
mov [edi], edi
|
||||||
|
cmpsd
|
||||||
|
jne .kernel
|
||||||
|
; FIXME It's annoying that fast A20
|
||||||
|
; will crash old systems, but it's
|
||||||
|
; just so small...
|
||||||
|
in al, 0x92 ; fast A20
|
||||||
|
test al, 2
|
||||||
|
jnz .fa20_end
|
||||||
|
or al, 2
|
||||||
|
and al, 0xFE
|
||||||
|
out 0x92, al
|
||||||
|
.fa20_end:
|
||||||
|
jmp .a20
|
||||||
|
.kernel:
|
||||||
|
mov esi, 0x7E00
|
||||||
|
mov edi, 0x100000
|
||||||
|
mov ecx, 0x10000
|
||||||
|
rep movsb
|
||||||
|
xchg bx,bx
|
||||||
|
jmp 08h:0x100000
|
||||||
|
|
||||||
|
gdt_desc:
|
||||||
|
dw gdt_end - gdt
|
||||||
|
dd gdt
|
||||||
|
gdt:
|
||||||
|
gdt_null: dq 0
|
||||||
|
gdt_code: dw 0xFFFF, 0 ; bits 0-15 limit (4GB), bits 0-15 base address
|
||||||
|
db 0 ; bits 16-23 base address
|
||||||
|
db 10011010b ; access byte
|
||||||
|
db 11001111b ; bits 16-19 limit (4GB), 4 bits flags
|
||||||
|
db 0 ; bits 24-31 base address
|
||||||
|
gdt_data: dw 0xFFFF, 0 ; bits 0-15 limit (4GB), bits 0-15 base address
|
||||||
|
db 0 ; bits 16-23 base address
|
||||||
|
db 10010010b ; access byte
|
||||||
|
db 11001111b ; bits 16-19 limit (4GB), 4 bits flags
|
||||||
|
db 0 ; bits 24-31 base address
|
||||||
|
gdt_end:
|
||||||
|
|
||||||
|
string: db 'DISK ERROR'
|
||||||
|
string_end:
|
||||||
|
|
||||||
|
addr_packet:
|
||||||
|
db 0x10, 0x00 ; size, reserved
|
||||||
|
dw secreadcnt ; blocks
|
||||||
|
addr_packet_transfer_buff_off: dw 0x7E00 ; transfer buffer offset
|
||||||
|
addr_packet_transfer_buff_seg: dw 0x0000 ; transfer buffer segment
|
||||||
|
addr_packet_start_block: dq 0 ; start block
|
196
disk.c
Normal file
196
disk.c
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
#include "disk.h"
|
||||||
|
#include "v86defs.h"
|
||||||
|
#include "print.h"
|
||||||
|
#include "stdint.h"
|
||||||
|
|
||||||
|
extern void *memcpy(void *restrict dest, const void *restrict src, uintptr_t n);
|
||||||
|
|
||||||
|
#define SECTOR_SIZE 512
|
||||||
|
#define DISKCACHEBLOCKSIZE 0x1000 // 512 * 4
|
||||||
|
#define DISKCACHESECTORMASK 7
|
||||||
|
#define DISKCACHESECTORSIZE 8
|
||||||
|
#define DISKCACHEBLOCKCOUNT 128
|
||||||
|
uint8_t (*const DiskCache)[DISKCACHEBLOCKCOUNT][DISKCACHEBLOCKSIZE] = (uint8_t (* const)[DISKCACHEBLOCKCOUNT][DISKCACHEBLOCKSIZE])0x280000;
|
||||||
|
uint32_t DiskCacheLastRead[DISKCACHEBLOCKCOUNT];
|
||||||
|
uint32_t DiskCacheSector[DISKCACHEBLOCKCOUNT];
|
||||||
|
|
||||||
|
extern uint32_t _gpf_eax_save;
|
||||||
|
extern uint32_t _gpf_eflags_save;
|
||||||
|
extern uint16_t error_screen[80*50]; // defined in kernel.c
|
||||||
|
__attribute__((__no_caller_saved_registers__))
|
||||||
|
__attribute__((__noreturn__))
|
||||||
|
extern void error_environment(); // defined in kernel.c
|
||||||
|
uint32_t numHead;
|
||||||
|
uint32_t secPerTrack;
|
||||||
|
uint32_t maxCylinder;
|
||||||
|
char useCHS = -1;
|
||||||
|
// TODO This function also inits cache, make that go somewhere else
|
||||||
|
void Disk_SetupCHS() {
|
||||||
|
union V86Regs_t regs;
|
||||||
|
// Check for INT 13 Extensions support
|
||||||
|
regs.h.ah = 0x41;
|
||||||
|
regs.w.bx = 0x55AA;
|
||||||
|
regs.h.dl = 0x80;
|
||||||
|
V8086Int(0x13, ®s);
|
||||||
|
// LBA supported if CF clear
|
||||||
|
if (!(_gpf_eflags_save & 0x1)) {
|
||||||
|
useCHS = 0;
|
||||||
|
} else {
|
||||||
|
FARPTR v86_entry = i386LinearToFp(v86DiskGetGeometry);
|
||||||
|
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), ®s);
|
||||||
|
// check CF for error
|
||||||
|
if (_gpf_eflags_save & 0x1) {
|
||||||
|
uint16_t *vga = error_screen;
|
||||||
|
vga += printStr("Could not get Disk Geometry.", vga);
|
||||||
|
error_environment();
|
||||||
|
for(;;);
|
||||||
|
}
|
||||||
|
numHead = ((_gpf_eax_save & 0xff00) >> 8) + 1;
|
||||||
|
secPerTrack = _gpf_eax_save & 0x3f;
|
||||||
|
maxCylinder = ((_gpf_eax_save & 0xff0000) >> 16) | ((_gpf_eax_save & 0xc0) << 2);
|
||||||
|
useCHS = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern uint32_t TIMERVAL;
|
||||||
|
uint8_t *FindInCache(uint32_t sector) {
|
||||||
|
uint32_t maskedSector = sector & ~DISKCACHESECTORMASK;
|
||||||
|
for (int i = 0; i < DISKCACHEBLOCKCOUNT; i++) {
|
||||||
|
// Found
|
||||||
|
if (DiskCacheSector[i] == maskedSector) {
|
||||||
|
DiskCacheLastRead[i] = TIMERVAL;
|
||||||
|
return &(*DiskCache)[i][(sector & DISKCACHESECTORMASK) << 9];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Not Found
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddToCache(uint32_t sector, uint8_t *buffer) {
|
||||||
|
uintptr_t lowestFoundTime = DiskCacheLastRead[0];
|
||||||
|
uintptr_t lowestFoundIdx = 0;
|
||||||
|
// TODO We should really use something more fast and less lazy
|
||||||
|
for (int i = 1; i < DISKCACHEBLOCKCOUNT; i++) {
|
||||||
|
if (DiskCacheLastRead[i] < lowestFoundTime) {
|
||||||
|
lowestFoundTime = DiskCacheLastRead[i];
|
||||||
|
lowestFoundIdx = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < DISKCACHEBLOCKSIZE / sizeof(uint32_t); i++)
|
||||||
|
((uint32_t *)((*DiskCache)[lowestFoundIdx]))[i] = ((uint32_t*)buffer)[i];
|
||||||
|
DiskCacheLastRead[lowestFoundIdx] = TIMERVAL;
|
||||||
|
DiskCacheSector[lowestFoundIdx] = sector;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE Only updates one sector (512B)
|
||||||
|
void UpdateCache(uint32_t sector, uint8_t *buffer) {
|
||||||
|
uint8_t *cache = FindInCache(sector);
|
||||||
|
// Not in cache, nothing to update
|
||||||
|
if (!cache) return;
|
||||||
|
for (int i = 0; i < SECTOR_SIZE/sizeof(uint32_t); i++)
|
||||||
|
((uint32_t*)cache)[i] = ((uint32_t*)buffer)[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitCache() {
|
||||||
|
for (int i = 0; i < DISKCACHEBLOCKCOUNT; i++) {
|
||||||
|
DiskCacheLastRead[i] = 0;
|
||||||
|
DiskCacheSector[i] = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE Must be run before reading from disk
|
||||||
|
void InitDisk() {
|
||||||
|
InitCache();
|
||||||
|
Disk_SetupCHS();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Disk_ReadSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count) {
|
||||||
|
uint8_t *cache = FindInCache(sector);
|
||||||
|
if (cache) {
|
||||||
|
memcpy(buffer, cache, count * SECTOR_SIZE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE If the buffer provided is outside the 0x20000-0x2F000 range,
|
||||||
|
// the function will use 0x20000 for the Virtual 8086 process
|
||||||
|
// and copy to the other buffer after
|
||||||
|
uint8_t *v86buf = (uintptr_t)buffer >= 0x20000 && (uintptr_t)buffer <= 0x2F000 ?
|
||||||
|
buffer :
|
||||||
|
(uint8_t*)0x20000;
|
||||||
|
|
||||||
|
// TODO Do error handling
|
||||||
|
if (!useCHS) {
|
||||||
|
// LBA Read
|
||||||
|
v86disk_addr_packet.start_block = sector & ~DISKCACHESECTORMASK;
|
||||||
|
v86disk_addr_packet.blocks = DISKCACHESECTORSIZE;
|
||||||
|
v86disk_addr_packet.transfer_buffer =
|
||||||
|
(uintptr_t)v86buf & 0x000F |
|
||||||
|
(((uintptr_t)v86buf & 0xFFFF0) << 12);
|
||||||
|
union V86Regs_t regs;
|
||||||
|
regs.h.ah = 0x42;
|
||||||
|
FARPTR v86_entry = i386LinearToFp(v86DiskOp);
|
||||||
|
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), ®s);
|
||||||
|
if (v86disk_addr_packet.blocks != DISKCACHESECTORSIZE) {
|
||||||
|
uint16_t *vga = error_screen;
|
||||||
|
vga += printStr("INT13 Read Secs: ", vga);
|
||||||
|
vga += printDec(v86disk_addr_packet.blocks, vga);
|
||||||
|
error_environment();
|
||||||
|
for(;;);
|
||||||
|
}
|
||||||
|
AddToCache(sector & ~DISKCACHESECTORMASK, v86buf);
|
||||||
|
v86buf = &v86buf[(sector & DISKCACHESECTORMASK) << 9];
|
||||||
|
} else {
|
||||||
|
uint32_t tmp = sector / secPerTrack;
|
||||||
|
uint32_t sec = (sector % (secPerTrack)) + 1;
|
||||||
|
uint32_t head = tmp % numHead;
|
||||||
|
uint32_t cyl = tmp / numHead;
|
||||||
|
union V86Regs_t regs;
|
||||||
|
regs.w.ax = 0x0201;
|
||||||
|
regs.h.ch = cyl & 0xff;
|
||||||
|
regs.h.cl = sec | ((cyl >> 2) & 0xc0);
|
||||||
|
regs.h.dh = head;
|
||||||
|
regs.h.dl = 0x80;
|
||||||
|
regs.w.bx = (uintptr_t)v86buf & 0xFFFF;
|
||||||
|
FARPTR v86_entry = i386LinearToFp(v86DiskReadCHS);
|
||||||
|
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), ®s);
|
||||||
|
}
|
||||||
|
if (v86buf != buffer)
|
||||||
|
memcpy(buffer, v86buf, count * SECTOR_SIZE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint32_t Disk_WriteSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count) {
|
||||||
|
// NOTE If the buffer provided is outside the 0x20000-0x2FE00 range,
|
||||||
|
// the function will use copy that buffer into the Virtual 8086 disk range
|
||||||
|
uint8_t *v86buf = buffer;
|
||||||
|
if ((uintptr_t)v86buf < 0x20000 || (uintptr_t)v86buf > 0x2FE00) {
|
||||||
|
v86buf = (uint8_t *)0x20000;
|
||||||
|
memcpy(v86buf, buffer, count * SECTOR_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO This check should probably happen at the kernel level
|
||||||
|
if (useCHS == -1) {
|
||||||
|
Disk_SetupCHS();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Do error handling
|
||||||
|
if (!useCHS) {
|
||||||
|
// LBA Write
|
||||||
|
v86disk_addr_packet.start_block = sector;
|
||||||
|
v86disk_addr_packet.blocks = count;
|
||||||
|
v86disk_addr_packet.transfer_buffer =
|
||||||
|
(uintptr_t)v86buf & 0x000F |
|
||||||
|
(((uintptr_t)v86buf & 0xFFFF0) << 12);
|
||||||
|
union V86Regs_t regs;
|
||||||
|
regs.w.ax = 0x4300;
|
||||||
|
FARPTR v86_entry = i386LinearToFp(v86DiskOp);
|
||||||
|
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), ®s);
|
||||||
|
UpdateCache(sector, buffer);
|
||||||
|
} else {
|
||||||
|
uint16_t *vga = error_screen;
|
||||||
|
vga += printStr("CHS Write Unimplemented", vga);
|
||||||
|
error_environment();
|
||||||
|
for(;;);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
7
disk.h
Normal file
7
disk.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void InitDisk();
|
||||||
|
|
||||||
|
uint32_t Disk_ReadSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count);
|
||||||
|
uint32_t Disk_WriteSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count);
|
1249
dosfs/dosfs.c
Executable file
1249
dosfs/dosfs.c
Executable file
File diff suppressed because it is too large
Load Diff
374
dosfs/dosfs.h
Executable file
374
dosfs/dosfs.h
Executable file
@ -0,0 +1,374 @@
|
|||||||
|
/*
|
||||||
|
DOSFS Embedded FAT-Compatible Filesystem
|
||||||
|
(C) 2005 Lewin A.R.W. Edwards (sysadm@zws.com)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _DOSFS_H
|
||||||
|
#define _DOSFS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
//===================================================================
|
||||||
|
// User-supplied functions
|
||||||
|
uint32_t DFS_ReadSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count);
|
||||||
|
uint32_t DFS_WriteSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count);
|
||||||
|
|
||||||
|
|
||||||
|
//===================================================================
|
||||||
|
// Configurable items
|
||||||
|
#define MAX_PATH 64 // Maximum path length (increasing this will
|
||||||
|
// GREATLY increase stack requirements!)
|
||||||
|
#define DIR_SEPARATOR '/' // character separating directory components
|
||||||
|
|
||||||
|
// End of configurable items
|
||||||
|
//===================================================================
|
||||||
|
|
||||||
|
//===================================================================
|
||||||
|
// 32-bit error codes
|
||||||
|
#define DFS_OK 0 // no error
|
||||||
|
#define DFS_EOF 1 // end of file (not an error)
|
||||||
|
#define DFS_WRITEPROT 2 // volume is write protected
|
||||||
|
#define DFS_NOTFOUND 3 // path or file not found
|
||||||
|
#define DFS_PATHLEN 4 // path too long
|
||||||
|
#define DFS_ALLOCNEW 5 // must allocate new directory cluster
|
||||||
|
#define DFS_ERRMISC 0xffffffff // generic error
|
||||||
|
|
||||||
|
//===================================================================
|
||||||
|
// File access modes
|
||||||
|
#define DFS_READ 1 // read-only
|
||||||
|
#define DFS_WRITE 2 // write-only
|
||||||
|
|
||||||
|
//===================================================================
|
||||||
|
// Miscellaneous constants
|
||||||
|
#define SECTOR_SIZE 512 // sector size in bytes
|
||||||
|
|
||||||
|
//===================================================================
|
||||||
|
// Internal subformat identifiers
|
||||||
|
#define FAT12 0
|
||||||
|
#define FAT16 1
|
||||||
|
#define FAT32 2
|
||||||
|
|
||||||
|
//===================================================================
|
||||||
|
// DOS attribute bits
|
||||||
|
#define ATTR_READ_ONLY 0x01
|
||||||
|
#define ATTR_HIDDEN 0x02
|
||||||
|
#define ATTR_SYSTEM 0x04
|
||||||
|
#define ATTR_VOLUME_ID 0x08
|
||||||
|
#define ATTR_DIRECTORY 0x10
|
||||||
|
#define ATTR_ARCHIVE 0x20
|
||||||
|
#define ATTR_LONG_NAME (ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Directory entry structure
|
||||||
|
note: if name[0] == 0xe5, this is a free dir entry
|
||||||
|
if name[0] == 0x00, this is a free entry and all subsequent entries are free
|
||||||
|
if name[0] == 0x05, the first character of the name is 0xe5 [a kanji nicety]
|
||||||
|
|
||||||
|
Date format: bit 0-4 = day of month (1-31)
|
||||||
|
bit 5-8 = month, 1=Jan..12=Dec
|
||||||
|
bit 9-15 = count of years since 1980 (0-127)
|
||||||
|
Time format: bit 0-4 = 2-second count, (0-29)
|
||||||
|
bit 5-10 = minutes (0-59)
|
||||||
|
bit 11-15= hours (0-23)
|
||||||
|
*/
|
||||||
|
typedef struct _tagDIRENT {
|
||||||
|
uint8_t name[11]; // filename
|
||||||
|
uint8_t attr; // attributes (see ATTR_* constant definitions)
|
||||||
|
uint8_t reserved; // reserved, must be 0
|
||||||
|
uint8_t crttimetenth; // create time, 10ths of a second (0-199 are valid)
|
||||||
|
uint8_t crttime_l; // creation time low byte
|
||||||
|
uint8_t crttime_h; // creation time high byte
|
||||||
|
uint8_t crtdate_l; // creation date low byte
|
||||||
|
uint8_t crtdate_h; // creation date high byte
|
||||||
|
uint8_t lstaccdate_l; // last access date low byte
|
||||||
|
uint8_t lstaccdate_h; // last access date high byte
|
||||||
|
uint8_t startclus_h_l; // high word of first cluster, low byte (FAT32)
|
||||||
|
uint8_t startclus_h_h; // high word of first cluster, high byte (FAT32)
|
||||||
|
uint8_t wrttime_l; // last write time low byte
|
||||||
|
uint8_t wrttime_h; // last write time high byte
|
||||||
|
uint8_t wrtdate_l; // last write date low byte
|
||||||
|
uint8_t wrtdate_h; // last write date high byte
|
||||||
|
uint8_t startclus_l_l; // low word of first cluster, low byte
|
||||||
|
uint8_t startclus_l_h; // low word of first cluster, high byte
|
||||||
|
uint8_t filesize_0; // file size, low byte
|
||||||
|
uint8_t filesize_1; //
|
||||||
|
uint8_t filesize_2; //
|
||||||
|
uint8_t filesize_3; // file size, high byte
|
||||||
|
} DIRENT, *PDIRENT;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Partition table entry structure
|
||||||
|
*/
|
||||||
|
typedef struct _tagPTINFO {
|
||||||
|
uint8_t active; // 0x80 if partition active
|
||||||
|
uint8_t start_h; // starting head
|
||||||
|
uint8_t start_cs_l; // starting cylinder and sector (low byte)
|
||||||
|
uint8_t start_cs_h; // starting cylinder and sector (high byte)
|
||||||
|
uint8_t type; // type ID byte
|
||||||
|
uint8_t end_h; // ending head
|
||||||
|
uint8_t end_cs_l; // ending cylinder and sector (low byte)
|
||||||
|
uint8_t end_cs_h; // ending cylinder and sector (high byte)
|
||||||
|
uint8_t start_0; // starting sector# (low byte)
|
||||||
|
uint8_t start_1; //
|
||||||
|
uint8_t start_2; //
|
||||||
|
uint8_t start_3; // starting sector# (high byte)
|
||||||
|
uint8_t size_0; // size of partition (low byte)
|
||||||
|
uint8_t size_1; //
|
||||||
|
uint8_t size_2; //
|
||||||
|
uint8_t size_3; // size of partition (high byte)
|
||||||
|
} PTINFO, *PPTINFO;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Master Boot Record structure
|
||||||
|
*/
|
||||||
|
typedef struct _tagMBR {
|
||||||
|
uint8_t bootcode[0x1be]; // boot sector
|
||||||
|
PTINFO ptable[4]; // four partition table structures
|
||||||
|
uint8_t sig_55; // 0x55 signature byte
|
||||||
|
uint8_t sig_aa; // 0xaa signature byte
|
||||||
|
} MBR, *PMBR;
|
||||||
|
|
||||||
|
/*
|
||||||
|
BIOS Parameter Block structure (FAT12/16)
|
||||||
|
*/
|
||||||
|
typedef struct _tagBPB {
|
||||||
|
uint8_t bytepersec_l; // bytes per sector low byte (0x00)
|
||||||
|
uint8_t bytepersec_h; // bytes per sector high byte (0x02)
|
||||||
|
uint8_t secperclus; // sectors per cluster (1,2,4,8,16,32,64,128 are valid)
|
||||||
|
uint8_t reserved_l; // reserved sectors low byte
|
||||||
|
uint8_t reserved_h; // reserved sectors high byte
|
||||||
|
uint8_t numfats; // number of FAT copies (2)
|
||||||
|
uint8_t rootentries_l; // number of root dir entries low byte (0x00 normally)
|
||||||
|
uint8_t rootentries_h; // number of root dir entries high byte (0x02 normally)
|
||||||
|
uint8_t sectors_s_l; // small num sectors low byte
|
||||||
|
uint8_t sectors_s_h; // small num sectors high byte
|
||||||
|
uint8_t mediatype; // media descriptor byte
|
||||||
|
uint8_t secperfat_l; // sectors per FAT low byte
|
||||||
|
uint8_t secperfat_h; // sectors per FAT high byte
|
||||||
|
uint8_t secpertrk_l; // sectors per track low byte
|
||||||
|
uint8_t secpertrk_h; // sectors per track high byte
|
||||||
|
uint8_t heads_l; // heads low byte
|
||||||
|
uint8_t heads_h; // heads high byte
|
||||||
|
uint8_t hidden_0; // hidden sectors low byte
|
||||||
|
uint8_t hidden_1; // (note - this is the number of MEDIA sectors before
|
||||||
|
uint8_t hidden_2; // first sector of VOLUME - we rely on the MBR instead)
|
||||||
|
uint8_t hidden_3; // hidden sectors high byte
|
||||||
|
uint8_t sectors_l_0; // large num sectors low byte
|
||||||
|
uint8_t sectors_l_1; //
|
||||||
|
uint8_t sectors_l_2; //
|
||||||
|
uint8_t sectors_l_3; // large num sectors high byte
|
||||||
|
} BPB, *PBPB;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Extended BIOS Parameter Block structure (FAT12/16)
|
||||||
|
*/
|
||||||
|
typedef struct _tagEBPB {
|
||||||
|
uint8_t unit; // int 13h drive#
|
||||||
|
uint8_t head; // archaic, used by Windows NT-class OSes for flags
|
||||||
|
uint8_t signature; // 0x28 or 0x29
|
||||||
|
uint8_t serial_0; // serial#
|
||||||
|
uint8_t serial_1; // serial#
|
||||||
|
uint8_t serial_2; // serial#
|
||||||
|
uint8_t serial_3; // serial#
|
||||||
|
uint8_t label[11]; // volume label
|
||||||
|
uint8_t system[8]; // filesystem ID
|
||||||
|
} EBPB, *PEBPB;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Extended BIOS Parameter Block structure (FAT32)
|
||||||
|
*/
|
||||||
|
typedef struct _tagEBPB32 {
|
||||||
|
uint8_t fatsize_0; // big FAT size in sectors low byte
|
||||||
|
uint8_t fatsize_1; //
|
||||||
|
uint8_t fatsize_2; //
|
||||||
|
uint8_t fatsize_3; // big FAT size in sectors high byte
|
||||||
|
uint8_t extflags_l; // extended flags low byte
|
||||||
|
uint8_t extflags_h; // extended flags high byte
|
||||||
|
uint8_t fsver_l; // filesystem version (0x00) low byte
|
||||||
|
uint8_t fsver_h; // filesystem version (0x00) high byte
|
||||||
|
uint8_t root_0; // cluster of root dir, low byte
|
||||||
|
uint8_t root_1; //
|
||||||
|
uint8_t root_2; //
|
||||||
|
uint8_t root_3; // cluster of root dir, high byte
|
||||||
|
uint8_t fsinfo_l; // sector pointer to FSINFO within reserved area, low byte (2)
|
||||||
|
uint8_t fsinfo_h; // sector pointer to FSINFO within reserved area, high byte (0)
|
||||||
|
uint8_t bkboot_l; // sector pointer to backup boot sector within reserved area, low byte (6)
|
||||||
|
uint8_t bkboot_h; // sector pointer to backup boot sector within reserved area, high byte (0)
|
||||||
|
uint8_t reserved[12]; // reserved, should be 0
|
||||||
|
|
||||||
|
uint8_t unit; // int 13h drive#
|
||||||
|
uint8_t head; // archaic, used by Windows NT-class OSes for flags
|
||||||
|
uint8_t signature; // 0x28 or 0x29
|
||||||
|
uint8_t serial_0; // serial#
|
||||||
|
uint8_t serial_1; // serial#
|
||||||
|
uint8_t serial_2; // serial#
|
||||||
|
uint8_t serial_3; // serial#
|
||||||
|
uint8_t label[11]; // volume label
|
||||||
|
uint8_t system[8]; // filesystem ID
|
||||||
|
} EBPB32, *PEBPB32;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Logical Boot Record structure (volume boot sector)
|
||||||
|
*/
|
||||||
|
typedef struct _tagLBR {
|
||||||
|
uint8_t jump[3]; // JMP instruction
|
||||||
|
uint8_t oemid[8]; // OEM ID, space-padded
|
||||||
|
BPB bpb; // BIOS Parameter Block
|
||||||
|
union {
|
||||||
|
EBPB ebpb; // FAT12/16 Extended BIOS Parameter Block
|
||||||
|
EBPB32 ebpb32; // FAT32 Extended BIOS Parameter Block
|
||||||
|
} ebpb;
|
||||||
|
uint8_t code[420]; // boot sector code
|
||||||
|
uint8_t sig_55; // 0x55 signature byte
|
||||||
|
uint8_t sig_aa; // 0xaa signature byte
|
||||||
|
} LBR, *PLBR;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Volume information structure (Internal to DOSFS)
|
||||||
|
*/
|
||||||
|
typedef struct _tagVOLINFO {
|
||||||
|
uint8_t unit; // unit on which this volume resides
|
||||||
|
uint8_t filesystem; // formatted filesystem
|
||||||
|
|
||||||
|
// These two fields aren't very useful, so support for them has been commented out to
|
||||||
|
// save memory. (Note that the "system" tag is not actually used by DOS to determine
|
||||||
|
// filesystem type - that decision is made entirely on the basis of how many clusters
|
||||||
|
// the drive contains. DOSFS works the same way).
|
||||||
|
// See tag: OEMID in dosfs.c
|
||||||
|
// uint8_t oemid[9]; // OEM ID ASCIIZ
|
||||||
|
// uint8_t system[9]; // system ID ASCIIZ
|
||||||
|
uint8_t label[12]; // volume label ASCIIZ
|
||||||
|
uint32_t startsector; // starting sector of filesystem
|
||||||
|
uint8_t secperclus; // sectors per cluster
|
||||||
|
uint16_t reservedsecs; // reserved sectors
|
||||||
|
uint32_t numsecs; // number of sectors in volume
|
||||||
|
uint32_t secperfat; // sectors per FAT
|
||||||
|
uint16_t rootentries; // number of root dir entries
|
||||||
|
|
||||||
|
uint32_t numclusters; // number of clusters on drive
|
||||||
|
|
||||||
|
// The fields below are PHYSICAL SECTOR NUMBERS.
|
||||||
|
uint32_t fat1; // starting sector# of FAT copy 1
|
||||||
|
uint32_t rootdir; // starting sector# of root directory (FAT12/FAT16) or cluster (FAT32)
|
||||||
|
uint32_t dataarea; // starting sector# of data area (cluster #2)
|
||||||
|
} VOLINFO, *PVOLINFO;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Flags in DIRINFO.flags
|
||||||
|
*/
|
||||||
|
#define DFS_DI_BLANKENT 0x01 // Searching for blank entry
|
||||||
|
|
||||||
|
/*
|
||||||
|
Directory search structure (Internal to DOSFS)
|
||||||
|
*/
|
||||||
|
typedef struct _tagDIRINFO {
|
||||||
|
uint32_t currentcluster; // current cluster in dir
|
||||||
|
uint8_t currentsector; // current sector in cluster
|
||||||
|
uint8_t currententry; // current dir entry in sector
|
||||||
|
uint8_t *scratch; // ptr to user-supplied scratch buffer (one sector)
|
||||||
|
uint8_t flags; // internal DOSFS flags
|
||||||
|
} DIRINFO, *PDIRINFO;
|
||||||
|
|
||||||
|
/*
|
||||||
|
File handle structure (Internal to DOSFS)
|
||||||
|
*/
|
||||||
|
typedef struct _tagFILEINFO {
|
||||||
|
PVOLINFO volinfo; // VOLINFO used to open this file
|
||||||
|
uint32_t dirsector; // physical sector containing dir entry of this file
|
||||||
|
uint8_t diroffset; // # of this entry within the dir sector
|
||||||
|
uint8_t mode; // mode in which this file was opened
|
||||||
|
uint32_t firstcluster; // first cluster of file
|
||||||
|
uint32_t filelen; // byte length of file
|
||||||
|
|
||||||
|
uint32_t cluster; // current cluster
|
||||||
|
uint32_t pointer; // current (BYTE) pointer
|
||||||
|
} FILEINFO, *PFILEINFO;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get starting sector# of specified partition on drive #unit
|
||||||
|
NOTE: This code ASSUMES an MBR on the disk.
|
||||||
|
scratchsector should point to a SECTOR_SIZE scratch area
|
||||||
|
Returns 0xffffffff for any error.
|
||||||
|
If pactive is non-NULL, this function also returns the partition active flag.
|
||||||
|
If pptype is non-NULL, this function also returns the partition type.
|
||||||
|
If psize is non-NULL, this function also returns the partition size.
|
||||||
|
*/
|
||||||
|
uint32_t DFS_GetPtnStart(uint8_t unit, uint8_t *scratchsector, uint8_t pnum, uint8_t *pactive, uint8_t *pptype, uint32_t *psize);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Retrieve volume info from BPB and store it in a VOLINFO structure
|
||||||
|
You must provide the unit and starting sector of the filesystem, and
|
||||||
|
a pointer to a sector buffer for scratch
|
||||||
|
Attempts to read BPB and glean information about the FS from that.
|
||||||
|
Returns 0 OK, nonzero for any error.
|
||||||
|
*/
|
||||||
|
uint32_t DFS_GetVolInfo(uint8_t unit, uint8_t *scratchsector, uint32_t startsector, PVOLINFO volinfo);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Open a directory for enumeration by DFS_GetNextDirEnt
|
||||||
|
You must supply a populated VOLINFO (see DFS_GetVolInfo)
|
||||||
|
The empty string or a string containing only the directory separator are
|
||||||
|
considered to be the root directory.
|
||||||
|
Returns 0 OK, nonzero for any error.
|
||||||
|
*/
|
||||||
|
uint32_t DFS_OpenDir(PVOLINFO volinfo, uint8_t *dirname, PDIRINFO dirinfo);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get next entry in opened directory structure. Copies fields into the dirent
|
||||||
|
structure, updates dirinfo. Note that it is the _caller's_ responsibility to
|
||||||
|
handle the '.' and '..' entries.
|
||||||
|
A deleted file will be returned as a NULL entry (first char of filename=0)
|
||||||
|
by this code. Filenames beginning with 0x05 will be translated to 0xE5
|
||||||
|
automatically. Long file name entries will be returned as NULL.
|
||||||
|
returns DFS_EOF if there are no more entries, DFS_OK if this entry is valid,
|
||||||
|
or DFS_ERRMISC for a media error
|
||||||
|
*/
|
||||||
|
uint32_t DFS_GetNext(PVOLINFO volinfo, PDIRINFO dirinfo, PDIRENT dirent);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Open a file for reading or writing. You supply populated VOLINFO, a path to the file,
|
||||||
|
mode (DFS_READ or DFS_WRITE) and an empty fileinfo structure. You also need to
|
||||||
|
provide a pointer to a sector-sized scratch buffer.
|
||||||
|
Returns various DFS_* error states. If the result is DFS_OK, fileinfo can be used
|
||||||
|
to access the file from this point on.
|
||||||
|
*/
|
||||||
|
uint32_t DFS_OpenFile(PVOLINFO volinfo, uint8_t *path, uint8_t mode, uint8_t *scratch, PFILEINFO fileinfo);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Read an open file
|
||||||
|
You must supply a prepopulated FILEINFO as provided by DFS_OpenFile, and a
|
||||||
|
pointer to a SECTOR_SIZE scratch buffer.
|
||||||
|
Note that returning DFS_EOF is not an error condition. This function updates the
|
||||||
|
successcount field with the number of bytes actually read.
|
||||||
|
*/
|
||||||
|
uint32_t DFS_ReadFile(PFILEINFO fileinfo, uint8_t *scratch, uint8_t *buffer, uint32_t *successcount, uint32_t len);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Write an open file
|
||||||
|
You must supply a prepopulated FILEINFO as provided by DFS_OpenFile, and a
|
||||||
|
pointer to a SECTOR_SIZE scratch buffer.
|
||||||
|
This function updates the successcount field with the number of bytes actually written.
|
||||||
|
*/
|
||||||
|
uint32_t DFS_WriteFile(PFILEINFO fileinfo, uint8_t *scratch, uint8_t *buffer, uint32_t *successcount, uint32_t len);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Seek file pointer to a given position
|
||||||
|
This function does not return status - refer to the fileinfo->pointer value
|
||||||
|
to see where the pointer wound up.
|
||||||
|
Requires a SECTOR_SIZE scratch buffer
|
||||||
|
*/
|
||||||
|
void DFS_Seek(PFILEINFO fileinfo, uint32_t offset, uint8_t *scratch);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Delete a file
|
||||||
|
scratch must point to a sector-sized buffer
|
||||||
|
*/
|
||||||
|
uint32_t DFS_UnlinkFile(PVOLINFO volinfo, uint8_t *path, uint8_t *scratch);
|
||||||
|
|
||||||
|
// If we are building a host-emulation version, include host support
|
||||||
|
#ifdef HOSTVER
|
||||||
|
#include "hostemu.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // _DOSFS_H
|
366
dosfs/readme.txt
Executable file
366
dosfs/readme.txt
Executable file
@ -0,0 +1,366 @@
|
|||||||
|
README.TXT (C) Copyright 2006
|
||||||
|
DOSFS Level 1 Version 1.02 Lewin A.R.W. Edwards (sysadm@zws.com)
|
||||||
|
=====================================================================
|
||||||
|
|
||||||
|
Abstract
|
||||||
|
========
|
||||||
|
DOSFS is a FAT-compatible filesystem intended for fairly low-end
|
||||||
|
embedded applications. It is not the leanest possible implementation
|
||||||
|
(the leanest FAT implementations operate in << 512 bytes of RAM, with
|
||||||
|
heavy restrictions). This code strikes a good balance between size
|
||||||
|
and functionality, with an emphasis on RAM footprint.
|
||||||
|
|
||||||
|
Intended target systems would be in the ballpark of 1K RAM, 4K ROM
|
||||||
|
or more.
|
||||||
|
|
||||||
|
Features:
|
||||||
|
* Supports FAT12, FAT16 and FAT32 volumes
|
||||||
|
* Supports storage devices up to 2048Gbytes in size (LBA32)
|
||||||
|
* Supports devices with or without MBRs (hard disks vs. floppy disks
|
||||||
|
or ZIP drives formatted as "big floppies")
|
||||||
|
* Supports multiple partitions on disks with MBRs
|
||||||
|
* Supports subdirectories
|
||||||
|
* Can be operated with a single global 512-byte sector buffer
|
||||||
|
* Fully reentrant code (assuming the underlying physical device driver
|
||||||
|
is reentrant and global sector buffers are not used). There are no
|
||||||
|
global variables in the filesystem
|
||||||
|
* Does not perform any memory allocation
|
||||||
|
* Partial support for random-access files
|
||||||
|
|
||||||
|
Applications:
|
||||||
|
* Firmware upgrades
|
||||||
|
* Failsafe IPL
|
||||||
|
* Media playback
|
||||||
|
* Data logging
|
||||||
|
* Configuration storage
|
||||||
|
|
||||||
|
There is no technical support for this free product; however, if you
|
||||||
|
have questions or suggestions, you are encouraged to email Lewin
|
||||||
|
Edwards at sysadm@zws.com. If you need custom additions to the code,
|
||||||
|
or if you have other projects for which you need engineering
|
||||||
|
assistance, please feel free to email or call (646) 549-3715.
|
||||||
|
|
||||||
|
License
|
||||||
|
=======
|
||||||
|
The license for DOSFS is very simple but verbose to state.
|
||||||
|
|
||||||
|
1. DOSFS is (C) Copyright 2006 by Lewin A.R.W. Edwards ("Author").
|
||||||
|
All rights not explicitly granted herein are reserved. The DOSFS
|
||||||
|
code is the permanent property of the Author and no transfer of
|
||||||
|
ownership is implied by this license.
|
||||||
|
|
||||||
|
2. DOSFS is an educational project, provided as-is. No guarantee of
|
||||||
|
performance or suitability for any application is stated or
|
||||||
|
implied. You use this product entirely at your own risk. Use of
|
||||||
|
this product in any manner automatically waives any right to seek
|
||||||
|
compensation or damages of any sort from the Author. Since the
|
||||||
|
products you might make are entirely out of the Author's control,
|
||||||
|
use of this product also constitutes an agreement by you to take
|
||||||
|
full responsibility for and indemnify the Author against any
|
||||||
|
action for any loss or damage (including economic loss of any
|
||||||
|
type, and specifically including patent litigation) that arises
|
||||||
|
from a product made by you that incorporates any portion of
|
||||||
|
the DOSFS code.
|
||||||
|
|
||||||
|
3. If you live under the jurisdiction of any legislation that would
|
||||||
|
prohibit or limit any condition in this license, you cannot be
|
||||||
|
licensed to use this product.
|
||||||
|
|
||||||
|
4. If you do not fall into the excluded category in point 3, you are
|
||||||
|
hereby licensed to use the DOSFS code in any application that you
|
||||||
|
see fit. You are not required to pay any fee or notify the Author
|
||||||
|
that you are using DOSFS. Any modifications made by you to the
|
||||||
|
DOSFS code are your property and you may distribute the modified
|
||||||
|
version in any manner that you wish. You are not required to
|
||||||
|
disclose sourcecode to such modifications, either to the Author or
|
||||||
|
to any third party. Any such disclosure made to the Author will
|
||||||
|
irrevocably become the property of the Author in the absence of a
|
||||||
|
formal agreement to the contrary, established prior to such
|
||||||
|
disclosure being made.
|
||||||
|
|
||||||
|
To summarize the intent of the above: DOSFS is free. You can do what
|
||||||
|
you want with it. Anything that happens as a result is entirely your
|
||||||
|
responsibility. You can't take ownership of my code and stop me from
|
||||||
|
doing whatever I want with it. If you do something nifty with DOSFS
|
||||||
|
and send me the sourcecode, I may include your changes in the next
|
||||||
|
distribution and it will be released to the world as free software.
|
||||||
|
If someone sues you because your DOSFS-containing product causes
|
||||||
|
any sort of legal, financial or other problem, it's your lawsuit,
|
||||||
|
not mine, and you'll exclude me from the proceedings.
|
||||||
|
|
||||||
|
User-Supplied Functions
|
||||||
|
=======================
|
||||||
|
You must provide functions to read sectors into memory and write
|
||||||
|
them back to the target media. The demo suite includes an emulation
|
||||||
|
module that reads/writes a disk image file (#define HOSTVER pulls
|
||||||
|
in hostemu.h which wraps the prototypes for these functions).
|
||||||
|
There are various tools for UNIX, DOS, Windows et al, to create
|
||||||
|
images from storage media; my preferred utility is dd.
|
||||||
|
|
||||||
|
The functions you must supply in your embedded app are:
|
||||||
|
|
||||||
|
DFS_ReadSector(unit,buffer,sector,count)
|
||||||
|
DFS_WriteSector(unit,buffer,sector,count)
|
||||||
|
|
||||||
|
These two functions read and write, respectively, "count" sectors of
|
||||||
|
size SECTOR_SIZE (512 bytes; see below) from/to physical sector
|
||||||
|
#"sector" of device "unit", to/from the scratch buffer "buffer". They
|
||||||
|
should return 0 for success or nonzero for failure. In the current
|
||||||
|
implementation of DOSFS, count will always be 1.
|
||||||
|
|
||||||
|
The "unit" argument is designed to permit implementation of multiple
|
||||||
|
storage devices, for example multiple media slots on a single device,
|
||||||
|
or to differentiate between master and slave devices on an ATAPI bus.
|
||||||
|
|
||||||
|
This code is designed for 512-byte sectors. Although the sector size
|
||||||
|
is a #define, you should not tinker with it because the vast majority
|
||||||
|
of FAT filesystems use 512-byte sectors, and the DOSFS code doesn't
|
||||||
|
support runtime determination of sector size. This will not affect the
|
||||||
|
vast majority of users.
|
||||||
|
|
||||||
|
Example Code
|
||||||
|
============
|
||||||
|
Refer to the tests in main.c to see how to call DOSFS functions.
|
||||||
|
(These tests are all commented out). Note that the only two files
|
||||||
|
you need to add to your project are dosfs.c and dosfs.h.
|
||||||
|
|
||||||
|
|
||||||
|
Mounting Volumes
|
||||||
|
================
|
||||||
|
--If the device has a partition table (practically all removable flash
|
||||||
|
media are formatted this way), call DFS_GetPtnStart to get the
|
||||||
|
starting sector# of the desired partition. You can optionally also
|
||||||
|
retrieve the active state, partition type byte and partition size
|
||||||
|
in this step. The reason this step is broken out separately is so
|
||||||
|
you can support devices that are formatted like a floppy disk, i.e.
|
||||||
|
the volume starts directly at physical sector 0 of the media.
|
||||||
|
|
||||||
|
--Call DFS_GetVolInfo to read filesystem info into a VOLINFO structure.
|
||||||
|
DFS_GetVolInfo needs to know the unit number and partition starting
|
||||||
|
sector (as returned by DFS_GetPtnStart, or 0 if this is a "floppy-
|
||||||
|
format" volume without an MBR).
|
||||||
|
|
||||||
|
From this point on, the VOLINFO structure is all you'll need - you can
|
||||||
|
forget the unit and partition start sector numbers.
|
||||||
|
|
||||||
|
Enumerating Directory Contents
|
||||||
|
==============================
|
||||||
|
--Call DFS_Opendir and supply a path, populated VOLINFO and a
|
||||||
|
DIRINFO structure to receive the results. Note - you must PREPOPULATE
|
||||||
|
the DIRINFO.scratch field with a pointer to a sector scratch buffer.
|
||||||
|
This buffer must remain unmolested while you have the directory open
|
||||||
|
for searching.
|
||||||
|
--Call DFS_GetNext to receive the DIRENT contents for the next directory
|
||||||
|
item. This function returns DFS_OK for no error, and DFS_EOF if there
|
||||||
|
are no more entries in the directory being searched.
|
||||||
|
Before using the DIRENT, check the first character of the name. If it
|
||||||
|
is NULL, then this is an unusable entry - call DFS_GetNext again to
|
||||||
|
keep searching. LFN directory entries are automatically tagged this way
|
||||||
|
so your application will not be pestered by them.
|
||||||
|
|
||||||
|
Note: A designed side-effect of this code is that when you locate the
|
||||||
|
file of interest, the DIRINFO.currentcluster, DIRINFO.currentsector
|
||||||
|
and DIRINFO.currententry-1 fields will identify the directory entry of
|
||||||
|
interest.
|
||||||
|
|
||||||
|
Reading a File
|
||||||
|
==============
|
||||||
|
--Call DFS_OpenFile with mode = DFS_READ and supply a path and the relevant
|
||||||
|
VOLINFO structure. DFS_OpenFile will populate a FILEINFO that can be used
|
||||||
|
to refer to the file.
|
||||||
|
--Optionally call DFS_Seek to set the file pointer. If you attempt to set
|
||||||
|
the file pointer past the end of file, the file will NOT be extended. Check
|
||||||
|
the FILEINFO.pointer value after DFS_Seek to verify that the pointer is
|
||||||
|
where you expect it to be.
|
||||||
|
--Observe that functionality similar to the "whence" parameter of fseek() can
|
||||||
|
be obtained by using simple arithmetic on the FILEINFO.pointer and
|
||||||
|
FILEINFO.filelen members.
|
||||||
|
--Call DFS_ReadFile with the FILEINFO you obtained from OpenFile, and a
|
||||||
|
pointer to a buffer plus the desired number of bytes to read, and a
|
||||||
|
pointer to a sector-sized scratch buffer. The reason a scratch sector is
|
||||||
|
required is because the underlying sector read function doesn't know
|
||||||
|
about partial reads.
|
||||||
|
--Note that a file opened for reading cannot be written. If you need r/w
|
||||||
|
access, open with mode = DFS_WRITE (see below).
|
||||||
|
|
||||||
|
Writing a file
|
||||||
|
==============
|
||||||
|
--Call DFS_OpenFile with mode = DFS_WRITE and supply a path and the relevant
|
||||||
|
VOLINFO structure. DFS_OpenFile will populate a FILEINFO that can be used to
|
||||||
|
refer to the file.
|
||||||
|
--Optionally call DFS_Seek to set the file pointer. Refer to the notes on
|
||||||
|
this topic in the section on reading files, above.
|
||||||
|
--Call DFS_WriteFile with the FILEINFO you obtained from OpenFile, and a
|
||||||
|
pointer to the source buffer, and a pointer to a sector-sized scratch
|
||||||
|
buffer.
|
||||||
|
--Note that a file open for writing can also be read.
|
||||||
|
--Files are created automatically if they do not exist. Subdirectories are
|
||||||
|
NOT automatically created.
|
||||||
|
--If you open an existing file for writing, the file pointer will start at
|
||||||
|
the beginning of the data; if you want to append, seek to the end before
|
||||||
|
writing new data.
|
||||||
|
--If you perform random-access writes to a file, the length will NOT change
|
||||||
|
unless you exceed the file's original length. There is currently no
|
||||||
|
function to truncate a file at the current pointer position.
|
||||||
|
--On-disk consistency is guaranteed when DFS_WriteFile exits, unless your
|
||||||
|
physical layer has a writeback cache in it.
|
||||||
|
|
||||||
|
Deleting a file
|
||||||
|
===============
|
||||||
|
--Call DFS_UnlinkFile
|
||||||
|
--WARNING: This call will delete a subdirectory (correctly) but will NOT
|
||||||
|
first recurse the directory to delete the contents - so you will end up
|
||||||
|
with lost clusters.
|
||||||
|
|
||||||
|
Notes
|
||||||
|
=====
|
||||||
|
Some platforms may require explicit pragmas or attributes to the structures
|
||||||
|
and unions. For example, arm-gcc will require __attribute__ ((__packed__))
|
||||||
|
otherwise it will try to be "smart" and place the uint8_t members on 4-byte
|
||||||
|
boundaries. There is no truly elegant compiler-independent method to get
|
||||||
|
around this sort of problem.
|
||||||
|
|
||||||
|
The code assumes either a von Neumann architecture, or a compiler that
|
||||||
|
is smart enough to understand where your pointers are aimed and emit
|
||||||
|
the right kind of memory read and write instructions. The implications
|
||||||
|
of this statement depend on your target processor and the compiler you
|
||||||
|
are using. Be very careful not to straddle bank boundaries on bank-
|
||||||
|
switched memory systems.
|
||||||
|
|
||||||
|
Physical 32-bit sector numbers are used throughout. Therefore, the
|
||||||
|
CHS geometry (if any) of the storage media is not known to DOSFS. Your
|
||||||
|
sector r/w functions may need to query the CHS geometry and perform
|
||||||
|
mapping.
|
||||||
|
|
||||||
|
File timestamps set by DOSFS are always 1:01:00am on Jan 1, 2006. If
|
||||||
|
your system has a concept of real time, you can enhance this.
|
||||||
|
|
||||||
|
FILEINFO structures contain a pointer to the corresponding VOLINFO
|
||||||
|
used to open the file, mainly in order to avoid mixups but also to
|
||||||
|
obviate the need for an extra parameter to every file read/write. DOSFS
|
||||||
|
assumes that the VOLINFO won't move around. If you need to move or
|
||||||
|
destroy VOLINFOs pertaining to open files, you'll have to fix up the
|
||||||
|
pointer in the FILEINFO structure yourself.
|
||||||
|
|
||||||
|
The subdirectory delimiter is a forward slash ( '/' ) by default. The
|
||||||
|
reason for this is to avoid the common programming error of forgetting
|
||||||
|
that backslash is an escape character in C strings; i.e. "\MYDIR\FILE"
|
||||||
|
is NOT what you want; "\\MYDIR\\FILE" is what you wanted to type. If you
|
||||||
|
are porting DOS code into an embedded environment, feel free to change
|
||||||
|
this #define.
|
||||||
|
|
||||||
|
DOSFS does not have a concept of "current directory". A current directory
|
||||||
|
is owned by a process, and a process is an operating system concept.
|
||||||
|
DOSFS is a filesystem library, not an operating system. Therefore, any
|
||||||
|
path you provide to a DOSFS call is assumed to be relative to the root of
|
||||||
|
the volume.
|
||||||
|
|
||||||
|
There is no call to close a file or directory that is open for reading or
|
||||||
|
writing. You can simply destroy or reuse the data structures allocated for
|
||||||
|
that operation; there is no internal state in DOSFS so no cleanup is
|
||||||
|
necessary. Similarly, there is no call to close a file that is open for
|
||||||
|
writing. (Observe that dosfs.c has no global variables. All state information
|
||||||
|
is stored in data structures provided by the caller).
|
||||||
|
|
||||||
|
MAX_PATH is defined as 64. MS-type DOS filesystems support 128 characters
|
||||||
|
or more in paths. You can increase this define, but it may GREATLY
|
||||||
|
increase memory requirements.
|
||||||
|
|
||||||
|
VFAT long filenames are not supported. There is a certain amount of
|
||||||
|
patent controversy about them, but more importantly they don't really
|
||||||
|
belong in the scope of a "minimalist embedded filesystem".
|
||||||
|
|
||||||
|
Improving Performance
|
||||||
|
=====================
|
||||||
|
Read performance is fairly good, but can be improved by implementing read
|
||||||
|
caching on the FAT (see below) and, depending on your hardware platform,
|
||||||
|
possibly by implementing multi-sector reads.
|
||||||
|
|
||||||
|
Write performance may benefit ENORMOUSLY from platform-specific
|
||||||
|
optimization, especially if you are working with a flash media type that
|
||||||
|
has a large erase block size. While it is not possible to offer detailed
|
||||||
|
platform-independent advice, my general advice is to implement writeback
|
||||||
|
caching on the FAT area. One method for doing this would be to have a
|
||||||
|
cache system that lives in the DFS_ReadSector/WriteSector functions (on
|
||||||
|
top of the physical sector r/w functions) and is initially switched off.
|
||||||
|
Once you have called DFS_GetVolInfo, you then extract the VOLINFO.fat1
|
||||||
|
and VOLINFO.rootdir parameters and pass them to your caching layer.
|
||||||
|
Sectors >= fat1 and < rootdir should be cached. The cache strategy is
|
||||||
|
determined by the physical storage medium underlying the filesystem.
|
||||||
|
|
||||||
|
CACHING HINT:
|
||||||
|
Observe that there will be numerous read-modify-write operations in the
|
||||||
|
region from VOLINFO.fat1 through VOLINFO.fat1+VOLINFO.secperfat-1, but
|
||||||
|
in the region from VOLINFO.fat1+VOLINFO.secperfat through VOLINFO.rootdir
|
||||||
|
there will ONLY be write operations.
|
||||||
|
|
||||||
|
Platform Compatibility
|
||||||
|
======================
|
||||||
|
DOSFS was derived from code originally written for ARM7TDMI but
|
||||||
|
designed to be portable. It has been tested on AVR (using avrgcc),
|
||||||
|
MSP430 (using Rowley's CrossWorks) and PPC603e (using gcc); the host
|
||||||
|
test suite has also been validated on x86 using gcc under both Cygwin
|
||||||
|
and 32-bit Fedora Core 4 Linux.
|
||||||
|
|
||||||
|
TODO list
|
||||||
|
=========
|
||||||
|
* Add function to create subdirectory
|
||||||
|
* Make DFS_UnlinkFile recognize non-empty subdirectories
|
||||||
|
* Support "fast write" files where the FAT is not updated, for
|
||||||
|
logging applications where latency is important.
|
||||||
|
|
||||||
|
Test cases for V1.02
|
||||||
|
====================
|
||||||
|
Version 1.02 has NOT been through full regression testing. However the
|
||||||
|
bugs fixed in this version are important, and people have been asking
|
||||||
|
about them.
|
||||||
|
|
||||||
|
Test cases for V1.01
|
||||||
|
====================
|
||||||
|
See below.
|
||||||
|
|
||||||
|
Test cases for V1.00
|
||||||
|
====================
|
||||||
|
These are the test cases that were used to validate the correct
|
||||||
|
functionality of the DOSFS suite. Each test was performed on FAT12,
|
||||||
|
FAT16 and FAT32 volumes. P=Pass, F=Fail.
|
||||||
|
|
||||||
|
Case F12 F16 F32
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
Get volume information P P P
|
||||||
|
Open root directory P P P
|
||||||
|
List contents of root directory (fully populated) P P P
|
||||||
|
Open subdirectory P P P
|
||||||
|
List contents of subdirectory (<= 1 cluster) P P P
|
||||||
|
List contents of large subdirectory (> 1 cluster) P P P
|
||||||
|
Open 5-level nested subdirectory P P P
|
||||||
|
Open existing file for reading P P P
|
||||||
|
Open nonexistent file for reading P P P
|
||||||
|
Seek past EOF, file open for reading P P P
|
||||||
|
Seek to cluster boundary P P P
|
||||||
|
Seek past cluster boundary P P P
|
||||||
|
Seek backwards to nonzero offset, pointer > cluster size P P P
|
||||||
|
Block-read entire file >1 cluster in size, odd size P P P
|
||||||
|
Seek to odd location in file P P P
|
||||||
|
Perform <1 sector reads from random file locations P P P
|
||||||
|
Open nonexistent file for writing in root dir P P P
|
||||||
|
Open nonexistent file for writing in subdir P P P
|
||||||
|
Repeat prev. 2 tests on volume with 0 free clusters P P P
|
||||||
|
Seek past EOF, file open for writing P P P
|
||||||
|
Open existing file for writing in root dir P P P
|
||||||
|
Write random-length records to file, 20 clusters total P P P
|
||||||
|
MS-DOS 6.0 SCANDISK cross-check P P P
|
||||||
|
|
||||||
|
Revision History
|
||||||
|
================
|
||||||
|
Jan-06-2005 larwe Initial release (1.0)
|
||||||
|
Jan-29-2006 larwe Bugfix release (1.01)
|
||||||
|
- Fixed error in FAT12 FAT read on boundary of sector
|
||||||
|
- Improved compilability under avrgcc
|
||||||
|
Sep-16-2006 larwe Bugfix release (1.02)
|
||||||
|
- DFS_Seek would not correctly rewind to start of file
|
||||||
|
- DFS_Seek would not correctly seek to a position not on a cluster
|
||||||
|
boundary
|
||||||
|
- DFS_OpenFile fencepost error caused memory access at [start of
|
||||||
|
string-1] with a local variable
|
||||||
|
- DFS_OpenFile could not open a file in the root directory
|
99
dosfs/tmpstring.c
Normal file
99
dosfs/tmpstring.c
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
void *memcpy(void *restrict dest, const void *restrict src, size_t n)
|
||||||
|
{
|
||||||
|
unsigned char *d = dest;
|
||||||
|
const unsigned char *s = src;
|
||||||
|
for (; n; n--) *d++ = *s++;
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *memset(void *dest, int c, size_t n)
|
||||||
|
{
|
||||||
|
unsigned char *s = dest;
|
||||||
|
size_t k;
|
||||||
|
|
||||||
|
/* Fill head and tail with minimal branching. Each
|
||||||
|
* conditional ensures that all the subsequently used
|
||||||
|
* offsets are well-defined and in the dest region. */
|
||||||
|
|
||||||
|
if (!n) return dest;
|
||||||
|
s[0] = c;
|
||||||
|
s[n-1] = c;
|
||||||
|
if (n <= 2) return dest;
|
||||||
|
s[1] = c;
|
||||||
|
s[2] = c;
|
||||||
|
s[n-2] = c;
|
||||||
|
s[n-3] = c;
|
||||||
|
if (n <= 6) return dest;
|
||||||
|
s[3] = c;
|
||||||
|
s[n-4] = c;
|
||||||
|
if (n <= 8) return dest;
|
||||||
|
|
||||||
|
/* Advance pointer to align it at a 4-byte boundary,
|
||||||
|
* and truncate n to a multiple of 4. The previous code
|
||||||
|
* already took care of any head/tail that get cut off
|
||||||
|
* by the alignment. */
|
||||||
|
|
||||||
|
k = -(uintptr_t)s & 3;
|
||||||
|
s += k;
|
||||||
|
n -= k;
|
||||||
|
n &= -4;
|
||||||
|
|
||||||
|
/* Pure C fallback with no aliasing violations. */
|
||||||
|
for (; n; n--, s++) *s = c;
|
||||||
|
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t strlen(const char *s)
|
||||||
|
{
|
||||||
|
const char *a = s;
|
||||||
|
for (; *s; s++);
|
||||||
|
return s-a;
|
||||||
|
}
|
||||||
|
|
||||||
|
int memcmp(const void *vl, const void *vr, size_t n)
|
||||||
|
{
|
||||||
|
const unsigned char *l=vl, *r=vr;
|
||||||
|
for (; n && *l == *r; n--, l++, r++);
|
||||||
|
return n ? *l-*r : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *strncpy(char *restrict d, const char *restrict s, size_t n)
|
||||||
|
{
|
||||||
|
for (; n && (*d=*s); n--, s++, d++);
|
||||||
|
memset(d, 0, n);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *strcpy(char *restrict dest, const char *restrict src)
|
||||||
|
{
|
||||||
|
char *restrict d = dest;
|
||||||
|
const char *restrict s = src;
|
||||||
|
for (; (*d=*s); s++, d++);
|
||||||
|
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
int strcmp(const char *l, const char *r)
|
||||||
|
{
|
||||||
|
for (; *l==*r && *l; l++, r++);
|
||||||
|
return *(unsigned char *)l - *(unsigned char *)r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* STDLIB DIV FUNCTIONS */
|
||||||
|
typedef struct { int32_t quot, rem; } div_t;
|
||||||
|
typedef struct { int64_t quot, rem; } ldiv_t;
|
||||||
|
div_t div(int num, int den)
|
||||||
|
{
|
||||||
|
return (div_t){ num/den, num%den };
|
||||||
|
}
|
||||||
|
ldiv_t ldiv(long num, long den)
|
||||||
|
{
|
||||||
|
return (ldiv_t){ num/den, num%den };
|
||||||
|
}
|
||||||
|
|
139
entry.nasm
139
entry.nasm
@ -1,143 +1,40 @@
|
|||||||
[BITS 16]
|
|
||||||
global entry
|
global entry
|
||||||
entry:
|
entry:
|
||||||
cli ; no interrupts
|
mov dword [0xb8000], 0x07000700 | 'E' | 'N' << 16
|
||||||
xor ax,ax
|
mov dword [0xb8004], 0x07000700 | 'T' | 'R' << 16
|
||||||
mov ds, ax
|
mov dword [0xb8008], 0x07000700 | 'Y' | ' ' << 16
|
||||||
lgdt [gdt_desc] ; load gdt register
|
lgdt [gdt_desc] ; load gdt register
|
||||||
mov eax, cr0 ; set pmode bit
|
|
||||||
or al, 1
|
|
||||||
mov cr0, eax
|
|
||||||
jmp 08h:Pmodecode
|
jmp 08h:Pmodecode
|
||||||
|
|
||||||
|
extern gdt_desc
|
||||||
|
|
||||||
[BITS 32]
|
[BITS 32]
|
||||||
Pmodecode:
|
Pmodecode:
|
||||||
mov ax, 0x10 ; set up segments
|
mov ax, 0x10 ; set up segments
|
||||||
mov ds, ax
|
mov ds, ax
|
||||||
mov es, ax
|
mov es, ax
|
||||||
mov ss, ax
|
mov ss, ax
|
||||||
mov ebp, 0x400000
|
mov fs, ax
|
||||||
|
mov gs, ax
|
||||||
|
mov ebp, 0x800000
|
||||||
mov esp, ebp
|
mov esp, ebp
|
||||||
mov eax, 0x1f001f00
|
mov eax, 0x1f001f00
|
||||||
mov ecx, (80*25)/2
|
mov ecx, (80*25)/2
|
||||||
mov edi, 0xb8000
|
mov edi, 0xb8000
|
||||||
rep stosd ; clear screen
|
rep stosd ; clear screen
|
||||||
|
mov eax, dword [kernel_check]
|
||||||
|
cmp eax, 0x12345678
|
||||||
|
jne err
|
||||||
call start
|
call start
|
||||||
hlt_loop:
|
hlt_loop:
|
||||||
hlt
|
hlt
|
||||||
jmp hlt_loop
|
jmp hlt_loop
|
||||||
|
err:
|
||||||
|
mov dword [0xb8000], 0x07000700 | 'L' | 'O' << 16
|
||||||
|
mov dword [0xb8004], 0x07000700 | 'A' | 'D' << 16
|
||||||
|
mov dword [0xb8008], 0x07000700 | 'F' | 'A' << 16
|
||||||
|
mov dword [0xb800C], 0x07000700 | 'I' | 'L' << 16
|
||||||
|
jmp hlt_loop
|
||||||
|
|
||||||
extern start
|
extern start
|
||||||
|
extern kernel_check
|
||||||
; currently unused first 8MB identity paging
|
|
||||||
; taken from Linux 0.01
|
|
||||||
setup_paging:
|
|
||||||
mov ecx, 1024*3 ; 3K?
|
|
||||||
xor eax, eax
|
|
||||||
mov edi, 0x1000
|
|
||||||
rep stosd ; zero first 3K for some reason
|
|
||||||
mov edi, 0x4000
|
|
||||||
mov eax, 0x800007 ; 8MB + 7
|
|
||||||
std ; fill backwards
|
|
||||||
.fill:
|
|
||||||
stosd
|
|
||||||
sub eax, 0x1000
|
|
||||||
jge .fill
|
|
||||||
cld ; fix direction
|
|
||||||
mov dword [0x0000], 0x2000 + 7
|
|
||||||
mov dword [0x0004], 0x3000 + 7
|
|
||||||
mov eax, 0x1000
|
|
||||||
mov cr3, eax ; page dir start 0x1000
|
|
||||||
mov eax, cr0
|
|
||||||
or eax, 0x80000000
|
|
||||||
mov cr0, eax ; set paging bit
|
|
||||||
ret ; flushes pre-fetch queue
|
|
||||||
|
|
||||||
user_test:
|
|
||||||
mov dword [0xb8000], 0x0f000f00 | 'U' | 's' << 16
|
|
||||||
mov dword [0xb8004], 0x0f000f00 | 'e' | 'r' << 16
|
|
||||||
mov dword [0xb8008], 0x0f000f00 | 'm' | 'o' << 16
|
|
||||||
mov dword [0xb800C], 0x0f000f00 | 'd' | 'e' << 16
|
|
||||||
mov word [0xb8010], 0x0f00 | '!'
|
|
||||||
mov edi, 0xA0000
|
|
||||||
xor eax, eax
|
|
||||||
.loop:
|
|
||||||
mov ecx, 320
|
|
||||||
rep stosb
|
|
||||||
inc al
|
|
||||||
cmp eax, 200
|
|
||||||
jl .loop
|
|
||||||
mov eax, 0xA0000
|
|
||||||
int 0x30 ; Exit
|
|
||||||
xor ebx, ebx
|
|
||||||
div bl ; Unhandled DIV0 exception
|
|
||||||
|
|
||||||
global jmp_usermode_test
|
|
||||||
jmp_usermode_test:
|
|
||||||
pop eax ; return address
|
|
||||||
mov ebp, esp ; return stack
|
|
||||||
push ss
|
|
||||||
push ebp
|
|
||||||
pushfd
|
|
||||||
push cs
|
|
||||||
push eax ; return address
|
|
||||||
push ds ; other segs, pop
|
|
||||||
push es ; before iret
|
|
||||||
push fs ; in exit handler
|
|
||||||
push gs
|
|
||||||
mov dword [0x20004], esp ; tss ESP0
|
|
||||||
mov ax, 0x20 | 3
|
|
||||||
mov ds, ax
|
|
||||||
mov es, ax
|
|
||||||
mov fs, ax
|
|
||||||
mov gs, ax
|
|
||||||
mov eax, esp
|
|
||||||
push 0x20 | 3
|
|
||||||
push eax
|
|
||||||
pushfd
|
|
||||||
push 0x18 | 3
|
|
||||||
push user_test
|
|
||||||
iret
|
|
||||||
|
|
||||||
global flushTSS
|
|
||||||
flushTSS:
|
|
||||||
mov ax, 0x28
|
|
||||||
ltr ax
|
|
||||||
ret
|
|
||||||
|
|
||||||
extern tss_data
|
|
||||||
|
|
||||||
global ivt
|
|
||||||
ivt: dd 0x00000000
|
|
||||||
|
|
||||||
gdt_desc:
|
|
||||||
dw gdt_end - gdt
|
|
||||||
dd gdt
|
|
||||||
gdt:
|
|
||||||
gdt_null: dq 0
|
|
||||||
gdt_code: dw 0xFFFF, 0 ; bits 0-15 limit (4GB), bits 0-15 base address
|
|
||||||
db 0 ; bits 16-23 base address
|
|
||||||
db 10011010b ; access byte
|
|
||||||
db 11001111b ; bits 16-19 limit (4GB), 4 bits flags
|
|
||||||
db 0 ; bits 24-31 base address
|
|
||||||
gdt_data: dw 0xFFFF, 0 ; bits 0-15 limit (4GB), bits 0-15 base address
|
|
||||||
db 0 ; bits 16-23 base address
|
|
||||||
db 10010010b ; access byte
|
|
||||||
db 11001111b ; bits 16-19 limit (4GB), 4 bits flags
|
|
||||||
db 0 ; bits 24-31 base address
|
|
||||||
gdt_r3code: dw 0xFFFF, 0 ; bits 0-15 limit (4GB), bits 0-15 base address
|
|
||||||
db 0 ; bits 16-23 base address
|
|
||||||
db 0xFA ; access byte
|
|
||||||
db 11001111b ; bits 16-19 limit (4GB), 4 bits flags
|
|
||||||
db 0 ; bits 24-31 base address
|
|
||||||
gdt_r3data: dw 0xFFFF, 0 ; bits 0-15 limit (4GB), bits 0-15 base address
|
|
||||||
db 0 ; bits 16-23 base address
|
|
||||||
db 0xF2 ; access byte
|
|
||||||
db 11001111b ; bits 16-19 limit (4GB), 4 bits flags
|
|
||||||
db 0 ; bits 24-31 base address
|
|
||||||
gdt_tss: dw 0x2080, 0x0000;26*4, tss_data ; bits 0-15 limit (4GB), bits 0-15 base address
|
|
||||||
db 0x2 ; bits 16-23 base address
|
|
||||||
db 0x89 ; access byte
|
|
||||||
db 00000000b ; bits 16-19 limit (4GB), 4 bits flags
|
|
||||||
db 0 ; bits 24-31 base address
|
|
||||||
gdt_end:
|
|
||||||
|
275
fault.nasm
Normal file
275
fault.nasm
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
extern error_screen ; 80x50 words
|
||||||
|
|
||||||
|
; Switches to text mode and shit
|
||||||
|
extern error_environment
|
||||||
|
_fault_coda:
|
||||||
|
xchg bx,bx
|
||||||
|
mov ax, 0x10
|
||||||
|
mov es, ax
|
||||||
|
; move to 'safe' location
|
||||||
|
mov ebp, 0x318000
|
||||||
|
; copy stack a bit
|
||||||
|
mov ecx, 0
|
||||||
|
.copy:
|
||||||
|
mov eax, [esp+ecx*4]
|
||||||
|
mov [ebp+ecx*4], eax
|
||||||
|
inc ecx
|
||||||
|
cmp ecx, 6
|
||||||
|
jl .copy
|
||||||
|
mov esp, ebp
|
||||||
|
call error_environment
|
||||||
|
.hlt:
|
||||||
|
hlt
|
||||||
|
jmp .hlt
|
||||||
|
|
||||||
|
global _gpf_eax_save
|
||||||
|
_gpf_eax_save: dd 0
|
||||||
|
global _gpf_eflags_save
|
||||||
|
_gpf_eflags_save: dd 0
|
||||||
|
extern gpf_handler_v86
|
||||||
|
global gpfHandler
|
||||||
|
gpfHandler:
|
||||||
|
cli ; make sure we're in a 'friendly' env
|
||||||
|
push eax
|
||||||
|
push ebx
|
||||||
|
push ecx
|
||||||
|
; save old ds
|
||||||
|
mov bx, ds
|
||||||
|
mov ax, 0x10
|
||||||
|
mov ds, ax
|
||||||
|
mov word [_gpf_old_ds], bx
|
||||||
|
; relocate stack so other interrupts don't fuck us over
|
||||||
|
; not sure if this is necessary, it doesn't seem to fix our race conditions...
|
||||||
|
mov ebx, esp
|
||||||
|
sub esp, 0x1000
|
||||||
|
xor ecx, ecx
|
||||||
|
.l:
|
||||||
|
mov eax, [ebx]
|
||||||
|
mov [esp+ecx], eax
|
||||||
|
add ebx, 4
|
||||||
|
add ecx, 4
|
||||||
|
cmp ebx, 0x320000 ; tss esp0
|
||||||
|
jl .l
|
||||||
|
pop ecx
|
||||||
|
pop ebx
|
||||||
|
sti ; we shouldn't crash now?
|
||||||
|
mov eax, dword [esp+16] ; EFLAGS
|
||||||
|
mov dword [_gpf_eflags_save], eax ; save
|
||||||
|
and eax, 1 << 17 ; VM flag
|
||||||
|
test eax, eax
|
||||||
|
pop eax
|
||||||
|
mov dword [_gpf_eax_save], eax
|
||||||
|
jnz gpf_handler_v86
|
||||||
|
jmp gpf_handler_32
|
||||||
|
gpf_unhandled:
|
||||||
|
mov dword [error_screen+0x00], 0x0f000f00 | 'G' | 'P' << 16
|
||||||
|
mov dword [error_screen+0x04], 0x0f000f00 | 'F' | '!' << 16
|
||||||
|
xchg bx,bx
|
||||||
|
jmp _fault_coda
|
||||||
|
|
||||||
|
_gpf_old_ds: dw 0
|
||||||
|
extern get_key
|
||||||
|
extern get_scancode
|
||||||
|
extern task_ptr
|
||||||
|
extern _enter_v86_internal_no_task
|
||||||
|
extern return_prev_task
|
||||||
|
extern v86Interrupt
|
||||||
|
gpf_handler_32:
|
||||||
|
push eax
|
||||||
|
mov eax, dword [esp+8] ; EIP
|
||||||
|
movzx eax, word [eax]
|
||||||
|
cmp eax, 0x30CD ; int 0x30
|
||||||
|
je .int30
|
||||||
|
cmp eax, 0x21CD ; int 0x21
|
||||||
|
je .int21
|
||||||
|
jmp gpf_unhandled
|
||||||
|
|
||||||
|
.int21:
|
||||||
|
pop eax ; command
|
||||||
|
cmp al, 0x00 ; get key
|
||||||
|
jne .s1
|
||||||
|
call get_key
|
||||||
|
jmp .return_to_offender
|
||||||
|
.s1: cmp al, 0x01 ; get scancode
|
||||||
|
jne .s86
|
||||||
|
call get_scancode
|
||||||
|
jmp .return_to_offender
|
||||||
|
.s86: cmp al, 0x86 ; v86 interrupt call
|
||||||
|
je .v86int
|
||||||
|
cmp ax, 0x86D8 ; get v86 data pointer
|
||||||
|
jne .return_to_offender
|
||||||
|
mov eax, 0x30000 ; V86 Data
|
||||||
|
jmp .return_to_offender
|
||||||
|
|
||||||
|
.v86int:
|
||||||
|
add esp, 4
|
||||||
|
add dword [esp+0], 2
|
||||||
|
; add a new task
|
||||||
|
call _gpf_create_return_task
|
||||||
|
; now enter v86 mode
|
||||||
|
; get int & regs from return stack
|
||||||
|
mov eax, [esp+12] ; return esp
|
||||||
|
mov eax, [eax] ; interrupt
|
||||||
|
and eax, 0xff ; ensure 1 byte
|
||||||
|
shl eax, 8
|
||||||
|
or eax, 0x30CD00CD ; command
|
||||||
|
mov dword [v86Interrupt], eax
|
||||||
|
mov eax, [esp+12] ; return esp
|
||||||
|
mov eax, [eax+4] ; regs
|
||||||
|
push eax ; regs
|
||||||
|
mov eax, v86Interrupt
|
||||||
|
and eax, 0xffff
|
||||||
|
push eax ; ip
|
||||||
|
mov eax, v86Interrupt
|
||||||
|
shr eax, 4
|
||||||
|
and eax, 0xf000
|
||||||
|
push eax ; cs
|
||||||
|
push 0xFF00 ; sp
|
||||||
|
push 0x8000 ; ss
|
||||||
|
jmp _enter_v86_internal_no_task ; NOT a function call
|
||||||
|
|
||||||
|
.int30:
|
||||||
|
pop eax ; return value
|
||||||
|
jmp return_prev_task
|
||||||
|
.return_to_offender:
|
||||||
|
add dword [esp+4], 2
|
||||||
|
push eax
|
||||||
|
mov ax, word [_gpf_old_ds]
|
||||||
|
mov ds, ax
|
||||||
|
pop eax
|
||||||
|
add esp, 4 ; error code
|
||||||
|
iret
|
||||||
|
|
||||||
|
_gpf_create_return_task:
|
||||||
|
; handler stack stored in edx
|
||||||
|
mov edx, esp
|
||||||
|
mov esp, dword [task_ptr]
|
||||||
|
mov eax, [edx+20] ; ss
|
||||||
|
push eax
|
||||||
|
mov eax, [edx+16] ; esp
|
||||||
|
push eax
|
||||||
|
mov eax, [edx+12] ; eflags
|
||||||
|
push eax
|
||||||
|
mov eax, [edx+8] ; cs
|
||||||
|
push eax
|
||||||
|
mov eax, [edx+4] ; eip
|
||||||
|
push eax
|
||||||
|
mov ax, word [_gpf_old_ds] ; restore old ds
|
||||||
|
mov ds, ax
|
||||||
|
push ds
|
||||||
|
push es
|
||||||
|
push fs
|
||||||
|
push gs
|
||||||
|
push ebp
|
||||||
|
push ebx
|
||||||
|
push esi
|
||||||
|
push edi
|
||||||
|
mov ax, 0x10
|
||||||
|
mov ds, ax
|
||||||
|
mov dword [task_ptr], esp ; save new task pointer
|
||||||
|
mov esp, edx ; restore handler stack
|
||||||
|
ret
|
||||||
|
|
||||||
|
global unhandled_handler
|
||||||
|
unhandled_handler:
|
||||||
|
mov ax, 0x10
|
||||||
|
mov ds, ax
|
||||||
|
mov dword [error_screen+0x00], 0x0f000f00 | 'E' | 'R' << 16
|
||||||
|
mov dword [error_screen+0x04], 0x0f000f00 | 'R' | 'O' << 16
|
||||||
|
mov dword [error_screen+0x08], 0x0f000f00 | 'R' | '!' << 16
|
||||||
|
jmp _fault_coda
|
||||||
|
|
||||||
|
global pageFaultHandler
|
||||||
|
pageFaultHandler:
|
||||||
|
mov ax, 0x10
|
||||||
|
mov ds, ax
|
||||||
|
pop eax ; error code
|
||||||
|
mov ebx, 0x0f000f00 | '0' | '!' << 16
|
||||||
|
and eax, 0x7 ; U/S,R/W,P
|
||||||
|
add ebx, eax
|
||||||
|
mov dword [error_screen+0x00], 0x0f000f00 | 'P' | 'G' << 16
|
||||||
|
mov dword [error_screen+0x04], 0x0f000f00 | 'F' | 'L' << 16
|
||||||
|
mov dword [error_screen+0x08], 0x0f000f00 | 'T' | ':' << 16
|
||||||
|
mov dword [error_screen+0x0C], ebx
|
||||||
|
jmp _fault_coda
|
||||||
|
|
||||||
|
|
||||||
|
global divisionErrorHandler
|
||||||
|
divisionErrorHandler:
|
||||||
|
mov ax, 0x10
|
||||||
|
mov ds, ax
|
||||||
|
mov dword [error_screen+0x00], 0x0f000f00 | '#' | 'D' << 16
|
||||||
|
mov dword [error_screen+0x04], 0x0f000f00 | 'E' | '!' << 16
|
||||||
|
jmp _fault_coda
|
||||||
|
global boundRangeHandler
|
||||||
|
boundRangeHandler:
|
||||||
|
mov ax, 0x10
|
||||||
|
mov ds, ax
|
||||||
|
mov dword [error_screen+0x00], 0x0f000f00 | '#' | 'B' << 16
|
||||||
|
mov dword [error_screen+0x04], 0x0f000f00 | 'R' | '!' << 16
|
||||||
|
jmp _fault_coda
|
||||||
|
global invalidOpcodeHandler
|
||||||
|
invalidOpcodeHandler:
|
||||||
|
mov ax, 0x10
|
||||||
|
mov ds, ax
|
||||||
|
mov dword [error_screen+0x00], 0x0f000f00 | '#' | 'U' << 16
|
||||||
|
mov dword [error_screen+0x04], 0x0f000f00 | 'D' | '!' << 16
|
||||||
|
jmp _fault_coda
|
||||||
|
global deviceNotAvailableHandler
|
||||||
|
deviceNotAvailableHandler:
|
||||||
|
mov ax, 0x10
|
||||||
|
mov ds, ax
|
||||||
|
mov dword [error_screen+0x00], 0x0f000f00 | '#' | 'N' << 16
|
||||||
|
mov dword [error_screen+0x04], 0x0f000f00 | 'D' | '!' << 16
|
||||||
|
jmp _fault_coda
|
||||||
|
global doubleFaultHandler
|
||||||
|
doubleFaultHandler:
|
||||||
|
mov ax, 0x10
|
||||||
|
mov ds, ax
|
||||||
|
mov dword [0xb8000+0x00], 0x0f000f00 | '#' | 'D' << 16
|
||||||
|
mov dword [0xb8000+0x04], 0x0f000f00 | 'F' | '!' << 16
|
||||||
|
; double faults simply abort right then
|
||||||
|
.hlt: hlt
|
||||||
|
jmp .hlt
|
||||||
|
global invalidTSSHandler
|
||||||
|
invalidTSSHandler:
|
||||||
|
mov ax, 0x10
|
||||||
|
mov ds, ax
|
||||||
|
mov dword [error_screen+0x00], 0x0f000f00 | '#' | 'T' << 16
|
||||||
|
mov dword [error_screen+0x04], 0x0f000f00 | 'S' | '!' << 16
|
||||||
|
jmp _fault_coda
|
||||||
|
global segmentNotPresentHandler
|
||||||
|
segmentNotPresentHandler:
|
||||||
|
mov ax, 0x10
|
||||||
|
mov ds, ax
|
||||||
|
mov dword [error_screen+0x00], 0x0f000f00 | '#' | 'N' << 16
|
||||||
|
mov dword [error_screen+0x04], 0x0f000f00 | 'P' | '!' << 16
|
||||||
|
jmp _fault_coda
|
||||||
|
global stackSegmentHandler
|
||||||
|
stackSegmentHandler:
|
||||||
|
mov ax, 0x10
|
||||||
|
mov ds, ax
|
||||||
|
mov dword [error_screen+0x00], 0x0f000f00 | '#' | 'S' << 16
|
||||||
|
mov dword [error_screen+0x04], 0x0f000f00 | 'S' | '!' << 16
|
||||||
|
jmp _fault_coda
|
||||||
|
global x87FloatingHandler
|
||||||
|
x87FloatingHandler:
|
||||||
|
mov ax, 0x10
|
||||||
|
mov ds, ax
|
||||||
|
mov dword [error_screen+0x00], 0x0f000f00 | '#' | 'M' << 16
|
||||||
|
mov dword [error_screen+0x04], 0x0f000f00 | 'F' | '!' << 16
|
||||||
|
jmp _fault_coda
|
||||||
|
global alignmentCheckHandler
|
||||||
|
alignmentCheckHandler:
|
||||||
|
mov ax, 0x10
|
||||||
|
mov ds, ax
|
||||||
|
mov dword [error_screen+0x00], 0x0f000f00 | '#' | 'A' << 16
|
||||||
|
mov dword [error_screen+0x04], 0x0f000f00 | 'C' | '!' << 16
|
||||||
|
jmp _fault_coda
|
||||||
|
global controlProtectionHandler
|
||||||
|
controlProtectionHandler:
|
||||||
|
mov ax, 0x10
|
||||||
|
mov ds, ax
|
||||||
|
mov dword [error_screen+0x00], 0x0f000f00 | '#' | 'C' << 16
|
||||||
|
mov dword [error_screen+0x04], 0x0f000f00 | 'P' | '!' << 16
|
||||||
|
jmp _fault_coda
|
113
file.c
Normal file
113
file.c
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
#include "file.h"
|
||||||
|
#include "fs.h"
|
||||||
|
|
||||||
|
// TODO Replace with something better
|
||||||
|
extern uint8_t ActiveFsId;
|
||||||
|
|
||||||
|
// Sets adjusted_path to the path without filesystem info,
|
||||||
|
// returns the filesystem calculated
|
||||||
|
uint8_t GetFsFromPath(char *path, char **adjusted_path) {
|
||||||
|
// On active filsystem
|
||||||
|
if (*path == '/') {
|
||||||
|
*adjusted_path = path;
|
||||||
|
return GetActiveFilesystemId();
|
||||||
|
}
|
||||||
|
// Read the filesystem ID
|
||||||
|
uint8_t id = 0;
|
||||||
|
char *tmp_path = path;
|
||||||
|
// Find first /
|
||||||
|
for (;*tmp_path != '/';tmp_path++);
|
||||||
|
*adjusted_path = tmp_path;
|
||||||
|
// Read octal num
|
||||||
|
tmp_path--;
|
||||||
|
for (uint8_t bit_off = 0; tmp_path >= path; tmp_path--, bit_off+=3) {
|
||||||
|
// Outside of octal range, error
|
||||||
|
if (*tmp_path < '0' || *tmp_path > '8') return 0;
|
||||||
|
id += (*tmp_path - '0') << bit_off;
|
||||||
|
|
||||||
|
}
|
||||||
|
// Return filesystem
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns 0 on success, non-zero error code on error. Fills provided struct FILE
|
||||||
|
int file_open(FILE *file, char *path, char mode) {
|
||||||
|
char *adj_path;
|
||||||
|
uint8_t fsid = GetFsFromPath(path, &adj_path);
|
||||||
|
filesystem *fs = GetFilesystem(fsid);
|
||||||
|
int err = fs->ops.file_open(fs->fs_data, file, adj_path, mode);
|
||||||
|
file->filesystem_id = fsid;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns 0 on success, non-zero error code on error.
|
||||||
|
int file_seek(FILE *file, uint32_t offset) {
|
||||||
|
filesystem *fs = GetFilesystem(file->filesystem_id);
|
||||||
|
return fs->ops.file_seek(fs->fs_data, file, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns 0 on error, bytes read on success.
|
||||||
|
int file_read(FILE *file, uint8_t *dest, uint32_t len) {
|
||||||
|
filesystem *fs = GetFilesystem(file->filesystem_id);
|
||||||
|
return fs->ops.file_read(fs->fs_data, file, dest, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns 0 on error, bytes written on success.
|
||||||
|
int file_write(FILE *file, uint8_t *src, uint32_t len) {
|
||||||
|
filesystem *fs = GetFilesystem(file->filesystem_id);
|
||||||
|
return fs->ops.file_write(fs->fs_data, file, src, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void file_close(FILE *file) {
|
||||||
|
filesystem *fs = GetFilesystem(file->filesystem_id);
|
||||||
|
return fs->ops.file_close(fs->fs_data, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns 0 on success, non-zero error code on error. Fills provided struct DIR
|
||||||
|
int dir_open(DIR *dir, char *path) {
|
||||||
|
char *adj_path;
|
||||||
|
uint8_t fsid = GetFsFromPath(path, &adj_path);
|
||||||
|
filesystem *fs = GetFilesystem(fsid);
|
||||||
|
int err = fs->ops.dir_open(fs->fs_data, dir, adj_path);
|
||||||
|
dir->filesystem_id = fsid;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return 0 on success, non-zero error code on error. Fills provided struct dirent.
|
||||||
|
int dir_nextentry(DIR *dir, dirent *ent) {
|
||||||
|
filesystem *fs = GetFilesystem(dir->filesystem_id);
|
||||||
|
return fs->ops.dir_nextentry(fs->fs_data, dir, ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dir_close(DIR *dir) {
|
||||||
|
filesystem *fs = GetFilesystem(dir->filesystem_id);
|
||||||
|
return fs->ops.dir_close(fs->fs_data, dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns 0 on success, non-zero error code on error. Fills provided struct dirent.
|
||||||
|
int path_getinfo(char *path, dirent *ent) {
|
||||||
|
char *adj_path;
|
||||||
|
filesystem *fs = GetFilesystem(GetFsFromPath(path, &adj_path));
|
||||||
|
return fs->ops.path_getinfo(fs->fs_data, adj_path, ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns 0 on success, non-zero error code on error.
|
||||||
|
int path_mkdir(char *path) {
|
||||||
|
char *adj_path;
|
||||||
|
filesystem *fs = GetFilesystem(GetFsFromPath(path, &adj_path));
|
||||||
|
return fs->ops.path_mkdir(fs->fs_data, adj_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns 0 on success, non-zero error code on error.
|
||||||
|
int path_rmdir(char *path) {
|
||||||
|
char *adj_path;
|
||||||
|
filesystem *fs = GetFilesystem(GetFsFromPath(path, &adj_path));
|
||||||
|
return fs->ops.path_rmdir(fs->fs_data, adj_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns 0 on success, non-zero error code on error.
|
||||||
|
int path_rmfile(char *path) {
|
||||||
|
char *adj_path;
|
||||||
|
filesystem *fs = GetFilesystem(GetFsFromPath(path, &adj_path));
|
||||||
|
return fs->ops.path_rmfile(fs->fs_data, adj_path);
|
||||||
|
}
|
36
file.h
Normal file
36
file.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "file_s.h"
|
||||||
|
|
||||||
|
// Returns 0 on success, non-zero error code on error. Fills provided struct FILE
|
||||||
|
int file_open(FILE *file, char *path, char mode);
|
||||||
|
|
||||||
|
// Returns 0 on success, non-zero error code on error.
|
||||||
|
int file_seek(FILE *file, uint32_t offset);
|
||||||
|
|
||||||
|
// Returns 0 on error, bytes read on success.
|
||||||
|
int file_read(FILE *file, uint8_t *dest, uint32_t len);
|
||||||
|
|
||||||
|
// Returns 0 on error, bytes written on success.
|
||||||
|
int file_write(FILE *file, uint8_t *src, uint32_t len);
|
||||||
|
|
||||||
|
void file_close(FILE *file);
|
||||||
|
|
||||||
|
// Returns 0 on success, non-zero error code on error. Fills provided struct DIR
|
||||||
|
int dir_open(DIR *dir, char *path);
|
||||||
|
|
||||||
|
// Return 0 on success, non-zero error code on error. Fills provided struct dirent.
|
||||||
|
int dir_nextentry(DIR *dir, dirent *ent);
|
||||||
|
|
||||||
|
void dir_close(DIR *dir);
|
||||||
|
|
||||||
|
// Returns 0 on success, non-zero error code on error. Fills provided struct dirent.
|
||||||
|
int path_getinfo(char *path, dirent *ent);
|
||||||
|
|
||||||
|
// Returns 0 on success, non-zero error code on error.
|
||||||
|
int path_mkdir(char *path);
|
||||||
|
|
||||||
|
// Returns 0 on success, non-zero error code on error.
|
||||||
|
int path_rmdir(char *path);
|
||||||
|
|
||||||
|
// Returns 0 on success, non-zero error code on error.
|
||||||
|
int path_rmfile(char *path);
|
31
file_s.h
Normal file
31
file_s.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct FILE {
|
||||||
|
uint8_t filesystem_id;
|
||||||
|
uint8_t bytes[0x3F];
|
||||||
|
} __attribute__((__packed__)) FILE;
|
||||||
|
|
||||||
|
typedef struct DIR {
|
||||||
|
uint8_t filesystem_id;
|
||||||
|
uint8_t bytes[0x3F];
|
||||||
|
} __attribute__((__packed__)) DIR;
|
||||||
|
|
||||||
|
typedef enum filetype {
|
||||||
|
FT_UNKNOWN,
|
||||||
|
FT_REG,
|
||||||
|
FT_DIR
|
||||||
|
} filetype;
|
||||||
|
|
||||||
|
typedef struct dirent {
|
||||||
|
filetype type;
|
||||||
|
uint32_t size;
|
||||||
|
uint32_t last_modified;
|
||||||
|
uint32_t last_accessed;
|
||||||
|
uint32_t created;
|
||||||
|
uint8_t namelen;
|
||||||
|
char name[255];
|
||||||
|
} dirent;
|
||||||
|
|
||||||
|
#define OPENREAD 1
|
||||||
|
#define OPENWRITE 2
|
130
fs.c
Normal file
130
fs.c
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
#include "fs.h"
|
||||||
|
#include "disk.h"
|
||||||
|
|
||||||
|
struct FsType {
|
||||||
|
uint32_t type_id;
|
||||||
|
int (*init_func)(filesystem *, uint32_t);
|
||||||
|
// Not yet decided
|
||||||
|
char (*detect_func)(uint32_t);
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO Get these dynamically somehow
|
||||||
|
int InitDosFs(filesystem *fs, uint32_t start_sector);
|
||||||
|
char DetectDosPart(uint32_t start_sector);
|
||||||
|
|
||||||
|
struct FsType SupportedFilesystems[] = {
|
||||||
|
{
|
||||||
|
.type_id = 0xD05,
|
||||||
|
.init_func = InitDosFs,
|
||||||
|
.detect_func = DetectDosPart
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MAXFS 255
|
||||||
|
filesystem *ActiveFilesystems = (filesystem *)0x240000;
|
||||||
|
|
||||||
|
uint8_t ActiveFsIdx;
|
||||||
|
|
||||||
|
filesystem *GetFilesystem(uint8_t idx) {
|
||||||
|
if (idx >= MAXFS) return 0;
|
||||||
|
filesystem *fs = &ActiveFilesystems[idx];
|
||||||
|
return fs->type != 0 ? fs : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
filesystem *GetActiveFilesystem() {
|
||||||
|
return &ActiveFilesystems[ActiveFsIdx];
|
||||||
|
}
|
||||||
|
uint8_t GetActiveFilesystemId() {
|
||||||
|
return ActiveFsIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
filesystem *SetActiveFilesystem(uint8_t idx) {
|
||||||
|
if (idx >= MAXFS) return 0;
|
||||||
|
filesystem *fs = &ActiveFilesystems[idx];
|
||||||
|
if (fs->type == 0) return 0;
|
||||||
|
ActiveFsIdx = idx;
|
||||||
|
return fs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActiveFilesystemBitmap(char *bitmap) {
|
||||||
|
for (int i = 0; i < 256; i++)
|
||||||
|
bitmap[i] = ActiveFilesystems[i].type != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PartTableEntry_t {
|
||||||
|
uint8_t attr;
|
||||||
|
uint8_t start_chs0;
|
||||||
|
uint8_t start_chs1;
|
||||||
|
uint8_t start_chs2;
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t end_chs0;
|
||||||
|
uint8_t end_chs1;
|
||||||
|
uint8_t end_chs2;
|
||||||
|
uint32_t start_lba;
|
||||||
|
uint32_t num_sectors;
|
||||||
|
};
|
||||||
|
typedef struct MBR_t {
|
||||||
|
char boot_code[0x1B8];
|
||||||
|
uint32_t signature;
|
||||||
|
uint16_t reserved;
|
||||||
|
struct PartTableEntry_t partTable[4];
|
||||||
|
uint16_t boot_sig;
|
||||||
|
} __attribute__((__packed__)) MBR_t;
|
||||||
|
|
||||||
|
// TODO Just check for this
|
||||||
|
uint8_t BootPartition;
|
||||||
|
uint8_t NextAvailableFilesystem;
|
||||||
|
|
||||||
|
MBR_t SysMbr;
|
||||||
|
|
||||||
|
void MakeMBRPartitions() {
|
||||||
|
// Get MBR
|
||||||
|
MBR_t *mbr = &SysMbr;
|
||||||
|
Disk_ReadSector(0, (uint8_t*)mbr, 0, 1);
|
||||||
|
|
||||||
|
// Scan partitions
|
||||||
|
for (int p = 0; p < 4; p++) {
|
||||||
|
if (p == BootPartition) continue;
|
||||||
|
if (mbr->partTable[p].type == 0) continue;
|
||||||
|
uint32_t partStart = mbr->partTable[p].start_lba;
|
||||||
|
if (partStart == 0) continue;
|
||||||
|
// Scan supported filesystems
|
||||||
|
filesystem *sys = &ActiveFilesystems[NextAvailableFilesystem];
|
||||||
|
for (int i = 0; i < sizeof(SupportedFilesystems)/sizeof(struct FsType); i++) {
|
||||||
|
if (!SupportedFilesystems[i].detect_func(partStart)) continue;
|
||||||
|
SupportedFilesystems[i].init_func(sys, partStart);
|
||||||
|
sys->type = SupportedFilesystems[i].type_id;
|
||||||
|
ActiveFsIdx = NextAvailableFilesystem;
|
||||||
|
NextAvailableFilesystem++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int MakeSystemVolume(uint8_t bootPartition) {
|
||||||
|
// Clear out filesystem area
|
||||||
|
for (int i = 0; i < (sizeof(filesystem)*MAXFS)/sizeof(uint32_t);i++)
|
||||||
|
((uint32_t*)ActiveFilesystems)[i] = 0;
|
||||||
|
|
||||||
|
BootPartition = bootPartition;
|
||||||
|
NextAvailableFilesystem = 1;
|
||||||
|
// Get MBR
|
||||||
|
MBR_t *mbr = &SysMbr;
|
||||||
|
Disk_ReadSector(0, (uint8_t*)mbr, 0, 1);
|
||||||
|
// Get boot partition sector
|
||||||
|
uint32_t sys_sector = mbr->partTable[bootPartition].start_lba;
|
||||||
|
|
||||||
|
// Scan supported filesystems
|
||||||
|
filesystem *sys = &ActiveFilesystems[0];
|
||||||
|
for (int i = 0; i < sizeof(SupportedFilesystems)/sizeof(struct FsType); i++) {
|
||||||
|
asm volatile("xchg %bx,%bx");
|
||||||
|
if (!SupportedFilesystems[i].detect_func(sys_sector)) continue;
|
||||||
|
SupportedFilesystems[i].init_func(sys, sys_sector);
|
||||||
|
sys->type = SupportedFilesystems[i].type_id;
|
||||||
|
ActiveFsIdx = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init Failed
|
||||||
|
return -1;
|
||||||
|
}
|
32
fs.h
Normal file
32
fs.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "file_s.h"
|
||||||
|
|
||||||
|
typedef struct filesystem {
|
||||||
|
uint32_t resv0;
|
||||||
|
uint32_t type;
|
||||||
|
struct fs_operations {
|
||||||
|
int (*file_open)(uint8_t *, FILE *, char *, char);
|
||||||
|
int (*file_seek)(uint8_t *, FILE *, uint32_t);
|
||||||
|
int (*file_read)(uint8_t *, FILE *, uint8_t *, uint32_t);
|
||||||
|
int (*file_write)(uint8_t *, FILE *, uint8_t *, uint32_t);
|
||||||
|
void (*file_close)(uint8_t *, FILE *);
|
||||||
|
int (*dir_open)(uint8_t *, DIR *, char *);
|
||||||
|
int (*dir_nextentry)(uint8_t *, DIR *, dirent *);
|
||||||
|
void (*dir_close)(uint8_t *, DIR *);
|
||||||
|
int (*path_getinfo)(uint8_t *, char *, dirent *);
|
||||||
|
int (*path_mkdir)(uint8_t *, char *);
|
||||||
|
int (*path_rmdir)(uint8_t *, char *);
|
||||||
|
int (*path_rmfile)(uint8_t *, char *);
|
||||||
|
void (*endfs)(uint8_t *);
|
||||||
|
} ops;
|
||||||
|
uint8_t labellen;
|
||||||
|
char label[255];
|
||||||
|
uint8_t fs_data[2048-4-4-44-256];
|
||||||
|
} __attribute__((packed)) filesystem;
|
||||||
|
|
||||||
|
filesystem *GetFilesystem(uint8_t idx);
|
||||||
|
filesystem *GetActiveFilesystem();
|
||||||
|
uint8_t GetActiveFilesystemId();
|
||||||
|
filesystem *SetActiveFilesystem(uint8_t idx);
|
||||||
|
void ActiveFilesystemBitmap(char *bitmap);
|
239
fs_dos.c
Normal file
239
fs_dos.c
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
#include "disk.h"
|
||||||
|
#include "fs.h"
|
||||||
|
#include "dosfs/dosfs.h"
|
||||||
|
|
||||||
|
char *strncpy(char *restrict d, const char *restrict s, uintptr_t n);
|
||||||
|
int strcmp(const char *l, const char *r);
|
||||||
|
void *memcpy(void *restrict dest, const void *restrict src, uintptr_t n);
|
||||||
|
|
||||||
|
// Implementations for DOSFS
|
||||||
|
|
||||||
|
uint32_t DFS_ReadSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count) {
|
||||||
|
return Disk_ReadSector(unit, buffer, sector, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t DFS_WriteSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count) {
|
||||||
|
return Disk_WriteSector(unit, buffer, sector, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// System Implementations
|
||||||
|
|
||||||
|
typedef struct fsdat {
|
||||||
|
VOLINFO vi;
|
||||||
|
} fsdat;
|
||||||
|
|
||||||
|
int file83ToPath(uint8_t *src, char *path) {
|
||||||
|
uint8_t tmp, trailingSpace;
|
||||||
|
for (trailingSpace=0, tmp = 0; tmp < 8 && src[tmp]; tmp++) {
|
||||||
|
path[tmp] = src[tmp];
|
||||||
|
if (src[tmp] == ' ') trailingSpace++;
|
||||||
|
else trailingSpace = 0;
|
||||||
|
}
|
||||||
|
tmp -= trailingSpace;
|
||||||
|
path[tmp++] = '.';
|
||||||
|
trailingSpace = 0;
|
||||||
|
for (int i = 8; i < 11 && src[i]; i++, tmp++) {
|
||||||
|
path[tmp] = src[i];
|
||||||
|
if (src[i] == ' ') trailingSpace++;
|
||||||
|
else trailingSpace = 0;
|
||||||
|
}
|
||||||
|
tmp -= trailingSpace;
|
||||||
|
if (trailingSpace == 3) tmp--;
|
||||||
|
path[tmp] = 0;
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t stripToDir(char *path) {
|
||||||
|
int i = 0;
|
||||||
|
// find end of string
|
||||||
|
for (;path[i];i++);
|
||||||
|
// find last /
|
||||||
|
for (;path[i] != '/' && i >= 0;i--);
|
||||||
|
// path[i] == '/'
|
||||||
|
// set next to end, return split location
|
||||||
|
path[i+1] = 0;
|
||||||
|
return i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dos_file_open(uint8_t *dat, FILE *f, char *path, char mode) {
|
||||||
|
fsdat *fs = (fsdat *)dat;
|
||||||
|
uint8_t *scratch = (uint8_t *)0x20000;
|
||||||
|
uint8_t dfs_mode =
|
||||||
|
(mode & OPENREAD ? DFS_READ : 0) |
|
||||||
|
(mode & OPENWRITE ? DFS_WRITE : 0);
|
||||||
|
return DFS_OpenFile(&fs->vi, (uint8_t *)path, dfs_mode, scratch, (FILEINFO *)f->bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
int dos_file_seek(uint8_t *dat, FILE *f, uint32_t offset) {
|
||||||
|
fsdat *fs = (fsdat *)dat;
|
||||||
|
uint8_t *scratch = (uint8_t *)0x20000;
|
||||||
|
DFS_Seek((FILEINFO *)f->bytes, offset, scratch);
|
||||||
|
if (((FILEINFO *)f->bytes)->pointer != offset) return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dos_file_read(uint8_t *dat, FILE *f, uint8_t *dest, uint32_t len) {
|
||||||
|
fsdat *fs = (fsdat *)dat;
|
||||||
|
uint8_t *scratch = (uint8_t *)0x20000;
|
||||||
|
uint32_t successcount;
|
||||||
|
uint32_t err = DFS_ReadFile((FILEINFO *)f->bytes, scratch, dest, &successcount, len);
|
||||||
|
// Error
|
||||||
|
if (err != 0 && err != DFS_EOF)
|
||||||
|
return 0;
|
||||||
|
// Success or EOF
|
||||||
|
return successcount;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dos_file_write(uint8_t *dat, FILE *f, uint8_t *src, uint32_t len) {
|
||||||
|
fsdat *fs = (fsdat *)dat;
|
||||||
|
uint8_t *scratch = (uint8_t *)0x20000;
|
||||||
|
uint32_t successcount;
|
||||||
|
uint32_t err = DFS_WriteFile((FILEINFO *)f->bytes, scratch, src, &successcount, len);
|
||||||
|
// Error
|
||||||
|
if (err != 0) return 0;
|
||||||
|
// Success
|
||||||
|
return successcount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DOSFS doesn't have anything to clean up
|
||||||
|
void dos_file_close(uint8_t *dat, FILE *f) { return; }
|
||||||
|
|
||||||
|
int dos_dir_open(uint8_t *dat, DIR *d, char *path) {
|
||||||
|
fsdat *fs = (fsdat *)dat;
|
||||||
|
uint8_t *scratch = (uint8_t *)0x20000;
|
||||||
|
((DIRINFO *)d->bytes)->scratch = scratch;
|
||||||
|
return DFS_OpenDir(&fs->vi, (uint8_t *)path, (DIRINFO *)d->bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
int dos_dir_nextentry(uint8_t *dat, DIR *d, dirent *ent) {
|
||||||
|
fsdat *fs = (fsdat *)dat;
|
||||||
|
DIRENT de;
|
||||||
|
for (;;) {
|
||||||
|
uint32_t code = DFS_GetNext(&fs->vi, (DIRINFO *)d->bytes, &de);
|
||||||
|
if (code == DFS_EOF) return 1;
|
||||||
|
if (code != DFS_OK) return -1;
|
||||||
|
// Deleted file, continue to next entry
|
||||||
|
if (de.name[0] == 0) continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Copy info
|
||||||
|
ent->type = de.attr & ATTR_DIRECTORY ? FT_DIR : FT_REG;
|
||||||
|
ent->size = (uint32_t)de.filesize_0 +
|
||||||
|
((uint32_t)de.filesize_1 << 8) +
|
||||||
|
((uint32_t)de.filesize_2 << 16) +
|
||||||
|
((uint32_t)de.filesize_3 << 24);
|
||||||
|
// Haven't decided format on these yet
|
||||||
|
ent->last_modified = 0;
|
||||||
|
ent->last_accessed = 0;
|
||||||
|
ent->created = 0;
|
||||||
|
|
||||||
|
ent->namelen = file83ToPath(de.name, ent->name);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DOSFS doesn't have anything to clean up
|
||||||
|
void dos_dir_close(uint8_t *dat, DIR *d) { return; }
|
||||||
|
|
||||||
|
// TODO Make this less expensive -> Use DOSFS directly?
|
||||||
|
int dos_path_getinfo(uint8_t *dat, char *path, dirent *d) {
|
||||||
|
fsdat *fs = (fsdat *)dat;
|
||||||
|
uint8_t *scratch = (uint8_t *)0x20000;
|
||||||
|
|
||||||
|
// Get directory path is in
|
||||||
|
uint8_t tmppath[MAX_PATH];
|
||||||
|
strncpy((char*)tmppath,path,MAX_PATH);
|
||||||
|
tmppath[MAX_PATH-1]=0;
|
||||||
|
uintptr_t nameidx = stripToDir((char*)tmppath);
|
||||||
|
char *name = &path[nameidx];
|
||||||
|
|
||||||
|
// Open directory
|
||||||
|
DIR dir;
|
||||||
|
dos_dir_open(dat, &dir, (char*)tmppath);
|
||||||
|
|
||||||
|
dirent de;
|
||||||
|
// Enumerate info
|
||||||
|
for (;dos_dir_nextentry(dat, &dir, &de) == 0;) {
|
||||||
|
// Check if correct entry
|
||||||
|
if (strcmp(de.name, name) == 0) {
|
||||||
|
// Copy to caller dirent
|
||||||
|
for (int i = 0; i < sizeof(dirent); i++)
|
||||||
|
((uint8_t*)d)[i] = ((uint8_t*)&de)[i];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Did not find or error
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Unimplemented
|
||||||
|
int dos_path_mkdir(uint8_t *dat, char *path) {
|
||||||
|
fsdat *fs = (fsdat *)dat;
|
||||||
|
uint8_t *scratch = (uint8_t *)0x20000;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Unimplemented
|
||||||
|
int dos_path_rmdir(uint8_t *dat, char *path) {
|
||||||
|
fsdat *fs = (fsdat *)dat;
|
||||||
|
uint8_t *scratch = (uint8_t *)0x20000;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Unimplemented
|
||||||
|
int dos_path_rmfile(uint8_t *dat, char *path) {
|
||||||
|
fsdat *fs = (fsdat *)dat;
|
||||||
|
uint8_t *scratch = (uint8_t *)0x20000;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DOSFS doesn't have anything to clean up
|
||||||
|
void dos_endfs(uint8_t *dat) { return; }
|
||||||
|
|
||||||
|
// Try to detect if partition is a valid DOS partition
|
||||||
|
char DetectDosPart(uint32_t start_sector) {
|
||||||
|
// Read sector
|
||||||
|
//uint8_t *scratch = (uint8_t *)0x20000;
|
||||||
|
//Disk_ReadSector(0, scratch, start_sector, 1);
|
||||||
|
//// Check for start bytes EBXX90
|
||||||
|
//if (((*(uint32_t*)&scratch[0]) & 0x00FF00FF) != 0x9000EB) return 0;
|
||||||
|
//// Check for bytes per sector == 512 (We don't support other values anyway)
|
||||||
|
//if (*(uint16_t*)&scratch[0xB] != 512) return 0;
|
||||||
|
|
||||||
|
// TODO Check more, so we *know* it's FAT
|
||||||
|
|
||||||
|
// We're probably FAT
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int InitDosFs(filesystem *fs, uint32_t start_sector) {
|
||||||
|
uint8_t *diskReadBuf = (uint8_t *)0x20000;
|
||||||
|
|
||||||
|
VOLINFO *vi = (VOLINFO *)fs->fs_data;
|
||||||
|
if (DFS_GetVolInfo(0, diskReadBuf, start_sector, (VOLINFO *)fs->fs_data)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; vi->label[i] && i < sizeof(vi->label); i++)
|
||||||
|
fs->label[i] = vi->label[i];
|
||||||
|
fs->labellen = i;
|
||||||
|
|
||||||
|
fs->ops.file_open = dos_file_open;
|
||||||
|
fs->ops.file_seek = dos_file_seek;
|
||||||
|
fs->ops.file_read = dos_file_read;
|
||||||
|
fs->ops.file_write = dos_file_write;
|
||||||
|
fs->ops.file_close = dos_file_close;
|
||||||
|
fs->ops.dir_open = dos_dir_open;
|
||||||
|
fs->ops.dir_nextentry = dos_dir_nextentry;
|
||||||
|
fs->ops.dir_close = dos_dir_close;
|
||||||
|
fs->ops.path_getinfo = dos_path_getinfo;
|
||||||
|
fs->ops.path_mkdir = dos_path_mkdir;
|
||||||
|
fs->ops.path_rmdir = dos_path_rmdir;
|
||||||
|
fs->ops.path_rmfile = dos_path_rmfile;
|
||||||
|
fs->ops.endfs = dos_endfs;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
35
gdt.nasm
Normal file
35
gdt.nasm
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
global ivt
|
||||||
|
ivt: dd 0x00000000
|
||||||
|
|
||||||
|
global gdt_desc
|
||||||
|
gdt_desc:
|
||||||
|
dw gdt_end - gdt
|
||||||
|
dd gdt
|
||||||
|
gdt:
|
||||||
|
gdt_null: dq 0
|
||||||
|
gdt_code: dw 0xFFFF, 0 ; bits 0-15 limit (4GB), bits 0-15 base address
|
||||||
|
db 0 ; bits 16-23 base address
|
||||||
|
db 10011010b ; access byte
|
||||||
|
db 11001111b ; bits 16-19 limit (4GB), 4 bits flags
|
||||||
|
db 0 ; bits 24-31 base address
|
||||||
|
gdt_data: dw 0xFFFF, 0 ; bits 0-15 limit (4GB), bits 0-15 base address
|
||||||
|
db 0 ; bits 16-23 base address
|
||||||
|
db 10010010b ; access byte
|
||||||
|
db 11001111b ; bits 16-19 limit (4GB), 4 bits flags
|
||||||
|
db 0 ; bits 24-31 base address
|
||||||
|
gdt_r3code: dw 0xFFFF, 0 ; bits 0-15 limit (4GB), bits 0-15 base address
|
||||||
|
db 0 ; bits 16-23 base address
|
||||||
|
db 0xFA ; access byte
|
||||||
|
db 11001111b ; bits 16-19 limit (4GB), 4 bits flags
|
||||||
|
db 0 ; bits 24-31 base address
|
||||||
|
gdt_r3data: dw 0xFFFF, 0 ; bits 0-15 limit (4GB), bits 0-15 base address
|
||||||
|
db 0 ; bits 16-23 base address
|
||||||
|
db 0xF2 ; access byte
|
||||||
|
db 11001111b ; bits 16-19 limit (4GB), 4 bits flags
|
||||||
|
db 0 ; bits 24-31 base address
|
||||||
|
gdt_tss: dw 0x2080, 0x0000;26*4, tss_data ; bits 0-15 limit (4GB), bits 0-15 base address
|
||||||
|
db 0x20 ; bits 16-23 base address
|
||||||
|
db 0x89 ; access byte
|
||||||
|
db 00000000b ; bits 16-19 limit (4GB), 4 bits flags
|
||||||
|
db 0 ; bits 24-31 base address
|
||||||
|
gdt_end:
|
60
handler.nasm
60
handler.nasm
@ -1,48 +1,4 @@
|
|||||||
extern unhandled_handler
|
oldscancodesToAscii: db 0, 0 ; 0x00 - 0x01
|
||||||
unhandled_handler:
|
|
||||||
mov ax, 0x10
|
|
||||||
mov ds, ax
|
|
||||||
mov dword [0xb8000], 0x0f000f00 | 'E' | 'R' << 16
|
|
||||||
mov dword [0xb8004], 0x0f000f00 | 'R' | 'O' << 16
|
|
||||||
mov dword [0xb8008], 0x0f000f00 | 'R' | '!' << 16
|
|
||||||
.hlt:
|
|
||||||
hlt
|
|
||||||
jmp .hlt
|
|
||||||
|
|
||||||
extern gpf_handler_v86
|
|
||||||
global gpfHandler
|
|
||||||
gpfHandler:
|
|
||||||
push eax
|
|
||||||
mov ax, 0x10
|
|
||||||
mov ds, ax
|
|
||||||
mov eax, dword [esp+16] ; EFLAGS
|
|
||||||
and eax, 1 << 17 ; VM flag
|
|
||||||
test eax, eax
|
|
||||||
pop eax
|
|
||||||
jnz gpf_handler_v86
|
|
||||||
jmp gpf_handler_32
|
|
||||||
gpf_unhandled:
|
|
||||||
mov dword [0xb8000], 0x0f000f00 | 'G' | 'P' << 16
|
|
||||||
mov dword [0xb8004], 0x0f000f00 | 'F' | '!' << 16
|
|
||||||
.hlt:
|
|
||||||
hlt
|
|
||||||
jmp .hlt
|
|
||||||
|
|
||||||
gpf_handler_32:
|
|
||||||
push eax
|
|
||||||
mov eax, dword [esp+8] ; EIP
|
|
||||||
movzx eax, word [eax]
|
|
||||||
cmp eax, 0x30CD ; int 0x30
|
|
||||||
jne gpf_unhandled
|
|
||||||
pop eax ; return value
|
|
||||||
mov esp, dword [0x20004] ; return info
|
|
||||||
pop gs
|
|
||||||
pop fs
|
|
||||||
pop es
|
|
||||||
pop ds
|
|
||||||
iret ; return to original caller
|
|
||||||
|
|
||||||
scancodesToAscii: db 0, 0 ; 0x00 - 0x01
|
|
||||||
db "1234567890" ; 0x02 - 0x0B
|
db "1234567890" ; 0x02 - 0x0B
|
||||||
db "-=" ; 0x0C - 0x0D
|
db "-=" ; 0x0C - 0x0D
|
||||||
db 0, 0 ; 0x0E - 0x0F
|
db 0, 0 ; 0x0E - 0x0F
|
||||||
@ -58,8 +14,8 @@ db ' ' ; 0x39
|
|||||||
db 'C'
|
db 'C'
|
||||||
scancodesToAsciiEnd:
|
scancodesToAsciiEnd:
|
||||||
cursorCurrent: dd 0xb8000 + (80*6*2)
|
cursorCurrent: dd 0xb8000 + (80*6*2)
|
||||||
global keyboardHandler
|
global oldkeyboardHandler
|
||||||
keyboardHandler:
|
oldkeyboardHandler:
|
||||||
push eax
|
push eax
|
||||||
push ebx
|
push ebx
|
||||||
push ds
|
push ds
|
||||||
@ -69,7 +25,7 @@ xor eax, eax
|
|||||||
in al, 0x60
|
in al, 0x60
|
||||||
cmp eax, 0x3A
|
cmp eax, 0x3A
|
||||||
jg .done
|
jg .done
|
||||||
mov al, [scancodesToAscii+eax]
|
mov al, [oldscancodesToAscii+eax]
|
||||||
test al, al
|
test al, al
|
||||||
jz .done
|
jz .done
|
||||||
mov ebx, [cursorCurrent]
|
mov ebx, [cursorCurrent]
|
||||||
@ -85,8 +41,7 @@ pop eax
|
|||||||
iret
|
iret
|
||||||
|
|
||||||
KBDWAIT: db 0
|
KBDWAIT: db 0
|
||||||
global kbd_wait
|
oldkbd_wait:
|
||||||
kbd_wait:
|
|
||||||
mov byte [KBDWAIT], 0
|
mov byte [KBDWAIT], 0
|
||||||
.loop:
|
.loop:
|
||||||
hlt
|
hlt
|
||||||
@ -95,13 +50,16 @@ test eax, eax
|
|||||||
jz .loop
|
jz .loop
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
global TIMERVAL
|
||||||
|
TIMERVAL: dd 0
|
||||||
global timerHandler
|
global timerHandler
|
||||||
timerHandler:
|
timerHandler:
|
||||||
push eax
|
push eax
|
||||||
push ds
|
push ds
|
||||||
mov ax, 0x10
|
mov ax, 0x10
|
||||||
mov ds, ax
|
mov ds, ax
|
||||||
inc byte [(0xb8000 + (80*8*2))]
|
;inc byte [(0xb8000 + (80*8*2))]
|
||||||
|
inc dword [TIMERVAL]
|
||||||
mov al, 0x20
|
mov al, 0x20
|
||||||
out 0x20, al
|
out 0x20, al
|
||||||
pop ds
|
pop ds
|
||||||
|
53
helper.c
Normal file
53
helper.c
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#include "helper.h"
|
||||||
|
|
||||||
|
uint16_t *nextLine(uint16_t *p, uint16_t *b) {
|
||||||
|
uintptr_t v = (uintptr_t)p;
|
||||||
|
return (uint16_t *)(v + (160 - ((v - (uintptr_t)b) % 160)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void trimPath(char *path, char *buff, uint32_t maxLen) {
|
||||||
|
int pathLen = 0;
|
||||||
|
for (;path[pathLen];pathLen++);
|
||||||
|
pathLen++;
|
||||||
|
if (pathLen < maxLen) {
|
||||||
|
for(int i = 0; i < pathLen; i++)
|
||||||
|
buff[i] = path[i];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
buff[i] = '.';
|
||||||
|
for (int i = 3; i < maxLen; i++) {
|
||||||
|
buff[i] = path[pathLen-maxLen+i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void V8086Int(uint8_t interrupt, union V86Regs_t *regs) {
|
||||||
|
// Edit the v8086 code with the interrupt
|
||||||
|
// Writing 4 bytes to ensure proper code
|
||||||
|
*(uint32_t*)v86Interrupt = 0x30CD00CD | (interrupt << 8);
|
||||||
|
FARPTR v86_entry = i386LinearToFp(v86Interrupt);
|
||||||
|
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetVideo25Lines() {
|
||||||
|
union V86Regs_t regs;
|
||||||
|
regs.w.ax = 0x1114; // 80x25 mode
|
||||||
|
regs.w.bx = 0x0000;
|
||||||
|
V8086Int(0x10, ®s);
|
||||||
|
}
|
||||||
|
void SetVideo50Lines() {
|
||||||
|
union V86Regs_t regs;
|
||||||
|
regs.w.ax = 0x1112; // 80x50 mode
|
||||||
|
regs.w.bx = 0x0000;
|
||||||
|
V8086Int(0x10, ®s);
|
||||||
|
}
|
||||||
|
void SetCursorDisabled() {
|
||||||
|
union V86Regs_t regs;
|
||||||
|
regs.w.ax = 0x0100; // set cursor
|
||||||
|
regs.w.cx = 0x3F00; // disabled
|
||||||
|
V8086Int(0x10, ®s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetFileList(DIR *dir, dirent *entries, int32_t *entCount, int32_t maxEntries) {
|
||||||
|
for ((*entCount) = 0; *entCount < maxEntries && !dir_nextentry(dir, &entries[*entCount]); (*entCount)++);
|
||||||
|
}
|
17
helper.h
Normal file
17
helper.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "interrupt.h"
|
||||||
|
#include "v86defs.h"
|
||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
void V8086Int(uint8_t interrupt, union V86Regs_t *regs);
|
||||||
|
|
||||||
|
void SetVideo25Lines();
|
||||||
|
void SetVideo50Lines();
|
||||||
|
void SetCursorDisabled();
|
||||||
|
|
||||||
|
uint16_t *nextLine(uint16_t *p, uint16_t *b);
|
||||||
|
void trimPath(char *path, char *buff, uint32_t maxLen);
|
||||||
|
|
||||||
|
void GetFileList(DIR *dir, dirent *entries, int32_t *entCount, int32_t maxEntries);
|
454
hexedit.c
Normal file
454
hexedit.c
Normal file
@ -0,0 +1,454 @@
|
|||||||
|
#include "file.h"
|
||||||
|
#include "progs.h"
|
||||||
|
|
||||||
|
#define BLOCKSIZE 0x10000 // 64K
|
||||||
|
#define BLOCKMASK ~0xffff // block = (offset & blockMask)
|
||||||
|
#define BLOCKSHIFT 16 // blockSize = 1 << blockShift
|
||||||
|
#define MAXFILESIZE 0x80000000 // 2GB
|
||||||
|
#define TOTALBLOCKS (MAXFILESIZE/BLOCKSIZE)
|
||||||
|
uint16_t writtenMap[TOTALBLOCKS] __attribute__((section(".hexbss")));;
|
||||||
|
uint32_t blockLenMap[TOTALBLOCKS] __attribute__((section(".hexbss")));;
|
||||||
|
// NOTE This is linked at the end of program BSS section,
|
||||||
|
// so that it can be expanded without telling C how much
|
||||||
|
// it actually needs
|
||||||
|
uint8_t writeStoreBase[BLOCKSIZE] __attribute__((section(".hexlatebss")));
|
||||||
|
void HexEditor(char *path) {
|
||||||
|
uint32_t err;
|
||||||
|
uint16_t *vga_text = (uint16_t *)0xb8000;
|
||||||
|
uint32_t screenSize = 80*25;
|
||||||
|
uint8_t (*writeStore)[BLOCKSIZE] = &writeStoreBase;
|
||||||
|
for (int i = 0; i < TOTALBLOCKS; i++)
|
||||||
|
writtenMap[i] = 0;
|
||||||
|
uint8_t *screenBuff = *writeStore;
|
||||||
|
// First two blocks are screen buffer
|
||||||
|
uint32_t nextFreeBlock = 2;
|
||||||
|
|
||||||
|
vga_text = (uint16_t *)0xb8000;
|
||||||
|
for (int i = 0; i < 80*50; i++)
|
||||||
|
vga_text[i] = 0x0f00;
|
||||||
|
dirent de;
|
||||||
|
err = path_getinfo(path, &de);
|
||||||
|
if (err) {
|
||||||
|
vga_text += printStr("Error getting file info.", vga_text);
|
||||||
|
kbd_wait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32_t filelen = de.size;
|
||||||
|
if (filelen == 0) {
|
||||||
|
vga_text += printStr("File ", vga_text);
|
||||||
|
vga_text += printStr((char*)path, vga_text);
|
||||||
|
vga_text += printStr(" has no data.", vga_text);
|
||||||
|
kbd_wait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (filelen > MAXFILESIZE) {
|
||||||
|
vga_text += printStr("File ", vga_text);
|
||||||
|
vga_text += printStr((char*)path, vga_text);
|
||||||
|
vga_text += printStr(" is too large (> 2GB).", vga_text);
|
||||||
|
kbd_wait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FILE file;
|
||||||
|
err = file_open(&file, path, OPENREAD|OPENWRITE);
|
||||||
|
if (err) {
|
||||||
|
vga_text += printStr("Open Error: ", vga_text);
|
||||||
|
printDword(err, vga_text);
|
||||||
|
kbd_wait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32_t drawOffset = 0, lastDrawOffset = -1;
|
||||||
|
char cont = 1;
|
||||||
|
uint32_t byteCount = 16*24, lastByteCount = 0;
|
||||||
|
// Cursor offset from *drawOff*
|
||||||
|
int32_t cursorScreenOff = 0, lastCursorScreenOff = -1;
|
||||||
|
// Pointer to last cursor in *VGA mem*
|
||||||
|
uint8_t *lastCursorScreenPtr = (uint8_t*)0xb8000;
|
||||||
|
char cursorNibble = 1, lastCursorNibble = 0;
|
||||||
|
char cursorRedraw = 1;
|
||||||
|
char reread = 1, redraw = 1;
|
||||||
|
uint8_t *currBuff = 0, *nextBuff = 0;
|
||||||
|
uint32_t currBuffLength, nextBuffLength, totalBuffLength;
|
||||||
|
uint16_t currLoadedBlock = -1, nextLoadedBlock = -1;
|
||||||
|
char currInMap = 0, nextInMap = 0;
|
||||||
|
uint32_t changeOffset = -1, lastChangeOffset = -1;
|
||||||
|
char fileChanged = 0;
|
||||||
|
for (;cont;) {
|
||||||
|
// Check if we need to scroll screen for cursor
|
||||||
|
// Should never be more than 16 away, if it is,
|
||||||
|
// things will be caught by sanity checks.
|
||||||
|
// Scroll Back
|
||||||
|
if (cursorScreenOff < 0) {
|
||||||
|
if (drawOffset - 16 < filelen)
|
||||||
|
drawOffset -= 16;
|
||||||
|
cursorScreenOff += 16;
|
||||||
|
}
|
||||||
|
// Scroll Forward
|
||||||
|
if (cursorScreenOff >= byteCount) {
|
||||||
|
if (drawOffset + 16 < filelen)
|
||||||
|
drawOffset += 16;
|
||||||
|
cursorScreenOff -= 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity checks
|
||||||
|
if (cursorScreenOff >= byteCount)
|
||||||
|
cursorScreenOff = byteCount - 1;
|
||||||
|
if (cursorScreenOff + drawOffset >= filelen)
|
||||||
|
cursorScreenOff = filelen - drawOffset - 1;
|
||||||
|
if (cursorScreenOff < 0) cursorScreenOff = 0;
|
||||||
|
|
||||||
|
if (cursorNibble != lastCursorNibble)
|
||||||
|
cursorRedraw = 1;
|
||||||
|
if (cursorScreenOff != lastCursorScreenOff)
|
||||||
|
cursorRedraw = 1;
|
||||||
|
if (cursorRedraw) {
|
||||||
|
const uint32_t hexViewScreenOff = 10 * 2;
|
||||||
|
const uint32_t asciiViewScreenOff = 61 * 2;
|
||||||
|
uint16_t *screen = (uint16_t*)0xb8000;
|
||||||
|
// Byte draw starts on first line
|
||||||
|
uint16_t *line = &screen[80 * (1 + (cursorScreenOff >> 4))];
|
||||||
|
uint8_t *cursorPtr = (uint8_t*)line;
|
||||||
|
if (cursorNibble == 0) {
|
||||||
|
// Each byte takes 3 chars on screen
|
||||||
|
cursorPtr += hexViewScreenOff + (cursorScreenOff & 0xF) * 3 * 2;
|
||||||
|
if ((cursorScreenOff & 0xF) > 7)
|
||||||
|
cursorPtr += 2;
|
||||||
|
// Low nibble
|
||||||
|
cursorPtr += 2;
|
||||||
|
} else if (cursorNibble == 1) {
|
||||||
|
// Each byte takes 3 chars on screen
|
||||||
|
cursorPtr += hexViewScreenOff + (cursorScreenOff & 0xF) * 3 * 2;
|
||||||
|
if ((cursorScreenOff & 0xF) > 7)
|
||||||
|
cursorPtr += 2;
|
||||||
|
// ASCII area
|
||||||
|
} else {
|
||||||
|
// Each byte takes 1 char on screen
|
||||||
|
cursorPtr += asciiViewScreenOff + (cursorScreenOff & 0xF) * 2;
|
||||||
|
}
|
||||||
|
// We want the color byte, not char
|
||||||
|
cursorPtr++;
|
||||||
|
if (cursorPtr != lastCursorScreenPtr) {
|
||||||
|
*lastCursorScreenPtr = 0x0f;
|
||||||
|
*cursorPtr = 0xf0;
|
||||||
|
lastCursorScreenPtr = cursorPtr;
|
||||||
|
}
|
||||||
|
lastCursorScreenOff = cursorScreenOff;
|
||||||
|
lastCursorNibble = cursorNibble;
|
||||||
|
cursorRedraw = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((drawOffset & BLOCKMASK) != (lastDrawOffset & BLOCKMASK)) {
|
||||||
|
lastDrawOffset = (drawOffset & BLOCKMASK);
|
||||||
|
reread = 1;
|
||||||
|
redraw = 1;
|
||||||
|
}
|
||||||
|
if (drawOffset != lastDrawOffset) {
|
||||||
|
lastDrawOffset = drawOffset;
|
||||||
|
redraw = 1;
|
||||||
|
}
|
||||||
|
if (byteCount != lastByteCount) {
|
||||||
|
lastByteCount = byteCount;
|
||||||
|
redraw = 1;
|
||||||
|
}
|
||||||
|
if (changeOffset != lastChangeOffset) {
|
||||||
|
redraw = 1;
|
||||||
|
lastChangeOffset = changeOffset;
|
||||||
|
}
|
||||||
|
// Check for changes to block
|
||||||
|
if (changeOffset != -1) {
|
||||||
|
char isCurr = changeOffset < BLOCKSIZE;
|
||||||
|
char changeInMap = isCurr ? currInMap : nextInMap;
|
||||||
|
uint8_t *changedBlock = isCurr ? currBuff : nextBuff;
|
||||||
|
// If changes are in map we don't need to do anything
|
||||||
|
if (!changeInMap) {
|
||||||
|
uint16_t loadedBlock = isCurr ? currLoadedBlock : nextLoadedBlock;
|
||||||
|
uint16_t storeBlockIdx = writtenMap[loadedBlock];
|
||||||
|
// Add a new map entry
|
||||||
|
if (!storeBlockIdx) {
|
||||||
|
writtenMap[loadedBlock] = storeBlockIdx = nextFreeBlock++;
|
||||||
|
blockLenMap[loadedBlock] = isCurr ? currBuffLength : nextBuffLength;
|
||||||
|
}
|
||||||
|
uint8_t *storeBlock = writeStore[storeBlockIdx];
|
||||||
|
for (int i = 0; i < BLOCKSIZE; i++)
|
||||||
|
storeBlock[i] = changedBlock[i];
|
||||||
|
// Move the current buffer to map
|
||||||
|
if (isCurr) { currBuff = storeBlock; currInMap = 1; }
|
||||||
|
else { nextBuff = storeBlock; nextInMap = 1; }
|
||||||
|
}
|
||||||
|
changeOffset = -1;
|
||||||
|
fileChanged = 1;
|
||||||
|
redraw = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reread) {
|
||||||
|
// TODO We should probably check if we already have
|
||||||
|
// the block to read in nextBuff from previous stuff
|
||||||
|
// We are in a modified block
|
||||||
|
uint16_t newBlock = drawOffset >> BLOCKSHIFT;
|
||||||
|
if (writtenMap[newBlock]) {
|
||||||
|
currBuff = writeStore[writtenMap[newBlock]];
|
||||||
|
currBuffLength = blockLenMap[newBlock];
|
||||||
|
totalBuffLength = currBuffLength;
|
||||||
|
currInMap = 1;
|
||||||
|
// We are in an unmodified block
|
||||||
|
} else {
|
||||||
|
uint32_t blockOffset = drawOffset & BLOCKMASK;
|
||||||
|
vga_text = &((uint16_t*)0xb8000)[80];
|
||||||
|
if (file_seek(&file, blockOffset)) {
|
||||||
|
vga_text += printStr("Seek Error", vga_text);
|
||||||
|
kbd_wait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currBuffLength = file_read(&file, screenBuff, BLOCKSIZE);
|
||||||
|
if (!currBuffLength && blockOffset != filelen) {
|
||||||
|
vga_text += printStr("Read Error", vga_text);
|
||||||
|
kbd_wait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currBuff = screenBuff;
|
||||||
|
totalBuffLength = currBuffLength;
|
||||||
|
currInMap = 0;
|
||||||
|
}
|
||||||
|
currLoadedBlock = newBlock;
|
||||||
|
reread = 0;
|
||||||
|
nextInMap = 0;
|
||||||
|
}
|
||||||
|
// We will be drawing stuff in the next block
|
||||||
|
// TODO This would be better served with nice functions!
|
||||||
|
if (currBuffLength == BLOCKSIZE && (drawOffset & ~BLOCKMASK) > (BLOCKSIZE - byteCount)) {
|
||||||
|
// TODO We should probably check if we already have
|
||||||
|
// the next block in currBuff from previous stuff
|
||||||
|
// Next is a modified block
|
||||||
|
uint16_t newBlock = (drawOffset >> BLOCKSHIFT) + 1;
|
||||||
|
if (nextLoadedBlock != newBlock) {
|
||||||
|
if (writtenMap[newBlock]) {
|
||||||
|
nextBuffLength = blockLenMap[newBlock];
|
||||||
|
nextBuff = writeStore[writtenMap[newBlock]];
|
||||||
|
nextInMap = 1;
|
||||||
|
// Next is an unmodified block
|
||||||
|
} else {
|
||||||
|
uint32_t blockOffset = (drawOffset & BLOCKMASK) + BLOCKSIZE;
|
||||||
|
vga_text = &((uint16_t*)0xb8000)[80];
|
||||||
|
if (file_seek(&file, blockOffset)) {
|
||||||
|
vga_text += printStr("Seek Error", vga_text);
|
||||||
|
kbd_wait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nextBuffLength = file_read(&file, &screenBuff[BLOCKSIZE], BLOCKSIZE);
|
||||||
|
if (!nextBuffLength && blockOffset != filelen) {
|
||||||
|
vga_text += printStr("Read Error", vga_text);
|
||||||
|
kbd_wait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nextBuff = &screenBuff[BLOCKSIZE];
|
||||||
|
nextInMap = 0;
|
||||||
|
}
|
||||||
|
nextLoadedBlock = newBlock;
|
||||||
|
}
|
||||||
|
totalBuffLength = currBuffLength + nextBuffLength;
|
||||||
|
}
|
||||||
|
if (redraw) {
|
||||||
|
vga_text = (uint16_t *)0xb8000;
|
||||||
|
{
|
||||||
|
const char prnt[] = "Scroll: Up/Down PgUp/PgDown Home/End Exit: F1";
|
||||||
|
vga_text = &((uint16_t*)0xb8000)[0];
|
||||||
|
char pathBuff[22];
|
||||||
|
trimPath((char*)path, pathBuff, sizeof(pathBuff));
|
||||||
|
vga_text += printStr(pathBuff, vga_text);
|
||||||
|
vga_text += printChar(fileChanged ? '*' : ' ', vga_text);
|
||||||
|
for (;vga_text < &((uint16_t*)0xb8000)[80-sizeof(prnt)];)
|
||||||
|
vga_text += printChar(' ', vga_text);
|
||||||
|
vga_text += printStr((char*)prnt, vga_text);
|
||||||
|
}
|
||||||
|
vga_text = &((uint16_t*)0xb8000)[80];
|
||||||
|
uint32_t offsetInBuff = drawOffset & ~BLOCKMASK;
|
||||||
|
uint8_t *drawFromBuff = &currBuff[offsetInBuff];
|
||||||
|
uint32_t maxDrawLen = totalBuffLength - (drawOffset & ~BLOCKMASK);
|
||||||
|
for (int i = 0; i < maxDrawLen && i < byteCount; i += 16) {
|
||||||
|
// Blocks are BLOCKSIZE aligned, so 16-byte alignment should work
|
||||||
|
if (i + offsetInBuff >= BLOCKSIZE) {
|
||||||
|
drawFromBuff = &nextBuff[-i];
|
||||||
|
offsetInBuff = 0;
|
||||||
|
}
|
||||||
|
vga_text += printDword(i + drawOffset, vga_text);
|
||||||
|
vga_text += printChar(' ', vga_text);
|
||||||
|
vga_text += printChar(' ', vga_text);
|
||||||
|
for (uint32_t j = 0; j < 16; j++) {
|
||||||
|
if (i + j < maxDrawLen)
|
||||||
|
vga_text += printByte(drawFromBuff[i + j], vga_text);
|
||||||
|
else {
|
||||||
|
vga_text += printChar(' ', vga_text);
|
||||||
|
vga_text += printChar(' ', vga_text);
|
||||||
|
}
|
||||||
|
vga_text += printChar(' ', vga_text);
|
||||||
|
if (j == 7)
|
||||||
|
vga_text += printChar(' ', vga_text);
|
||||||
|
}
|
||||||
|
vga_text += printChar(' ', vga_text);
|
||||||
|
vga_text += printChar('|', vga_text);
|
||||||
|
for (uint32_t j = 0; j < 16; j++) {
|
||||||
|
if (i + j < maxDrawLen)
|
||||||
|
vga_text += printChar(drawFromBuff[i + j], vga_text);
|
||||||
|
else vga_text += printChar(' ', vga_text);
|
||||||
|
}
|
||||||
|
vga_text += printChar('|', vga_text);
|
||||||
|
vga_text = nextLine(vga_text,(uint16_t*)0xb8000);
|
||||||
|
}
|
||||||
|
// Clear remainder of screen
|
||||||
|
for (;vga_text < &((uint16_t*)0xb8000)[screenSize]; vga_text++)
|
||||||
|
*vga_text = 0x0f00;
|
||||||
|
redraw = 0;
|
||||||
|
}
|
||||||
|
uint16_t key = get_scancode();
|
||||||
|
union V86Regs_t regs;
|
||||||
|
FARPTR v86_entry;
|
||||||
|
char try_edit = 0;
|
||||||
|
switch (key & 0xff) {
|
||||||
|
case KEY_DOWN:
|
||||||
|
// Stay in file
|
||||||
|
if ((cursorScreenOff + 16 + drawOffset) < filelen)
|
||||||
|
cursorScreenOff += 16;
|
||||||
|
break;
|
||||||
|
case KEY_UP:
|
||||||
|
// Stay in file
|
||||||
|
if ((uint32_t)(cursorScreenOff - 16 + drawOffset) < filelen)
|
||||||
|
cursorScreenOff -= 16;
|
||||||
|
break;
|
||||||
|
case KEY_LEFT:
|
||||||
|
if (cursorNibble == 0) { cursorNibble = 1; break; }
|
||||||
|
// Exiting text area
|
||||||
|
else if (cursorNibble == 2 && (cursorScreenOff & 0xF) == 0) {
|
||||||
|
cursorNibble = 0;
|
||||||
|
cursorScreenOff |= 0xF;
|
||||||
|
// Stay in file
|
||||||
|
} else if ((cursorScreenOff - 1 + drawOffset) < filelen) {
|
||||||
|
cursorScreenOff--;
|
||||||
|
if (cursorNibble == 1) cursorNibble = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KEY_RIGHT:
|
||||||
|
if (cursorNibble == 1) { cursorNibble = 0; break; }
|
||||||
|
// Entering text area
|
||||||
|
else if (cursorNibble == 0 && (cursorScreenOff & 0xF) == 0xF) {
|
||||||
|
cursorNibble = 2;
|
||||||
|
cursorScreenOff &= ~0xF;
|
||||||
|
// Stay in file
|
||||||
|
} else if ((cursorScreenOff + 1 + drawOffset) < filelen) {
|
||||||
|
cursorScreenOff++;
|
||||||
|
if (cursorNibble == 0) cursorNibble = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KEY_PGDOWN:
|
||||||
|
if (drawOffset + byteCount < filelen)
|
||||||
|
drawOffset += byteCount;
|
||||||
|
else if ((filelen / byteCount) * byteCount > drawOffset)
|
||||||
|
drawOffset = (filelen / byteCount) * byteCount;
|
||||||
|
break;
|
||||||
|
case KEY_PGUP:
|
||||||
|
if (drawOffset - byteCount < filelen)
|
||||||
|
drawOffset -= byteCount;
|
||||||
|
else drawOffset = 0;
|
||||||
|
break;
|
||||||
|
case KEY_HOME:
|
||||||
|
cursorScreenOff &= ~0xF;
|
||||||
|
if (cursorNibble == 0) cursorNibble = 1;
|
||||||
|
break;
|
||||||
|
case KEY_END:
|
||||||
|
cursorScreenOff |= 0xF;
|
||||||
|
if (cursorNibble == 1) cursorNibble = 0;
|
||||||
|
break;
|
||||||
|
case KEY_F1:
|
||||||
|
cont = 0;
|
||||||
|
break;
|
||||||
|
case KEY_F3: // start of file
|
||||||
|
drawOffset = 0;
|
||||||
|
break;
|
||||||
|
case KEY_F4: // end of file
|
||||||
|
if ((filelen / byteCount) * byteCount > drawOffset)
|
||||||
|
drawOffset = (filelen / byteCount) * byteCount;
|
||||||
|
break;
|
||||||
|
case KEY_F6: // TODO write file
|
||||||
|
break;
|
||||||
|
case KEY_F2:
|
||||||
|
if (byteCount != 16*24) {
|
||||||
|
SetVideo25Lines();
|
||||||
|
SetCursorDisabled();
|
||||||
|
screenSize = 80*25;
|
||||||
|
byteCount = 16*24;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KEY_F5:
|
||||||
|
if (byteCount != 16*49) {
|
||||||
|
SetVideo50Lines();
|
||||||
|
SetCursorDisabled();
|
||||||
|
screenSize = 80*50;
|
||||||
|
byteCount = 16*49;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
try_edit = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!try_edit) continue;
|
||||||
|
// Check if in range
|
||||||
|
char k = key >> 8; // ASCII portion
|
||||||
|
char v;
|
||||||
|
if (cursorNibble == 2 && k >= 0x20 && k <= 0x7E) v = k;
|
||||||
|
else if (k >= '0' && k <= '9')
|
||||||
|
v = k - '0';
|
||||||
|
else if (k >= 'a' && k <= 'f')
|
||||||
|
v = k - 'a' + 0xA;
|
||||||
|
else if (k >= 'A' && k <= 'F')
|
||||||
|
v = k - 'A' + 0xA;
|
||||||
|
else continue;
|
||||||
|
// Change buffer at cursor
|
||||||
|
uint32_t screenOffsetInBuff = drawOffset & ~BLOCKMASK;
|
||||||
|
changeOffset = screenOffsetInBuff + cursorScreenOff;
|
||||||
|
char isCurr = changeOffset < BLOCKSIZE;
|
||||||
|
uint8_t *changeBuff = isCurr ? currBuff : nextBuff;
|
||||||
|
uint32_t changeOffsetInBuff = isCurr ? changeOffset : changeOffset - BLOCKSIZE;
|
||||||
|
if (cursorNibble == 0)
|
||||||
|
changeBuff[changeOffsetInBuff] = (changeBuff[changeOffsetInBuff] & 0xF0) | v;
|
||||||
|
else if (cursorNibble == 1)
|
||||||
|
changeBuff[changeOffsetInBuff] = (changeBuff[changeOffsetInBuff] & 0x0F) | (v << 4);
|
||||||
|
else changeBuff[changeOffsetInBuff] = v;
|
||||||
|
redraw = 1;
|
||||||
|
}
|
||||||
|
if (!fileChanged) return;
|
||||||
|
vga_text = (uint16_t*)0xb8000;
|
||||||
|
{
|
||||||
|
char pathBuff[23];
|
||||||
|
trimPath((char*)path, pathBuff, sizeof(pathBuff));
|
||||||
|
vga_text += printStr(pathBuff, vga_text);
|
||||||
|
vga_text += printChar(fileChanged ? '*' : ' ', vga_text);
|
||||||
|
}
|
||||||
|
vga_text += printChar(' ', vga_text);
|
||||||
|
vga_text += printStr("Save changes to file? (Y/N)", vga_text);
|
||||||
|
for (;vga_text < &((uint16_t*)0xb8000)[80];vga_text++)
|
||||||
|
*vga_text = 0x0f00;
|
||||||
|
uint16_t key=0;
|
||||||
|
for(;(key & 0xff) != KEY_N && (key & 0xff) != KEY_Y;key = get_scancode());
|
||||||
|
if ((key & 0xff) != KEY_Y) return;
|
||||||
|
// Write changes
|
||||||
|
for (int i = 0; i < TOTALBLOCKS && (i << BLOCKSHIFT) < filelen; i++) {
|
||||||
|
// No change in current block
|
||||||
|
uint16_t blockIdx = writtenMap[i];
|
||||||
|
uint32_t blockLen = blockLenMap[i];
|
||||||
|
if (!blockIdx) continue;
|
||||||
|
// Write block to file
|
||||||
|
uint32_t blockOff = i << BLOCKSHIFT;
|
||||||
|
if (file_seek(&file, blockOff)) {
|
||||||
|
vga_text = (uint16_t*)0xb8000;
|
||||||
|
vga_text += printStr("Seek Error ", vga_text);
|
||||||
|
kbd_wait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32_t successcount = file_write(&file, writeStore[blockIdx], blockLen);
|
||||||
|
if (successcount < blockLen) {
|
||||||
|
vga_text = (uint16_t*)0xb8000;
|
||||||
|
vga_text += printStr("Write Error ", vga_text);
|
||||||
|
kbd_wait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
106
interrupt.c
106
interrupt.c
@ -2,6 +2,8 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#include "interrupt.h"
|
#include "interrupt.h"
|
||||||
|
#include "kbd.h"
|
||||||
|
#include "v86defs.h"
|
||||||
|
|
||||||
char int_nibbleToHex(uint8_t n) {
|
char int_nibbleToHex(uint8_t n) {
|
||||||
return n > 9 ? (n - 10) + 'A' : n + '0';
|
return n > 9 ? (n - 10) + 'A' : n + '0';
|
||||||
@ -20,6 +22,13 @@ void int_printDword(uint32_t v, uint16_t *buff) {
|
|||||||
int_printWord(v >> 16, buff);
|
int_printWord(v >> 16, buff);
|
||||||
int_printWord(v, &buff[4]);
|
int_printWord(v, &buff[4]);
|
||||||
}
|
}
|
||||||
|
__attribute((__no_caller_saved_registers__))
|
||||||
|
uintptr_t int_printStr(char *v, uint16_t *buff) {
|
||||||
|
char *s;
|
||||||
|
for (s = v;*s;s++,buff++)
|
||||||
|
*(char*)buff = *s;
|
||||||
|
return s - v;
|
||||||
|
}
|
||||||
|
|
||||||
struct __attribute__((__packed__)) IDTR_t {
|
struct __attribute__((__packed__)) IDTR_t {
|
||||||
uint16_t size;
|
uint16_t size;
|
||||||
@ -51,7 +60,7 @@ FARPTR i386LinearToFp(void *ptr)
|
|||||||
{
|
{
|
||||||
unsigned seg, off;
|
unsigned seg, off;
|
||||||
off = (uintptr_t) ptr & 0xffff;
|
off = (uintptr_t) ptr & 0xffff;
|
||||||
seg = ((uintptr_t) ptr >> 16);
|
seg = ((uintptr_t) ptr >> 4) & 0xf000;
|
||||||
return MK_FP(seg, off);
|
return MK_FP(seg, off);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,11 +91,17 @@ void IRQ_clear_mask(char IRQline) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
char v86_if = 0;
|
char v86_if = 0;
|
||||||
|
extern uint16_t error_screen[80*50]; // defined in kernel.c
|
||||||
extern uint16_t *ivt;
|
extern uint16_t *ivt;
|
||||||
extern void real_test();
|
extern void real_test();
|
||||||
__attribute((__no_caller_saved_registers__))
|
|
||||||
extern void kbd_wait();
|
|
||||||
extern void jmp_usermode_test();
|
extern void jmp_usermode_test();
|
||||||
|
__attribute__((__no_caller_saved_registers__))
|
||||||
|
__attribute__((__noreturn__))
|
||||||
|
extern void return_prev_task();
|
||||||
|
__attribute__((__no_caller_saved_registers__))
|
||||||
|
__attribute__((__noreturn__))
|
||||||
|
extern void error_environment(); // defined in kernel.c
|
||||||
|
extern uint32_t _gpf_eax_save;
|
||||||
#define VALID_FLAGS 0xDFF
|
#define VALID_FLAGS 0xDFF
|
||||||
__attribute__ ((interrupt))
|
__attribute__ ((interrupt))
|
||||||
void gpf_handler_v86(struct interrupt_frame *frame, unsigned long error_code) {
|
void gpf_handler_v86(struct interrupt_frame *frame, unsigned long error_code) {
|
||||||
@ -99,14 +114,14 @@ void gpf_handler_v86(struct interrupt_frame *frame, unsigned long error_code) {
|
|||||||
stack = FP_TO_LINEAR(frame->ss, frame->esp);
|
stack = FP_TO_LINEAR(frame->ss, frame->esp);
|
||||||
stack32 = (uint32_t*)stack;
|
stack32 = (uint32_t*)stack;
|
||||||
|
|
||||||
char *vga = (char*)0xb8000 + (160 * 10);
|
char *vga = (char*)error_screen + (160 * 10);
|
||||||
vga[0] = 'I'; vga[2] = 'P'; int_printWord(frame->eip, (uint16_t*)&vga[4]); vga += 14;
|
vga[0] = 'I'; vga[2] = 'P'; int_printWord(frame->eip, (uint16_t*)&vga[4]); vga += 14;
|
||||||
vga[0] = 'C'; vga[2] = 'S'; int_printWord(frame->cs, (uint16_t*)&vga[4]); vga += 14;
|
vga[0] = 'C'; vga[2] = 'S'; int_printWord(frame->cs, (uint16_t*)&vga[4]); vga += 14;
|
||||||
vga[0] = 'F'; vga[2] = 'L'; int_printDword(frame->eflags, (uint16_t*)&vga[4]); vga += 14;
|
vga[0] = 'F'; vga[2] = 'L'; int_printDword(frame->eflags, (uint16_t*)&vga[4]); vga += 14;
|
||||||
vga = (char*)0xb8000 + (160 * 11);
|
vga = (char*)error_screen + (160 * 11);
|
||||||
vga[0] = 'S'; vga[2] = 'P'; int_printWord(frame->esp, (uint16_t*)&vga[4]); vga += 14;
|
vga[0] = 'S'; vga[2] = 'P'; int_printWord(frame->esp, (uint16_t*)&vga[4]); vga += 14;
|
||||||
vga[0] = 'S'; vga[2] = 'S'; int_printWord(frame->ss, (uint16_t*)&vga[4]); vga += 14;
|
vga[0] = 'S'; vga[2] = 'S'; int_printWord(frame->ss, (uint16_t*)&vga[4]); vga += 14;
|
||||||
vga = (char*)0xb8000 + (160 * 12);
|
vga = (char*)error_screen + (160 * 12);
|
||||||
vga[0] = 'E'; vga[2] = 'S'; int_printWord(frame->es, (uint16_t*)&vga[4]); vga += 14;
|
vga[0] = 'E'; vga[2] = 'S'; int_printWord(frame->es, (uint16_t*)&vga[4]); vga += 14;
|
||||||
vga[0] = 'D'; vga[2] = 'S'; int_printWord(frame->ds, (uint16_t*)&vga[4]); vga += 14;
|
vga[0] = 'D'; vga[2] = 'S'; int_printWord(frame->ds, (uint16_t*)&vga[4]); vga += 14;
|
||||||
vga[0] = 'F'; vga[2] = 'S'; int_printWord(frame->fs, (uint16_t*)&vga[4]); vga += 14;
|
vga[0] = 'F'; vga[2] = 'S'; int_printWord(frame->fs, (uint16_t*)&vga[4]); vga += 14;
|
||||||
@ -127,8 +142,8 @@ void gpf_handler_v86(struct interrupt_frame *frame, unsigned long error_code) {
|
|||||||
// printByte(ip[i], vga);
|
// printByte(ip[i], vga);
|
||||||
// vga += (sizeof(uint8_t)*2)*2;
|
// vga += (sizeof(uint8_t)*2)*2;
|
||||||
//}
|
//}
|
||||||
vga = (char*)0xb8000 + (160*3);
|
|
||||||
uint32_t *tss_esp0 = (uint32_t*)0x20004;
|
vga = (char*)error_screen + (160*3);
|
||||||
for(;;) {
|
for(;;) {
|
||||||
switch (ip[0]) {
|
switch (ip[0]) {
|
||||||
case 0x66: // O32
|
case 0x66: // O32
|
||||||
@ -174,13 +189,15 @@ void gpf_handler_v86(struct interrupt_frame *frame, unsigned long error_code) {
|
|||||||
frame->eip = (uint16_t)(frame->eip + 1);
|
frame->eip = (uint16_t)(frame->eip + 1);
|
||||||
goto done;
|
goto done;
|
||||||
case 0xCD: // INT n
|
case 0xCD: // INT n
|
||||||
vga[0] = 'I'; vga[2]++; if (vga[2] < '0') vga[2] = '0';
|
//vga[0] = 'I'; vga[2]++; if (vga[2] < '0') vga[2] = '0';
|
||||||
switch (ip[1]) {
|
switch (ip[1]) {
|
||||||
case 0x30:
|
case 0x30:
|
||||||
asm ("mov %%eax, %%esp\nret"::"a"(*tss_esp0));
|
return_prev_task();
|
||||||
for(;;);
|
for(;;);
|
||||||
case 0x3:
|
case 0x3:
|
||||||
kbd_wait();
|
kbd_wait();
|
||||||
|
frame->eip = (uint16_t) (frame->eip + 2);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
stack = &stack[-3];
|
stack = &stack[-3];
|
||||||
frame->esp = ((frame->esp & 0xffff) - 6) & 0xffff;
|
frame->esp = ((frame->esp & 0xffff) - 6) & 0xffff;
|
||||||
@ -215,18 +232,38 @@ void gpf_handler_v86(struct interrupt_frame *frame, unsigned long error_code) {
|
|||||||
frame->eip = (uint16_t) (frame->eip + 1);
|
frame->eip = (uint16_t) (frame->eip + 1);
|
||||||
goto done;
|
goto done;
|
||||||
default:
|
default:
|
||||||
|
{
|
||||||
|
uint16_t *e = error_screen;
|
||||||
|
for (int i = 0; i < 80; i++)
|
||||||
|
e[i] = 0x0f00;
|
||||||
|
e += int_printStr("Unknown instruction caused V86 GPF(", e);
|
||||||
|
int_printWord(error_code, e);
|
||||||
|
e += 4;
|
||||||
|
e += int_printStr("):", e);
|
||||||
|
*(uint32_t*)e = 0x7f007f00;
|
||||||
|
int_printDword(*(uint32_t*)ip, e);
|
||||||
|
e += 9;
|
||||||
|
*(uint8_t*)e = '@';
|
||||||
|
e += 1;
|
||||||
|
int_printWord(frame->cs, e);
|
||||||
|
e += 4;
|
||||||
|
*(uint8_t*)e = ':';
|
||||||
|
e += 1;
|
||||||
|
int_printWord(frame->eip, e);
|
||||||
|
error_environment();
|
||||||
|
}
|
||||||
for(;;);
|
for(;;);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
done:;
|
done:;
|
||||||
vga = (char*)0xb8000 + (160 * 13);
|
vga = (char*)error_screen + (160 * 13);
|
||||||
vga[0] = 'I'; vga[2] = 'P'; int_printWord(frame->eip, (uint16_t*)&vga[4]); vga += 14;
|
vga[0] = 'I'; vga[2] = 'P'; int_printWord(frame->eip, (uint16_t*)&vga[4]); vga += 14;
|
||||||
vga[0] = 'C'; vga[2] = 'S'; int_printWord(frame->cs, (uint16_t*)&vga[4]); vga += 14;
|
vga[0] = 'C'; vga[2] = 'S'; int_printWord(frame->cs, (uint16_t*)&vga[4]); vga += 14;
|
||||||
vga[0] = 'F'; vga[2] = 'L'; int_printDword(frame->eflags, (uint16_t*)&vga[4]); vga += 14;
|
vga[0] = 'F'; vga[2] = 'L'; int_printDword(frame->eflags, (uint16_t*)&vga[4]); vga += 14;
|
||||||
vga = (char*)0xb8000 + (160 * 14);
|
vga = (char*)error_screen + (160 * 14);
|
||||||
vga[0] = 'S'; vga[2] = 'P'; int_printWord(frame->esp, (uint16_t*)&vga[4]); vga += 14;
|
vga[0] = 'S'; vga[2] = 'P'; int_printWord(frame->esp, (uint16_t*)&vga[4]); vga += 14;
|
||||||
vga[0] = 'S'; vga[2] = 'S'; int_printWord(frame->ss, (uint16_t*)&vga[4]); vga += 14;
|
vga[0] = 'S'; vga[2] = 'S'; int_printWord(frame->ss, (uint16_t*)&vga[4]); vga += 14;
|
||||||
vga = (char*)0xb8000 + (160 * 15);
|
vga = (char*)error_screen + (160 * 15);
|
||||||
vga[0] = 'E'; vga[2] = 'S'; int_printWord(frame->es, (uint16_t*)&vga[4]); vga += 14;
|
vga[0] = 'E'; vga[2] = 'S'; int_printWord(frame->es, (uint16_t*)&vga[4]); vga += 14;
|
||||||
vga[0] = 'D'; vga[2] = 'S'; int_printWord(frame->ds, (uint16_t*)&vga[4]); vga += 14;
|
vga[0] = 'D'; vga[2] = 'S'; int_printWord(frame->ds, (uint16_t*)&vga[4]); vga += 14;
|
||||||
vga[0] = 'F'; vga[2] = 'S'; int_printWord(frame->fs, (uint16_t*)&vga[4]); vga += 14;
|
vga[0] = 'F'; vga[2] = 'S'; int_printWord(frame->fs, (uint16_t*)&vga[4]); vga += 14;
|
||||||
@ -234,9 +271,20 @@ void gpf_handler_v86(struct interrupt_frame *frame, unsigned long error_code) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern void timerHandler();
|
extern void timerHandler();
|
||||||
extern void keyboardHandler();
|
|
||||||
extern void gpfHandler();
|
extern void gpfHandler();
|
||||||
|
extern void pageFaultHandler();
|
||||||
extern void unhandled_handler();
|
extern void unhandled_handler();
|
||||||
|
extern void divisionErrorHandler();
|
||||||
|
extern void boundRangeHandler();
|
||||||
|
extern void invalidOpcodeHandler();
|
||||||
|
extern void deviceNotAvailableHandler();
|
||||||
|
extern void doubleFaultHandler();
|
||||||
|
extern void invalidTSSHandler();
|
||||||
|
extern void segmentNotPresentHandler();
|
||||||
|
extern void stackSegmentHandler();
|
||||||
|
extern void x87FloatingHandler();
|
||||||
|
extern void alignmentCheckHandler();
|
||||||
|
extern void controlProtectionHandler();
|
||||||
extern void picInit();
|
extern void picInit();
|
||||||
void set_system_gate(uint8_t gate, void (*handler)()) {
|
void set_system_gate(uint8_t gate, void (*handler)()) {
|
||||||
IDT[gate].offset_1 = (uint32_t)(size_t)handler & 0xFFFF;
|
IDT[gate].offset_1 = (uint32_t)(size_t)handler & 0xFFFF;
|
||||||
@ -251,19 +299,14 @@ void set_trap_gate(uint8_t gate, void (*handler)()) {
|
|||||||
IDT[gate].type_attributes = 0x8F;
|
IDT[gate].type_attributes = 0x8F;
|
||||||
}
|
}
|
||||||
void setup_interrupts() {
|
void setup_interrupts() {
|
||||||
|
asm volatile("cli");
|
||||||
IDTR.size = 256*8 - 1;
|
IDTR.size = 256*8 - 1;
|
||||||
IDTR.offset = (uint32_t)(size_t)IDT;
|
IDTR.offset = (uint32_t)(size_t)IDT;
|
||||||
for (int i = 0; i < 256; i++) {
|
for (int i = 0; i < 256; i++) {
|
||||||
*(uint64_t*)&IDT[i] = 0;
|
*(uint64_t*)&IDT[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 9; i++) {
|
for (int i = 0; i < 256; i++) {
|
||||||
set_trap_gate(i, unhandled_handler);
|
|
||||||
}
|
|
||||||
for (int i = 10; i < 15; i++) {
|
|
||||||
set_trap_gate(i, unhandled_handler);
|
|
||||||
}
|
|
||||||
for (int i = 16; i < 22; i++) {
|
|
||||||
set_trap_gate(i, unhandled_handler);
|
set_trap_gate(i, unhandled_handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,6 +314,19 @@ void setup_interrupts() {
|
|||||||
set_system_gate(0x21, keyboardHandler);
|
set_system_gate(0x21, keyboardHandler);
|
||||||
//set_trap_gate(13, gpf_handler_v86);
|
//set_trap_gate(13, gpf_handler_v86);
|
||||||
set_trap_gate(13, gpfHandler);
|
set_trap_gate(13, gpfHandler);
|
||||||
|
set_trap_gate(14, pageFaultHandler);
|
||||||
|
|
||||||
|
set_trap_gate(0, divisionErrorHandler);
|
||||||
|
set_trap_gate(5, boundRangeHandler);
|
||||||
|
set_trap_gate(6, invalidOpcodeHandler);
|
||||||
|
set_trap_gate(7, deviceNotAvailableHandler);
|
||||||
|
set_trap_gate(8, doubleFaultHandler);
|
||||||
|
set_trap_gate(10, invalidTSSHandler);
|
||||||
|
set_trap_gate(11, segmentNotPresentHandler);
|
||||||
|
set_trap_gate(12, stackSegmentHandler);
|
||||||
|
set_trap_gate(16, x87FloatingHandler);
|
||||||
|
set_trap_gate(17, alignmentCheckHandler);
|
||||||
|
set_trap_gate(21, controlProtectionHandler);
|
||||||
|
|
||||||
asm volatile("lidt %0": : "m"(IDTR));
|
asm volatile("lidt %0": : "m"(IDTR));
|
||||||
picInit();
|
picInit();
|
||||||
@ -279,3 +335,11 @@ void setup_interrupts() {
|
|||||||
asm volatile("sti");
|
asm volatile("sti");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__attribute__((__noreturn__))
|
||||||
|
void triple_fault() {
|
||||||
|
IDTR.size = 0;
|
||||||
|
asm volatile("lidt %0": : "m"(IDTR));
|
||||||
|
asm volatile("sti");
|
||||||
|
asm volatile("int $1");
|
||||||
|
for(;;);
|
||||||
|
}
|
||||||
|
22
interrupt.h
22
interrupt.h
@ -7,28 +7,6 @@ struct interrupt_frame {
|
|||||||
uint32_t esp, ss;
|
uint32_t esp, ss;
|
||||||
uint32_t es, ds, fs, gs;
|
uint32_t es, ds, fs, gs;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Real Mode helper macros */
|
|
||||||
/* segment:offset pair */
|
|
||||||
typedef uint32_t FARPTR;
|
|
||||||
|
|
||||||
/* Make a FARPTR from a segment and an offset */
|
|
||||||
#define MK_FP(seg, off) ((FARPTR) (((uint32_t) (seg) << 16) | (uint16_t) (off)))
|
|
||||||
|
|
||||||
/* Extract the segment part of a FARPTR */
|
|
||||||
#define FP_SEG(fp) (((FARPTR) fp) >> 16)
|
|
||||||
|
|
||||||
/* Extract the offset part of a FARPTR */
|
|
||||||
#define FP_OFF(fp) (((FARPTR) fp) & 0xffff)
|
|
||||||
|
|
||||||
/* Convert a segment:offset pair to a linear address */
|
|
||||||
#define FP_TO_LINEAR(seg, off) ((void*)(uintptr_t)((((uint32_t)seg) << 4) + ((uint32_t)off)))
|
|
||||||
|
|
||||||
#define EFLAG_IF ((uint32_t)1 << 9)
|
|
||||||
#define EFLAG_VM ((uint32_t)1 << 17)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
__attribute__ ((interrupt))
|
__attribute__ ((interrupt))
|
||||||
void gpf_handler_v86(struct interrupt_frame *frame, unsigned long error_code);
|
void gpf_handler_v86(struct interrupt_frame *frame, unsigned long error_code);
|
||||||
|
|
||||||
|
108
kbd.c
Normal file
108
kbd.c
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
#include "interrupt.h"
|
||||||
|
#include "kbd.h"
|
||||||
|
|
||||||
|
uint8_t scancodesToAscii[0x3B] =
|
||||||
|
"\0\0" // 0x00 - 0x01
|
||||||
|
"1234567890" // 0x02 - 0x0B
|
||||||
|
"-=" // 0x0C - 0x0D
|
||||||
|
"\0\0" // 0x0E - 0x0F
|
||||||
|
"qwertyuiop[]" // 0x10 - 0x1B
|
||||||
|
"\0\0" // 0x1C - 0x1D
|
||||||
|
"asdfghjkl;'`" // 0x1E - 0x29
|
||||||
|
"\0" // 0x2A
|
||||||
|
"\\zxcvbnm,./" // 0x2B - 0x35
|
||||||
|
"\0" // 0x36
|
||||||
|
"*" // 0x37
|
||||||
|
"\0" // 0x38
|
||||||
|
" " // 0x39
|
||||||
|
"\0"; // 0x3A
|
||||||
|
uint8_t scancodesToAsciiShift[0x3B] =
|
||||||
|
"\0\0" // 0x00 - 0x01
|
||||||
|
"!@#$%^&*()" // 0x02 - 0x0B
|
||||||
|
"_+" // 0x0C - 0x0D
|
||||||
|
"\0\0" // 0x0E - 0x0F
|
||||||
|
"QWERTYUIOP{}" // 0x10 - 0x1B
|
||||||
|
"\0\0" // 0x1C - 0x1D
|
||||||
|
"ASDFGHJKL:\"~" // 0x1E - 0x29
|
||||||
|
"\0" // 0x2A
|
||||||
|
"|ZXCVBNM<>?" // 0x2B - 0x35
|
||||||
|
"\0" // 0x36
|
||||||
|
"*" // 0x37
|
||||||
|
"\0" // 0x38
|
||||||
|
" " // 0x39
|
||||||
|
"\0"; // 0x3A
|
||||||
|
uint8_t _KBDWAIT;
|
||||||
|
uint8_t _KEYCAPS = 0, _KEYSHIFT = 0;
|
||||||
|
uint8_t _LSTKEY_ASCII = 0, _LSTKEY_SCAN = 0;
|
||||||
|
__attribute__ ((interrupt))
|
||||||
|
void keyboardHandler(struct interrupt_frame *frame) {
|
||||||
|
uint16_t old_ds;
|
||||||
|
asm volatile(
|
||||||
|
"mov %%ds, %%bx\n"
|
||||||
|
"mov $0x10, %%ax\n"
|
||||||
|
"mov %%ax, %%ds\n"
|
||||||
|
:"=b"(old_ds)::"%ax"
|
||||||
|
);
|
||||||
|
uint8_t key;
|
||||||
|
asm volatile("inb $0x60, %%al":"=a"(key));
|
||||||
|
if (key == 0x3A) { // caps lock press
|
||||||
|
_KEYCAPS = !_KEYCAPS;
|
||||||
|
} else if (key == 0x2A || key == 0x36) { // left and right shift press
|
||||||
|
_KEYSHIFT = 1;
|
||||||
|
} else if (key == 0xAA || key == 0xB6) { // left and right shift release
|
||||||
|
_KEYSHIFT = 0;
|
||||||
|
} else if (key < 0x3B) {
|
||||||
|
uint8_t ascii = _KEYCAPS != _KEYSHIFT ?
|
||||||
|
scancodesToAsciiShift[key] :
|
||||||
|
scancodesToAscii[key];
|
||||||
|
if (ascii) {
|
||||||
|
_LSTKEY_ASCII = ascii;
|
||||||
|
_LSTKEY_SCAN = key;
|
||||||
|
_KBDWAIT = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_LSTKEY_ASCII = 0;
|
||||||
|
_LSTKEY_SCAN = key;
|
||||||
|
}
|
||||||
|
asm volatile("outb %%al, $0x20"::"a"(0x20));
|
||||||
|
asm volatile(
|
||||||
|
"mov %%ax, %%ds\n"
|
||||||
|
::"a"(old_ds)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute((__no_caller_saved_registers__))
|
||||||
|
void kbd_clear() {
|
||||||
|
_KBDWAIT = 0;
|
||||||
|
_LSTKEY_ASCII = 0;
|
||||||
|
_LSTKEY_SCAN = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute((__no_caller_saved_registers__))
|
||||||
|
void kbd_wait() {
|
||||||
|
_KBDWAIT = 0;
|
||||||
|
while(!_KBDWAIT) {
|
||||||
|
asm volatile("hlt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute((__no_caller_saved_registers__))
|
||||||
|
uint8_t get_key() {
|
||||||
|
while(!_LSTKEY_ASCII) {
|
||||||
|
asm volatile("hlt");
|
||||||
|
}
|
||||||
|
uint8_t k = _LSTKEY_ASCII;
|
||||||
|
_LSTKEY_ASCII = 0;
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
__attribute((__no_caller_saved_registers__))
|
||||||
|
uint16_t get_scancode() {
|
||||||
|
while(!_LSTKEY_SCAN) {
|
||||||
|
asm volatile("hlt");
|
||||||
|
}
|
||||||
|
uint16_t k = _LSTKEY_SCAN | (_LSTKEY_ASCII << 8);
|
||||||
|
_LSTKEY_SCAN = 0;
|
||||||
|
_LSTKEY_ASCII = 0;
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
48
kbd.h
Normal file
48
kbd.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "interrupt.h"
|
||||||
|
|
||||||
|
__attribute((__no_caller_saved_registers__))
|
||||||
|
void kbd_wait();
|
||||||
|
|
||||||
|
__attribute((__no_caller_saved_registers__))
|
||||||
|
void kbd_clear();
|
||||||
|
|
||||||
|
__attribute((__no_caller_saved_registers__))
|
||||||
|
uint8_t get_key();
|
||||||
|
|
||||||
|
__attribute((__no_caller_saved_registers__))
|
||||||
|
uint16_t get_scancode();
|
||||||
|
|
||||||
|
__attribute__ ((interrupt))
|
||||||
|
void keyboardHandler(struct interrupt_frame *frame);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
KEY_ESCAPE=0x01, KEY_1=0x02, KEY_2=0x03,
|
||||||
|
KEY_3=0x04, KEY_4=0x05, KEY_5=0x06, KEY_6=0x07,
|
||||||
|
KEY_7=0x08, KEY_8=0x09, KEY_9=0x0A, KEY_0=0x0B,
|
||||||
|
KEY_HYPHEN=0x0C, KEY_EQUALS=0x0D, KEY_BACKSPACE=0x0E, KEY_TAB=0x0F,
|
||||||
|
KEY_Q=0x10, KEY_W=0x11, KEY_E=0x12, KEY_R=0x13,
|
||||||
|
KEY_T=0x14, KEY_Y=0x15, KEY_U=0x16, KEY_I=0x17,
|
||||||
|
KEY_O=0x18, KEY_P=0x19, KEY_SQBRKLEFT=0x1A, KEY_SQBRKRIGHT=0x1B,
|
||||||
|
KEY_ENTER=0x1C, KEY_CTRL=0x1D, KEY_A=0x1E, KEY_S=0x1F,
|
||||||
|
KEY_D=0x20, KEY_F=0x21, KEY_G=0x22, KEY_H=0x23,
|
||||||
|
KEY_J=0x24, KEY_K=0x25, KEY_L=0x26, KEY_SEMICOLON=0x27,
|
||||||
|
KEY_QUOTE=0x28, KEY_BACKTICK=0x29, KEY_SHIFTLEFT=0x2A, KEY_BACKSTROKE=0x2B,
|
||||||
|
KEY_Z=0x2C, KEY_X=0x2D, KEY_C=0x2E, KEY_V=0x2F,
|
||||||
|
KEY_B=0x30, KEY_N=0x31, KEY_M=0x32, KEY_COMMA=0x33,
|
||||||
|
KEY_PERIOD=0x34, KEY_SLASH=0x35, KEY_SHIFTRIGHT=0x36, KEY_KPDSTAR=0x37,
|
||||||
|
KEY_ALT=0x38, KEY_SPACE=0x39, KEY_CAPSLOCK=0x3A, KEY_F1=0x3B,
|
||||||
|
KEY_F2=0x3C, KEY_F3=0x3D, KEY_F4=0x3E, KEY_F5=0x3F,
|
||||||
|
KEY_F6=0x40, KEY_F7=0x41, KEY_F8=0x42, KEY_F9=0x43,
|
||||||
|
KEY_F10=0x44, KEY_NUMLOCK=0x45, KEY_SCROLLLOCK=0x46, KEY_KPD7=0x47,
|
||||||
|
KEY_KPD8=0x48, KEY_KPD9=0x49, KEY_KPDMINUS=0x4A, KEY_KPD4=0x4B,
|
||||||
|
KEY_KPD5=0x4C, KEY_KPD6=0x4D, KEY_KPDPLUS=0x4E, KEY_KPD1=0x4F,
|
||||||
|
KEY_KPD2=0x50, KEY_KPD3=0x51, KEY_KPD0=0x52, KEY_KPDPERIOD=0x53,
|
||||||
|
KEY_F11=0x57, KEY_F12=0x58,
|
||||||
|
KEY_DOWN=KEY_KPD2,KEY_UP=KEY_KPD8,
|
||||||
|
KEY_LEFT=KEY_KPD4,KEY_RIGHT=KEY_KPD6,
|
||||||
|
KEY_PGUP=KEY_KPD9,KEY_PGDOWN=KEY_KPD3,
|
||||||
|
KEY_HOME=KEY_KPD7,KEY_END=KEY_KPD1,
|
||||||
|
KEY_INSERT=KEY_KPD0, KEY_DELETE=KEY_KPDPERIOD
|
||||||
|
} SCANCODE;
|
544
kernel.c
544
kernel.c
@ -1,9 +1,17 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "file.h"
|
||||||
|
#include "fs.h"
|
||||||
#include "print.h"
|
#include "print.h"
|
||||||
#include "interrupt.h"
|
#include "interrupt.h"
|
||||||
|
#include "kbd.h"
|
||||||
#include "tss.c"
|
#include "tss.h"
|
||||||
|
#include "paging.h"
|
||||||
|
#include "v86defs.h"
|
||||||
|
#include "tests.h"
|
||||||
|
#include "progs.h"
|
||||||
|
#include "helper.h"
|
||||||
|
#include "disk.h"
|
||||||
|
|
||||||
typedef unsigned short word;
|
typedef unsigned short word;
|
||||||
|
|
||||||
@ -17,6 +25,11 @@ char check_sse() {
|
|||||||
asm("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1));
|
asm("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1));
|
||||||
return (edx & (1 << 25)) != 0;
|
return (edx & (1 << 25)) != 0;
|
||||||
}
|
}
|
||||||
|
char check_cmov() {
|
||||||
|
uint32_t eax, ebx, ecx, edx;
|
||||||
|
asm("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1));
|
||||||
|
return (edx & (1 << 15)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
void enable_sse() {
|
void enable_sse() {
|
||||||
asm volatile(
|
asm volatile(
|
||||||
@ -31,59 +44,490 @@ void enable_sse() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_flags() {
|
uint32_t get_flags() {
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
asm volatile("pushfd\npop %%eax":"=a"(flags));
|
asm volatile("pushfd\npop %%eax":"=a"(flags));
|
||||||
printDword(flags, 0xB8000 + (160*4) + 50);
|
return flags;
|
||||||
}
|
}
|
||||||
void print_cr0() {
|
uint32_t get_cr0() {
|
||||||
uint32_t reg;
|
uint32_t reg;
|
||||||
asm volatile("mov %%cr0, %%eax":"=a"(reg));
|
asm volatile("mov %%cr0, %%eax":"=a"(reg));
|
||||||
printDword(reg, 0xB8000 + (160*5) + 50);
|
return reg;
|
||||||
}
|
}
|
||||||
void print_cr3() {
|
uint32_t get_cr3() {
|
||||||
uint32_t reg;
|
uint32_t reg;
|
||||||
asm volatile("mov %%cr3, %%eax":"=a"(reg));
|
asm volatile("mov %%cr3, %%eax":"=a"(reg));
|
||||||
printDword(reg, 0xB8000 + (160*5) + 50 + 8*2 + 2);
|
return reg;
|
||||||
}
|
}
|
||||||
void print_cr4() {
|
uint32_t get_cr4() {
|
||||||
uint32_t reg;
|
uint32_t reg;
|
||||||
asm volatile("mov %%cr4, %%eax":"=a"(reg));
|
asm volatile("mov %%cr4, %%eax":"=a"(reg));
|
||||||
printDword(reg, 0xB8000 + (160*5) + 50 + 8*4 + 4);
|
return reg;
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute((__no_caller_saved_registers__))
|
extern char _edata, _v86code, _ev86code, _bstart, _bend;
|
||||||
extern void enter_v86(uint32_t ss, uint32_t esp, uint32_t cs, uint32_t eip);
|
void setup_binary() {
|
||||||
extern void v86Code();
|
// Put V86 code in proper place based on linker
|
||||||
__attribute((__no_caller_saved_registers__))
|
char *s = &_edata;
|
||||||
extern char *jmp_usermode_test();
|
char *d = &_v86code;
|
||||||
|
while (d < &_ev86code)
|
||||||
|
*d++ = *s++;
|
||||||
|
|
||||||
|
// Clear BSS area
|
||||||
|
for (d = &_bstart; d < &_bend; d++)
|
||||||
|
*d = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t error_screen[80*50]; // 50-line VGA screen of error content
|
||||||
|
|
||||||
|
extern uint16_t *ivt;
|
||||||
|
uint16_t ivt_bkup[0x200];
|
||||||
|
uint8_t bios_bkup[0x40000];
|
||||||
|
void backup_ivtbios() {
|
||||||
|
for (int i = 0; i < 0x200; i++)
|
||||||
|
ivt_bkup[i] = ivt[i];
|
||||||
|
for (int i = 0; i < 0x40000; i++)
|
||||||
|
bios_bkup[i] = ((uint8_t*)0xC0000)[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should only be used during fault handling, as it is expensive
|
||||||
|
void ensure_v86env() {
|
||||||
|
for (int i = 0; i < 0x200; i++)
|
||||||
|
ivt[i] = ivt_bkup[i];
|
||||||
|
for (int i = 0; i < 0x40000; i++)
|
||||||
|
((uint8_t*)0xC0000)[i] = bios_bkup[i];
|
||||||
|
char *s = &_edata;
|
||||||
|
char *d = &_v86code;
|
||||||
|
while (d < &_ev86code)
|
||||||
|
*d++ = *s++;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t _ERRORCODE = 0;
|
||||||
|
__attribute__((__no_caller_saved_registers__))
|
||||||
|
uint32_t check_error_code() {
|
||||||
|
uint32_t v = _ERRORCODE;
|
||||||
|
_ERRORCODE = 0;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((__no_caller_saved_registers__))
|
||||||
|
__attribute__((__noreturn__))
|
||||||
|
extern void return_prev_task();
|
||||||
|
__attribute__((__no_caller_saved_registers__))
|
||||||
|
__attribute__((__noreturn__))
|
||||||
|
void error_environment(uint32_t stack0, uint32_t stack1, uint32_t stack2, uint32_t stack3, uint32_t stack4, uint32_t stack5) {
|
||||||
|
ensure_v86env();
|
||||||
|
setup_interrupts(); // just in case
|
||||||
|
for (int i = 0; i < 80*50; i++)
|
||||||
|
if (!(error_screen[i] & 0xFF00))
|
||||||
|
error_screen[i] = 0x0f00 | (error_screen[i] & 0x00FF);
|
||||||
|
union V86Regs_t regs;
|
||||||
|
FARPTR v86_entry = i386LinearToFp(v86TextMode);
|
||||||
|
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), ®s);
|
||||||
|
char str0[] = "Oh noes!!! System error! ;c STKDMP:";
|
||||||
|
printStr(str0, &error_screen[80]);
|
||||||
|
uint16_t *tmp = &error_screen[80+sizeof(str0)-1];
|
||||||
|
tmp += printDword(stack0, tmp);
|
||||||
|
tmp++;
|
||||||
|
tmp += printDword(stack1, tmp);
|
||||||
|
tmp++;
|
||||||
|
tmp += printDword(stack2, tmp);
|
||||||
|
tmp = &error_screen[80*2+sizeof(str0)-1];
|
||||||
|
tmp += printDword(stack3, tmp);
|
||||||
|
tmp++;
|
||||||
|
tmp += printDword(stack4, tmp);
|
||||||
|
tmp++;
|
||||||
|
tmp += printDword(stack5, tmp);
|
||||||
|
printStr("Press E for a fun recovery :3", &error_screen[80*2]);
|
||||||
|
printStr("Press R to return to previous task", &error_screen[80*3]);
|
||||||
|
uint16_t *vga_text = ((uint16_t*)0xB8000);
|
||||||
|
for (int i = 0; i < 80*50; i++)
|
||||||
|
vga_text[i] = error_screen[i];
|
||||||
|
for(;;) {
|
||||||
|
uint8_t key = get_scancode() & 0xff;
|
||||||
|
if (key == KEY_E) {
|
||||||
|
v86_entry = i386LinearToFp(v86TransFlag);
|
||||||
|
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), ®s);
|
||||||
|
}
|
||||||
|
if (key == KEY_R) break;
|
||||||
|
}
|
||||||
|
// reset error screen
|
||||||
|
for (int i = 0; i < (80*50)/2; i++)
|
||||||
|
((uint32_t*)error_screen)[i] = 0x0f000f00;
|
||||||
|
_ERRORCODE = -1;
|
||||||
|
return_prev_task();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t GetFreeStack() {
|
||||||
|
uint32_t stack;
|
||||||
|
asm volatile("mov %%esp,%%eax":"=a"(stack));
|
||||||
|
stack = ((stack - 0x2000) / 0x1000) * 0x1000;
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Real Mode Accessible (First MB)
|
Real Mode Accessible (First MB)
|
||||||
00000 - 02000 IVT
|
00000 - 00400 IVT (1kB)
|
||||||
01000 - 04000 Paging
|
00400 - 01000 Unused (3kB)
|
||||||
04000 - 07C00 Free
|
01000 - 04000 Free (12kB)
|
||||||
07C00 - 08000 Boot
|
04000 - 07C00 V86 Code (15kB)
|
||||||
08000 - 20000 Kernel Code
|
07C00 - 08000 Boot & V86 Code (512B)
|
||||||
20000 - 22080 TSS
|
08000 - 20000 Free (96kB)
|
||||||
80000 - 90000 Real Mode Stack
|
20000 - 30000 Disk Buffer (64kB)
|
||||||
90000 - A0000 Free
|
30000 - 40000 V86 Data (64kB)
|
||||||
A0000 - FFFFF BIOS Area
|
40000 - 80000 Free (256kB)
|
||||||
|
80000 - 90000 V86 Stack (64kB)
|
||||||
|
90000 - A0000 Free (64kB)
|
||||||
|
A0000 - C0000 VGA (128kB)
|
||||||
|
C0000 - FFFFF BIOS Area (256kB)
|
||||||
Protected Only (1MB+)
|
Protected Only (1MB+)
|
||||||
100000 - Free
|
100000 - 200000 Kernel Code (1mB)
|
||||||
|
200000 - 200080 TSS (128B)
|
||||||
|
200080 - 202080 TSS IOMAP (8kB)
|
||||||
|
202080 - 208000 Free (~24kB)
|
||||||
|
208000 - 240000 Kernel File Stack (224kB)
|
||||||
|
240000 - 280000 Active Filesystems (128kB)
|
||||||
|
280000 - 300000 Disk Cache (512kB)
|
||||||
|
300000 - 310000 Task Stack (64kB)
|
||||||
|
310000 - 320000 Interrupt Stack (64kB)
|
||||||
|
320000 - 400000 Kernel Stack (896kB)
|
||||||
|
400000 - 700000 Usermode Code (3mB)
|
||||||
|
700000 - 800000 Usermode Stack (1mB)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
void DrawScreen(uint16_t *vga) {
|
||||||
|
uint16_t *vga_text = vga;
|
||||||
|
// clear screen
|
||||||
|
for (int i = 0; i < 80*25; i++)
|
||||||
|
vga_text[i] = 0x1f00;
|
||||||
|
// draw border
|
||||||
|
for (int c = 1; c < 79; c++) {
|
||||||
|
vga_text[c] = 0x1fc4; // top line
|
||||||
|
vga_text[(2*80)+c] = 0x1fc4; // 3rd line
|
||||||
|
vga_text[(24*80)+c] = 0x1fc4; // bottom line
|
||||||
|
}
|
||||||
|
for (int l = 1; l < 24; l++) {
|
||||||
|
vga_text[80*l] = 0x1fb3;
|
||||||
|
vga_text[(80*l)+79] = 0x1fb3;
|
||||||
|
}
|
||||||
|
vga_text[0] = 0x1fda;
|
||||||
|
vga_text[79] = 0x1fbf;
|
||||||
|
vga_text[2*80] = 0x1fc3;
|
||||||
|
vga_text[2*80+79] = 0x1fb4;
|
||||||
|
vga_text[24*80] = 0x1fc0;
|
||||||
|
vga_text[24*80+79] = 0x1fd9;
|
||||||
|
// name
|
||||||
|
vga_text[80+34] = 0x1f00 | '-';
|
||||||
|
vga_text[80+35] = 0x1f00 | ' ';
|
||||||
|
vga_text[80+36] = 0x1f00 | 'L';
|
||||||
|
vga_text[80+37] = 0x1f00 | 'u';
|
||||||
|
vga_text[80+38] = 0x1f00 | 'c';
|
||||||
|
vga_text[80+39] = 0x1f00 | 'i';
|
||||||
|
vga_text[80+40] = 0x1f00 | 'a';
|
||||||
|
vga_text[80+41] = 0x1f00 | 'O';
|
||||||
|
vga_text[80+42] = 0x1f00 | 'S';
|
||||||
|
vga_text[80+43] = 0x1f00 | ' ';
|
||||||
|
vga_text[80+44] = 0x1f00 | '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetPalette() {
|
||||||
|
union V86Regs_t regs;
|
||||||
|
regs.w.bx = 1; // index
|
||||||
|
regs.h.dh = 0x3D;
|
||||||
|
regs.h.ch = 0x2A;
|
||||||
|
regs.h.cl = 0x2E;
|
||||||
|
regs.w.ax = 0x1010; // set DAC color register
|
||||||
|
V8086Int(0x10, ®s);
|
||||||
|
}
|
||||||
|
// NOTE Does not set text mode
|
||||||
|
void RestoreVGA() {
|
||||||
|
SetVideo25Lines();
|
||||||
|
SetCursorDisabled();
|
||||||
|
SetPalette();
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t fileCount, fileOffset;
|
||||||
|
// We store dir entries in usermode space,
|
||||||
|
// which is nice because there's nothing after,
|
||||||
|
// but it does mean we need to reload the dir
|
||||||
|
// after every task called. This might be fine,
|
||||||
|
// since the task might have modified the directory.
|
||||||
|
extern char _USERMODE;
|
||||||
|
dirent *const DirEntries = (dirent*)&_USERMODE;
|
||||||
|
#define MAXDISPFILES 16
|
||||||
|
void PrintFileList(uint16_t *vga) {
|
||||||
|
uint16_t *vga_text = &((uint16_t *)vga)[80*6+3];
|
||||||
|
for (int i = 0; (i + fileOffset) < fileCount && i < MAXDISPFILES; i++) {
|
||||||
|
dirent *de = &DirEntries[i + fileOffset];
|
||||||
|
de->name[de->namelen < 20 ? de->namelen : 20] = 0;
|
||||||
|
vga_text += printStr(de->name, vga_text);
|
||||||
|
vga_text += printStr(" ", vga_text);
|
||||||
|
vga_text += printDec(de->size, vga_text);
|
||||||
|
*(uint8_t*)vga_text++ = 'B';
|
||||||
|
vga_text = nextLine(vga_text, vga) + 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
char IsDir(dirent *de) {
|
||||||
|
return de->type == FT_DIR;
|
||||||
|
}
|
||||||
|
void ScancodeTest() {
|
||||||
|
uint16_t *vga = (uint16_t*)0xb8000;
|
||||||
|
uint16_t scancode;
|
||||||
|
while(((scancode = get_scancode()) & 0xff) != KEY_F1) {
|
||||||
|
vga += printWord(scancode, vga);
|
||||||
|
vga++;
|
||||||
|
if ((uintptr_t)vga >= 0xb8000+80*25*2)
|
||||||
|
vga = (uint16_t*)0xb8000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extern void create_child(uint32_t esp, uint32_t eip, uint32_t argc, ...);
|
||||||
|
uint16_t FileSelectScreen[80*25];
|
||||||
|
char ValidFilesystems[256];
|
||||||
|
void FileSelect() {
|
||||||
|
ActiveFilesystemBitmap(ValidFilesystems);
|
||||||
|
uint8_t currentFsId = 0;
|
||||||
|
char current_path[80];
|
||||||
|
uintptr_t current_path_end;
|
||||||
|
for (int i = 0; i < sizeof(current_path); i++)
|
||||||
|
current_path[i] = 0;
|
||||||
|
current_path[0] = '0';
|
||||||
|
current_path[1] = '/';
|
||||||
|
current_path_end = 2;
|
||||||
|
fileCount = 5;
|
||||||
|
uint16_t *vga_text = (uint16_t *)FileSelectScreen;
|
||||||
|
int32_t fileHovered = 0;
|
||||||
|
fileOffset = 0;
|
||||||
|
for (char reload = 1;;) {
|
||||||
|
DrawScreen(vga_text);
|
||||||
|
// Info line (4)
|
||||||
|
{
|
||||||
|
uint16_t *vga = &vga_text[80*4 + 79 - 24];
|
||||||
|
printStr("X to view in hex", vga);
|
||||||
|
vga += 80;
|
||||||
|
printStr("T to view as text", vga);
|
||||||
|
vga += 80;
|
||||||
|
printStr("P to load as program", vga);
|
||||||
|
vga += 80;
|
||||||
|
printStr("O to open directory", vga);
|
||||||
|
vga += 80;
|
||||||
|
printStr("S to switch volume", vga);
|
||||||
|
vga += 80;
|
||||||
|
printStr("F4 to run tests", vga);
|
||||||
|
}
|
||||||
|
printStr(current_path, &vga_text[80*4 + 2]);
|
||||||
|
for (int i = 2; i < 15; i++)
|
||||||
|
*(uint8_t*)&vga_text[80*5 + i] = '-';
|
||||||
|
DIR dir;
|
||||||
|
if (reload) {
|
||||||
|
current_path[current_path_end] = 0;
|
||||||
|
dir_open(&dir, current_path);
|
||||||
|
GetFileList(&dir, DirEntries, &fileCount, INT32_MAX);
|
||||||
|
reload = 0;
|
||||||
|
}
|
||||||
|
if (fileHovered >= fileCount) {
|
||||||
|
fileOffset = fileCount - MAXDISPFILES;
|
||||||
|
fileHovered = fileCount - 1;
|
||||||
|
}
|
||||||
|
if ((fileHovered - fileOffset) >= MAXDISPFILES)
|
||||||
|
fileOffset = fileHovered - MAXDISPFILES + 1;
|
||||||
|
else if ((fileHovered - fileOffset) < 0)
|
||||||
|
fileOffset = fileHovered;
|
||||||
|
PrintFileList(vga_text);
|
||||||
|
for (int i = 6; i < 24; i++) {
|
||||||
|
*(uint8_t*)&vga_text[80*i+2] = ' ';
|
||||||
|
}
|
||||||
|
*(uint8_t*)&vga_text[80*(6+(fileHovered-fileOffset))+2] = '>';
|
||||||
|
// Copy to real VGA
|
||||||
|
for (int i = 0; i < 80*25; i++)
|
||||||
|
((uint16_t*)0xb8000)[i] = vga_text[i];
|
||||||
|
uint16_t key = get_scancode();
|
||||||
|
uint8_t path[13];
|
||||||
|
switch (key & 0xff) { // scancode component
|
||||||
|
case KEY_DOWN: // down
|
||||||
|
fileHovered++;
|
||||||
|
if (fileHovered >= fileCount) { fileHovered = 0; fileOffset = 0; }
|
||||||
|
break;
|
||||||
|
case KEY_UP: // up
|
||||||
|
fileHovered--;
|
||||||
|
if (fileHovered < 0) fileHovered = fileCount - 1;
|
||||||
|
break;
|
||||||
|
case KEY_F4:
|
||||||
|
create_child(GetFreeStack(), (uintptr_t)RunTests, 0);
|
||||||
|
// Set Text mode, tests might return in gfx
|
||||||
|
{
|
||||||
|
union V86Regs_t regs;
|
||||||
|
regs.w.ax = 3;
|
||||||
|
V8086Int(0x10, ®s);
|
||||||
|
}
|
||||||
|
RestoreVGA();
|
||||||
|
reload = 1;
|
||||||
|
break;
|
||||||
|
case KEY_P:
|
||||||
|
if (IsDir(&DirEntries[fileHovered])) break;
|
||||||
|
for (int i = 0; i < DirEntries[fileHovered].namelen; i++)
|
||||||
|
current_path[current_path_end + i] = DirEntries[fileHovered].name[i];
|
||||||
|
current_path[current_path_end + DirEntries[fileHovered].namelen] = 0;
|
||||||
|
create_child(GetFreeStack(), (uintptr_t)ProgramLoadTest, 1, current_path);
|
||||||
|
current_path[current_path_end] = 0;
|
||||||
|
RestoreVGA();
|
||||||
|
reload = 1;
|
||||||
|
break;
|
||||||
|
case KEY_X:
|
||||||
|
if (IsDir(&DirEntries[fileHovered])) break;
|
||||||
|
for (int i = 0; i < DirEntries[fileHovered].namelen; i++)
|
||||||
|
current_path[current_path_end + i] = DirEntries[fileHovered].name[i];
|
||||||
|
current_path[current_path_end + DirEntries[fileHovered].namelen] = 0;
|
||||||
|
create_child(GetFreeStack(), (uintptr_t)HexEditor, 1, current_path);
|
||||||
|
current_path[current_path_end] = 0;
|
||||||
|
RestoreVGA();
|
||||||
|
reload = 1;
|
||||||
|
break;
|
||||||
|
case KEY_T:
|
||||||
|
if (IsDir(&DirEntries[fileHovered])) break;
|
||||||
|
//TextViewTest(path, &vi);
|
||||||
|
for (int i = 0; i < DirEntries[fileHovered].namelen; i++)
|
||||||
|
current_path[current_path_end + i] = DirEntries[fileHovered].name[i];
|
||||||
|
current_path[current_path_end + DirEntries[fileHovered].namelen] = 0;
|
||||||
|
create_child(GetFreeStack(), (uintptr_t)TextViewTest, 1, current_path);
|
||||||
|
current_path[current_path_end] = 0;
|
||||||
|
RestoreVGA();
|
||||||
|
reload = 1;
|
||||||
|
break;
|
||||||
|
case KEY_O:
|
||||||
|
case 0x9C: // enter release
|
||||||
|
if (IsDir(&DirEntries[fileHovered])) {
|
||||||
|
uint8_t tmp_path[80];
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < DirEntries[fileHovered].namelen && i < sizeof(tmp_path)-1; i++)
|
||||||
|
tmp_path[i] = DirEntries[fileHovered].name[i];
|
||||||
|
tmp_path[i] = 0;
|
||||||
|
}
|
||||||
|
if ((*(uint32_t*)tmp_path & 0xffff) == ('.' | 0x0000)) {
|
||||||
|
// Current dir, do nothing
|
||||||
|
break;
|
||||||
|
} else if ((*(uint32_t*)tmp_path & 0xffffff) == ('.' | '.' << 8 | 0x000000)) {
|
||||||
|
// Up one directory
|
||||||
|
for (current_path_end--;current_path_end >= 1 && current_path[current_path_end - 1] != '/'; current_path_end--);
|
||||||
|
current_path[current_path_end] = 0;
|
||||||
|
reload = 1;
|
||||||
|
fileHovered = 0;
|
||||||
|
fileOffset = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (int i = 0; (i + current_path_end) < sizeof(current_path); i++)
|
||||||
|
current_path[current_path_end + i] = tmp_path[i];
|
||||||
|
for (;current_path_end < sizeof(current_path) - 2 && current_path[current_path_end]; current_path_end++);
|
||||||
|
current_path[current_path_end] = '/';
|
||||||
|
current_path_end++;
|
||||||
|
current_path[current_path_end] = 0;
|
||||||
|
reload = 1;
|
||||||
|
fileHovered = 0;
|
||||||
|
fileOffset = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KEY_S:
|
||||||
|
// Next filesystem TODO Support over 077 I'm so lazy right now
|
||||||
|
for (currentFsId = (currentFsId + 1) % 64; !ValidFilesystems[currentFsId]; currentFsId = (currentFsId + 1) % 64);
|
||||||
|
if (currentFsId < 8) {
|
||||||
|
current_path[0] = '0' + currentFsId;
|
||||||
|
current_path[1] = '/';
|
||||||
|
current_path[2] = 0;
|
||||||
|
current_path_end = 2;
|
||||||
|
} else {
|
||||||
|
current_path[0] = '0' + (currentFsId >> 3);
|
||||||
|
current_path[1] = '0' + (currentFsId & 7);
|
||||||
|
current_path[2] = '/';
|
||||||
|
current_path[3] = 0;
|
||||||
|
current_path_end = 3;
|
||||||
|
}
|
||||||
|
reload = 1;
|
||||||
|
fileHovered = 0;
|
||||||
|
fileOffset = 0;
|
||||||
|
break;
|
||||||
|
case KEY_F6:
|
||||||
|
ScancodeTest();
|
||||||
|
reload = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int MakeSystemVolume(uint8_t sysPartition);
|
||||||
|
void MakeMBRPartitions();
|
||||||
|
void SystemRun(uint8_t sysPartition) {
|
||||||
|
uint16_t *vga_text = (word *)0xb8000;
|
||||||
|
RestoreVGA();
|
||||||
|
DrawScreen((uint16_t*)0xb8000);
|
||||||
|
|
||||||
|
InitDisk();
|
||||||
|
|
||||||
|
// Check for FAT partition
|
||||||
|
{
|
||||||
|
while (1) {
|
||||||
|
create_child(GetFreeStack(), (uintptr_t)MakeSystemVolume, 1, sysPartition);
|
||||||
|
if (!check_error_code()) break;
|
||||||
|
vga_text = &((word*)0xb8000)[80*4 + 2];
|
||||||
|
vga_text += printStr("Error loading file select. Ensure the disk has a valid MBR and FAT partition.", vga_text);
|
||||||
|
vga_text = &((word*)0xb8000)[80*5 + 2];
|
||||||
|
vga_text += printStr("Press R to retry.", vga_text);
|
||||||
|
for (;(get_scancode() & 0xff) != KEY_R;);
|
||||||
|
}
|
||||||
|
create_child(GetFreeStack(), (uintptr_t)MakeMBRPartitions, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
create_child(GetFreeStack(), (uintptr_t)FileSelect, 0);
|
||||||
|
// should never return, so if it does,
|
||||||
|
// we have an error
|
||||||
|
{
|
||||||
|
union V86Regs_t regs;
|
||||||
|
FARPTR v86_entry = i386LinearToFp(v86TextMode);
|
||||||
|
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), ®s);
|
||||||
|
}
|
||||||
|
RestoreVGA();
|
||||||
|
DrawScreen((uint16_t*)0xb8000);
|
||||||
|
vga_text = &((word*)0xb8000)[80*4 + 2];
|
||||||
|
vga_text += printStr("Error loading file select. Ensure the disk has a valid MBR and FAT partition.", vga_text);
|
||||||
|
vga_text = &((word*)0xb8000)[80*5 + 2];
|
||||||
|
vga_text += printStr("Press R to retry.", vga_text);
|
||||||
|
for (;(get_scancode() & 0xff) != KEY_R;);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((__noreturn__))
|
||||||
|
extern void triple_fault();
|
||||||
|
uint32_t kernel_check = 0x12345678;
|
||||||
void start() {
|
void start() {
|
||||||
word *vga_text = (word *)0xb8000;
|
word *vga_text = (word *)0xb8000;
|
||||||
char h[] = "LuciaOS";
|
char h[] = "LuciaOS";
|
||||||
for (int i = 0; i < sizeof(h); i++)
|
for (int i = 0; i < sizeof(h); i++)
|
||||||
*(char *)&vga_text[i] = h[i];
|
*(char *)&vga_text[i] = h[i];
|
||||||
|
vga_text = &vga_text[80];
|
||||||
|
|
||||||
|
// DL *should* be preserved
|
||||||
|
uint16_t boot_dx;
|
||||||
|
asm volatile("nop":"=d"(boot_dx));
|
||||||
|
|
||||||
|
if (!check_cmov()) {
|
||||||
|
char cmov_err[] = "NO CMOV";
|
||||||
|
for (int i = 0; i < sizeof(cmov_err); i++)
|
||||||
|
*(char *)&vga_text[i] = cmov_err[i];
|
||||||
|
for (;;) asm volatile("hlt");
|
||||||
|
}
|
||||||
|
|
||||||
|
vga_text += printWord(boot_dx, vga_text);
|
||||||
|
vga_text++;
|
||||||
|
|
||||||
uint32_t o;
|
uint32_t o;
|
||||||
asm("mov %%esp, %%eax" : "=a"(o) : :);
|
asm("mov %%esp, %%eax" : "=a"(o) : :);
|
||||||
printDword(o, (uint16_t *)&vga_text[80]);
|
vga_text += printDword(o, vga_text);
|
||||||
|
vga_text++;
|
||||||
asm("mov %%ebp, %%eax" : "=a"(o) : :);
|
asm("mov %%ebp, %%eax" : "=a"(o) : :);
|
||||||
printDword(o, (uint16_t *)&vga_text[160]);
|
vga_text += printDword(o, vga_text);
|
||||||
|
vga_text++;
|
||||||
|
|
||||||
//char c[] = "APIC support: ";
|
//char c[] = "APIC support: ";
|
||||||
//char apic;
|
//char apic;
|
||||||
@ -92,27 +536,37 @@ void start() {
|
|||||||
// *(char *)&vga_text[i+(80*3)] = c[i];
|
// *(char *)&vga_text[i+(80*3)] = c[i];
|
||||||
//if (!apic) return;
|
//if (!apic) return;
|
||||||
|
|
||||||
char sse_str[] = "SSE support: ";
|
//char sse_str[] = "SSE support: ";
|
||||||
char sse;
|
//vga_text += printStr("SSE: ", vga_text);
|
||||||
printByte(sse = check_sse(), (uint16_t*)&vga_text[80*4 + sizeof(sse_str) - 1]);
|
//char sse = check_sse();
|
||||||
for (int i = 0; i < sizeof(sse_str); i++)
|
//if (!sse) {
|
||||||
*(char *)&vga_text[i+(80*4)] = sse_str[i];
|
// *vga_text = 'N';
|
||||||
if (!sse) return;
|
//}
|
||||||
enable_sse();
|
//vga_text += printStr("Y ", vga_text);
|
||||||
|
//enable_sse();
|
||||||
|
|
||||||
// edit
|
//print_flags();
|
||||||
|
vga_text += printStr("CR0:", vga_text);
|
||||||
|
vga_text += printDword(get_cr0(), vga_text);
|
||||||
|
vga_text++;
|
||||||
|
//print_cr3();
|
||||||
|
//print_cr4();
|
||||||
|
|
||||||
|
// Setup system
|
||||||
|
setup_binary();
|
||||||
setup_interrupts();
|
setup_interrupts();
|
||||||
setup_tss();
|
setup_tss();
|
||||||
print_flags();
|
init_paging();
|
||||||
print_cr0();
|
backup_ivtbios();
|
||||||
print_cr3();
|
|
||||||
print_cr4();
|
|
||||||
FARPTR v86_entry = i386LinearToFp(v86Code);
|
|
||||||
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry));
|
|
||||||
char *vga = jmp_usermode_test();
|
|
||||||
|
|
||||||
for (int i = 0; i < 320; i++) {
|
// DL contained disk number, DH contained active partition
|
||||||
vga[i] = i;
|
uint8_t SystemPartition = boot_dx >> 8;
|
||||||
}
|
|
||||||
|
create_child(GetFreeStack(), (uintptr_t)SystemRun, 1, SystemPartition);
|
||||||
|
// If this returns, something is *very* wrong, reboot the system
|
||||||
|
// TODO Maybe try to recover?
|
||||||
|
|
||||||
|
// Triple fault
|
||||||
|
triple_fault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
49
link.ld
49
link.ld
@ -2,18 +2,51 @@ OUTPUT_FORMAT(binary)
|
|||||||
ENTRY(entry)
|
ENTRY(entry)
|
||||||
|
|
||||||
SECTIONS {
|
SECTIONS {
|
||||||
. = 0x8000;
|
. = 0x100000;
|
||||||
|
_USERMODE = 0x800000;
|
||||||
|
_USERMODE_END = 0x1000000;
|
||||||
|
|
||||||
.text : ALIGN(0x1000) {
|
.text : {
|
||||||
*(.text)
|
*(.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.data : ALIGN(0x1000) {
|
.data : {
|
||||||
*(.data)
|
*(.data*);
|
||||||
*(.rodata)
|
*(.data);
|
||||||
|
*(.rodata);
|
||||||
|
*(.rodata*);
|
||||||
|
_edata = .;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bss : ALIGN(0x1000) {
|
.realmode 0x4000 :
|
||||||
*(.bss)
|
AT ( _edata )
|
||||||
|
{ _v86code = .; *(.v86); _ev86code = .; }
|
||||||
|
. = _edata + SIZEOF(.realmode);
|
||||||
|
|
||||||
|
.bss : ALIGN(0x1000)
|
||||||
|
{
|
||||||
|
_bstart = .; *(.bss); *(.bss*) _bend = .;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bss _USERMODE (NOLOAD) : AT(_bend) {
|
||||||
|
_bhexstart = .;
|
||||||
|
*(.hexbss);
|
||||||
|
*(.hexbss*);
|
||||||
|
_bhexend = .;
|
||||||
|
*(.hexlatebss);
|
||||||
|
*(.hexlatebss*);
|
||||||
|
}
|
||||||
|
.bss _USERMODE (NOLOAD) : AT(_bend) {
|
||||||
|
_btextstart = .;
|
||||||
|
*(.textbss);
|
||||||
|
*(.textbss*);
|
||||||
|
_btextend = .;
|
||||||
|
*(.textlatebss);
|
||||||
|
*(.textlatebss*);
|
||||||
|
}
|
||||||
|
|
||||||
|
/DISCARD/ : {
|
||||||
|
*(.note*)
|
||||||
|
*(.comment*)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
63
paging.c
Normal file
63
paging.c
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#include "paging.h"
|
||||||
|
|
||||||
|
uint32_t page_directory[1024] __attribute__((aligned(4096)));
|
||||||
|
uint32_t page_tables[4][1024] __attribute__((aligned(4096)));
|
||||||
|
|
||||||
|
void enable_paging() {
|
||||||
|
asm(
|
||||||
|
"mov %%eax, %%cr3\n"
|
||||||
|
"mov %%cr0, %%eax\n"
|
||||||
|
"or $0x80000001, %%eax\n"
|
||||||
|
"mov %%eax, %%cr0\n"
|
||||||
|
::"a"(page_directory));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Add a function which can dynamically add and remove pages,
|
||||||
|
// that keeps track internally of what's already used?
|
||||||
|
|
||||||
|
// NOTE This function cannot cross 4MB (1024 page) boundaries
|
||||||
|
void map_pages(uintptr_t page, uintptr_t num_pages, uintptr_t phy_address, char access_flags) {
|
||||||
|
uintptr_t access = access_flags & 0x7;
|
||||||
|
|
||||||
|
// Each page is 4K, Each page table is 4M
|
||||||
|
uintptr_t page_dir_entry = page >> 10;
|
||||||
|
if (page_dir_entry >= 4) return;
|
||||||
|
uintptr_t page_index = page & 0x3ff;
|
||||||
|
if (page_index + num_pages > 1024) return;
|
||||||
|
|
||||||
|
// Address must be 4K aligned
|
||||||
|
if (phy_address & 0x3ff) return;
|
||||||
|
|
||||||
|
for (int i = 0; i < num_pages; i++)
|
||||||
|
page_tables[page_dir_entry][page_index + i] = phy_address + (i * 0x1000) | access_flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
// User, R/W, Present
|
||||||
|
#define USERACCESS (4|2|1)
|
||||||
|
// Supervisor, R/W, Present
|
||||||
|
#define SUPERACCESS (2|1)
|
||||||
|
|
||||||
|
void init_paging() {
|
||||||
|
for (int i = 0; i < 1024; i++)
|
||||||
|
// Supervisor, R/W, Not Present
|
||||||
|
page_directory[i] = 2;
|
||||||
|
|
||||||
|
// 1024 Pages = 1MB
|
||||||
|
// First MB: Real Mode
|
||||||
|
// TODO Make some of this not accessible to usermode?
|
||||||
|
map_pages(0x00000000 >> 12, 256, 0x00000000, USERACCESS);
|
||||||
|
// Next 3MB: Kernel
|
||||||
|
map_pages(0x00100000 >> 12, 768, 0x00100000, SUPERACCESS);
|
||||||
|
// Next 4MB: Kernel
|
||||||
|
map_pages(0x00400000 >> 12, 1024, 0x00400000, SUPERACCESS);
|
||||||
|
|
||||||
|
// Next 8MB: Usermode
|
||||||
|
map_pages(0x00800000 >> 12, 1024, 0x00800000, USERACCESS);
|
||||||
|
map_pages(0x00C00000 >> 12, 1024, 0x00C00000, USERACCESS);
|
||||||
|
|
||||||
|
// We aren't using page directory permissions
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
page_directory[i] = ((uintptr_t)&page_tables[i]) | USERACCESS;
|
||||||
|
|
||||||
|
enable_paging();
|
||||||
|
}
|
34
print.c
34
print.c
@ -3,15 +3,43 @@
|
|||||||
char nibbleToHex(uint8_t n) {
|
char nibbleToHex(uint8_t n) {
|
||||||
return n > 9 ? (n - 10) + 'A' : n + '0';
|
return n > 9 ? (n - 10) + 'A' : n + '0';
|
||||||
}
|
}
|
||||||
void printByte(uint8_t v, uint16_t *buff) {
|
uintptr_t printByte(uint8_t v, uint16_t *buff) {
|
||||||
*(char *)&buff[0] = nibbleToHex((v >> 4) & 0xF);
|
*(char *)&buff[0] = nibbleToHex((v >> 4) & 0xF);
|
||||||
*(char *)&buff[1] = nibbleToHex(v & 0xF);
|
*(char *)&buff[1] = nibbleToHex(v & 0xF);
|
||||||
|
return 2;
|
||||||
}
|
}
|
||||||
void printWord(uint16_t v, uint16_t *buff) {
|
uintptr_t printWord(uint16_t v, uint16_t *buff) {
|
||||||
printByte(v >> 8, buff);
|
printByte(v >> 8, buff);
|
||||||
printByte(v, &buff[2]);
|
printByte(v, &buff[2]);
|
||||||
|
return 4;
|
||||||
}
|
}
|
||||||
void printDword(uint32_t v, uint16_t *buff) {
|
uintptr_t printDword(uint32_t v, uint16_t *buff) {
|
||||||
printWord(v >> 16, buff);
|
printWord(v >> 16, buff);
|
||||||
printWord(v, &buff[4]);
|
printWord(v, &buff[4]);
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t printStr(char *v, uint16_t *buff) {
|
||||||
|
char *s;
|
||||||
|
for (s = v;*s;s++,buff++)
|
||||||
|
*(char*)buff = *s;
|
||||||
|
return s - v;
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t printChar(char v, uint16_t *buff) {
|
||||||
|
*(char*)buff = v;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t printDec(uint32_t v, uint16_t *buff) {
|
||||||
|
char b[12];
|
||||||
|
char *s = &b[11];
|
||||||
|
if (!v) {
|
||||||
|
*(uint16_t*)&b[10] = '0'; // '0',0x00
|
||||||
|
s = &b[10];
|
||||||
|
} else {
|
||||||
|
*s = 0;
|
||||||
|
for (;v;v/=10) *--s = '0' + v%10;
|
||||||
|
}
|
||||||
|
return printStr(s, buff);
|
||||||
}
|
}
|
||||||
|
10
print.h
10
print.h
@ -1,6 +1,8 @@
|
|||||||
#pragma once
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
void printByte(uint8_t v, uint16_t *buff);
|
uintptr_t printByte(uint8_t v, uint16_t *buff);
|
||||||
void printWord(uint16_t v, uint16_t *buff);
|
uintptr_t printWord(uint16_t v, uint16_t *buff);
|
||||||
void printDword(uint32_t v, uint16_t *buff);
|
uintptr_t printDword(uint32_t v, uint16_t *buff);
|
||||||
|
uintptr_t printStr(char *v, uint16_t *buff);
|
||||||
|
uintptr_t printDec(uint32_t v, uint16_t *buff);
|
||||||
|
uintptr_t printChar(char v, uint16_t *buff);
|
||||||
|
61
progs.c
Normal file
61
progs.c
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#include "progs.h"
|
||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
extern char _USERMODE, _USERMODE_END;
|
||||||
|
extern uint32_t create_user_child(uint32_t esp, uint32_t eip, uint32_t argc, ...);
|
||||||
|
void ProgramLoadTest(char *path) {
|
||||||
|
uint16_t *vga_text = (uint16_t *)0xb8000;
|
||||||
|
for (int i = 0; i < 80*25; i++)
|
||||||
|
vga_text[i] = 0x0f00;
|
||||||
|
uint32_t successcount;
|
||||||
|
uint8_t *diskReadBuf = (uint8_t *)&_USERMODE;
|
||||||
|
{
|
||||||
|
uint32_t err;
|
||||||
|
dirent de;
|
||||||
|
err = path_getinfo(path, &de);
|
||||||
|
if (de.size > 0x300000 || err) {
|
||||||
|
vga_text += printStr("File too large or error.", vga_text);
|
||||||
|
kbd_wait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FILE file;
|
||||||
|
err = file_open(&file, path, OPENREAD);
|
||||||
|
if (err) {
|
||||||
|
vga_text += printStr("Open Error: ", vga_text);
|
||||||
|
printDword(err, vga_text);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
err = file_seek(&file, 0);
|
||||||
|
if (err) {
|
||||||
|
vga_text += printStr("Seek Error", vga_text);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
successcount = err = file_read(&file, diskReadBuf, de.size);
|
||||||
|
if (!err && de.size > 0) {
|
||||||
|
vga_text += printStr("Read Error", vga_text);
|
||||||
|
printDword(err, vga_text);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vga_text += printStr("Successfully loaded program \"", vga_text);
|
||||||
|
vga_text += printStr((char*)path, vga_text);
|
||||||
|
vga_text += printStr("\", ", vga_text);
|
||||||
|
vga_text += printDec(successcount, vga_text);
|
||||||
|
vga_text += printStr(" Bytes.", vga_text);
|
||||||
|
vga_text = nextLine(vga_text,(uint16_t*)0xb8000);
|
||||||
|
vga_text += printStr("Press any key to run.", vga_text);
|
||||||
|
kbd_wait();
|
||||||
|
uint32_t res = create_user_child((uintptr_t)&_USERMODE_END, (uintptr_t)&_USERMODE, 0);
|
||||||
|
union V86Regs_t regs;
|
||||||
|
regs.w.ax = 3; // text mode
|
||||||
|
V8086Int(0x10, ®s);
|
||||||
|
vga_text = (uint16_t *)0xb8000;
|
||||||
|
for (int i = 0; i < 80*25; i++)
|
||||||
|
vga_text[i] = 0x0f00;
|
||||||
|
vga_text += printStr("Program returned code: ", vga_text);
|
||||||
|
vga_text += printDec(res, vga_text);
|
||||||
|
vga_text += printStr(" (0x", vga_text);
|
||||||
|
vga_text += printDword(res, vga_text);
|
||||||
|
vga_text += printStr("). Press any key to exit.", vga_text);
|
||||||
|
kbd_wait();
|
||||||
|
}
|
12
progs.h
Normal file
12
progs.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "print.h"
|
||||||
|
#include "kbd.h"
|
||||||
|
#include "v86defs.h"
|
||||||
|
#include "helper.h"
|
||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
void HexEditor(char *path);
|
||||||
|
void TextViewTest(char *path);
|
||||||
|
void ProgramLoadTest(char *path);
|
204
task.nasm
Normal file
204
task.nasm
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
global flushTSS
|
||||||
|
flushTSS:
|
||||||
|
mov ax, 0x28
|
||||||
|
ltr ax
|
||||||
|
ret
|
||||||
|
|
||||||
|
global task_ptr
|
||||||
|
task_ptr: equ (0x310000-4)
|
||||||
|
|
||||||
|
; TODO Is varargs too compiler dependent?
|
||||||
|
; extern void create_child(uint32_t esp, uint32_t eip, uint32_t argc, ...);
|
||||||
|
global create_child
|
||||||
|
create_child:
|
||||||
|
mov eax, [esp] ; return address
|
||||||
|
lea ecx, [esp+4] ; return stack, minus address
|
||||||
|
call save_current_task
|
||||||
|
mov eax, [esp+8] ; new eip
|
||||||
|
mov ecx, [esp+12] ; argc
|
||||||
|
mov esi, esp ; old esp
|
||||||
|
mov esp, [esp+4] ; new esp
|
||||||
|
lea esi, [esi+16] ; args
|
||||||
|
mov edx, ecx
|
||||||
|
neg edx
|
||||||
|
lea esp, [esp+edx*4] ; adjust for args
|
||||||
|
mov edi, esp
|
||||||
|
; copy varargs to new stack
|
||||||
|
rep movsd
|
||||||
|
push return_prev_task ; if child returns, return to prev task
|
||||||
|
push eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
; extern void create_user_child(uint32_t esp, uint32_t eip, uint32_t argc, ...);
|
||||||
|
global create_user_child
|
||||||
|
create_user_child:
|
||||||
|
mov eax, [esp] ; return address
|
||||||
|
lea ecx, [esp+4] ; return stack, minus address
|
||||||
|
call save_current_task
|
||||||
|
mov eax, [esp+8] ; new eip
|
||||||
|
mov ecx, [esp+12] ; argc
|
||||||
|
mov esi, esp ; old esp
|
||||||
|
mov esp, [esp+4] ; new esp
|
||||||
|
lea esi, [esi+16] ; args
|
||||||
|
mov edx, ecx
|
||||||
|
neg edx
|
||||||
|
lea esp, [esp+edx*4] ; adjust for args
|
||||||
|
mov edi, esp
|
||||||
|
; copy varargs to new stack
|
||||||
|
rep movsd
|
||||||
|
push 0x00000000 ; if child returns, return to null, crashing
|
||||||
|
mov ecx, 0x20 | 3
|
||||||
|
mov ds, cx
|
||||||
|
mov es, cx
|
||||||
|
mov fs, cx
|
||||||
|
mov gs, cx
|
||||||
|
mov ecx, esp
|
||||||
|
push 0x20 | 3
|
||||||
|
push ecx
|
||||||
|
pushfd
|
||||||
|
push 0x18 | 3
|
||||||
|
push eax
|
||||||
|
iret
|
||||||
|
|
||||||
|
; return address in EAX
|
||||||
|
; return stack in ECX
|
||||||
|
; we can modify EAX, ECX, EDX
|
||||||
|
; i.e. save all others in task
|
||||||
|
global save_current_task
|
||||||
|
save_current_task:
|
||||||
|
push edx
|
||||||
|
mov edx, esp ; EDX holds our tmp stack, unsaved
|
||||||
|
mov esp, dword [task_ptr] ; load current task pointer
|
||||||
|
push ss
|
||||||
|
push ecx ; return stack
|
||||||
|
pushfd
|
||||||
|
push cs
|
||||||
|
push eax ; return address
|
||||||
|
push ds ; other segs, pop
|
||||||
|
push es ; before iret
|
||||||
|
push fs ; in exit handler
|
||||||
|
push gs
|
||||||
|
push ebp ; saved
|
||||||
|
push ebx ; saved
|
||||||
|
push esi ; saved
|
||||||
|
push edi ; saved
|
||||||
|
mov dword [task_ptr], esp ; save new task pointer
|
||||||
|
mov esp, edx
|
||||||
|
pop edx
|
||||||
|
ret
|
||||||
|
|
||||||
|
; FIXME System will crash if last task is invalid
|
||||||
|
global return_prev_task
|
||||||
|
return_prev_task:
|
||||||
|
mov ecx, eax ; save return value for later
|
||||||
|
mov edx, dword [task_ptr] ; load current task pointer
|
||||||
|
add dword [task_ptr], 52 ; adjust to last task pointer
|
||||||
|
mov edi, [edx+0]
|
||||||
|
mov esi, [edx+4]
|
||||||
|
mov ebx, [edx+8]
|
||||||
|
mov ebp, [edx+12]
|
||||||
|
mov eax, [edx+0+16] ; gs
|
||||||
|
mov gs, ax
|
||||||
|
mov eax, [edx+4+16] ; fs
|
||||||
|
mov fs, ax
|
||||||
|
mov eax, [edx+8+16] ; es
|
||||||
|
mov es, ax
|
||||||
|
; SS:ESP <- return stack
|
||||||
|
push ecx
|
||||||
|
mov eax, [edx+32+16] ; ss
|
||||||
|
mov ecx, [edx+20+16] ; cs
|
||||||
|
and eax, 3
|
||||||
|
and ecx, 3
|
||||||
|
or eax, ecx
|
||||||
|
pop ecx
|
||||||
|
cmp eax, 3
|
||||||
|
je .ret_stack
|
||||||
|
.ret_no_stack:
|
||||||
|
mov esp, [edx+28+16] ; esp
|
||||||
|
mov eax, [edx+32+16] ; ss
|
||||||
|
mov ss, ax
|
||||||
|
mov eax, [edx+24+16] ; eflags
|
||||||
|
push eax
|
||||||
|
mov eax, [edx+20+16] ; cs
|
||||||
|
push eax
|
||||||
|
mov eax, [edx+16+16] ; eip
|
||||||
|
push eax
|
||||||
|
mov eax, [edx+12+16] ; ds
|
||||||
|
mov ds, ax
|
||||||
|
mov eax, ecx ; restore return value
|
||||||
|
iret
|
||||||
|
.ret_stack:
|
||||||
|
mov eax, [edx+32+16] ; ss
|
||||||
|
push eax
|
||||||
|
mov eax, [edx+28+16] ; esp
|
||||||
|
push eax
|
||||||
|
mov eax, [edx+24+16] ; eflags
|
||||||
|
push eax
|
||||||
|
mov eax, [edx+20+16] ; cs
|
||||||
|
push eax
|
||||||
|
mov eax, [edx+16+16] ; eip
|
||||||
|
push eax
|
||||||
|
mov eax, [edx+12+16] ; ds
|
||||||
|
mov ds, ax
|
||||||
|
mov eax, ecx ; restore return value
|
||||||
|
iret
|
||||||
|
|
||||||
|
; extern void enter_v86(uint32_t ss, uint32_t esp, uint32_t cs, uint32_t eip, union V86Regs_t *regs);
|
||||||
|
global enter_v86
|
||||||
|
global _enter_v86_internal_no_task
|
||||||
|
enter_v86:
|
||||||
|
pop eax ; return address
|
||||||
|
mov ecx, esp ; return stack
|
||||||
|
call save_current_task
|
||||||
|
_enter_v86_internal_no_task:
|
||||||
|
mov ebp, esp ; save stack pointer
|
||||||
|
; push v86 stuff for iret
|
||||||
|
mov eax, 0x3000
|
||||||
|
push eax ; gs
|
||||||
|
push eax ; fs
|
||||||
|
push eax ; ds
|
||||||
|
push eax ; es
|
||||||
|
push dword [ebp+0] ; ss
|
||||||
|
push dword [ebp+4] ; esp
|
||||||
|
pushfd ; eflags
|
||||||
|
or dword [esp], (1 << 17) ; set VM flags
|
||||||
|
;or dword [esp], (3 << 12) ; IOPL 3
|
||||||
|
push dword [ebp+8] ; cs
|
||||||
|
push dword [ebp+12] ; eip
|
||||||
|
; check if we have regs
|
||||||
|
mov eax, dword [ebp+16] ; regs
|
||||||
|
test eax, eax
|
||||||
|
jz .no_regs
|
||||||
|
; load regs: ebp, edi, esi, ebx, edx, ecx, eax
|
||||||
|
mov ebp, dword [eax+0]
|
||||||
|
mov edi, dword [eax+4]
|
||||||
|
mov esi, dword [eax+8]
|
||||||
|
mov ebx, dword [eax+12]
|
||||||
|
mov edx, dword [eax+16]
|
||||||
|
mov ecx, dword [eax+20]
|
||||||
|
mov eax, dword [eax+24]
|
||||||
|
.no_regs:
|
||||||
|
iret
|
||||||
|
|
||||||
|
; return address in eax, return stack in ebp
|
||||||
|
;extern save_current_task
|
||||||
|
|
||||||
|
;extern user_test
|
||||||
|
;global jmp_usermode_test
|
||||||
|
;jmp_usermode_test:
|
||||||
|
;pop eax ; return address
|
||||||
|
;mov ecx, esp ; return stack
|
||||||
|
;call save_current_task
|
||||||
|
;mov esp, 0x800000 ; usermode stack
|
||||||
|
;mov eax, 0x20 | 3
|
||||||
|
;mov ds, ax
|
||||||
|
;mov es, ax
|
||||||
|
;mov fs, ax
|
||||||
|
;mov gs, ax
|
||||||
|
;mov eax, esp
|
||||||
|
;push 0x20 | 3
|
||||||
|
;push eax
|
||||||
|
;pushfd
|
||||||
|
;push 0x18 | 3
|
||||||
|
;push user_test
|
||||||
|
;iret
|
36
testpgrm.nasm
Normal file
36
testpgrm.nasm
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
[BITS 32]
|
||||||
|
[ORG 0x800000]
|
||||||
|
xchg bx,bx
|
||||||
|
mov edi, 0xB8000
|
||||||
|
mov ecx, 80*25
|
||||||
|
mov eax, 0x2f00
|
||||||
|
rep stosw
|
||||||
|
mov edi, 0xB8000
|
||||||
|
mov esi, str0
|
||||||
|
mov ecx, str0_end - str0
|
||||||
|
mov eax, 0x2f00
|
||||||
|
.loop:
|
||||||
|
lodsb
|
||||||
|
stosw
|
||||||
|
loop .loop
|
||||||
|
mov edi, 0xB8000 + 160 - 2
|
||||||
|
.key_loop:
|
||||||
|
mov eax, 1 ; command = 01h, get scancode
|
||||||
|
int 0x21 ; OS call
|
||||||
|
cmp al, 0x3B ; F1
|
||||||
|
je .exit
|
||||||
|
cmp al, 0x3C ; F2
|
||||||
|
je .crash
|
||||||
|
cmp ah, 0x00
|
||||||
|
je .key_loop
|
||||||
|
mov byte [edi], ah ; ASCII value
|
||||||
|
add edi, 2
|
||||||
|
jmp .key_loop
|
||||||
|
.exit:
|
||||||
|
mov eax, 0x1337 ; Exit code/Return value
|
||||||
|
int 0x30 ; Exit
|
||||||
|
.crash:
|
||||||
|
jmp 0x00000000
|
||||||
|
|
||||||
|
str0: db "Running usermode program from disk! Press F1 to exit. Press F2 to crash."
|
||||||
|
str0_end:
|
271
tests.c
Normal file
271
tests.c
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
#include "tests.h"
|
||||||
|
|
||||||
|
extern char *user_test();
|
||||||
|
extern uint32_t create_user_child(uint32_t esp, uint32_t eip, uint32_t argc, ...);
|
||||||
|
|
||||||
|
void TestV86() {
|
||||||
|
union V86Regs_t regs;
|
||||||
|
regs.d.edi = 0x11111111;
|
||||||
|
regs.d.esi = 0x22222222;
|
||||||
|
regs.d.ebx = 0x33333333;
|
||||||
|
regs.d.edx = 0x44444444;
|
||||||
|
regs.d.ecx = 0x55555555;
|
||||||
|
regs.d.eax = 0x66666666;
|
||||||
|
FARPTR v86_entry = i386LinearToFp(v86Test);
|
||||||
|
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), ®s);
|
||||||
|
uint16_t *vga_text = (uint16_t *)0xb8000 + (80*2);
|
||||||
|
vga_text += printStr("Done.", vga_text);
|
||||||
|
}
|
||||||
|
extern char _USERMODE, _USERMODE_END;
|
||||||
|
extern char _binary_usermode_bin_start, _binary_usermode_bin_end;
|
||||||
|
void ReloadUser() {
|
||||||
|
// Put Usermode code in proper place based on linker
|
||||||
|
char *s = &_binary_usermode_bin_start;
|
||||||
|
char *d = (char *)&_USERMODE;
|
||||||
|
while (s < &_binary_usermode_bin_end)
|
||||||
|
*d++ = *s++;
|
||||||
|
}
|
||||||
|
char TestUser() {
|
||||||
|
ReloadUser();
|
||||||
|
char *vga = (char *)(uintptr_t)create_user_child((uintptr_t)&_USERMODE_END, (uintptr_t)&_USERMODE, 0);
|
||||||
|
if ((uintptr_t)vga != 0xA0000) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 320; i++) {
|
||||||
|
vga[i] = i;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
void TestDiskRead() {
|
||||||
|
char *diskReadBuf = (char *)0x20000;
|
||||||
|
v86disk_addr_packet.transfer_buffer =
|
||||||
|
(uintptr_t)diskReadBuf & 0x000F |
|
||||||
|
(((uintptr_t)diskReadBuf & 0xFFFF0) << 12);
|
||||||
|
v86disk_addr_packet.start_block = 0;
|
||||||
|
v86disk_addr_packet.blocks = 2;
|
||||||
|
union V86Regs_t regs;
|
||||||
|
kbd_clear();
|
||||||
|
for (;;) {
|
||||||
|
regs.h.ah = 0x42;
|
||||||
|
FARPTR v86_entry = i386LinearToFp(v86DiskOp);
|
||||||
|
enter_v86(0x0000, 0x8000, FP_SEG(v86_entry), FP_OFF(v86_entry), ®s);
|
||||||
|
uint16_t *vga_text = (uint16_t *)0xb8000;
|
||||||
|
for (int i = 0; i < 25; i++) {
|
||||||
|
uint16_t *vga_line = &vga_text[i*80];
|
||||||
|
for (int j = 0; j < 26; j++) {
|
||||||
|
printByte(diskReadBuf[i*26 + j], &vga_line[j*2]);
|
||||||
|
*(uint8_t*)&vga_line[j + (26*2)+1] = diskReadBuf[i*26 + j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint16_t scan = get_scancode();
|
||||||
|
if ((scan & 0xff) == KEY_PGUP) {
|
||||||
|
if(v86disk_addr_packet.start_block > 0) v86disk_addr_packet.start_block--;
|
||||||
|
} else if ((scan & 0xff) == KEY_PGDOWN)
|
||||||
|
v86disk_addr_packet.start_block++;
|
||||||
|
else if (scan & 0xff00) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extern uint32_t _gpf_eax_save;
|
||||||
|
extern uint32_t _gpf_eflags_save;
|
||||||
|
void TestCHS() {
|
||||||
|
uint16_t *vga_text = (uint16_t*)0xb8000;
|
||||||
|
for (int i = 0; i < 80*25; i++)
|
||||||
|
vga_text[i] = 0x0f00;
|
||||||
|
printStr("CHS Test ", vga_text);
|
||||||
|
// CHS Read
|
||||||
|
union V86Regs_t regs;
|
||||||
|
FARPTR v86_entry = i386LinearToFp(v86DiskGetGeometry);
|
||||||
|
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), ®s);
|
||||||
|
// check CF for error
|
||||||
|
if (_gpf_eflags_save & 0x1) {
|
||||||
|
uint16_t *vga = &((uint16_t*)0xb8000)[80];
|
||||||
|
vga += printStr("Could not get Disk Geometry.", vga);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32_t numHead = ((_gpf_eax_save & 0xff00) >> 8) + 1;
|
||||||
|
uint32_t secPerTrack = _gpf_eax_save & 0x3f;
|
||||||
|
uint32_t maxCyl = ((_gpf_eax_save & 0xff0000) >> 16) | ((_gpf_eax_save & 0xc0) << 2);
|
||||||
|
uint32_t maxLBA = numHead * secPerTrack * (maxCyl + 1);
|
||||||
|
for (uint32_t sector = 0; sector < maxLBA && sector < 0x8000 /* don't bother reading huge disks */; sector += (sector + secPerTrack < maxLBA) ? secPerTrack : 1) {
|
||||||
|
uint16_t *vga = &((uint16_t*)0xb8000)[9];
|
||||||
|
vga += printWord(sector, vga); vga++;
|
||||||
|
// LBA -> CHS
|
||||||
|
uint32_t tmp = sector / secPerTrack;
|
||||||
|
uint32_t sec = (sector % (secPerTrack)) + 1;
|
||||||
|
uint32_t head = tmp % numHead;
|
||||||
|
uint32_t cyl = tmp / numHead;
|
||||||
|
vga += printWord(cyl, vga); vga += printChar(':', vga);
|
||||||
|
vga += printByte(head, vga); vga += printChar(':', vga);
|
||||||
|
vga += printByte(sec, vga); vga += printChar(' ', vga);
|
||||||
|
vga += printStr("/ ", vga);
|
||||||
|
vga += printWord(maxCyl, vga); vga += printChar(':', vga);
|
||||||
|
vga += printByte(numHead - 1, vga); vga += printChar(':', vga);
|
||||||
|
vga += printByte(secPerTrack, vga); vga += printChar(' ', vga);
|
||||||
|
regs.w.ax = 0x0201;
|
||||||
|
regs.h.ch = cyl & 0xff;
|
||||||
|
regs.h.cl = sec | ((cyl >> 2) & 0xc0);
|
||||||
|
regs.h.dh = head;
|
||||||
|
regs.h.dl = 0x80;
|
||||||
|
regs.w.bx = 0x0000;
|
||||||
|
v86_entry = i386LinearToFp(v86DiskReadCHS);
|
||||||
|
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), ®s);
|
||||||
|
// if CF was set, we have error
|
||||||
|
if (_gpf_eflags_save & 0x1) {
|
||||||
|
uint16_t *vga = &((uint16_t*)0xb8000)[80];
|
||||||
|
vga += printStr("Disk Error: ", vga);
|
||||||
|
vga += printByte(_gpf_eax_save & 0xff, vga);
|
||||||
|
vga += printStr(" CHS=", vga);
|
||||||
|
vga += printWord(cyl, vga); vga += printChar(':', vga);
|
||||||
|
vga += printByte(head, vga); vga += printChar(':', vga);
|
||||||
|
vga += printByte(sec, vga);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//void TestFAT() {
|
||||||
|
// uint16_t *vga_text = (uint16_t *)0xb8000;
|
||||||
|
// uint8_t *diskReadBuf = (uint8_t *)0x22400;
|
||||||
|
// for (int i = 0; i < 80*25; i++)
|
||||||
|
// vga_text[i] = 0x0f00;
|
||||||
|
// VOLINFO vi;
|
||||||
|
//
|
||||||
|
// uint8_t pactive, ptype;
|
||||||
|
// uint32_t pstart, psize;
|
||||||
|
// pstart = DFS_GetPtnStart(0, diskReadBuf, SystemPartition, &pactive, &ptype, &psize);
|
||||||
|
// vga_text = (uint16_t *)0xb8000;
|
||||||
|
// vga_text += printStr("PartStart: ", vga_text);
|
||||||
|
// vga_text += printDword(pstart, vga_text);
|
||||||
|
// vga_text += 2;
|
||||||
|
// vga_text += printStr("PartSize: ", vga_text);
|
||||||
|
// vga_text += printDword(psize, vga_text);
|
||||||
|
// vga_text += 2;
|
||||||
|
// vga_text += printStr("PartActive: ", vga_text);
|
||||||
|
// vga_text += printByte(pactive, vga_text);
|
||||||
|
// vga_text += 2;
|
||||||
|
// vga_text += printStr("PartType: ", vga_text);
|
||||||
|
// vga_text += printByte(ptype, vga_text);
|
||||||
|
// vga_text = (uint16_t *)((((((uintptr_t)vga_text)-0xb8000) - ((((uintptr_t)vga_text)-0xb8000) % 160)) + 160)+0xb8000);
|
||||||
|
// //asm ("xchgw %bx, %bx");
|
||||||
|
//
|
||||||
|
// DFS_GetVolInfo(0, diskReadBuf, pstart, &vi);
|
||||||
|
// vga_text += printStr("Label: ", vga_text);
|
||||||
|
// vga_text += printStr((char*)vi.label, vga_text);
|
||||||
|
// vga_text += 2;
|
||||||
|
// vga_text += printStr("Sec/Clus: ", vga_text);
|
||||||
|
// vga_text += printByte(vi.secperclus, vga_text);
|
||||||
|
// vga_text += 2;
|
||||||
|
// vga_text += printStr("ResrvSec: ", vga_text);
|
||||||
|
// vga_text += printWord(vi.reservedsecs, vga_text);
|
||||||
|
// vga_text += 2;
|
||||||
|
// vga_text += printStr("NumSec: ", vga_text);
|
||||||
|
// vga_text += printDword(vi.numsecs, vga_text);
|
||||||
|
// vga_text += 2;
|
||||||
|
// vga_text += printStr("Sec/FAT: ", vga_text);
|
||||||
|
// vga_text += printDword(vi.secperfat, vga_text);
|
||||||
|
// vga_text += 2;
|
||||||
|
// vga_text += printStr("FAT1@: ", vga_text);
|
||||||
|
// vga_text += printDword(vi.fat1, vga_text);
|
||||||
|
// vga_text += 2;
|
||||||
|
// vga_text += printStr("ROOT@: ", vga_text);
|
||||||
|
// vga_text += printDword(vi.rootdir, vga_text);
|
||||||
|
// vga_text = (uint16_t *)((((((uintptr_t)vga_text)-0xb8000) - ((((uintptr_t)vga_text)-0xb8000) % 160)) + 160)+0xb8000);
|
||||||
|
// //asm ("xchgw %bx, %bx");
|
||||||
|
//
|
||||||
|
// vga_text += printStr("Files in root:", vga_text);
|
||||||
|
// DIRINFO di;
|
||||||
|
// di.scratch = diskReadBuf;
|
||||||
|
// DFS_OpenDir(&vi, (uint8_t*)"", &di);
|
||||||
|
// vga_text = (uint16_t *)((((((uintptr_t)vga_text)-0xb8000) - ((((uintptr_t)vga_text)-0xb8000) % 160)) + 160)+0xb8000);
|
||||||
|
// DIRENT de;
|
||||||
|
// while (!DFS_GetNext(&vi, &di, &de)) {
|
||||||
|
// if (de.name[0]) {
|
||||||
|
// for (int i = 0; i < 11 && de.name[i]; i++) {
|
||||||
|
// if (i == 8) { *(uint8_t*)vga_text = ' '; vga_text++; } // space for 8.3
|
||||||
|
// *(uint8_t *)vga_text = de.name[i];
|
||||||
|
// vga_text++;
|
||||||
|
// }
|
||||||
|
// vga_text += printStr(" ", vga_text);
|
||||||
|
// vga_text += printDec((uint32_t)de.filesize_0 + ((uint32_t)de.filesize_1 << 8) + ((uint32_t)de.filesize_2 << 16) + ((uint32_t)de.filesize_3 << 24), vga_text);
|
||||||
|
// *(uint8_t*)vga_text++ = 'B';
|
||||||
|
// vga_text = (uint16_t *)((((((uintptr_t)vga_text)-0xb8000) - ((((uintptr_t)vga_text)-0xb8000) % 160)) + 160)+0xb8000);
|
||||||
|
// }
|
||||||
|
// //asm ("xchgw %bx, %bx");
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
void RunTests() {
|
||||||
|
char doTests = 1;
|
||||||
|
uint16_t *vga_text = (uint16_t*)0xb8000;
|
||||||
|
uint8_t key;
|
||||||
|
for (char l = 1;l;) {
|
||||||
|
if (doTests) {
|
||||||
|
vga_text = (uint16_t*)0xb8000;
|
||||||
|
for (int i = 0; i < 80*25; i++)
|
||||||
|
vga_text[i] = 0x1f00;
|
||||||
|
vga_text += printStr("V86 Test... ", vga_text);
|
||||||
|
//asm ("xchgw %bx, %bx");
|
||||||
|
TestV86(); // has int 3 wait in v86
|
||||||
|
vga_text = (uint16_t *)0xb8000 + (80*3);
|
||||||
|
vga_text += printStr("Press 'N' for next test.", vga_text);
|
||||||
|
for(;;) {
|
||||||
|
key = get_key();
|
||||||
|
if (key == 'N' || key == 'n') break;
|
||||||
|
*vga_text = (*vga_text & 0xFF00) | key;
|
||||||
|
vga_text++;
|
||||||
|
}
|
||||||
|
if (TestUser()) {
|
||||||
|
union V86Regs_t regs;
|
||||||
|
regs.w.ax = 3; // text mode
|
||||||
|
V8086Int(0x10, ®s);
|
||||||
|
vga_text = (uint16_t *)0xb8000 + (80*5);
|
||||||
|
printStr("Usermode test failed! Press any key to continue.", vga_text);
|
||||||
|
kbd_wait();
|
||||||
|
} else {
|
||||||
|
kbd_wait();
|
||||||
|
union V86Regs_t regs;
|
||||||
|
regs.w.ax = 3; // text mode
|
||||||
|
V8086Int(0x10, ®s);
|
||||||
|
}
|
||||||
|
doTests = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
vga_text = &((uint16_t*)0xB8000)[80*14];
|
||||||
|
vga_text += printStr("Press E for a flagrant system error. ", vga_text);
|
||||||
|
vga_text = &((uint16_t*)0xB8000)[80*15];
|
||||||
|
vga_text += printStr("Press D for disk tests. ", vga_text);
|
||||||
|
vga_text = &((uint16_t*)0xB8000)[80*16];
|
||||||
|
vga_text += printStr("Press R to repeat tests. ", vga_text);
|
||||||
|
vga_text = &((uint16_t*)0xB8000)[80*17];
|
||||||
|
vga_text += printStr("Press C to continue... ", vga_text);
|
||||||
|
switch (key = get_key()) {
|
||||||
|
case 'e':
|
||||||
|
case 'E':
|
||||||
|
// flagrant system error
|
||||||
|
*((uint8_t*)0x1000000) = 0;
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
case 'C':
|
||||||
|
// continue
|
||||||
|
l = 0;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
case 'D':
|
||||||
|
// disk tests
|
||||||
|
TestCHS();
|
||||||
|
kbd_wait();
|
||||||
|
TestDiskRead();
|
||||||
|
//TestFAT();
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
case 'R':
|
||||||
|
doTests = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
*vga_text = (*vga_text & 0xFF00) | key;
|
||||||
|
vga_text++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
8
tests.h
Normal file
8
tests.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "print.h"
|
||||||
|
#include "interrupt.h"
|
||||||
|
#include "v86defs.h"
|
||||||
|
#include "kbd.h"
|
||||||
|
#include "helper.h"
|
||||||
|
|
||||||
|
void RunTests();
|
244
textedit.c
Normal file
244
textedit.c
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
#include "progs.h"
|
||||||
|
|
||||||
|
#define MAXFILESIZE 0x300000 // 3MB
|
||||||
|
#define MAXLINES 100000
|
||||||
|
#define BLOCKSIZE 0x1000
|
||||||
|
#define BLOCKMASK 0xfff
|
||||||
|
#define BLOCKSHIFT 12
|
||||||
|
#define TOTALBLOCKS (MAXFILESIZE/BLOCKSIZE)
|
||||||
|
uintptr_t lineOffsets[MAXLINES] __attribute__((section(".textbss")));;
|
||||||
|
uintptr_t lineLengths[MAXLINES] __attribute__((section(".textbss")));;
|
||||||
|
uint8_t editedBlocks[TOTALBLOCKS] __attribute__((section(".textbss")));;
|
||||||
|
uint8_t fileBuffer[MAXFILESIZE]
|
||||||
|
__attribute__((aligned(0x1000)))
|
||||||
|
__attribute__((section(".textlatebss")));
|
||||||
|
void TextViewTest(char *path) {
|
||||||
|
uint16_t *vga_text = (uint16_t *)0xb8000;
|
||||||
|
uint32_t fileLen;
|
||||||
|
{
|
||||||
|
uint32_t err;
|
||||||
|
dirent de;
|
||||||
|
err = path_getinfo(path, &de);
|
||||||
|
if (err) {
|
||||||
|
vga_text += printStr("Error getting file info.", vga_text);
|
||||||
|
kbd_wait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fileLen = de.size;
|
||||||
|
FILE file;
|
||||||
|
err = file_open(&file, path, OPENREAD);
|
||||||
|
if (err) {
|
||||||
|
vga_text += printStr("Open Error: ", vga_text);
|
||||||
|
printDword(err, vga_text);
|
||||||
|
kbd_wait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// file too large
|
||||||
|
if (fileLen > MAXFILESIZE) {
|
||||||
|
vga_text += printStr("File too large.", vga_text);
|
||||||
|
kbd_wait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (file_seek(&file, 0)) {
|
||||||
|
vga_text += printStr("Seek Error", vga_text);
|
||||||
|
kbd_wait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32_t bytesRead = file_read(&file, fileBuffer, fileLen);
|
||||||
|
if (bytesRead < fileLen) {
|
||||||
|
vga_text += printStr("Read Error: ", vga_text);
|
||||||
|
printDword(err, vga_text);
|
||||||
|
kbd_wait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint32_t lastLine;
|
||||||
|
{
|
||||||
|
char nl;
|
||||||
|
uint8_t c = 0x0A; // start with a pretend newline
|
||||||
|
uint32_t line = -1; // start a pretend line behind
|
||||||
|
uint32_t lineLen = 0;
|
||||||
|
for (int32_t o = -1; o < (int32_t)fileLen; lineLen++, c = fileBuffer[++o]) {
|
||||||
|
// newline
|
||||||
|
if (c == 0x0A) {
|
||||||
|
lineOffsets[++line] = o;
|
||||||
|
lineLengths[line] = lineLen > 0 ? lineLen : 1;
|
||||||
|
lineLen = 0;
|
||||||
|
}
|
||||||
|
// file too large
|
||||||
|
if (line >= MAXLINES) {
|
||||||
|
vga_text += printStr("File too large.", vga_text);
|
||||||
|
kbd_wait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lineLengths[line] = lineLen;
|
||||||
|
lastLine = line;
|
||||||
|
}
|
||||||
|
uint32_t currLine = 0;
|
||||||
|
char cont = 1;
|
||||||
|
uint32_t screenSize = 80*25;
|
||||||
|
char redraw = 1;
|
||||||
|
uint32_t linesOnScreen = 0;
|
||||||
|
uint32_t cursorLine = 0;
|
||||||
|
uint32_t cursorLineOffset = 0;
|
||||||
|
char cursorChange = 0;
|
||||||
|
for (;cont;) {
|
||||||
|
if (cursorLine > lastLine) {
|
||||||
|
cursorLine = lastLine;
|
||||||
|
cursorLineOffset = lineLengths[lastLine] - 2;
|
||||||
|
cursorChange = 1;
|
||||||
|
}
|
||||||
|
if (lineLengths[cursorLine+1] == 1 && cursorLineOffset == 0){}
|
||||||
|
else if (cursorLineOffset >= lineLengths[cursorLine+1] - 1) {
|
||||||
|
cursorLineOffset = 0;
|
||||||
|
cursorLine++;
|
||||||
|
cursorChange = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (redraw || cursorChange) {
|
||||||
|
vga_text = (uint16_t *)0xb8000;
|
||||||
|
for (int i = 0; i < screenSize; i++)
|
||||||
|
vga_text[i] = 0x0f00;
|
||||||
|
char pathBuff[22];
|
||||||
|
trimPath((char*)path, pathBuff, sizeof(pathBuff));
|
||||||
|
vga_text += printStr(pathBuff, vga_text);
|
||||||
|
vga_text += 2;
|
||||||
|
vga_text += printDec(currLine, vga_text);
|
||||||
|
vga_text += printChar('/', vga_text);
|
||||||
|
vga_text += printDec(lastLine, vga_text);
|
||||||
|
vga_text += printStr(" Scroll: Up/Down PgUp/PgDown Home/End", vga_text);
|
||||||
|
{
|
||||||
|
const char prnt[] = "Exit: F1";
|
||||||
|
vga_text = &((uint16_t*)0xb8000)[80-sizeof(prnt)+1];
|
||||||
|
vga_text += printStr((char*)prnt, vga_text);
|
||||||
|
}
|
||||||
|
for (vga_text = &((uint16_t*)0xb8000)[84]; vga_text < &((uint16_t*)0xb8000)[screenSize]; vga_text += 80)
|
||||||
|
*(uint8_t*)vga_text = '|';
|
||||||
|
vga_text = &((uint16_t*)0xb8000)[0];
|
||||||
|
uint32_t screenLineOff = 6;
|
||||||
|
uint32_t lineOffset = 0;
|
||||||
|
uint8_t c = 0x0A; // start with a pretend newline
|
||||||
|
uint32_t line = currLine - 1; // start a pretend line behind
|
||||||
|
int32_t o = lineOffsets[currLine]; // the real or fake newline on previous line
|
||||||
|
linesOnScreen = screenSize/80;
|
||||||
|
for (; o < (int32_t)fileLen && vga_text < &((uint16_t*)0xb8000)[screenSize]; c = fileBuffer[++o]) {
|
||||||
|
// newline
|
||||||
|
if (c == 0x0A) {
|
||||||
|
vga_text = nextLine(vga_text,(uint16_t*)0xb8000);
|
||||||
|
line++;
|
||||||
|
lineOffset = 0;
|
||||||
|
{
|
||||||
|
uint16_t *vga_tmp = vga_text;
|
||||||
|
uint16_t decTmp[11];
|
||||||
|
char cnt = printDec(line, decTmp);
|
||||||
|
char off = cnt <= 4 ? 0 : cnt - 4;
|
||||||
|
vga_tmp += 4 - (cnt - off);
|
||||||
|
for (int i = off; i < cnt; i++, vga_tmp++)
|
||||||
|
*(uint8_t*)vga_tmp = (uint8_t)decTmp[i];
|
||||||
|
}
|
||||||
|
vga_text += 6;
|
||||||
|
screenLineOff = 6;
|
||||||
|
if (cursorLine == line && cursorLineOffset == lineOffset) {
|
||||||
|
((uint8_t*)vga_text)[1] = 0xf0;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*(uint8_t*)vga_text = c;
|
||||||
|
if (cursorLine == line && cursorLineOffset == lineOffset) {
|
||||||
|
((uint8_t*)vga_text)[1] = 0xf0;
|
||||||
|
}
|
||||||
|
lineOffset++;
|
||||||
|
vga_text++;
|
||||||
|
screenLineOff++;
|
||||||
|
if (screenLineOff == 80) { // last char
|
||||||
|
vga_text += 6;
|
||||||
|
screenLineOff = 6;
|
||||||
|
linesOnScreen--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
redraw = 0;
|
||||||
|
}
|
||||||
|
uint16_t key = get_scancode();
|
||||||
|
union V86Regs_t regs;
|
||||||
|
FARPTR v86_entry;
|
||||||
|
switch (key & 0xff) {
|
||||||
|
/*case KEY_DOWN: // down
|
||||||
|
if (currLine < lastLine && lineOffsets[currLine+1] < fileLen) {
|
||||||
|
currLine++;
|
||||||
|
redraw = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KEY_UP: // up
|
||||||
|
if ((currLine > 0 && lineOffsets[currLine-1] < fileLen) || currLine == 1) {
|
||||||
|
currLine--;
|
||||||
|
redraw = 1;
|
||||||
|
}
|
||||||
|
break;*/
|
||||||
|
case KEY_DOWN:
|
||||||
|
cursorLine++;
|
||||||
|
cursorChange = 1;
|
||||||
|
break;
|
||||||
|
case KEY_UP:
|
||||||
|
if (cursorLine > 0) cursorLine--;
|
||||||
|
cursorChange = 1;
|
||||||
|
break;
|
||||||
|
case KEY_LEFT:
|
||||||
|
if (cursorLineOffset > 0) cursorLineOffset--;
|
||||||
|
cursorChange = 1;
|
||||||
|
break;
|
||||||
|
case KEY_RIGHT:
|
||||||
|
cursorLineOffset++;
|
||||||
|
cursorChange = 1;
|
||||||
|
break;
|
||||||
|
case KEY_PGDOWN:
|
||||||
|
if (currLine+(linesOnScreen/2) <= lastLine && lineOffsets[currLine+(linesOnScreen/2)] < fileLen) {
|
||||||
|
currLine += (linesOnScreen/2);
|
||||||
|
redraw = 1;
|
||||||
|
} else goto end;
|
||||||
|
break;
|
||||||
|
case KEY_PGUP:
|
||||||
|
if (currLine > (linesOnScreen/2) && lineOffsets[currLine-(linesOnScreen/2)] < fileLen) {
|
||||||
|
currLine -= (linesOnScreen/2);
|
||||||
|
redraw = 1;
|
||||||
|
} else if (currLine <= (linesOnScreen/2)) {
|
||||||
|
currLine = 0;
|
||||||
|
redraw = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KEY_HOME: home:
|
||||||
|
if (currLine != 0) {
|
||||||
|
currLine = 0;
|
||||||
|
redraw = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KEY_END: end:
|
||||||
|
if (currLine != lastLine) {
|
||||||
|
currLine = lastLine;
|
||||||
|
redraw = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KEY_F1:
|
||||||
|
cont = 0;
|
||||||
|
break;
|
||||||
|
case KEY_F2:
|
||||||
|
if (screenSize != 80*25) {
|
||||||
|
SetVideo25Lines();
|
||||||
|
SetCursorDisabled();
|
||||||
|
screenSize = 80*25;
|
||||||
|
redraw = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KEY_F5:
|
||||||
|
if (screenSize != 80*50) {
|
||||||
|
SetVideo50Lines();
|
||||||
|
SetCursorDisabled();
|
||||||
|
screenSize = 80*50;
|
||||||
|
redraw = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
tss.c
11
tss.c
@ -1,5 +1,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "tss.h"
|
||||||
|
|
||||||
struct __attribute__((__packed__)) tss_entry_struct {
|
struct __attribute__((__packed__)) tss_entry_struct {
|
||||||
uint32_t prev_tss;
|
uint32_t prev_tss;
|
||||||
uint32_t esp0;
|
uint32_t esp0;
|
||||||
@ -31,12 +33,17 @@ struct __attribute__((__packed__)) tss_entry_struct {
|
|||||||
};
|
};
|
||||||
struct tss_entry_struct *tss_data;
|
struct tss_entry_struct *tss_data;
|
||||||
void write_tss() {
|
void write_tss() {
|
||||||
tss_data = (struct tss_entry_struct *)0x20000;
|
tss_data = (struct tss_entry_struct *)0x200000;
|
||||||
for (int i = 0; i < 0x2080; i++)
|
for (int i = 0; i < 0x2080; i++)
|
||||||
((uint8_t*)tss_data)[i] = 0;
|
((uint8_t*)tss_data)[i] = 0;
|
||||||
tss_data->ss0 = 0x10;
|
tss_data->ss0 = 0x10;
|
||||||
tss_data->esp0 = 0x400000;
|
tss_data->esp0 = 0x320000;
|
||||||
tss_data->iomap_base = 0x80;
|
tss_data->iomap_base = 0x80;
|
||||||
|
|
||||||
|
// not technically TSS but set up task pointer
|
||||||
|
uint32_t *current_task_ptr = (uint32_t*)(0x310000-4);
|
||||||
|
*current_task_ptr = 0x310000-(20*4); // each task is 12 dwords, plus 1 for pointer
|
||||||
|
/* TODO setup null recovery task at start */
|
||||||
}
|
}
|
||||||
extern void flushTSS();
|
extern void flushTSS();
|
||||||
|
|
||||||
|
71
usermode.nasm
Normal file
71
usermode.nasm
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
[BITS 32]
|
||||||
|
[ORG 0x800000]
|
||||||
|
global user_test
|
||||||
|
user_test:
|
||||||
|
mov dword [0xb8000], 0x0f000f00 | 'U' | 's' << 16
|
||||||
|
mov dword [0xb8004], 0x0f000f00 | 'e' | 'r' << 16
|
||||||
|
mov dword [0xb8008], 0x0f000f00 | 'm' | 'o' << 16
|
||||||
|
mov dword [0xb800C], 0x0f000f00 | 'd' | 'e' << 16
|
||||||
|
mov dword [0xb8010], 0x0f000f00 | '!'
|
||||||
|
mov dword [0xb8000+160], 0x0f000f00 | 'F' | 'a' << 16
|
||||||
|
mov dword [0xb8004+160], 0x0f000f00 | 'u' | 'l' << 16
|
||||||
|
mov dword [0xb8008+160], 0x0f000f00 | 't' | 's' << 16
|
||||||
|
mov dword [0xb800C+160], 0x0f000f00 | ':' | ' ' << 16
|
||||||
|
mov dword [0xb8000+320], 0x0f000f00 | 'd' | ':' << 16
|
||||||
|
mov dword [0xb8004+320], 0x0f000f00 | ' ' | '#' << 16
|
||||||
|
mov dword [0xb8008+320], 0x0f000f00 | 'D' | 'I' << 16
|
||||||
|
mov dword [0xb800C+320], 0x0f000f00 | 'V' | ' ' << 16
|
||||||
|
mov dword [0xb8000+480], 0x0f000f00 | 'p' | ':' << 16
|
||||||
|
mov dword [0xb8004+480], 0x0f000f00 | ' ' | 'P' << 16
|
||||||
|
mov dword [0xb8008+480], 0x0f000f00 | 'G' | 'F' << 16
|
||||||
|
mov dword [0xb800C+480], 0x0f000f00 | 'L' | 'T' << 16
|
||||||
|
mov dword [0xb8000+640], 0x0f000f00 | 'u' | ':' << 16
|
||||||
|
mov dword [0xb8004+640], 0x0f000f00 | ' ' | '#' << 16
|
||||||
|
mov dword [0xb8008+640], 0x0f000f00 | 'U' | 'D' << 16
|
||||||
|
mov dword [0xb800C+640], 0x0f000f00 | '2' | ' ' << 16
|
||||||
|
mov dword [0xb8000+800], 0x0f000f00 | 'g' | ':' << 16
|
||||||
|
mov dword [0xb8004+800], 0x0f000f00 | ' ' | '#' << 16
|
||||||
|
mov dword [0xb8008+800], 0x0f000f00 | 'G' | 'P' << 16
|
||||||
|
mov dword [0xb800C+800], 0x0f000f00 | 'F' | ' ' << 16
|
||||||
|
mov eax, 0 ; command = 00h, get key
|
||||||
|
int 0x21 ; OS call
|
||||||
|
cmp al, 'd'
|
||||||
|
je .div_err
|
||||||
|
cmp al, 'p'
|
||||||
|
je .page_err
|
||||||
|
cmp al, 'u'
|
||||||
|
je .undefined_err
|
||||||
|
cmp al, 'g'
|
||||||
|
je .gpf_err
|
||||||
|
push 0x00000013 ; eax AH=0,AL=3 set video mode 3
|
||||||
|
push 0x00000000 ; ecx
|
||||||
|
push 0x00000000 ; edx
|
||||||
|
push 0x00000000 ; ebx
|
||||||
|
push 0x00000000 ; esi
|
||||||
|
push 0x00000000 ; edi
|
||||||
|
push 0x00000000 ; ebp
|
||||||
|
push esp ; regs
|
||||||
|
push 0x10 ; interrupt
|
||||||
|
mov eax, 0x86 ; command = 86h, virtual 8086 call
|
||||||
|
int 0x21 ; OS call
|
||||||
|
mov edi, 0xA0000
|
||||||
|
xor eax, eax
|
||||||
|
.loop:
|
||||||
|
mov ecx, 320
|
||||||
|
rep stosb
|
||||||
|
inc al
|
||||||
|
cmp eax, 200
|
||||||
|
jl .loop
|
||||||
|
mov eax, 0xA0000
|
||||||
|
int 0x30 ; Exit
|
||||||
|
.page_err:
|
||||||
|
mov edx, 0x105000 ; somewhere in kernel mem
|
||||||
|
mov edx, [edx] ; should page fault
|
||||||
|
.div_err:
|
||||||
|
xor ebx, ebx
|
||||||
|
div bl ; Unhandled DIV0 exception
|
||||||
|
.undefined_err:
|
||||||
|
ud2 ; undefined instruction
|
||||||
|
.gpf_err:
|
||||||
|
mov ax, 0x10
|
||||||
|
mov ds, ax ; load ds with bad value
|
166
v86.nasm
166
v86.nasm
@ -1,4 +1,87 @@
|
|||||||
[BITS 16]
|
[BITS 16]
|
||||||
|
[SECTION .v86]
|
||||||
|
global v86Interrupt
|
||||||
|
v86Interrupt:
|
||||||
|
int 0x00
|
||||||
|
int 0x30
|
||||||
|
ud2
|
||||||
|
|
||||||
|
global v86TransFlag
|
||||||
|
v86TransFlag:
|
||||||
|
push cs
|
||||||
|
pop es
|
||||||
|
mov ax, 0x13
|
||||||
|
int 0x10
|
||||||
|
mov ax,0x1012
|
||||||
|
xor bx,bx
|
||||||
|
mov cx,5
|
||||||
|
mov dx,.c
|
||||||
|
int 0x10
|
||||||
|
push 0xa000
|
||||||
|
pop es
|
||||||
|
xor di,di
|
||||||
|
xor ax,ax
|
||||||
|
.loop:
|
||||||
|
mov cx, 12800
|
||||||
|
rep stosb
|
||||||
|
inc ax
|
||||||
|
cmp ax,5
|
||||||
|
jl .loop
|
||||||
|
int 0x30
|
||||||
|
ud2
|
||||||
|
.c: db `\0263>=*.\?\?\?=*.\0263>`
|
||||||
|
|
||||||
|
global v86VideoInt
|
||||||
|
v86VideoInt:
|
||||||
|
int 0x10
|
||||||
|
int 0x30
|
||||||
|
ud2
|
||||||
|
|
||||||
|
global v86DiskOp
|
||||||
|
v86DiskOp:
|
||||||
|
xor bx, bx ; TODO fix assuming we're in first 64k
|
||||||
|
mov ds, bx
|
||||||
|
mov dl, 0x80 ; TODO get this from BIOS or something
|
||||||
|
mov si, v86disk_addr_packet ; ds:si
|
||||||
|
int 0x13
|
||||||
|
jc .err
|
||||||
|
int 0x30
|
||||||
|
.err:
|
||||||
|
ud2
|
||||||
|
global v86disk_addr_packet
|
||||||
|
v86disk_addr_packet:
|
||||||
|
db 0x10, 0x00 ; size, reserved
|
||||||
|
dw 0x1 ; blocks
|
||||||
|
dd 0x23000000 ; transfer buffer 0x23000
|
||||||
|
dq 0x1 ; start block
|
||||||
|
|
||||||
|
global v86DiskGetGeometry
|
||||||
|
v86DiskGetGeometry:
|
||||||
|
mov ah, 8
|
||||||
|
mov dl, 0x80
|
||||||
|
int 0x13
|
||||||
|
movzx eax, ch
|
||||||
|
shl eax, 16
|
||||||
|
mov al, cl
|
||||||
|
mov ah, dh
|
||||||
|
int 0x30
|
||||||
|
ud2
|
||||||
|
|
||||||
|
global v86DiskReadCHS
|
||||||
|
v86DiskReadCHS:
|
||||||
|
push 0x2000
|
||||||
|
pop es
|
||||||
|
int 0x13
|
||||||
|
int 0x30
|
||||||
|
ud2
|
||||||
|
|
||||||
|
global v86TextMode
|
||||||
|
v86TextMode:
|
||||||
|
mov ax, 0x3
|
||||||
|
int 0x10
|
||||||
|
int 0x30
|
||||||
|
ud2
|
||||||
|
|
||||||
real_hexprint:
|
real_hexprint:
|
||||||
xor cx, cx
|
xor cx, cx
|
||||||
mov bl, al
|
mov bl, al
|
||||||
@ -26,58 +109,51 @@ call real_hexprint
|
|||||||
mov ax, dx
|
mov ax, dx
|
||||||
call real_hexprint
|
call real_hexprint
|
||||||
ret
|
ret
|
||||||
extern v86Code
|
|
||||||
v86Code:
|
global v86Test
|
||||||
mov ax, 0xb814
|
v86Test:
|
||||||
|
mov ax, (0xB8000 + (80*2)) >> 4
|
||||||
mov es, ax
|
mov es, ax
|
||||||
mov di, 20
|
mov di, 0
|
||||||
|
mov word es:[di+0], 0x1f00 | 'S'
|
||||||
|
mov word es:[di+2], 0x1f00 | 'S'
|
||||||
|
mov word es:[di+4], 0x1f00 | ':'
|
||||||
|
add di, 6
|
||||||
|
mov ax, ss
|
||||||
|
call real_printword
|
||||||
|
add di, 2
|
||||||
|
mov word es:[di+0], 0x1f00 | 'S'
|
||||||
|
mov word es:[di+2], 0x1f00 | 'P'
|
||||||
|
mov word es:[di+4], 0x1f00 | ':'
|
||||||
|
add di, 6
|
||||||
mov ax, sp
|
mov ax, sp
|
||||||
call real_printword
|
call real_printword
|
||||||
add di, 2
|
add di, 2
|
||||||
|
mov word es:[di+0], 0x1f00 | 'D'
|
||||||
|
mov word es:[di+2], 0x1f00 | 'S'
|
||||||
|
mov word es:[di+4], 0x1f00 | ':'
|
||||||
|
add di, 6
|
||||||
mov ax, ds
|
mov ax, ds
|
||||||
call real_printword
|
call real_printword
|
||||||
add di, 2
|
add di, 2
|
||||||
|
mov word es:[di+0], 0x1f00 | 'C'
|
||||||
|
mov word es:[di+2], 0x1f00 | 'S'
|
||||||
|
mov word es:[di+4], 0x1f00 | ':'
|
||||||
|
add di, 6
|
||||||
mov ax, cs
|
mov ax, cs
|
||||||
call real_printword
|
call real_printword
|
||||||
.loop:
|
add di, 2
|
||||||
inc byte [0]
|
mov ax, cs
|
||||||
;mov ax, 0x1111
|
mov ds, ax
|
||||||
;mov ds, ax
|
mov si, .testStr
|
||||||
;mov ax, 0x2222
|
mov ah, 0x1f
|
||||||
;mov es, ax
|
mov cx, .testStr_end - .testStr
|
||||||
;mov ax, 0x3333
|
.print:
|
||||||
;mov fs, ax
|
lodsb
|
||||||
;mov ax, 0x4444
|
stosw
|
||||||
;mov gs, ax
|
loop .print
|
||||||
;mov ax, 0x5555
|
int 3 ; wait for key
|
||||||
;mov ss, ax
|
|
||||||
;mov ax, 0x6666
|
|
||||||
;mov sp, ax
|
|
||||||
int 3
|
|
||||||
int 3
|
|
||||||
;jmp .loop
|
|
||||||
mov ax, 0x13
|
|
||||||
int 0x10
|
|
||||||
int 0x30 ; exit
|
int 0x30 ; exit
|
||||||
jmp $
|
ud2
|
||||||
extern real_test
|
.testStr: db "PRESS ANY KEY"
|
||||||
real_test:
|
.testStr_end:
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
jmp $
|
|
||||||
[BITS 32]
|
|
||||||
; extern void enter_v86(uint32_t ss, uint32_t esp, uint32_t cs, uint32_t eip);
|
|
||||||
global enter_v86
|
|
||||||
enter_v86:
|
|
||||||
mov ebp, esp ; save stack pointer
|
|
||||||
mov dword [0x20004], ebp ; tss ESP0
|
|
||||||
push dword [ebp+4] ; ss
|
|
||||||
push dword [ebp+8] ; esp
|
|
||||||
pushfd ; eflags
|
|
||||||
or dword [esp], (1 << 17) ; set VM flags
|
|
||||||
;or dword [esp], (3 << 12) ; IOPL 3
|
|
||||||
push dword [ebp+12] ; cs
|
|
||||||
push dword [ebp+16] ; eip
|
|
||||||
iret
|
|
||||||
|
|
||||||
|
84
v86defs.h
Normal file
84
v86defs.h
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Labels of v8086 programs
|
||||||
|
// TODO Remove these and use
|
||||||
|
// a single define for location?
|
||||||
|
extern void v86Test();
|
||||||
|
extern void v86TransFlag();
|
||||||
|
extern void v86Interrupt();
|
||||||
|
extern void v86TextMode();
|
||||||
|
extern void v86DiskOp();
|
||||||
|
extern void v86DiskGetGeometry();
|
||||||
|
extern void v86DiskReadCHS();
|
||||||
|
|
||||||
|
union __attribute((__packed__)) V86Regs_t {
|
||||||
|
struct dword_regs {
|
||||||
|
uint32_t ebp;
|
||||||
|
uint32_t edi;
|
||||||
|
uint32_t esi;
|
||||||
|
uint32_t ebx;
|
||||||
|
uint32_t edx;
|
||||||
|
uint32_t ecx;
|
||||||
|
uint32_t eax;
|
||||||
|
} d;
|
||||||
|
struct word_regs {
|
||||||
|
uint16_t bp, _upper_bp;
|
||||||
|
uint16_t di, _upper_di;
|
||||||
|
uint16_t si, _upper_si;
|
||||||
|
uint16_t bx, _upper_bx;
|
||||||
|
uint16_t dx, _upper_dx;
|
||||||
|
uint16_t cx, _upper_cx;
|
||||||
|
uint16_t ax, _upper_ax;
|
||||||
|
} w;
|
||||||
|
struct byte_regs {
|
||||||
|
uint32_t ebp;
|
||||||
|
uint32_t edi;
|
||||||
|
uint32_t esi;
|
||||||
|
uint8_t bl, bh;
|
||||||
|
uint16_t _upper_bx;
|
||||||
|
uint8_t dl, dh;
|
||||||
|
uint16_t _upper_dx;
|
||||||
|
uint8_t cl, ch;
|
||||||
|
uint16_t _upper_cx;
|
||||||
|
uint8_t al, ah;
|
||||||
|
uint16_t _upper_ax;
|
||||||
|
} h;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern uint32_t enter_v86(uint32_t ss, uint32_t esp, uint32_t cs, uint32_t eip, union V86Regs_t *regs);
|
||||||
|
|
||||||
|
void V8086Int(uint8_t interrupt, union V86Regs_t *regs);
|
||||||
|
|
||||||
|
struct __attribute((__packed__)) Int13DiskPacket_t {
|
||||||
|
uint8_t size; // 0x10
|
||||||
|
uint8_t reserved; // 0x00
|
||||||
|
uint16_t blocks;
|
||||||
|
uint32_t transfer_buffer; // 0x2300:0000
|
||||||
|
uint64_t start_block;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct Int13DiskPacket_t v86disk_addr_packet;
|
||||||
|
|
||||||
|
/* Real Mode helper macros */
|
||||||
|
/* segment:offset pair */
|
||||||
|
typedef uint32_t FARPTR;
|
||||||
|
|
||||||
|
/* Make a FARPTR from a segment and an offset */
|
||||||
|
#define MK_FP(seg, off) ((FARPTR) (((uint32_t) (seg) << 16) | (uint16_t) (off)))
|
||||||
|
|
||||||
|
/* Extract the segment part of a FARPTR */
|
||||||
|
#define FP_SEG(fp) (((FARPTR) fp) >> 16)
|
||||||
|
|
||||||
|
/* Extract the offset part of a FARPTR */
|
||||||
|
#define FP_OFF(fp) (((FARPTR) fp) & 0xffff)
|
||||||
|
|
||||||
|
/* Convert a segment:offset pair to a linear address */
|
||||||
|
#define FP_TO_LINEAR(seg, off) ((void*)(uintptr_t)((((uint32_t)seg) << 4) + ((uint32_t)off)))
|
||||||
|
|
||||||
|
#define EFLAG_IF ((uint32_t)1 << 9)
|
||||||
|
#define EFLAG_VM ((uint32_t)1 << 17)
|
||||||
|
|
||||||
|
FARPTR i386LinearToFp(void *ptr);
|
||||||
|
|
||||||
|
void ensure_v86env();
|
BIN
virtdisk.bin.ex
Normal file
BIN
virtdisk.bin.ex
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user