; SPDX-License-Identifier: MIT ; Copyright (c) 2021 Juhani 'nortti' Krekelä. 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 assume cs:iogroup, ds:iogroup, es:iogroup org 0 ; Jump table jmp init ; 0000 jmp near ptr status ; 0003 jmp near ptr getch ; 0006 jmp near ptr putch ; 0009 jmp unimplemented ; 000c Output to printer jmp unimplemented ; 000f Serial read 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 flush ; 0027 jmp near ptr mapdev ; 002a 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, 64 shl al, cl ; Memory size is passed in dx mov dx, ax ; 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 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 mov al, '+' jmp unfinished open_error: mov al, '-' unfinished: mov ah, 0eh int 10h jmp hang ; OUT: ; al = character if any ; zf = there were no characters status proc far push bx push ax mov ah, 1 int 16h mov bl, al pop ax mov al, bl pop bx ret status endp ; 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 ; IN: ; al = character putch proc far push ax mov ah, 0eh int 10h pop ax ret putch endp ; IN: ; al = driver number ; ds:bx = buffer ; cx = number of sectors to read ; dx = LBA of first sector ; OUT: ; TODO: Document diskread proc far push ax mov ax, 0e00h + 'r' int 10h pop ax call far_caller 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 pop cx pop ax jmp try_sector_read sector_read_fail: 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 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 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 div cs:sectorspertrack ; ax = LBA / sectorspertrack ; dx = LBA % sectorspertrack ; sector mov cl, dl inc cl xor dx, dx div cs:heads ; ax = LBA / sectorspertrack / heads ; dx = LBA / sectorspertrack % heads ; head mov dh, dl ; cylinder (track) mov ch, al ;shr ax, 1 ;shr ax, 1 ;and al, 0c0h ;or cl, al mov ax, dx pop dx mov dh, ah pop ax ret chs endp diskwrite: mov al, 'w' jmp error ; 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 push ax mov ax, 0e00h + 'd' int 10h pop ax call far_caller ; TODO: Implement mov ax, 0100h clc ret diskchange endp setdate: mov al, 'd' jmp error settime: mov al, 't' jmp error gettime: mov al, 'T' jmp error flush proc far push ax flush_loop: mov ah, 1 int 16h jz flush_ret xor ah, ah int 16h jmp flush_loop flush_ret: pop ax ret flush endp mapdev proc far ; TODO: Implement xor al, al ret mapdev endp unimplemented: mov al, '@' error: mov ah, 0eh int 10h mov ah, 0eh mov al, '!' int 10h call far_caller hang: hlt jmp hang code ends ; TODO: STRUC? constants segment sectorspertrack dw 8 ; TODO: Don't hardcode heads dw 2 ; TODO: Don't hardcode disks_table: db 1 ; 1 drive, TODO: Don't hardcode 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 dw 112 ; Number of directory entries dw 320*2 ; Number of sectors constants ends data segment command_fcb: db 1 ; First drive, TODO: Don't hardcode db "COMMAND COM" db 25 dup (?) data ends code segment 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 code ends end