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
 |