ponydos/shell.asm

808 lines
12 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
.size:
endstruc
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
; Draw a rectangle on-screen
mov bx, [si + window.width]
mov cx, bx
mov dx, [si + window.height]
mov di, [si + window.x]
mov bp, [si + window.y]
mov si, [si + window.data]
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
mov ax, bx
push cx
; Y
xor bx, bx
mov bl, ch
; X
xor ch, ch
cmp cx, [si + window.x]
jl .outside
cmp bx, [si + window.y]
jl .outside
sub cx, [si + window.x]
cmp [si + window.width], cx
jle .outside
add cx, [si + window.x]
sub bx, [si + window.y]
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
; 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
; 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
; If clicked the window close button
cmp bx, [si + window.y]
jne .not_close
mov ax, [si + window.x]
add ax, [si + window.width]
dec ax
cmp ax, cx
jne .not_close
.close:
call hide_file_window
jmp .end
.not_close:
; If clicked within the content area
mov ax, bx
sub ax, [si + window.y]
jz .end
; Find the start of the line user clicked on
mov bx, [si + window.width]
shl bx, 1
mul bx
mov si, [si + window.data]
add si, ax
; Zero out launch_filename
mov di, launch_filename
mov cx, FS_DIRENT_NAME_SIZE
xor al, al
rep stosb
; Copy file name to launch_filename
mov di, launch_filename
.copy_filename_loop:
lodsw
test al, al
jz .copy_filename_loop_end
stosb
jmp .copy_filename_loop
.copy_filename_loop_end:
call launch
.end:
pop si
pop dx
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 ax, cs
mov es, ax
mov di, file_window
add di, [windows + 1*window.size + window.width]
add di, [windows + 1*window.size + window.width]
mov cx, [windows + 1*window.size + window.height]
dec cx
mov dx, [windows + 1*window.size + window.width]
call print_ls
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:
call get_window
mov si, [si + window.data]
add si, 20
mov [si], cl
call request_redraw
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 cx
push si
push es
mov cx, PONYDOS_SEG
mov es, cx
cmp [es:GLOBAL_WINDOW_CHAIN_HEAD], ax
je .already_top
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
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 ds
mov bp, PONYDOS_SEG
mov ds, bp
push cx
push di
mov si, GLOBAL_DIRENTS + 2
xor ax, ax ; Maximum filename size
.name_loop:
cmp word [ds:si - 2], 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, GLOBAL_DIRENTS + 0x200
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, GLOBAL_DIRENTS
.size_loop:
mov ax, word [ds:si]
test ax, ax
jz .done
mov byte [es:di + 8], 'K'
shr ax, 1
aam ; mango
add ax, 0x3030
cmp ah, 0x30
je .one_digit
mov byte [es:di + 2], ' '
mov [es:di + 4], ah
jmp .one_digit_print
.one_digit:
test word [ds:si], 1
jnz .one_and_half_digit
mov byte [es:di + 4], ' '
.one_digit_print:
mov [es:di + 6], al
jmp .next_iter_size_loop
.one_and_half_digit:
mov byte [es:di], ' '
mov byte [es:di + 2], al
mov byte [es:di + 4], '.'
mov byte [es:di + 6], '5'
.next_iter_size_loop:
; Move to next line
add di, dx
add di, dx
add si, FS_DIRENT_SIZE
cmp si, GLOBAL_DIRENTS + 0x200
jge .done
dec cx
jnz .size_loop
.done:
pop ds
pop bp
pop di
pop si
pop cx
pop bx
pop ax
ret
; in:
; ds:si = string
; out:
; cx = stlen
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
file_window:
db 'A', 0x0f, ':', 0x0f
times 37 db ' ', 0x0f
db 'x', 0x0f
times 15*40 db 0, 0xf0
windows:
times window.size db 0
times window.size db 0
launch_filename times FS_DIRENT_NAME_SIZE db 0