ordos/io.asm

705 lines
10 KiB
NASM
Raw Permalink Normal View History

2021-08-26 07:11:20 +00:00
; SPDX-License-Identifier: MIT
; Copyright (c) 2021 Juhani 'nortti' Krekelä.
debug equ 1
2021-08-26 07:11:20 +00:00
iosegment equ 60h
dossegment equ iosegment + 1*1024/16 ; DOS starts 1KiB after IO system
dos segment at dossegment
org 0
dosinit proc far
dosinit endp
dos ends
code segment
code ends
constants segment
constants ends
data segment
data ends
iogroup group code, constants, data
code segment
2021-09-26 10:43:38 +00:00
assume cs:iogroup
2021-08-26 07:11:20 +00:00
org 0
; Jump table
2021-09-07 09:30:59 +00:00
jmp init ; 0000
2021-09-10 16:39:58 +00:00
jmp near ptr status ; 0003
jmp near ptr getch ; 0006
jmp near ptr putch ; 0009
2021-09-07 09:30:59 +00:00
jmp unimplemented ; 000c Output to printer
jmp unimplemented ; 000f Serial read
jmp unimplemented ; 0012 Serial write
2021-09-10 16:39:58 +00:00
jmp near ptr diskread ; 0015
2021-09-07 09:30:59 +00:00
jmp diskwrite ; 0018
jmp near ptr diskchange ; 001b
2021-09-26 10:43:38 +00:00
jmp near ptr setdate ; 001e
jmp near ptr settime ; 0021
jmp near ptr gettime ; 0024
2021-09-10 16:39:58 +00:00
jmp near ptr flush ; 0027
2021-09-07 09:30:59 +00:00
jmp near ptr mapdev ; 002a
2021-08-26 07:11:20 +00:00
init:
cld
cli
mov ax, cs
mov ds, ax
mov es, ax
; Put setup stack just below 32K
xor ax, ax
mov ss, ax
mov sp, 8000h
sti
; Figure out memory size
int 12h
; AX = memory size in kilobytes
; We want it in paragraphs
; There are 64 paragraphs in a kilobyte
mov cx, 6
shl ax, cl
2021-08-26 07:11:20 +00:00
; Memory size is passed in dx
mov dx, ax
if debug
2021-09-26 08:46:50 +00:00
call hexprint16
call newline
endif
2021-08-26 07:11:20 +00:00
; Disk table is passed in si
mov si, offset iogroup:disks_table
call dosinit
2021-09-26 08:46:50 +00:00
; 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
2021-09-07 09:30:59 +00:00
mov ax, cs
mov ds, ax
; Open command.com
mov dx, offset iogroup:command_fcb
mov ah, 0fh
int 21h
test al, al
jnz open_error
if debug
2021-09-07 09:30:59 +00:00
mov al, '+'
2021-09-26 08:46:50 +00:00
mov ah, 0eh
int 10h
endif
2021-09-26 08:46:50 +00:00
; Set random record field 0
2021-09-26 10:43:38 +00:00
mov word ptr iogroup:command_fcb+33, 0
mov word ptr iogroup:command_fcb+35, 0
2021-09-26 08:46:50 +00:00
; Set record size to 1 byte
2021-09-26 10:43:38 +00:00
mov word ptr iogroup:command_fcb+14, 1
2021-09-26 08:46:50 +00:00
; Read command.com into memory
mov ah, 27h
int 21h
jcxz read_error
cmp al, 1
jne read_error
if debug
2021-09-26 08:46:50 +00:00
mov ax, cx
call hexprint16
mov al, '.'
mov ah, 0eh
int 10h
endif
2021-09-26 08:46:50 +00:00
; 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
2021-09-07 09:30:59 +00:00
2021-09-26 08:46:50 +00:00
trampoline proc far
ret
trampoline endp
; TODO: Better error reporting
2021-09-07 09:30:59 +00:00
open_error:
mov al, '-'
2021-09-26 08:46:50 +00:00
jmp unfinished
read_error:
mov al, '!'
2021-09-07 09:30:59 +00:00
unfinished:
mov ah, 0eh
int 10h
jmp hang
2021-08-26 07:11:20 +00:00
; OUT:
; al = character if any
; zf = there were no characters
status proc far
2021-09-10 16:39:58 +00:00
push bx
2021-09-07 09:30:59 +00:00
push ax
2021-09-10 16:39:58 +00:00
mov ah, 1
int 16h
mov bl, al
2021-09-07 09:30:59 +00:00
pop ax
2021-09-10 16:39:58 +00:00
mov al, bl
pop bx
2021-08-26 07:11:20 +00:00
ret
status endp
2021-09-10 16:39:58 +00:00
; OUT:
; al = character
getch proc far
push bx
push ax
xor ah, ah
int 16h
mov bl, al
pop ax
mov al, bl
pop bx
ret
getch endp
2021-08-26 07:11:20 +00:00
; IN:
2021-09-10 16:39:58 +00:00
; al = character
2021-08-26 07:11:20 +00:00
putch proc far
push ax
mov ah, 0eh
int 10h
pop ax
ret
putch endp
2021-09-07 09:30:59 +00:00
; IN:
; al = driver number
; ds:bx = buffer
; cx = number of sectors to read
; dx = LBA of first sector
; OUT:
; TODO: Document
diskread proc far
if debug
2021-09-07 09:30:59 +00:00
push ax
mov ax, 0e00h + 'r'
int 10h
pop ax
call far_caller
2021-09-26 08:46:50 +00:00
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
endif
2021-09-07 09:30:59 +00:00
2021-09-26 08:46:50 +00:00
; TODO: Everything except sregs can be trashed
2021-09-07 09:30:59 +00:00
push es
push ax
push bx
push cx
push dx
push di
; Save driver number
push ax
; BIOS uses es:bx instead of ds:bx
mov ax, ds
mov es, ax
mov ax, dx
; Put drive(r) number in dl
pop dx
sector_read_loop:
jcxz ret_diskread
mov di, 3 + 1 ; 3 retries, + 1 since we dec first
try_sector_read:
push ax
push cx
call chs
mov ah, 2
mov al, 1
int 13h
jnc sector_read_success
dec di
jz sector_read_fail
2022-03-29 01:41:45 +00:00
reset_disk:
if debug
mov al, '"'
mov ah, 0eh
int 10h
endif
xor ax, ax
int 13h
2021-09-07 09:30:59 +00:00
pop cx
pop ax
jmp try_sector_read
sector_read_fail:
2021-09-26 08:46:50 +00:00
mov al, ah
if debug
2021-09-26 08:46:50 +00:00
call hexprint8;debg
endif
2021-09-26 08:46:50 +00:00
mov al, '?'
mov ah, 0eh
int 10h
2021-09-07 09:30:59 +00:00
pop cx
pop ax
pop di
pop dx
pop ax ; Would be cx, don't overwrite
pop bx
pop ax
pop es
mov al, 12 ; TODO: Don't hardcode
2021-09-26 08:46:50 +00:00
stc
2021-09-07 09:30:59 +00:00
ret
sector_read_success:
pop cx
pop ax
dec cx
inc ax
add bx, 512
jmp sector_read_loop
ret_diskread:
pop di
pop dx
pop cx
pop bx
pop ax
pop es
2021-09-26 08:46:50 +00:00
clc
2021-09-07 09:30:59 +00:00
ret
diskread endp
; IN:
; ax = LBA
; OUT:
; ch = cylinder
; cl = sector & 2 high bits of cylinder
; dh = head
chs proc
push ax
push dx
xor dx, dx
; cylinder (track) - head - sector
; cylinder = LBA / sectorspertrack / heads
; head = LBA / sectorspertrack % heads
; sector = LBA % sectorspertrack + 1
2021-09-26 10:43:38 +00:00
div iogroup:sectorspertrack
2021-09-07 09:30:59 +00:00
; ax = LBA / sectorspertrack
; dx = LBA % sectorspertrack
; sector
mov cl, dl
inc cl
xor dx, dx
2021-09-26 10:43:38 +00:00
div iogroup:heads
2021-09-07 09:30:59 +00:00
; ax = LBA / sectorspertrack / heads
; dx = LBA / sectorspertrack % heads
; head
mov dh, dl
; cylinder (track)
mov ch, al
2021-09-26 08:46:50 +00:00
shr ax, 1
shr ax, 1
and al, 0c0h
or cl, al
2021-09-07 09:30:59 +00:00
mov ax, dx
pop dx
mov dh, ah
pop ax
ret
chs endp
2021-08-26 07:11:20 +00:00
diskwrite:
mov al, 'w'
jmp error
2021-09-07 09:30:59 +00:00
; IN:
; al = drive
; OUT:
; ah = -1 different / 0 dunno / 1 same
; cf = 0 -> al = driver num
; cf = 1 -> al = disk error code
diskchange proc far
; TODO: Implement
2021-09-26 08:46:50 +00:00
mov ax, 0000h
2021-09-07 09:30:59 +00:00
clc
ret
diskchange endp
2021-09-26 10:43:38 +00:00
; 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
2021-08-26 07:11:20 +00:00
2021-09-10 16:39:58 +00:00
flush proc far
2021-09-07 09:30:59 +00:00
push ax
2021-09-10 16:39:58 +00:00
flush_loop:
mov ah, 1
int 16h
jz flush_ret
xor ah, ah
int 16h
jmp flush_loop
flush_ret:
2021-09-07 09:30:59 +00:00
pop ax
2021-09-10 16:39:58 +00:00
ret
flush endp
mapdev proc far
; TODO: Implement
2021-09-07 09:30:59 +00:00
ret
mapdev endp
2021-08-26 07:11:20 +00:00
unimplemented:
mov al, '@'
error:
mov ah, 0eh
int 10h
2021-09-07 09:30:59 +00:00
mov ah, 0eh
mov al, '!'
int 10h
if debug
2021-09-07 09:30:59 +00:00
call far_caller
endif
2021-08-26 07:11:20 +00:00
hang:
hlt
jmp hang
code ends
2021-09-07 09:30:59 +00:00
; TODO: STRUC?
2021-08-26 07:11:20 +00:00
constants segment
2021-09-07 09:30:59 +00:00
sectorspertrack dw 8 ; TODO: Don't hardcode
heads dw 2 ; TODO: Don't hardcode
2021-08-26 07:11:20 +00:00
disks_table:
2021-09-07 09:30:59 +00:00
db 1 ; 1 drive, TODO: Don't hardcode
2021-08-26 07:11:20 +00:00
db 0 ; Physical drive 0
dw offset iogroup:parameters_320k
parameters_320k:
dw 512 ; Sector size in bytes
db 2 ; Sectors per cluster
dw 1 ; Number of reserved sectors
db 2 ; Number of FATs
2021-08-26 07:11:20 +00:00
dw 112 ; Number of directory entries
dw 320*2 ; Number of sectors
2021-09-26 10:43:38 +00:00
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
2021-08-26 07:11:20 +00:00
constants ends
2021-09-07 09:30:59 +00:00
data segment
command_fcb:
db 1 ; First drive, TODO: Don't hardcode
db "COMMAND COM"
db 25 dup (?)
2021-09-26 10:43:38 +00:00
days_since_epoch: dw 0
2021-09-07 09:30:59 +00:00
data ends
code segment
if debug
2021-09-07 09:30:59 +00:00
hexprint16 proc
xchg al, ah
call hexprint8
xchg al, ah
hexprint8 proc
rol al, 1
rol al, 1
rol al, 1
rol al, 1
call hexprint4
rol al, 1
rol al, 1
rol al, 1
rol al, 1
hexprint4 proc
push ax
and al, 0fh
cmp al, 9
jbe under_10
add al, 'a' - 10 - '0'
under_10:
add al, '0'
mov ah, 0eh
int 10h
pop ax
ret
hexprint4 endp
hexprint8 endp
hexprint16 endp
newline proc
push ax
mov ax, 0e0dh
int 10h
mov ax, 0e0ah
int 10h
pop ax
ret
newline endp
space proc
push ax
mov ax, 0e20h
int 10h
pop ax
ret
space endp
far_caller proc
push ax
push bx
mov bx, sp
mov ax, [ss:bx + 8]
call hexprint16
mov al, ':'
mov ah, 0eh
int 10h
pop bx
pop ax
near_caller proc
push ax
push bx
mov bx, sp
mov ax, [ss:bx + 6]
call hexprint16
call space
pop bx
pop ax
ret
near_caller endp
far_caller endp
logaddr proc
push ax
push bx
mov al, '>'
mov ah, 0eh
int 10h
mov bx, sp
mov ax, [ss:bx + 4]
call hexprint16
call space
pop bx
pop ax
ret
logaddr endp
endif
2021-09-07 09:30:59 +00:00
code ends
2021-08-26 07:11:20 +00:00
end