forked from offtopia/ponydos
1269 lines
22 KiB
NASM
1269 lines
22 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
|
|
.visible resb 1
|
|
.resizable resb 1
|
|
.mouse_released_inside resb 1
|
|
.status resb 1
|
|
.res_x resw 1
|
|
.res_y resw 1
|
|
.size:
|
|
endstruc
|
|
|
|
WINDOW_ID_ICON equ 0
|
|
WINDOW_ID_FILE_WINDOW equ 1
|
|
WINDOW_ID_OOM_ERROR equ 2
|
|
WINDOW_ID_LAUNCH_ERROR equ 3
|
|
|
|
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
|
|
; out:
|
|
; clobbers everything but ds
|
|
initialize:
|
|
push ds
|
|
|
|
push cs
|
|
pop ds
|
|
|
|
; Has shell been started already?
|
|
mov bp, PONYDOS_SEG
|
|
mov es, bp
|
|
xor bp, bp
|
|
cmp word [es:GLOBAL_WINDOW_CHAIN_HEAD], 0
|
|
je .not_relaunch
|
|
mov bp, 1
|
|
jmp .skip_desktop
|
|
.not_relaunch:
|
|
|
|
; Set wallpaper
|
|
call set_wallpaper
|
|
|
|
; Create icon for the disk on the desktop
|
|
mov word [windows + WINDOW_ID_ICON*window.size + window.width], 5
|
|
mov word [windows + WINDOW_ID_ICON*window.size + window.height], 3
|
|
mov word [windows + WINDOW_ID_ICON*window.size + window.x], 1
|
|
mov word [windows + WINDOW_ID_ICON*window.size + window.y], 1
|
|
mov word [windows + WINDOW_ID_ICON*window.size + window.data], disk_icon
|
|
mov byte [windows + WINDOW_ID_ICON*window.size + window.icon], 1
|
|
mov ax, cs
|
|
add ax, WINDOW_ID_ICON
|
|
mov si, windows + WINDOW_ID_ICON*window.size
|
|
call show_window
|
|
|
|
.skip_desktop:
|
|
; Initialize file window but don't show it
|
|
mov word [windows + WINDOW_ID_FILE_WINDOW*window.size + window.width], 40
|
|
mov word [windows + WINDOW_ID_FILE_WINDOW*window.size + window.height], 17
|
|
mov word [windows + WINDOW_ID_FILE_WINDOW*window.size + window.x], 10
|
|
mov word [windows + WINDOW_ID_FILE_WINDOW*window.size + window.y], 4
|
|
mov word [windows + WINDOW_ID_FILE_WINDOW*window.size + window.data], file_window
|
|
mov byte [windows + WINDOW_ID_FILE_WINDOW*window.size + window.resizable], 1
|
|
test bp, bp
|
|
jz .no_file_window_on_start
|
|
.file_window_on_start:
|
|
; Offset the window so that it's clearly another window
|
|
mov word [windows + WINDOW_ID_FILE_WINDOW*window.size + window.x], 7
|
|
mov word [windows + WINDOW_ID_FILE_WINDOW*window.size + window.y], 3
|
|
mov ax, cs
|
|
add ax, WINDOW_ID_FILE_WINDOW
|
|
mov si, windows + WINDOW_ID_FILE_WINDOW*window.size
|
|
call show_window
|
|
.no_file_window_on_start:
|
|
|
|
; Initialize error dialogs
|
|
mov word [windows + WINDOW_ID_OOM_ERROR*window.size + window.width], 13
|
|
mov word [windows + WINDOW_ID_OOM_ERROR*window.size + window.height], 2
|
|
mov word [windows + WINDOW_ID_OOM_ERROR*window.size + window.x], 30
|
|
mov word [windows + WINDOW_ID_OOM_ERROR*window.size + window.y], 10
|
|
mov word [windows + WINDOW_ID_OOM_ERROR*window.size + window.data], oom_error_dialog
|
|
|
|
mov word [windows + WINDOW_ID_LAUNCH_ERROR*window.size + window.width], FS_DIRENT_NAME_SIZE-1 ; Size includes null terminator
|
|
mov word [windows + WINDOW_ID_LAUNCH_ERROR*window.size + window.height], 3
|
|
mov word [windows + WINDOW_ID_LAUNCH_ERROR*window.size + window.x], 24
|
|
mov word [windows + WINDOW_ID_LAUNCH_ERROR*window.size + window.y], 11
|
|
mov word [windows + WINDOW_ID_LAUNCH_ERROR*window.size + window.data], launch_error_dialog
|
|
|
|
call request_redraw
|
|
|
|
.end:
|
|
pop ds
|
|
retf
|
|
|
|
; in:
|
|
; al = event
|
|
; bx = window ID
|
|
; cx, dx = event specific
|
|
; out:
|
|
; ax = event specific
|
|
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
|
|
jmp .end
|
|
.not_remove:
|
|
|
|
; We ignore WM_OPEN_FILE
|
|
|
|
.end:
|
|
cmp byte [open_windows], 0
|
|
jne .windows_open
|
|
call deallocate_own_memory
|
|
.windows_open:
|
|
|
|
pop es
|
|
pop ds
|
|
pop bp
|
|
pop di
|
|
pop si
|
|
pop dx
|
|
pop cx
|
|
pop bx
|
|
retf
|
|
|
|
; ------------------------------------------------------------------
|
|
; Event handlers
|
|
; ------------------------------------------------------------------
|
|
|
|
; in:
|
|
; al = WM_PAINT
|
|
; bx = window ID
|
|
; out:
|
|
; clobbers everything
|
|
paint:
|
|
call get_window
|
|
|
|
mov bx, [si + window.next]
|
|
call send_event
|
|
|
|
cmp si, windows + WINDOW_ID_FILE_WINDOW*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:
|
|
|
|
cmp cx, [si + window.x]
|
|
jl .outside ; x < window_x
|
|
cmp bx, [si + window.y]
|
|
jl .outside ; y < window_y
|
|
mov bp, [si + window.x]
|
|
add bp, [si + window.width]
|
|
cmp bp, cx
|
|
jle .outside ; window_x + window_width <= x
|
|
mov bp, [si + window.y]
|
|
add bp, [si + window.height]
|
|
cmp bp, bx
|
|
jle .outside ; window_y + window_height <= 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 send_event
|
|
ret
|
|
|
|
.outside:
|
|
mov byte [si + window.mouse_released_inside], 0
|
|
pop cx
|
|
mov bx, [si + window.next]
|
|
mov al, WM_MOUSE
|
|
call send_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:
|
|
mov ax, cs
|
|
add ax, WINDOW_ID_FILE_WINDOW
|
|
mov si, windows + WINDOW_ID_FILE_WINDOW*window.size
|
|
call show_window
|
|
call render_file_window
|
|
jmp .end
|
|
|
|
.file_window:
|
|
call raise_window
|
|
|
|
; Save window ID
|
|
mov bp, ax
|
|
|
|
cmp bx, [si + window.y]
|
|
jne .not_title_bar
|
|
|
|
; Resize button
|
|
mov ax, [si + window.x]
|
|
cmp byte [si + window.resizable], 0
|
|
je .not_resizable
|
|
cmp ax, cx
|
|
je .resize
|
|
.not_resizable:
|
|
; Window close button
|
|
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_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:
|
|
; 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 send_event
|
|
|
|
; Update next ID
|
|
; If window.next was zero, send_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
|
|
|
|
; ------------------------------------------------------------------
|
|
; Event handler subroutines
|
|
; ------------------------------------------------------------------
|
|
|
|
; 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
|
|
|
|
; ------------------------------------------------------------------
|
|
; Launching
|
|
; ------------------------------------------------------------------
|
|
|
|
; out:
|
|
; clobbers everything
|
|
launch:
|
|
mov si, launch_filename
|
|
|
|
; 4 letter file extension?
|
|
call strlen
|
|
cmp cx, 5
|
|
jb .less_than_5_chars
|
|
add si, cx
|
|
sub si, 5
|
|
; .text
|
|
mov di, text_extension
|
|
call strcmp
|
|
je .text_file
|
|
; .wall
|
|
mov di, wall_extension
|
|
call strcmp
|
|
je .wallpaper
|
|
.less_than_5_chars:
|
|
|
|
; 3 letter file extension?
|
|
cmp cx, 4
|
|
jb .not_launchable ; No, too short
|
|
mov si, launch_filename
|
|
add si, cx
|
|
sub si, 4
|
|
; .asm
|
|
mov di, asm_extension
|
|
call strcmp
|
|
je .text_file
|
|
; .txt
|
|
mov di, txt_extension
|
|
call strcmp
|
|
je .text_file
|
|
; .bin
|
|
mov di, bin_extension
|
|
call strcmp
|
|
jne .not_launchable ; No extension matched
|
|
|
|
mov si, launch_filename
|
|
call launch_binary
|
|
|
|
ret
|
|
|
|
.wallpaper:
|
|
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
|
|
|
|
ret
|
|
|
|
.not_launchable:
|
|
; Copy filename into the launch error dialog
|
|
mov si, launch_filename
|
|
call show_launch_error
|
|
ret
|
|
|
|
.text_file:
|
|
; Launch viewer.bin if it exists
|
|
mov si, viewer_file_name
|
|
call launch_binary
|
|
jc .viewer_not_found
|
|
|
|
; Send the WM_OPEN_FILE event to tell viewer the file we
|
|
; want it to open. launch_binary returned its segment in bx
|
|
; so we can just pass that to send_event, as window IDs are
|
|
; of the form segment | internal_id.
|
|
mov al, WM_OPEN_FILE
|
|
mov cx, launch_filename
|
|
call send_event
|
|
|
|
.viewer_not_found:
|
|
ret
|
|
|
|
; in:
|
|
; si = name of file not launchable
|
|
; out:
|
|
; clobbers everything
|
|
show_launch_error:
|
|
mov di, launch_error_dialog.filename
|
|
mov cx, FS_DIRENT_NAME_SIZE-1
|
|
mov ah, 0xf0
|
|
.copy:
|
|
lodsb
|
|
test al, al
|
|
je .copy_end
|
|
stosw
|
|
loop .copy
|
|
.copy_end:
|
|
; Zero out the rest
|
|
xor al, al
|
|
rep stosw
|
|
|
|
; Show dialog
|
|
mov ax, cs
|
|
add ax, WINDOW_ID_LAUNCH_ERROR
|
|
mov si, windows + WINDOW_ID_LAUNCH_ERROR*window.size
|
|
call show_window
|
|
|
|
ret
|
|
|
|
; in:
|
|
; si = name of the binary to launch
|
|
; out:
|
|
; cf = error occured when launching binary
|
|
; bx = segment of the launched binary
|
|
; clobbers everything else
|
|
launch_binary:
|
|
mov dx, 1 ; Don't create a new file if not found
|
|
call PONYDOS_SEG:SYS_OPEN_FILE
|
|
test ax, ax
|
|
jz .file_not_found
|
|
|
|
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
|
|
jmp .out_of_memory
|
|
.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
|
|
|
|
push es ; Save the segment for return value
|
|
|
|
; Transfer control to the newly loaded binary
|
|
push cs ; Return segment
|
|
mov ax, .success
|
|
push ax ; Return offset
|
|
push es ; Call segment
|
|
mov ax, PROC_INITIALIZE_ENTRYPOINT
|
|
push ax ; Call offset
|
|
retf
|
|
|
|
.success:
|
|
pop bx
|
|
clc
|
|
ret
|
|
|
|
.file_not_found:
|
|
call show_launch_error
|
|
jmp .error
|
|
|
|
.out_of_memory:
|
|
; Display an error dialog if we can't allocate a segment
|
|
mov ax, cs
|
|
add ax, WINDOW_ID_OOM_ERROR
|
|
mov si, windows + WINDOW_ID_OOM_ERROR*window.size
|
|
call show_window
|
|
pop cx
|
|
pop ax
|
|
|
|
.error:
|
|
stc
|
|
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
|
|
|
|
; ------------------------------------------------------------------
|
|
; Windows
|
|
; ------------------------------------------------------------------
|
|
|
|
; in:
|
|
; bx = window ID
|
|
; out:
|
|
; ax = return value of event handler; 0 if window ID is 0
|
|
send_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
|
|
|
|
; in:
|
|
; bx = valid window id for this process
|
|
; out:
|
|
; si = pointer to window structure
|
|
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
|
|
|
|
; in:
|
|
; ax = window ID
|
|
; si = pointer to window structure
|
|
show_window:
|
|
push ax
|
|
|
|
cmp byte [si + window.visible], 0
|
|
je .not_yet_visible
|
|
call raise_window
|
|
jmp .end
|
|
|
|
.not_yet_visible:
|
|
call hook_window
|
|
mov [si + window.next], ax
|
|
|
|
mov byte [si + window.visible], 1
|
|
|
|
call request_redraw
|
|
|
|
inc byte [open_windows]
|
|
|
|
.end:
|
|
pop ax
|
|
ret
|
|
|
|
; in:
|
|
; bp = window ID
|
|
; si = pointer to window structure
|
|
hide_window:
|
|
push cx
|
|
|
|
mov cx, bp
|
|
call unhook_window
|
|
|
|
mov byte [si + window.visible], 0
|
|
|
|
call request_redraw
|
|
|
|
dec byte [open_windows]
|
|
|
|
pop cx
|
|
ret
|
|
|
|
; in:
|
|
; ax = window ID to hook
|
|
; out:
|
|
; ax = next window ID
|
|
hook_window:
|
|
push bp
|
|
push es
|
|
|
|
mov bp, PONYDOS_SEG
|
|
mov es, bp
|
|
|
|
xchg [es:GLOBAL_WINDOW_CHAIN_HEAD], ax
|
|
|
|
pop es
|
|
pop bp
|
|
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 send_event
|
|
|
|
mov [es:GLOBAL_WINDOW_CHAIN_HEAD], ax
|
|
|
|
pop es
|
|
pop bx
|
|
pop ax
|
|
ret
|
|
|
|
; in:
|
|
; ax = window ID to raise
|
|
raise_window:
|
|
push ax
|
|
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
|
|
|
|
call hook_window
|
|
mov [si + window.next], ax
|
|
|
|
call request_redraw
|
|
|
|
.already_top:
|
|
pop es
|
|
pop si
|
|
pop cx
|
|
pop bx
|
|
pop ax
|
|
ret
|
|
|
|
; ------------------------------------------------------------------
|
|
; Painting
|
|
; ------------------------------------------------------------------
|
|
|
|
; in
|
|
; si = pointer to window structure
|
|
render_file_window:
|
|
call copy_dirents
|
|
|
|
push ax
|
|
push cx
|
|
push dx
|
|
push di
|
|
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 di
|
|
pop dx
|
|
pop cx
|
|
pop ax
|
|
ret
|
|
|
|
request_redraw:
|
|
push es
|
|
push ax
|
|
|
|
mov ax, PONYDOS_SEG
|
|
mov es, ax
|
|
mov byte [es:GLOBAL_REDRAW], 1
|
|
|
|
pop ax
|
|
pop es
|
|
ret
|
|
|
|
; ------------------------------------------------------------------
|
|
; File access
|
|
; ------------------------------------------------------------------
|
|
|
|
; 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
|
|
|
|
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
|
|
|
|
; ------------------------------------------------------------------
|
|
; Memory management
|
|
; ------------------------------------------------------------------
|
|
|
|
deallocate_own_memory:
|
|
push bx
|
|
push cx
|
|
push es
|
|
|
|
mov bx, PONYDOS_SEG
|
|
mov es, bx
|
|
|
|
; Segment 0xn000 corresponds to slot n in the allocation table
|
|
mov bx, cs
|
|
mov cl, 12
|
|
shr bx, cl
|
|
|
|
mov byte [es:GLOBAL_MEMORY_ALLOCATION_MAP + bx], 0
|
|
|
|
pop es
|
|
pop cx
|
|
pop bx
|
|
ret
|
|
|
|
; ------------------------------------------------------------------
|
|
; String routines
|
|
; ------------------------------------------------------------------
|
|
|
|
; 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
|
|
|
|
; ------------------------------------------------------------------
|
|
; Constants
|
|
; ------------------------------------------------------------------
|
|
|
|
asm_extension db '.asm', 0
|
|
bin_extension db '.bin', 0
|
|
txt_extension db '.txt', 0
|
|
text_extension db '.text', 0
|
|
wall_extension db '.wall', 0
|
|
|
|
viewer_file_name db 'viewer.bin', 0
|
|
|
|
; ------------------------------------------------------------------
|
|
; Variables
|
|
; ------------------------------------------------------------------
|
|
|
|
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
|
|
|
|
oom_error_dialog:
|
|
db 'E', 0x0f, 'r', 0x0f, 'r', 0x0f, 'o', 0x0f, 'r', 0x0f
|
|
times 13-5-1 db 0x00, 0x0f
|
|
db 'x', 0x0f
|
|
db 'O', 0xf0, 'u', 0xf0, 't', 0xf0, ' ', 0xf0, 'o', 0xf0, 'f', 0xf0
|
|
db ' ', 0xf0, 'm', 0xf0, 'e', 0xf0, 'm', 0xf0, 'o', 0xf0, 'r', 0xf0
|
|
db 'y', 0xf0
|
|
|
|
launch_error_dialog:
|
|
db 'E', 0x0f, 'r', 0x0f, 'r', 0x0f, 'o', 0x0f, 'r', 0x0f
|
|
times FS_DIRENT_NAME_SIZE-1-5-1 db 0x00, 0x0f
|
|
db 'x', 0x0f
|
|
db 'C', 0xf0, 'a', 0xf0, 'n', 0xf0, 'n', 0xf0, 'o', 0xf0, 't', 0xf0
|
|
db ' ', 0xf0, 'l', 0xf0, 'a', 0xf0, 'u', 0xf0, 'n', 0xf0, 'c', 0xf0
|
|
db 'h', 0xf0, ' ', 0xf0, 'f', 0xf0, 'i', 0xf0, 'l', 0xf0, 'e', 0xf0
|
|
db ':', 0xf0
|
|
times FS_DIRENT_NAME_SIZE-1-19 db 0x00, 0xf0
|
|
.filename times FS_DIRENT_NAME_SIZE-1 db 0x00, 0xf0
|
|
|
|
windows:
|
|
times window.size db 0
|
|
times window.size db 0
|
|
times window.size db 0
|
|
times window.size db 0
|
|
|
|
open_windows db 0
|
|
|
|
wallpaper_name db 'ponydos.wall'
|
|
times FS_DIRENT_NAME_SIZE-12 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
|