ponydos/ponydos.asm

473 lines
7.2 KiB
NASM

%include "ponydos.inc"
cpu 286
bits 16
org 0x7c00
COLUMNS equ 80
ROWS equ 25
DIRENTS equ 0x2000
DIRENT_SIZE equ 32
FILE_MAX_SIZE equ 128
jmp 0:start
; Thunks for application programs to use
; 0x7c05 SYS_OPEN_FILE
call open_file
retf
; 0x7c09 SYS_READ_SECTORS
call read_sectors
retf
; 0x7c0d SYS_DRAW_RECT
call draw_rect
retf
start:
cld
; Set up segments and stack
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
xor sp, sp
; Clear BSS
;xor al, al
mov di, _bss_start
mov cx, _bss_end - _bss_start
rep stosb
mov [boot_disk], dl
initialize_mouse:
; Initialize mouse
; https://www.ctyme.com/intr/rb-1601.htm
mov ax, 0xc205
mov bh, 3 ; TODO: This is the usual PS/2 mouse packet size, but is it correct here?
int 0x15
jc .error
; Set handler address
; https://www.ctyme.com/intr/rb-1603.htm
mov ax, 0xc207
; es is already set correctly
mov bx, mouse_handler
int 0x15
jc .error
; Enable mouse
; https://www.ctyme.com/intr/rb-1596.htm
mov ax, 0xc200
mov bh, 1
int 0x15
jc .error
jmp .done
.error:
; https://www.ctyme.com/intr/rb-1601.htm
mov ax, 0xc201
int 0x15
jmp initialize_mouse
.done:
load_shell:
push word 0x1000
pop es
mov si, shell_name
call open_file
xor bx, bx
call read_sectors
; TODO: error management? Surely this works...
xor ax, ax ; WM_INITIALIZE
call 0x1000:0
initialize_screen:
; Disable text cursor
mov ah, 0x01
mov ch, 0x20
int 0x10
draw:
; Set up segments for drawing routines
push word 0xb800
pop es
call draw_wallpaper
call flip_mouse_cursor
mainloop:
mov bx, [mouse_x]
shr bx, 1
mov cx, [mouse_y]
shr cx, 2
cmp [mouse_column], bl
jne .update_cursor
cmp [mouse_row], cl
jne .update_cursor
hlt
jmp mainloop
.update_cursor:
call flip_mouse_cursor
mov [mouse_column], bl
mov [mouse_row], cl
call flip_mouse_cursor
jmp mainloop
; ------------------------------------------------------------------
; Drawing subroutines
; ------------------------------------------------------------------
; in:
; bx = width of input buffer
; cx = width of rectangle
; dx = height of rectangle (must be at least 1)
; ds:si = beginning of source data
; di = X
; bp = Y
draw_rect:
pusha
push es
push word 0xb800
pop es
; Calculate the starting address in the screen buffer
; Assumes COLUMNS is 80
; X columns * 2 bytes / column
shl di, 1
; Y rows * 80 cells / row * 2 bytes / cell = Y * 160 bytes
; bp * 160 = bp * 128 + bp * 32 = (bp<<7) + (bp<<5)
shl bp, 5
add di, bp
shl bp, 2
add di, bp
; Convert widths to bytes
shl bx, 1
.loop:
; Copy a row
pusha
rep movsw
popa
; Move to the next row in the input buffer
add si, bx
; Move to the next row in the screen buffer
add di, 2*COLUMNS
dec dx
; Any rows left to copy?
jnz .loop
pop es
popa
ret
; requires:
; ds = 0
; es = 0xb800
draw_wallpaper:
pusha
mov si, GLOBAL_WALLPAPER
xor di, di
mov cx, 80*25
rep movsw
popa
ret
; requires:
; ds = 0
; es = 0xb800
flip_mouse_cursor:
pusha
; Column
xor bh, bh
mov bl, [mouse_column]
shl bx, 1
; Row
mov al, [mouse_row]
mov cl, COLUMNS*2
mul cl
add bx, ax
; Swap foreground and background colours
inc bx
mov al, [es:bx]
ror al, 4
mov [es:bx], al
popa
ret
; ------------------------------------------------------------------
; Disk subroutines
; ------------------------------------------------------------------
; in:
; ax = LBA of first sector
;; bl = drive number
; cx = number of sectors to read (must be at least 1)
; es:bx = output buffer
read_sectors:
pusha
xor di, di
.loop:
call modify_sector
inc ax
add bx, 512
loop .loop
popa
ret
; in:
; ax = LBA of first sector
;; bl = drive number, use [boot_disk] for now
; es:bx = output buffer
; di = 0x0100 for write, 0x0000 for read
modify_sector:
pusha
mov cx, 18
div cx
; cl = sector (1…18)
mov cl, dl
inc cl
; dh = head (0…1)
mov dh, 1
and dh, al
; ch = cylinder
shr ax, 1
mov ch, al
; dl = drive number
mov dl, [cs:boot_disk]
.retry:
mov ax, 0x0201 ; read/write one sector
add ax, di
int 0x13
jc .error
popa
ret
.error:
; Reset the disk system unconditionally, as we have no
; kernel panic handler to go to after 3 tries and proper
; error handling would take too much code
xor ah, ah
int 0x10
jmp .retry
; ------------------------------------------------------------------
; Filesystem
; ------------------------------------------------------------------
shell_name db 'shell.bin', 0
; in:
; ds:si = file name
; out:
; ax = LBA of first sector, 0 if no space left
; cx = length in sectors
open_file:
push si
push di
push bx
push es
; Stolen from https://stackoverflow.com/a/72746473, get strlen in cx
mov cx, ds
mov es, cx
mov di, si
mov cx, -1
xor ax, ax
repne scasb
not cx
dec cx
mov es, ax
;mov ax, 1
mov al, 1
mov bx, DIRENTS
xor di, di
call modify_sector
mov ax, 2
mov di, bx
.loop:
cmp word [es:di], 0
je .create_file
inc di
inc di
pusha
repe cmpsb
popa
je .success
add ax, FILE_MAX_SIZE
add di, DIRENT_SIZE - 2
cmp di, DIRENTS + 0x200
jl .loop
.error:
xor ax, ax
; Return with mangled cx
.success:
mov cx, [es:di - 2]
.return:
pop es
pop bx
pop di
pop si
ret
.create_file:
; TODO: zero out the sector for this file?
inc word [es:di]
inc di
inc di
rep movsb
push ax
mov ax, 1
;mov bx, DIRENTS
mov di, 0x0100 ; write
call modify_sector
pop ax
;mov cx, 1
mov cl, 1
jmp .return
; ------------------------------------------------------------------
; Mouse callback
; ------------------------------------------------------------------
Y_OVERFLOW equ 0x80
X_OVERFLOW equ 0x40
BUTTONS equ 0x03
X_MAX_VALUE equ 2*COLUMNS-1
Y_MAX_VALUE equ 4*ROWS-1
mouse_handler:
pusha
mov bp, sp
mov bx, [bp+2*8+10] ; status
test bl, X_OVERFLOW
jnz .x_end
.x:
mov si, [cs:mouse_x]
mov ax, [bp+2*8+8] ; X
; X and Y coördinates are stored as 9-bit signed integers
; using two's complement notation. The high bits are called
; "X negative" and "Y negative".
mov ah, bl
shl ah, 3 ; X negative is bit 4, shift it to sign position
sar ah, 7 ; Fill entire byte with sign bit's value
add si, ax
;cmp si, 0
jge .not_x_underflow
xor si, si
.not_x_underflow:
cmp si, X_MAX_VALUE
jle .not_x_overflow
mov si, X_MAX_VALUE
.not_x_overflow:
mov [cs:mouse_x], si
.x_end:
test bl, Y_OVERFLOW
jnz .y_end
.y:
mov si, [cs:mouse_y]
mov ax, [bp+2*8+6] ; Y
mov ah, bl
shl ah, 2 ; Y negative is bit 5
sar ah, 7
; Y direction is flipped compared to our coöridinate space
sub si, ax
;cmp si, 0
jge .not_y_underflow
xor si, si
.not_y_underflow:
cmp si, Y_MAX_VALUE
jl .not_y_overflow
mov si, Y_MAX_VALUE
.not_y_overflow:
mov [cs:mouse_y], si
.y_end:
and bl, BUTTONS
mov [cs:mouse_buttons], bl
popa
retf
; ------------------------------------------------------------------
; Padding and boot sector signature
; ------------------------------------------------------------------
%ifndef SIZE
times 510-($-$$) db 0
%endif
db 0x55
db 0xaa
; ------------------------------------------------------------------
; Zero-initialized variables
; ------------------------------------------------------------------
section .bss
_bss_start:
mouse_x resw 1
mouse_y resw 1
mouse_buttons resb 1
mouse_column resb 1
mouse_row resb 1
boot_disk resb 1
_bss_end: