d4c15687c8
Some BIOSs initialize the VGA card by default into a mode where the high bit of background nybble signals that the cell should blink. The simple way to avoid this is by restricting the background colours to the range 0…7. However, since our mouse cursor is implemented by swapping the foreground and the background colours, we also need to restrict the foreground colours to the range 0…7.
1281 lines
23 KiB
NASM
1281 lines
23 KiB
NASM
%include "ponydos.inc"
|
|
cpu 8086
|
|
bits 16
|
|
|
|
%ifdef BLINKY
|
|
TITLEBAR_ATTRIBUTE equ 0x07
|
|
WINDOW_ATTRIBUTE equ 0x70
|
|
DISK_ATTRIBUTE equ 0x07
|
|
%else
|
|
TITLEBAR_ATTRIBUTE equ 0x0f
|
|
WINDOW_ATTRIBUTE equ 0xf0
|
|
DISK_ATTRIBUTE equ 0x0f
|
|
%endif
|
|
|
|
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, WINDOW_ATTRIBUTE
|
|
.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, TITLEBAR_ATTRIBUTE<<8
|
|
rep stosw
|
|
|
|
mov cx, (ROWS-1)*COLUMNS
|
|
mov ax, WINDOW_ATTRIBUTE<<8
|
|
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, DISK_ATTRIBUTE, 0x00, DISK_ATTRIBUTE, 0x00, DISK_ATTRIBUTE, 0x00, DISK_ATTRIBUTE, 0x00, DISK_ATTRIBUTE
|
|
db 0x00, DISK_ATTRIBUTE, 0x00, DISK_ATTRIBUTE, 0x09, DISK_ATTRIBUTE, 0x00, DISK_ATTRIBUTE, 0x00, DISK_ATTRIBUTE
|
|
db 0x00, DISK_ATTRIBUTE, 0x00, DISK_ATTRIBUTE, '|', DISK_ATTRIBUTE, 0x00, DISK_ATTRIBUTE, 0x00, DISK_ATTRIBUTE
|
|
|
|
oom_error_dialog:
|
|
db 'E', TITLEBAR_ATTRIBUTE, 'r', TITLEBAR_ATTRIBUTE, 'r', TITLEBAR_ATTRIBUTE, 'o', TITLEBAR_ATTRIBUTE, 'r', TITLEBAR_ATTRIBUTE
|
|
times 13-5-1 db 0x00, TITLEBAR_ATTRIBUTE
|
|
db 'x', TITLEBAR_ATTRIBUTE
|
|
db 'O', WINDOW_ATTRIBUTE, 'u', WINDOW_ATTRIBUTE, 't', WINDOW_ATTRIBUTE, ' ', WINDOW_ATTRIBUTE, 'o', WINDOW_ATTRIBUTE, 'f', WINDOW_ATTRIBUTE
|
|
db ' ', WINDOW_ATTRIBUTE, 'm', WINDOW_ATTRIBUTE, 'e', WINDOW_ATTRIBUTE, 'm', WINDOW_ATTRIBUTE, 'o', WINDOW_ATTRIBUTE, 'r', WINDOW_ATTRIBUTE
|
|
db 'y', WINDOW_ATTRIBUTE
|
|
|
|
launch_error_dialog:
|
|
db 'E', TITLEBAR_ATTRIBUTE, 'r', TITLEBAR_ATTRIBUTE, 'r', TITLEBAR_ATTRIBUTE, 'o', TITLEBAR_ATTRIBUTE, 'r', TITLEBAR_ATTRIBUTE
|
|
times FS_DIRENT_NAME_SIZE-1-5-1 db 0x00, TITLEBAR_ATTRIBUTE
|
|
db 'x', TITLEBAR_ATTRIBUTE
|
|
db 'C', WINDOW_ATTRIBUTE, 'a', WINDOW_ATTRIBUTE, 'n', WINDOW_ATTRIBUTE, 'n', WINDOW_ATTRIBUTE, 'o', WINDOW_ATTRIBUTE, 't', WINDOW_ATTRIBUTE
|
|
db ' ', WINDOW_ATTRIBUTE, 'l', WINDOW_ATTRIBUTE, 'a', WINDOW_ATTRIBUTE, 'u', WINDOW_ATTRIBUTE, 'n', WINDOW_ATTRIBUTE, 'c', WINDOW_ATTRIBUTE
|
|
db 'h', WINDOW_ATTRIBUTE, ' ', WINDOW_ATTRIBUTE, 'f', WINDOW_ATTRIBUTE, 'i', WINDOW_ATTRIBUTE, 'l', WINDOW_ATTRIBUTE, 'e', WINDOW_ATTRIBUTE
|
|
db ':', WINDOW_ATTRIBUTE
|
|
times FS_DIRENT_NAME_SIZE-1-19 db 0x00, WINDOW_ATTRIBUTE
|
|
.filename times FS_DIRENT_NAME_SIZE-1 db 0x00, WINDOW_ATTRIBUTE
|
|
|
|
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
|