ponydos/shell.asm

1012 lines
15 KiB
NASM

%include "ponydos.inc"
struc window
.next resw 1
.width resw 1
.height resw 1
.x resw 1
.y resw 1
.data resw 1
.icon resb 1
.mouse_released_inside resb 1
.status resb 1
.res_x resw 1
.res_y resw 1
.size:
endstruc
WINDOW_MOVE equ 1
WINDOW_RESIZE equ 2
WINDOW_MIN_WIDTH equ 5
WINDOW_MIN_HEIGHT equ 2
cpu 8086
bits 16
org 0
; 0x0000
jmp near process_event
; 0x0003
initialize:
push ds
push cs
pop ds
; Has shell been started already?
mov bp, PONYDOS_SEG
mov es, bp
cmp word [es:GLOBAL_WINDOW_CHAIN_HEAD], 0
je .not_relaunch
.relaunch:
; TODO: Display an alert if trying to re-run shell
; Clean up memory when exiting
mov bx, cs
mov cl, 12
shr bx, cl
mov byte [es:GLOBAL_MEMORY_ALLOCATION_MAP + bx], 0
jmp .end
.not_relaunch:
; Set wallpaper
call set_wallpaper
; Create icon for the disk on the desktop
mov ax, cs
add ax, 0x000
xchg [es:GLOBAL_WINDOW_CHAIN_HEAD], ax
mov [windows + 0*window.size + window.next], ax
mov word [windows + 0*window.size + window.width], 5
mov word [windows + 0*window.size + window.height], 3
mov word [windows + 0*window.size + window.x], 1
mov word [windows + 0*window.size + window.y], 1
mov word [windows + 0*window.size + window.data], disk_icon
mov byte [windows + 0*window.size + window.icon], 1
mov byte [windows + 0*window.size + window.mouse_released_inside], 0
; Initialize file window but don't show it
mov word [windows + 1*window.size + window.width], 40
mov word [windows + 1*window.size + window.height], 16
mov word [windows + 1*window.size + window.x], 10
mov word [windows + 1*window.size + window.y], 4
mov word [windows + 1*window.size + window.data], file_window
mov byte [windows + 1*window.size + window.icon], 0
mov byte [windows + 1*window.size + window.mouse_released_inside], 0
call request_redraw
.end:
pop ds
retf
process_event:
push bx
push cx
push dx
push si
push di
push bp
push ds
push es
mov bp, cs
mov ds, bp
mov es, bp
cmp al, WM_PAINT
jne .not_paint
call paint
jmp .end
.not_paint:
cmp al, WM_MOUSE
jne .not_mouse
call mouse
jmp .end
.not_mouse:
cmp al, WM_KEYBOARD
jne .not_keyboard
call keyboard
jmp .end
.not_keyboard:
cmp al, WM_UNHOOK
jne .not_remove
call unhook
.not_remove:
.end:
pop es
pop ds
pop bp
pop di
pop si
pop dx
pop cx
pop bx
retf
; in:
; al = WM_PAINT
; bx = window ID
; out:
; clobbers everything
paint:
call get_window
mov bx, [si + window.next]
call forward_event
cmp si, windows + 1*window.size
jne .not_file_window
.file_window:
; See if the dirents have changed since we rendered the
; window contents. If yes, rerender.
push si
mov bp, PONYDOS_SEG
mov es, bp
mov si, dirents
mov di, GLOBAL_DIRENTS
mov cx, FS_DIRENT_SIZE*FS_DIRECTORY_DIRENTS
repe cmpsb
pop si
je .no_rerender
call render_file_window
.no_rerender:
.not_file_window:
; Draw a rectangle on-screen
mov bx, [si + window.width]
mov dx, [si + window.height]
mov di, [si + window.x]
mov bp, [si + window.y]
mov si, [si + window.data]
add dx, bp
cmp dx, ROWS
jle .no_clipping_height
mov dx, ROWS
.no_clipping_height:
sub dx, bp
mov cx, bx
add cx, di
cmp cx, COLUMNS
jle .no_clipping_width_right
mov cx, COLUMNS
.no_clipping_width_right:
sub cx, di
cmp di, 0
jge .no_clipping_width_left
sub si, di
sub si, di
add cx, di
xor di, di
.no_clipping_width_left:
call PONYDOS_SEG:SYS_DRAW_RECT
ret
; in:
; al = WM_MOUSE
; bx = window ID
; cl = X
; ch = Y
; dl = mouse buttons held down
; out:
; clobbers everything
mouse:
call get_window
test dl, MOUSE_PRIMARY | MOUSE_SECONDARY
jnz .any_buttons_held
mov byte [si + window.status], 0
.any_buttons_held:
mov ax, bx
push cx
; Y
xor bx, bx
mov bl, ch
; X
xor ch, ch
cmp byte [si + window.status], WINDOW_MOVE
jne .not_move
call move
.not_move:
cmp byte [si + window.status], WINDOW_RESIZE
jne .not_resize
call resize
.not_resize:
sub cx, [si + window.x]
jc .outside
cmp [si + window.width], cx
jle .outside
add cx, [si + window.x]
sub bx, [si + window.y]
jc .outside
cmp [si + window.height], bx
jle .outside
add bx, [si + window.y]
cmp byte [si + window.mouse_released_inside], 0
je .not_clicking
test dl, MOUSE_PRIMARY | MOUSE_SECONDARY
jz .not_clicking
.clicking:
call click
.not_clicking:
test dl, MOUSE_PRIMARY | MOUSE_SECONDARY
jz .not_buttons_held
.buttons_held:
mov byte [si + window.mouse_released_inside], 0
jmp .inside
.not_buttons_held:
mov byte [si + window.mouse_released_inside], 1
.inside:
pop cx
xor dx, dx
; Use coördinates (255,255) to make sure other windows in
; the chain don't think the cursor is inside them
mov cx, 0xffff
mov bx, [si + window.next]
mov al, WM_MOUSE
call forward_event
ret
.outside:
mov byte [si + window.mouse_released_inside], 0
pop cx
mov bx, [si + window.next]
mov al, WM_MOUSE
call forward_event
ret
; in:
; ax = window ID
; bx = Y coördinate
; cx = X coördinate
; si = pointer to window structure
resize:
push dx
push bp
xor bp, bp ; Change?
mov dx, [si + window.res_y]
sub dx, bx
jc .end_y
cmp dx, WINDOW_MIN_HEIGHT
jl .end_y
cmp dx, ROWS
jg .end_y
cmp [si + window.y], bx
je .end_y
inc bp
mov [si + window.y], bx
mov [si + window.height], dx
.end_y:
mov dx, [si + window.res_x]
sub dx, cx
jc .end_x
cmp dx, WINDOW_MIN_WIDTH
jl .end_x
cmp dx, COLUMNS
jg .end_x
cmp [si + window.x], cx
je .end_x
inc bp
mov [si + window.x], cx
mov [si + window.width], dx
.end_x:
test bp, bp
jz .end
call render_file_window
call request_redraw
.end:
pop bp
pop dx
ret
; in:
; ax = window ID
; bx = Y coördinate
; cx = X coördinate
; si = pointer to window structure
move:
push dx
mov dx, cx
sub dx, [si + window.res_x]
cmp [si + window.y], bx
jne .change
cmp [si + window.x], dx
jne .change
jmp .end
.change:
mov [si + window.y], bx
mov [si + window.x], dx
call request_redraw
.end:
pop dx
ret
; in:
; ax = window ID
; bx = Y coördinate
; cx = X coördinate
; dl = which buttons are held down
; si = pointer to window structure
; out:
; dl = which buttons are held down
; si = pointer to window structure
; clobbers everything else
click:
push dx
push si
cmp byte [si + window.icon], 0
je .file_window
.icon:
call show_file_window
jmp .end
.file_window:
call raise_window
cmp bx, [si + window.y]
jne .not_title_bar
; If clicked the window close button
mov ax, [si + window.x]
cmp ax, cx
je .resize
add ax, [si + window.width]
dec ax
cmp ax, cx
je .close
.move:
mov byte [si + window.status], WINDOW_MOVE
sub cx, [si + window.x]
mov [si + window.res_x], cx ; In-window x-coordinate of press
jmp .end
.resize:
mov byte [si + window.status], WINDOW_RESIZE
add cx, [si + window.width]
mov [si + window.res_x], cx ; Lower right corner + 1
add bx, [si + window.height]
mov [si + window.res_y], bx ; Lower right corner + 1
jmp .end
.close:
call hide_file_window
jmp .end
.not_title_bar:
; If clicked within the content area
mov ax, bx
sub ax, [si + window.y]
jz .end
dec ax
cmp ax, FS_DIRECTORY_DIRENTS
jae .end
mov bp, FS_DIRENT_SIZE
mul bp
mov si, dirents
add si, ax
cmp word [si], 0
je .end
; Copy file name to launch_filename
add si, FS_DIRENT_NAME_OFFSET
mov di, launch_filename
mov cx, FS_DIRENT_NAME_SIZE
rep movsb
call launch
.end:
pop si
pop dx
ret
; in
; si = pointer to window structure
render_file_window:
call copy_dirents
push ax
push cx
push dx
push es
mov ax, cs
mov es, ax
mov di, [si + window.data]
mov cx, [si + window.width]
mov ax, 0x0f00
rep stosw
mov cx, (ROWS-1)*COLUMNS
mov ax, 0xf000
rep stosw
mov di, [si + window.data]
mov byte [di], 0x17
mov byte [di + 2], 'A'
mov byte [di + 4], ':'
add di, [si + window.width]
add di, [si + window.width]
mov byte [di - 2], 'x'
mov cx, [si + window.height]
dec cx
mov dx, [si + window.width]
call print_ls
pop es
pop dx
pop cx
pop ax
ret
copy_dirents:
push cx
push si
push di
push ds
push es
mov bp, PONYDOS_SEG
mov ds, bp
mov bp, cs
mov es, bp
mov si, GLOBAL_DIRENTS
mov di, dirents
mov cx, FS_DIRENT_SIZE*FS_DIRECTORY_DIRENTS
rep movsb
pop es
pop ds
pop di
pop si
pop cx
ret
show_file_window:
cmp byte [file_window_visible], 0
jne .already_visible
push ax
mov ax, PONYDOS_SEG
mov es, ax
mov ax, cs
add ax, 0x001
xchg [es:GLOBAL_WINDOW_CHAIN_HEAD], ax
mov [windows + 1*window.size + window.next], ax
; Populate file window contents
mov si, windows + 1*window.size
call render_file_window
pop ax
mov byte [file_window_visible], 1
call request_redraw
.already_visible:
ret
hide_file_window:
mov cx, cs
add cx, 0x001
call unhook_window
mov byte [file_window_visible], 0
call request_redraw
ret
; out:
; clobbers everything
launch:
mov si, launch_filename
; Is it a .wall file?
call strlen
cmp cx, 5
jb .not_wall
add si, cx
sub si, 5
mov di, wall_extension
call strcmp
jne .not_wall
mov ax, cs
mov es, ax
mov si, launch_filename
mov di, wallpaper_name
mov cx, FS_DIRENT_NAME_SIZE
rep movsb
call set_wallpaper
call request_redraw
jmp .end
.not_wall:
; Is it a .bin file?
cmp cx, 4
jb .end ; No, too short
mov si, launch_filename
add si, cx
sub si, 4
mov di, bin_extension
call strcmp
jne .end ; No, wrong extension
mov si, launch_filename
mov dx, 1 ; Don't create a new file if not found
call PONYDOS_SEG:SYS_OPEN_FILE
test ax, ax
; TODO: Display an alert on file not being found
jz .end
push ax
push cx
; Allocate a segment
mov ax, PONYDOS_SEG
mov es, ax
mov si, GLOBAL_MEMORY_ALLOCATION_MAP
mov cx, MEM_ALLOCATION_MAP_SIZE
.find_free_segment:
mov al, [es:si]
test al, al
jz .found_free_segment
inc si
loop .find_free_segment
; TODO: Display an alert on OOM error
pop cx
pop ax
jmp .end
.found_free_segment:
mov byte [es:si], 1 ; Mark as used
; Set up es to point to the allocated segment
sub si, GLOBAL_MEMORY_ALLOCATION_MAP
mov cl, 12
shl si, cl
mov es, si
pop cx
pop ax
xor bx, bx ; Load at the start of the segment
xor di, di ; Read
call PONYDOS_SEG:SYS_MODIFY_SECTORS
; Transfer control to the newly loaded binary
push cs ; Return segment
mov ax, .end
push ax ; Return offset
push es ; Call segment
mov ax, PROC_INITIALIZE_ENTRYPOINT
push ax ; Call offset
retf
.end:
ret
; out:
; clobbers everything
set_wallpaper:
mov ax, PONYDOS_SEG
mov es, ax
mov si, wallpaper_name
xor dx, dx
call PONYDOS_SEG:SYS_OPEN_FILE
mov bx, GLOBAL_WALLPAPER
xor di, di ; read
call PONYDOS_SEG:SYS_MODIFY_SECTORS
ret
; in:
; al = WM_KEYBOARD
; bx = window ID
; cl = typed character
; cl = typed key
; out:
; clobbers everything
keyboard:
ret
; in:
; al = WM_UNHOOK
; bx = window ID
; cx = window to unhook
; out:
; ax = own ID if not the window to unhook
; next ID if the window to unhook
; clobbers everything else
unhook:
call get_window
cmp bx, cx
je .match
push bx
; Forward the event
mov bx, [si + window.next]
call forward_event
; Update next ID
; If window.next was zero, forward_event will also return zero so
; this is safe in all cases
mov [si + window.next], ax
; Return own ID to keep self in the chain
pop ax
ret
.match:
; Return next ID in the chain to unhook
mov ax, [si + window.next]
ret
; in:
; bx = valid window id for this process
; out:
; si = pointer to window's data block
get_window:
push bx
mov si, cs
sub bx, si
mov si, windows
push ax
push dx
mov ax, window.size
mul bx
add si, ax
pop dx
pop ax
pop bx
ret
request_redraw:
push es
push bp
mov bp, PONYDOS_SEG
mov es, bp
mov byte [es:GLOBAL_REDRAW], 1
pop bp
pop es
ret
; in:
; cx = window ID to unhook
unhook_window:
push ax
push bx
push es
mov bx, PONYDOS_SEG
mov es, bx
mov bx, [es:GLOBAL_WINDOW_CHAIN_HEAD]
mov al, WM_UNHOOK
call forward_event
mov [es:GLOBAL_WINDOW_CHAIN_HEAD], ax
pop es
pop bx
pop ax
ret
; in:
; ax = window ID to raise
raise_window:
push bx
push cx
push si
push es
mov cx, PONYDOS_SEG
mov es, cx
cmp [es:GLOBAL_WINDOW_CHAIN_HEAD], ax
je .already_top
mov bx, ax
call get_window
mov cx, ax
call unhook_window
xchg [es:GLOBAL_WINDOW_CHAIN_HEAD], cx
mov [si + window.next], cx
call request_redraw
.already_top:
pop es
pop si
pop cx
pop bx
ret
; in
; cx = height of window (>= 1)
; dx = width of window in characters
; es:di = start of output
print_ls:
push ax
push bx
push cx
push si
push di
push bp
push cx
push di
mov si, dirents + FS_DIRENT_NAME_OFFSET
xor ax, ax ; Maximum filename size
.name_loop:
cmp word [si - FS_DIRENT_NAME_OFFSET], 0
je .done_names
push cx
call strlen
mov bx, cx
pop cx
cmp bx, dx
jle .not_long_filename
mov bx, dx
.not_long_filename:
cmp ax, bx
jge .not_new_max
mov ax, bx
.not_new_max:
push si
push di
.copy:
movsb
inc di ; Formatting
dec bx
jnz .copy
pop di
pop si
; Move to next line
add di, dx
add di, dx
add si, FS_DIRENT_SIZE
cmp si, dirents + FS_DIRECTORY_DIRENTS*FS_DIRENT_SIZE
jge .done_names
dec cx
jnz .name_loop
.done_names:
pop di
pop cx
; Don't print sizes for too short a window
cmp dx, 10
jle .done
add ax, 5 ; 1 whitespace, 4 length
cmp ax, dx
jle .not_truncate
mov ax, dx
.not_truncate:
sub ax, 5 ; Go to start of where to put the file length
add di, ax
add di, ax
mov si, dirents
.size_loop:
mov ax, word [si]
test ax, ax
jz .done
mov byte [di + 8], 'K'
shr ax, 1
aam ; mango
add ax, 0x3030
cmp ah, 0x30
je .one_digit
mov byte [di + 2], ' '
mov [di + 4], ah
jmp .one_digit_print
.one_digit:
test word [si], 1
jnz .one_and_half_digit
mov byte [di + 4], ' '
.one_digit_print:
mov [di + 6], al
jmp .next_iter_size_loop
.one_and_half_digit:
mov byte [di], ' '
mov byte [di + 2], al
mov byte [di + 4], '.'
mov byte [di + 6], '5'
.next_iter_size_loop:
; Move to next line
add di, dx
add di, dx
add si, FS_DIRENT_SIZE
cmp si, dirents + FS_DIRECTORY_DIRENTS*FS_DIRENT_SIZE
jge .done
dec cx
jnz .size_loop
.done:
pop bp
pop di
pop si
pop cx
pop bx
pop ax
ret
; in:
; ds:si = string
; out:
; cx = strlen
strlen:
push ax
push di
push es
mov cx, ds
mov es, cx
mov di, si
mov cx, -1
xor ax, ax
repne scasb
not cx
dec cx
pop es
pop di
pop ax
ret
; in:
; ds:si = string1
; ds:di = string2
; out:
; zf(ef) = strings are equal
strcmp:
push si
push di
.loop:
lodsb
cmp [di], al
jne .end
test al, al
jz .end
inc di
jmp .loop
.end:
pop di
pop si
ret
; in:
; bx = window ID
; out:
; ax = return value of event handler; 0 if window ID is 0
forward_event:
push bp
cmp bx, 0
je .id_is_zero
push cs ; Return segment
mov bp, .end
push bp ; Return offset
mov bp, 0xf000
and bp, bx
push bp ; Call segment
xor bp, bp
push bp ; Call offset
retf
.id_is_zero:
; This gets skipped over in normal execution, because it
; explicitly returns to .end
xor ax, ax
.end:
pop bp
ret
wallpaper_name db 'ponydos.wall'
times FS_DIRENT_NAME_SIZE-12 db 0
bin_extension db '.bin', 0
wall_extension db '.wall', 0
%include "debug.inc"
file_window_visible db 0
disk_icon:
db 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f
db 0x00, 0x0f, 0x00, 0x0f, 0x09, 0x0f, 0x00, 0x0f, 0x00, 0x0f
db 0x00, 0x0f, 0x00, 0x0f, '|', 0x0f, 0x00, 0x0f, 0x00, 0x0f
windows:
times window.size db 0
times window.size db 0
launch_filename times FS_DIRENT_NAME_SIZE db 0
section .bss
dirents:
resb FS_DIRENT_SIZE*FS_DIRECTORY_DIRENTS
file_window:
resw ROWS*COLUMNS