%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