commit 33fa14c3a5252446421ca849bcde154dd4b20e96
parent 9981267a324c3523d22aeb0f87de5171b9977f26
Author: Virgil Dupras <hsoft@hardcoded.net>
Date: Fri, 28 Oct 2022 14:36:41 -0400
i386/pc: add floppy driver
For now, this has only been tried on QEMU. There are probably timing issues on
real hardware.
Also, have the makefile use the floppy option by default. It's significantly
slower, but it allows me to notice problems with the new driver.
I had to change the MBR bootloader so that it loads in multiple int13h calls.
With 18 sectors per track, it's not possible to load the whole kernel in one
shot. I also had to fix geometry detection which didn't work with floppies.
Diffstat:
10 files changed, 175 insertions(+), 49 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,7 +1,8 @@
TARGETS = dusk dis
BOOTFS_SRC = fs/xcomp/bootlo.fs posix/glue.fs fs/xcomp/boothi.fs
ALLSRCS = $(shell find fs)
-QEMU_FLAGS = -machine q35 -accel kvm -accel tcg
+QEMU_FLAGS = -accel kvm -accel tcg
+QEMU_DRVOPTS = format=raw,index=0,if=floppy
all: $(TARGETS)
fs/init.fs: posix/init.fs fs/xcomp/init.fs
@@ -17,20 +18,20 @@ dis: posix/dis.c
$(CC) posix/dis.c -Wall -o $@
pc.img: dusk $(ALLSRCS)
- dd if=/dev/zero of=$@ bs=1M count=1
+ dd if=/dev/zero of=$@ bs=512 count=2880
./dusk < buildpc.fs
.PHONY: pcrun
pcrun: pc.img
- qemu-system-i386 $(QEMU_FLAGS) -drive file=pc.img,format=raw
+ qemu-system-i386 $(QEMU_FLAGS) -drive file=pc.img,$(QEMU_DRVOPTS)
pctest.img: dusk $(ALLSRCS)
- dd if=/dev/zero of=$@ bs=1M count=1
+ dd if=/dev/zero of=$@ bs=512 count=2880
./dusk < buildpctest.fs
.PHONY: testpc
testpc: pctest.img
- qemu-system-i386 $(QEMU_FLAGS) -nographic -drive file=pctest.img,format=raw | tee /dev/stderr | grep "All tests passed"
+ qemu-system-i386 $(QEMU_FLAGS) -nographic -drive file=pctest.img,$(QEMU_DRVOPTS) | tee /dev/stderr | grep "All tests passed"
rpi.img: dusk $(ALLSRCS)
dd if=/dev/zero of=$@ bs=1K count=1
diff --git a/REFS.md b/REFS.md
@@ -27,6 +27,9 @@ helpful:
* [Intel's ICH7 datasheet][ich7]
* [AHCI 1.3.1 specifications][ahci]
* [Serial ATA 1.0 specifications][sata]
+* [Intel 82077 datasheet (floppy)][82077]
+* [Intel 8259 datasheet (PIC)][8259]
+* [Intel 82093 datasheet (APIC)][82093]
[osdev]: https://wiki.osdev.org/
[freevga]: http://www.osdever.net/FreeVGA/home.htm
@@ -34,3 +37,6 @@ helpful:
[ich7]: https://www.intel.com/content/dam/doc/datasheet/i-o-controller-hub-7-datasheet.pdf
[ahci]: https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1-3-1.pdf
[sata]: https://www.seagate.com/support/disc/manuals/sata/sata_im.pdf
+[82077]: http://www.buchty.net/casio/files/82077.pdf
+[8259]: http://pdos.csail.mit.edu/6.828/2005/readings/hardware/8259A.pdf
+[82093]: https://web.archive.org/web/20161128201003/http://www.intel.com/design/chipsets/datashts/290566.htm
diff --git a/fs/doc/design/purpose.txt b/fs/doc/design/purpose.txt
@@ -6,19 +6,19 @@ but that there's many modern computers still around.
As communities become isolated and those machines begin to break down, these
machines will need to be used in creative manners if one wants to use them at
-all. The complexity of modern operating systems, through their complexity, will
-hinder this creativity and make its success less likely.
+all. The complexity of modern operating systems will hinder this creativity and
+make its success less likely.
Dusk OS, through simplicity, aims to minimally hinder this creativity and
provide as many tools as possible to help it.
-Maximally use the hardware, alright, but what for? The ultimate purpose of these
-computers is open ended, Dusk OS is developed and optimized for a few of those
-purposes that are expected to be important during civilizational collapse.
+Maximally use the hardware, alright, but what for? While the ultimate purpose of
+these computers is open ended, Dusk OS is developed and optimized for a few of
+those purposes that are expected to be important during civilizational collapse.
## Access to archives
-We have enormous amount of knowledge stored on media that is only accessible
+We have enormous amounts of knowledge stored on media that is only accessible
through the use of a modern computer. Some of this knowledge will be of vital
importance to different communities as they become isolated. Access to this
knowledge will have a big influence on the fate of those communities.
@@ -39,7 +39,7 @@ and convenient.
Therefore, we want to be able to repurpose modern hardwre in wicked ways to
achieve goals with severly limited means. For example, one could imagine
repurposing a router's ethernet connectors to do simple bit banging to control
-a adhoc circuit. Dusk is design to maximally allow that.
+a adhoc circuit. Dusk is designed to maximally allow that.
## Efficient "free-handed" computing
diff --git a/fs/drv/pc/cmos.fs b/fs/drv/pc/cmos.fs
@@ -0,0 +1,8 @@
+\ CMOS access
+
+?f<< /asm/i386.fs
+
+code cmos@ ( regnum -- val )
+ al bp 0 d) mov, cli,
+ $70 i) al out, $71 i) al in,
+ sti, bp 0 d) al mov, ret,
diff --git a/fs/drv/pc/fdc.fs b/fs/drv/pc/fdc.fs
@@ -1,20 +1,91 @@
\ PC floppy disk controller driver
-
-\ get number of floppy drives from CMOS register $10
-$10 $70 pc! $71 pc@ dup $f and const FDSLAVETYPE 16 / const FDMASTERTYPE
+\ Based on Intel 82077AA specifications which, on the PC platform, is plugged on
+\ IO port $3f0.
+\ For now, we hardcode for 1.44MB floppy controller on master bus.
+\ Also, this has only been tested on QEMU so far.
+?f<< /drv/pc/cmos.fs
+: _err abort" FDC error" ; : _assert not if _err then ;
\ hardcode for 1.44M disks.
-
2 const FDHEADS
80 const FDCYLCNT
18 const FDSECPERTRK
FDSECPERTRK FDHEADS * const FDSECPERCYL
+\ the idea is that linear sectors begin a cyl0 head0 and sec0. Then, we
+\ increment sec until we get to secMax and rollover to head1. Then, we get to
+\ secMax again and rollover to next cylinder.
+\ remember: sectors are 1-based
: lba>chs ( lba -- cyl head sec )
- dup FDSECPERCYL / ( lba cyl )
- over FDHEADS mod ( lba cyl head )
- rot FDSECPERCYL mod ( cyl head sec ) ;
+ FDSECPERCYL /mod ( sec*hd cyl )
+ swap FDSECPERTRK /mod ( cyl sec head ) swap 1+ ;
+
+extends Drive struct[ FloppyDrive
+ $200 const SECSZ
+ $3f0 const SRA \ R
+ $3f1 const SRB \ R
+ $3f2 const DOR \ RW
+ $3f3 const TDR \ RW
+ $3f4 const MSR \ R RMQ DIO NONDMA CMDBSY DBSY3 DBSY2 DBSY1 DBSY0
+ $3f4 const DSR \ W
+ $3f5 const FIFO \ RW
+ $3f7 const DIR \ R
+ $3f7 const CCR \ W
+
+ \ is the FDC ready to receive a command? RMQ=1 DIO=0
+ : :ready? ( self -- f ) drop MSR pc@ $8f and $80 = ;
+ : :read? ( self -- f ) drop MSR pc@ $40 and bool ;
+ : :canread# ( self -- ) :read? _assert ;
+ : :canwrite# ( self -- ) drop MSR pc@ $40 and not _assert ;
+ : :waitready begin dup :ready? until drop ;
+ : :waitresult begin dup :waitready dup :read? until drop ;
+ \ Write a single command byte to FIFO
+ : :cmd! ( b self -- ) dup :waitready :canwrite# FIFO pc! ;
+ : :result@ ( self -- b ) dup :waitready :canread# FIFO pc@ ;
+ : :motoron ( self -- ) drop $14 DOR pc! ;
+ : :motoroff ( self -- ) drop $04 DOR pc! ;
+ : :version ( self -- version ) $10 over :cmd! :result@ ;
+ \ Configure to: EIS=1 EFIFO=0 POLL=1 FIFOTHR=1 PRETRK=0
+ : :configure ( self -- )
+ $13 over :cmd! 0 over :cmd! $51 over :cmd! 0 swap :cmd! ;
+ : :lock ( self -- ) $94 over :cmd! :result@ $10 = _assert ;
+ \ we use the "safest" (slowest) values HUT=0 SRT=0 HLT=0 NON-DMA=1
+ : :specify ( self -- ) $03 over :cmd! 0 over :cmd! $01 swap :cmd! ;
+ : :senseint ( self -- ) $08 over :cmd! dup :result@ drop :result@ drop ;
+ : :recalibrate ( self -- )
+ $07 over :cmd! $00 over :cmd! dup :waitready :senseint ;
+ \ RESET=1 POWERDOWN=0 PRECOMP=0 (default) DRATE=0 (500 Kbps)
+ : :reset ( self -- ) drop $80 DSR pc! ;
+ : :init
+ dup :version $90 = _assert dup :configure dup :lock dup :reset
+ dup :specify dup :motoron \ TODO: add a mechanism to turn this off
+ :recalibrate
+ \ TODO: I have no idea why, but if I don't poke the $60 port between a
+ \ calibration and a sector read, the read will fail (under QEMU). Port $60
+ \ is PS/2 controller's CMD port. Investigate.
+ $60 pc@ drop ;
+ : _sec@ ( sec dst self -- ) >r \ V1=self
+ swap lba>chs ( dst cyl head sec )
+ $46 ( MFM+READ ) V1 :cmd!
+ over 2 lshift V1 :cmd! ( dst cyl head sec )
+ rot V1 :cmd! ( dst head sec )
+ swap V1 :cmd! ( dst sec )
+ dup V1 :cmd! ( dst sec )
+ 2 ( 512b sector size ) V1 :cmd! ( dst sec )
+ ( EOT=sec: 1 sector read ) V1 :cmd!
+ $1b ( GAP1 default size ) V1 :cmd!
+ $ff ( no DTL ) V1 :cmd! ( dst )
+ SECSZ >r begin ( dst )
+ MSR pc@ $20 and _assert \ the NDMA bit tells us that this is data
+ V1 :waitready FIFO pc@ swap c!+ next drop ( )
+ V1 :waitresult
+ 7 >r begin V1 :result@ drop next rdrop ;
+ : _sec! ( sec src self -- ) abort" TODO" ;
+ : :new ( -- drv ) here SECSZ , ['] _sec@ , ['] _sec! , ;
+]struct
+
+FloppyDrive :new structbind FloppyDrive floppy
-\ TODO finish this. I was set on starting with this driver because I thought
-\ that my hardware treated boot USB key as a floppy, but it doesn't (master and
-\ slave config from CMOS are both 0). I'll come back to floppies later.
+: fdc?
+ $10 cmos@ bool \ we don't care about drive types or master/slave yet
+ if floppy :version $90 = else 0 then ;
diff --git a/fs/fs/fat.fs b/fs/fs/fat.fs
@@ -199,7 +199,7 @@ create _FATTemplate
( jmp ) $eb c, $3c c, $90 c, ( OEMName ) ," DuskFAT " ( BytsPerSec ) 0 c, 0 c,
( SecPerClus ) 0 c, ( RsvdSecCnt ) 0 c, 0 c, ( NumFAT ) 1 c,
( RootEntCnt ) 0 c, 2 c, ( TotSec16 ) 0 c, 0 c, ( Media ) $f0 c,
-( FATsz16 ) 0 c, 0 c, ( SecPerTrk ) $3f c, 0 c, ( NumHeads ) $10 c, 0 c,
+( FATsz16 ) 0 c, 0 c, ( SecPerTrk ) 18 c, 0 c, ( NumHeads ) 2 c, 0 c,
( HiddenSec ) 0 , ( TotSec32 ) 0 , ( DrvNum ) 0 c, ( Reserved1 ) 0 c,
( BootSig ) $29 c, ( VolID ) 0 , ( VolLabel ) ," NONAME "
( FilSysType ) ," FAT12 "
diff --git a/fs/xcomp/i386/kernel.fs b/fs/xcomp/i386/kernel.fs
@@ -29,7 +29,7 @@
lblcurword lblnextmeta lblret lblsysdict lblemit lblparsec lblparseh
lblparseud lblerrmsg lblrtype lblhere lbl[rcnt] lblmovewrite lblwrite
lblcwrite lblfind lblcompiling lblidt lblwoff
-$8000 const HERESTART \ TODO: find a better place
+$8000 const HERESTART
$500 to binstart
$2000 const STACKSZ
$7c00 const RSTOP
@@ -41,11 +41,11 @@ PSTOP STACKSZ - const HEREMAX
forward16 jmp, to L1
pc to lblintnoop iret,
\ Interrupt Descriptor Table
-pc $100 idtgen
+0 align4 pc $100 idtgen
pc to lblidt
$100 8 * 1- w, ( pc ) ,
L1 forward!
-lblidt m) lidt,
+lblidt m) lidt, sti,
forward16 jmp, to L1
xcode noop pc to lblret ret,
diff --git a/fs/xcomp/i386/pc/init.fs b/fs/xcomp/i386/pc/init.fs
@@ -1,6 +1,6 @@
\ Initialization for PC
-\ This is hardcoded for QEMU with -machine q35. You need to adapt to your
-\ own machine.
+\ This is hardcoded for QEMU with -machine pc and a floppy boot disk.
+\ You need to adapt to your own machine.
: ARCH S" i386" ;
\ we pre-load driver dependencies as much as possible here to keep file cursor
@@ -14,11 +14,20 @@ f<< /sys/grid.fs
' (emit) to emit
f<< /drv/pc/pci.fs
-f<< /drv/pc/ahci.fs
-ahci$ ahci? [if]
- ." Using AHCI driver...\n"
- 0 AHCIDrive :new dup bootfs to Filesystem drv ( drv )
- AHCIDrive :enable [then]
+\ Floppy boot drive
+f<< /drv/pc/fdc.fs
+floppy :init
+floppy :self bootfs to Filesystem drv
+
+\ IDE boot drive
+\ f<< /drv/pc/ata.fs
+\ ATADrive :reset drop ATA0:0 bootfs to Filesystem drv
+
+\ AHCI boot drive
+\ f<< /drv/pc/ahci.fs
+\ 0 AHCIDrive :new dup bootfs to Filesystem drv ( drv )
+\ AHCIDrive :enable
+
f<< /fs/fat.fs
f<< /drv/pc/ps28042.fs
@@ -27,5 +36,6 @@ f<< /sys/ps2.fs
' 8042ps2@? to ps2@?
' ps2keyset1 to key
+
f<< /drv/pc/a20.fs
a20$
diff --git a/fs/xcomp/i386/pc/kernel.fs b/fs/xcomp/i386/pc/kernel.fs
@@ -7,10 +7,10 @@ pc to L1 \ back to protected mode!
lblidt m) lidt, sti,
ret,
-pc $ffff w, 0 , \ real mode IVT
+pc $3ff w, 0 , \ real mode IVT
pc to L2 1 to realmode \ we're in realmode
- ax ax xor, es ax mov, ( pc ) m) lidt, sti,
+ ax ax xor, es ax mov, ds ax mov, ss ax mov, ( pc ) m) lidt, sti,
ax $0201 i) mov, $13 int,
\ we've done what we came for, let's go back to 32bit
cli, ax cr0 mov, ax 1 i) or, cr0 ax mov,
@@ -18,7 +18,7 @@ pc to L2 1 to realmode \ we're in realmode
pc to L1 \ segment with ffff limits
ax $20 i) mov, ds ax mov, ss ax mov, es ax mov, gs ax mov, fs ax mov,
- cli, ax cr0 mov, ax $fffffffe i) and, cr0 ax mov,
+ ax cr0 mov, ax $fffffffe i) and, cr0 ax mov,
0 L2 jmpfar,
0 to realmode
@@ -28,6 +28,15 @@ xcode int13h ( drv head cyl sec dst -- ) pc w>e lblsysdict pc>addr !
AX pspop, ch al mov, \ cyl
AX pspop, dh al mov, \ head
AX pspop, dl al mov, \ drive
- $18 L1 jmpfar,
+ cli, $18 L1 jmpfar,
+
+\ When booting from floppy under QEMU, I get IRQs 0 and 6 from the PIC (mapped
+\ to vector 8 and 14) and if I don't handle it with an ACK to the PIC, the
+\ system freezes. (often, but not always). With these handlers below, we always
+\ boot.
+\ TODO: have proper and complete IRQ handling
+pc lblidt pc>addr 1+ 1+ @ pc>addr 8 8 * + 16b !
+pc lblidt pc>addr 1+ 1+ @ pc>addr 14 8 * + 16b !
+ax push, al $20 i) mov, $20 i) al out, ax pop, iret,
pc lblbootptr pc>addr !
diff --git a/fs/xcomp/i386/pc/mbr.fs b/fs/xcomp/i386/pc/mbr.fs
@@ -31,21 +31,42 @@ pc to lblerror
pc to lblpayload 0 to realmode
\ initialize all segments
ax $10 i) mov, ds ax mov, ss ax mov, es ax mov, gs ax mov, fs ax mov,
- sti,
- \ Jump to payload
+ \ Jump to payload (interrupts disabled)
$08 $500 jmpfar,
1 to realmode
lblstart forward!
-cld, ax ax xor, es ax mov, ds ax mov,
-lblgdt m) lgdt,
-\ DL is set by BIOS to the proper drive number for int13h. The field
-\ BS_DrvNum is often not set properly, so let's set it.
-$7c24 m) dl mov,
-\ read BPB_RsvdSecCnt-1 sectors, starting from sector 2 from boot floppy in
-\ memory at address $500 DL is set by BIOS.
-ah 2 i) mov, al $7c0e ( BPB_RsvdSecCnt ) m) mov, al dec,
-bx $500 i) mov, cx $0002 i) mov, dh 0 i) mov, $13 int,
-lblerror abs>rel jc,
+cli, cld, ax ax xor, es ax mov, ds ax mov, ss ax mov, sp $7c00 i) mov,
+lblgdt m) lgdt, sti,
ax $0003 i) mov, $10 int, \ video mode 80x25
+\ Let's set proper values in BS_DrvNum, BPB_SecPerTrk and BPB_NumHeads
+$7c24 m) dl mov,
+dh 1 i) mov, cl 18 i) mov, \ hardcoded values if it's a floppy
+dl $80 i) cmp, forward8 jc, \ it's a hard drive, use int13h ah=8
+ ah 8 i) mov, $13 int, \ dh=numheads-1 cl&3f=sec per trk
+forward!
+cx $3f i) and,
+dh inc, $7c1a m) dh mov,
+$7c18 m) cx mov,
+si cx mov, \ si=sec per trk
+si dec, \ we begin at trk 2
+dl $7c24 m) mov,
+di $7c0e m) mov, \ di=sectors to copy+1
+di dec,
+bx $500 i) mov, \ bx=dest addr
+cx $0002 i) mov, \ ch=cyl cl=sec
+dh dh xor, \ dh=head
+\ read BPB_RsvdSecCnt-1 sectors, starting from sector 2 from boot disk in
+\ memory at address $500 DL is set by BIOS. If we're on floppy, this will break
+\ track boundaries so we need to proceed in multiple calls.
+pc
+ ax $0201 i) mov, \ ah=read cmd al=read 1 sector
+ $13 int, lblerror abs>rel jc,
+ si dec, forward8 jnz,
+ si $7c18 m) mov, cl cl xor,
+ dh inc, dh 2 i) cmp, forward8 jnz, dh dh xor, ch inc, forward!
+ forward!
+ bx $200 i) add,
+ cl inc,
+ di dec, abs>rel jnz,
cli, ax cr0 mov, ax 1 i) or, cr0 ax mov,
$08 lblpayload jmpfar,