Compare commits

...

3 Commits

Author SHA1 Message Date
Juhani Krekelä b9c83b11cd Implement date/time 2021-09-26 13:44:03 +03:00
Juhani Krekelä 68030372b7 Load and execute command.com 2021-09-26 11:46:50 +03:00
Juhani Krekelä 1967c06b85 Fix shift used for converting KiB to paragraphs
The old code was essentially a no-op due to x86 taking shifts mod 32.
This lead DOS to believe that it had 10KiB of memory, which is not
enough to contain io.sys, the DOS kernel, and command.com. This causes
command.com to overwrite the DOS kernel when it goes to relocate itself
at the end of the memory, leading to system crash.
2021-09-26 11:33:03 +03:00
1 changed files with 241 additions and 32 deletions

273
io.asm
View File

@ -22,7 +22,7 @@ data ends
iogroup group code, constants, data
code segment
assume cs:iogroup, ds:iogroup, es:iogroup
assume cs:iogroup
org 0
; Jump table
@ -36,9 +36,9 @@ jmp unimplemented ; 0012 Serial write
jmp near ptr diskread ; 0015
jmp diskwrite ; 0018
jmp near ptr diskchange ; 001b
jmp setdate ; 001e
jmp settime ; 0021
jmp gettime ; 0024
jmp near ptr setdate ; 001e
jmp near ptr settime ; 0021
jmp near ptr gettime ; 0024
jmp near ptr flush ; 0027
jmp near ptr mapdev ; 002a
@ -60,18 +60,30 @@ init:
; AX = memory size in kilobytes
; We want it in paragraphs
; There are 64 paragraphs in a kilobyte
mov cx, 64
shl al, cl
mov cx, 6
shl ax, cl
; Memory size is passed in dx
mov dx, ax
call hexprint16
call newline
; Disk table is passed in si
mov si, offset iogroup:disks_table
call dosinit
; Memory for command.com is at ds, save for later and revert ds
push ds
; Memory for command.com is at ds, with PSP already set up
; Retrieve amount of memory in the program segment
mov cx, word ptr ds:6
; Set up DTA
mov dx, 100h
mov ah, 1ah
int 21h
; Save command.com's segment for later and revert ds for opening the FCB
mov bx, ds
mov ax, cs
mov ds, ax
@ -79,15 +91,67 @@ init:
mov dx, offset iogroup:command_fcb
mov ah, 0fh
int 21h
test al, al
jnz open_error
mov al, '+'
jmp unfinished
mov ah, 0eh
int 10h
; Set random record field 0
mov word ptr iogroup:command_fcb+33, 0
mov word ptr iogroup:command_fcb+35, 0
; Set record size to 1 byte
mov word ptr iogroup:command_fcb+14, 1
; Read command.com into memory
mov ah, 27h
int 21h
jcxz read_error
cmp al, 1
jne read_error
mov ax, cx
call hexprint16
mov al, '.'
mov ah, 0eh
int 10h
; Set up segments for command.com
mov ds, bx
mov es, bx
mov ss, bx
mov sp, 5ch ; Microsoft's documentation says to use this
; Put a zero at top of the stack
xor ax, ax
push ax
; Set default DTA
mov dx, 80h
mov ah, 1ah
int 21h
; Jump to command.com
push bx
mov ax, 100h
push ax
trampoline proc far
ret
trampoline endp
; TODO: Better error reporting
open_error:
mov al, '-'
jmp unfinished
read_error:
mov al, '!'
unfinished:
mov ah, 0eh
@ -146,7 +210,24 @@ diskread proc far
int 10h
pop ax
call far_caller
push ax
call hexprint8
call space
mov ax, ds
call hexprint16
call space
mov ax, bx
call hexprint16
call space
mov ax, cx
call hexprint16
call space
mov ax, dx
call hexprint16
call newline
pop ax
; TODO: Everything except sregs can be trashed
push es
push ax
push bx
@ -190,6 +271,11 @@ diskread proc far
jmp try_sector_read
sector_read_fail:
mov al, ah
call hexprint8;debg
mov al, '?'
mov ah, 0eh
int 10h
pop cx
pop ax
@ -201,6 +287,7 @@ diskread proc far
pop es
mov al, 12 ; TODO: Don't hardcode
stc
ret
sector_read_success:
@ -219,6 +306,7 @@ diskread proc far
pop ax
pop es
clc
ret
diskread endp
@ -238,7 +326,7 @@ chs proc
; cylinder = LBA / sectorspertrack / heads
; head = LBA / sectorspertrack % heads
; sector = LBA % sectorspertrack + 1
div cs:sectorspertrack
div iogroup:sectorspertrack
; ax = LBA / sectorspertrack
; dx = LBA % sectorspertrack
@ -247,7 +335,7 @@ chs proc
inc cl
xor dx, dx
div cs:heads
div iogroup:heads
; ax = LBA / sectorspertrack / heads
; dx = LBA / sectorspertrack % heads
@ -256,10 +344,10 @@ chs proc
; cylinder (track)
mov ch, al
;shr ax, 1
;shr ax, 1
;and al, 0c0h
;or cl, al
shr ax, 1
shr ax, 1
and al, 0c0h
or cl, al
mov ax, dx
pop dx
@ -280,26 +368,138 @@ diskwrite:
; cf = 0 -> al = driver num
; cf = 1 -> al = disk error code
diskchange proc far
push ax
mov ax, 0e00h + 'd'
int 10h
pop ax
call far_caller
; TODO: Implement
mov ax, 0100h
mov ax, 0000h
clc
ret
diskchange endp
setdate:
mov al, 'd'
jmp error
settime:
mov al, 't'
jmp error
gettime:
mov al, 'T'
jmp error
; IN:
; ax = days since 1980-01-01
setdate proc far
mov iogroup:days_since_epoch, ax
ret
setdate endp
; IN:
; ch = hour
; cl = minute
; dh = second
; dl = 1/100 second
settime proc far
push ax
push bx
push cx
push dx
push si
push di
; Save second & 1/100 second in bx
mov bx, dx
; Hour
mov al, ch
cbw
mul iogroup:doubleticks_per_hour
shl ax, 1
rcl dx, 1
; Store the partial sum in si:di
mov di, ax
mov si, dx
; Minute
mov al, cl
cbw
mul iogroup:ticks_per_minute
add di, ax
adc si, dx
; Second
mov al, bh
cbw
mul iogroup:ticks_per_second
add di, ax
adc si, dx
; 1/100 second
mov al, bl
cbw
div iogroup:cs_per_tick
xor ah, ah
add di, ax
adc si, 0
; Move result to cx:ds and set timer
mov cx, si
mov dx, di
mov ah, 01h
int 1ah
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
settime endp
; OUT:
; ax = days since 1980-01-01
; ch = hour
; cl = minute
; dh = second
; dl = 1/100 second
gettime proc far
; TODO: Use RTC if available
push bx
; Get ticks since midnight and day rollover flag
xor ah, ah
int 1ah
; al is nonzero if day has changed
test al, al
jz same_day
inc iogroup:days_since_epoch
same_day:
; Divide the ticks by 2, to keep all our arithmetic within bounds
shr cx, 1
rcr dx, 1
; int 1ah / ah=00 returns the ticks in cx:dx, div operates on dx:ax
mov ax, dx
mov dx, cx
div iogroup:doubleticks_per_hour
mov ch, al ; Hour
; Remainder in dx, range [0,doubleticks_per_hour[ represents an hour
; Multiply by 60, divide by doubleticks_per_hour to get the minute
mov ax, dx
mul iogroup:sixty
div iogroup:doubleticks_per_hour
mov cl, al ; Minute
; Do same again to get seconds
mov ax, dx
mul iogroup:sixty
div iogroup:doubleticks_per_hour
mov bl, al ; Save seconds in bl, since dh gets destroyed by next muldiv
; For 1/100th of a second, multiply by 100 instead
mov ax, dx
mul iogroup:hundred
div iogroup:doubleticks_per_hour
mov dh, bl ; Second
mov dl, al ; 1/100 second
mov ax, iogroup:days_since_epoch
pop bx
ret
gettime endp
flush proc far
push ax
@ -320,7 +520,6 @@ flush endp
mapdev proc far
; TODO: Implement
xor al, al
ret
mapdev endp
@ -362,6 +561,14 @@ parameters_320k:
dw 112 ; Number of directory entries
dw 320*2 ; Number of sectors
doubleticks_per_hour dw 32772 ; 1800B0h / 2 / 24
ticks_per_minute dw 1092 ; 1800B0h / 24 / 60
ticks_per_second dw 18 ; 1800B0h / 24 / 60 / 60
cs_per_tick db 5 ; 24 * 60 * 60 * 100 / 1800B0h
sixty dw 60
hundred dw 100
constants ends
data segment
@ -371,6 +578,8 @@ command_fcb:
db "COMMAND COM"
db 25 dup (?)
days_since_epoch: dw 0
data ends
code segment