534 lines
8.6 KiB
NASM
534 lines
8.6 KiB
NASM
%ifdef SYMBOLS
|
|
[map symbols]
|
|
%endif
|
|
%include "ponydos_static.inc"
|
|
|
|
cpu 286
|
|
bits 16
|
|
|
|
org 0x7c00
|
|
|
|
X_SENSITIVITY equ 3
|
|
Y_SENSITIVITY equ 3
|
|
|
|
jmp 0:start
|
|
|
|
initialize_mouse_error:
|
|
; https://www.ctyme.com/intr/rb-1601.htm
|
|
mov ax, 0xc201
|
|
int 0x15
|
|
jmp initialize_mouse
|
|
|
|
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 - 1
|
|
rep stosb
|
|
|
|
; At boot_disk
|
|
mov [di], 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 initialize_mouse_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 initialize_mouse_error
|
|
|
|
; Enable mouse
|
|
; https://www.ctyme.com/intr/rb-1596.htm
|
|
mov ax, 0xc200
|
|
mov bh, 1
|
|
int 0x15
|
|
jc initialize_mouse_error
|
|
|
|
load_shell:
|
|
push word 0x1000
|
|
pop es
|
|
mov si, shell_name
|
|
xor dx, dx
|
|
call 0:open_file
|
|
xor bx, bx
|
|
xor di, di ; read
|
|
call 0:modify_sectors
|
|
; TODO: error management? Surely this works...
|
|
|
|
call 0x1000:PROC_INITIALIZE_ENTRYPOINT
|
|
|
|
initialize_screen:
|
|
; Disable text cursor
|
|
mov ah, 0x01
|
|
mov ch, 0x20
|
|
int 0x10
|
|
|
|
; Set up segments for drawing routines
|
|
push word 0xb800
|
|
pop es
|
|
|
|
mov di, mouse_column
|
|
|
|
mainloop:
|
|
xor al, al
|
|
xchg byte [di - mouse_column + redraw], al
|
|
test al, al
|
|
jz .draw_end
|
|
.draw:
|
|
call draw_wallpaper
|
|
|
|
; Draw windows
|
|
xor al, al ; WM_PAINT
|
|
call window_event
|
|
|
|
call flip_mouse_cursor
|
|
|
|
.draw_end:
|
|
|
|
xor al, al
|
|
xchg byte [di - mouse_column + mouse_change], al
|
|
test al, al
|
|
jz .mouse_change_end
|
|
.mouse_change:
|
|
mov cx, [di - mouse_column + mouse_x]
|
|
shr cx, X_SENSITIVITY
|
|
mov bx, [di - mouse_column + mouse_y]
|
|
shr bx, Y_SENSITIVITY
|
|
mov ch, bl
|
|
|
|
call flip_mouse_cursor
|
|
mov [di], cx
|
|
call flip_mouse_cursor
|
|
|
|
mov dl, [di - mouse_column + mouse_buttons]
|
|
|
|
mov al, WM_MOUSE
|
|
call window_event
|
|
.mouse_change_end:
|
|
|
|
mov ah, 1
|
|
int 0x16
|
|
jz .key_end
|
|
.key:
|
|
xor ah, ah
|
|
int 0x16
|
|
mov cx, ax
|
|
mov al, WM_KEYBOARD
|
|
call window_event
|
|
.key_end:
|
|
|
|
hlt
|
|
jmp mainloop
|
|
|
|
; requires:
|
|
; ds = 0
|
|
; di = mouse_column
|
|
; in:
|
|
; al = event
|
|
; out:
|
|
; clobbers bx
|
|
; clobbers bp
|
|
window_event:
|
|
push cs ; Return segment
|
|
push word draw_wallpaper.ret ; Return offset
|
|
|
|
mov bx, [di - mouse_column + window_chain_head]
|
|
|
|
mov bp, 0xf000
|
|
and bp, bx
|
|
push bp ; Call segment
|
|
;push word 0 ; Call offset
|
|
push cs ; Call offset
|
|
|
|
retf
|
|
|
|
; ------------------------------------------------------------------
|
|
; 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
|
|
; [Far calls only]
|
|
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
|
|
retf
|
|
|
|
; requires:
|
|
; ds = 0
|
|
; es = 0xb800
|
|
draw_wallpaper:
|
|
pusha
|
|
|
|
mov si, GLOBAL_WALLPAPER
|
|
xor di, di
|
|
mov cx, 80*25
|
|
rep movsw
|
|
|
|
popa
|
|
.ret: ret ; window_event needs this
|
|
|
|
; requires:
|
|
; di = mouse_column
|
|
; ds = 0
|
|
; es = 0xb800
|
|
flip_mouse_cursor:
|
|
pusha
|
|
|
|
mov bx, [di]
|
|
mov al, bh
|
|
|
|
; Column
|
|
xor bh, bh
|
|
shl bx, 1
|
|
|
|
; Row
|
|
mov cl, COLUMNS*2
|
|
mul cl
|
|
add bx, ax
|
|
|
|
; Swap foreground and background colours
|
|
inc bx
|
|
ror byte [es:bx], 4
|
|
|
|
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
|
|
; di = 0x0100 for write, 0x0000 for read
|
|
; [Far calls only]
|
|
modify_sectors:
|
|
pusha
|
|
|
|
.loop:
|
|
call modify_sector
|
|
inc ax
|
|
add bx, 512
|
|
loop .loop
|
|
|
|
popa
|
|
retf
|
|
|
|
; 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 cl, 18
|
|
div cl
|
|
|
|
; cl = sector (1…18)
|
|
mov cl, ah
|
|
inc cl
|
|
|
|
; dh = head (0…1)
|
|
mov dh, 1
|
|
and dh, al
|
|
|
|
; ch = cylinder
|
|
shr al, 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
|
|
; dx = non-zero => do not create new file
|
|
; out:
|
|
; ax = LBA of first sector, 0 if no space left or if dx non-zero and the
|
|
; specified file was not found
|
|
; cx = length in sectors
|
|
; di = dirent address (in GLOBAL_DIRENTS)
|
|
; [Far calls only]
|
|
open_file:
|
|
push si
|
|
push bx
|
|
push es
|
|
|
|
; Stolen from https://stackoverflow.com/a/72746473, get strlen (including
|
|
; null-termination) in cx
|
|
mov cx, ds
|
|
mov es, cx
|
|
mov di, si
|
|
mov cx, -1
|
|
xor ax, ax
|
|
repne scasb
|
|
not cx
|
|
|
|
mov es, ax
|
|
;mov ax, 1
|
|
mov al, 1
|
|
mov bx, GLOBAL_DIRENTS
|
|
xor di, di
|
|
call modify_sector
|
|
|
|
mov ax, 2
|
|
mov di, bx
|
|
.loop:
|
|
cmp word [es:di], 0
|
|
je .create_file
|
|
|
|
pusha
|
|
inc di
|
|
inc di
|
|
|
|
repe cmpsb
|
|
popa
|
|
je .success
|
|
|
|
add ax, FS_FILE_MAX_SIZE
|
|
add di, FS_DIRENT_SIZE
|
|
cmp di, GLOBAL_DIRENTS + 0x200
|
|
jl .loop
|
|
|
|
.error:
|
|
xor ax, ax
|
|
; Return with mangled cx, di
|
|
.success:
|
|
mov cx, [es:di]
|
|
.return:
|
|
pop es
|
|
pop bx
|
|
pop si
|
|
retf
|
|
|
|
.create_file:
|
|
test dx, dx
|
|
jnz .error
|
|
|
|
; TODO: zero out the sector for this file?
|
|
inc word [es:di]
|
|
|
|
pusha
|
|
inc di
|
|
inc di
|
|
rep movsb
|
|
|
|
mov ax, 1
|
|
;mov bx, GLOBAL_DIRENTS
|
|
mov di, 0x0100 ; write
|
|
call modify_sector
|
|
popa
|
|
|
|
jmp .success
|
|
|
|
; ------------------------------------------------------------------
|
|
; Mouse callback
|
|
; ------------------------------------------------------------------
|
|
|
|
Y_OVERFLOW equ 0x80
|
|
X_OVERFLOW equ 0x40
|
|
BUTTONS equ 0x03
|
|
|
|
X_MAX_VALUE equ (1 << X_SENSITIVITY)*COLUMNS-1
|
|
Y_MAX_VALUE equ (1 << Y_SENSITIVITY)*ROWS-1
|
|
|
|
; in:
|
|
; si = non-zero for Y
|
|
; di = &mouse_x/&mouse_y
|
|
; ax = X/Y
|
|
; bx = status
|
|
; cl = negative bit # in ah
|
|
; dx = MAX_VALUE
|
|
; zf = tested against overflow
|
|
; out
|
|
; [mouse_x]/[mouse_y] updated appropriately
|
|
; si = updated [mouse_x]/[mouse_y]
|
|
; di = di + 2
|
|
; clobbers ax
|
|
xy_handler:
|
|
jnz .overflow_return
|
|
|
|
; 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, cl ; Shift negative bit to sign position
|
|
sar ah, 7 ; Fill entire byte with sign bit's value
|
|
|
|
test si, si
|
|
jz .not_y
|
|
neg ax
|
|
|
|
.not_y:
|
|
mov si, [cs:di]
|
|
add si, ax
|
|
|
|
;cmp si, 0
|
|
jge .not_underflow
|
|
xor si, si
|
|
.not_underflow:
|
|
|
|
cmp si, dx
|
|
jle .not_overflow
|
|
mov si, dx
|
|
.not_overflow:
|
|
|
|
mov [cs:di], si
|
|
|
|
.overflow_return:
|
|
inc di
|
|
inc di
|
|
ret
|
|
|
|
mouse_handler:
|
|
pusha
|
|
|
|
mov bp, sp
|
|
|
|
mov bx, [bp+2*8+10] ; status
|
|
|
|
.x:
|
|
xor si, si
|
|
mov di, mouse_x
|
|
mov dx, X_MAX_VALUE
|
|
mov ax, [bp+2*8+8]
|
|
mov cl, 3
|
|
|
|
test bl, X_OVERFLOW
|
|
call xy_handler
|
|
|
|
.y:
|
|
inc si ; will be non-zero
|
|
mov ax, [bp+2*8+6]
|
|
mov cl, 2
|
|
mov dx, Y_MAX_VALUE
|
|
|
|
test bl, Y_OVERFLOW
|
|
call xy_handler
|
|
|
|
mov bh, 1 ; Mark that mouse state has updated
|
|
and bl, BUTTONS
|
|
mov [cs:di], bx
|
|
|
|
popa
|
|
retf
|
|
|
|
; ------------------------------------------------------------------
|
|
; Padding and boot sector signature
|
|
; ------------------------------------------------------------------
|
|
|
|
%ifndef SIZE
|
|
times 510-($-$$) db 0
|
|
%endif
|
|
|
|
memory_allocation_map:
|
|
db 0x55
|
|
db 0xaa
|
|
|
|
; ------------------------------------------------------------------
|
|
; Zero-initialized variables
|
|
; ------------------------------------------------------------------
|
|
|
|
section .bss
|
|
_bss_start:
|
|
resb 8 ; Rest of the memory allocation map
|
|
|
|
mouse_x resw 1
|
|
mouse_y resw 1 ; mouse_x + 2, do not touch
|
|
mouse_buttons resb 1 ; mouse_y + 2
|
|
mouse_change resb 1 ; mouse_buttons + 1
|
|
|
|
mouse_column resb 1
|
|
mouse_row resb 1 ; mouse_column + 1
|
|
|
|
window_chain_head resw 1
|
|
redraw resb 1
|
|
|
|
; Last thing in bss
|
|
boot_disk resb 1
|
|
|
|
_bss_end:
|