%ifdef SYMBOLS [map symbols] %endif %include "ponydos_static.inc" cpu 286 bits 16 org 0x7c00 X_SENSITIVITY equ 3 Y_SENSITIVITY equ 3 jmp 0:start initialize_mouse_error: ; https://www.ctyme.com/intr/rb-1601.htm mov ax, 0xc201 int 0x15 jmp initialize_mouse start: cld ; Set up segments and stack mov ax, cs mov ds, ax mov es, ax mov ss, ax xor sp, sp ; Clear BSS ;xor al, al mov di, _bss_start mov cx, _bss_end - _bss_start - 1 rep stosb ; At boot_disk mov [di], dl initialize_mouse: ; Initialize mouse ; https://www.ctyme.com/intr/rb-1601.htm mov ax, 0xc205 mov bh, 3 ; TODO: This is the usual PS/2 mouse packet size, but is it correct here? int 0x15 jc initialize_mouse_error ; Set handler address ; https://www.ctyme.com/intr/rb-1603.htm mov ax, 0xc207 ; es is already set correctly mov bx, mouse_handler int 0x15 jc initialize_mouse_error ; Enable mouse ; https://www.ctyme.com/intr/rb-1596.htm mov ax, 0xc200 mov bh, 1 int 0x15 jc initialize_mouse_error load_shell: push word 0x1000 pop es mov si, shell_name xor dx, dx call 0:open_file xor bx, bx xor di, di ; read call 0:modify_sectors ; TODO: error management? Surely this works... call 0x1000:PROC_INITIALIZE_ENTRYPOINT initialize_screen: ; Disable text cursor mov ah, 0x01 mov ch, 0x20 int 0x10 ; Set up segments for drawing routines push word 0xb800 pop es mov di, mouse_column mainloop: xor al, al xchg byte [di - mouse_column + redraw], al test al, al jz .draw_end .draw: call draw_wallpaper ; Draw windows xor al, al ; WM_PAINT call window_event call flip_mouse_cursor .draw_end: xor al, al xchg byte [di - mouse_column + mouse_change], al test al, al jz .mouse_change_end .mouse_change: mov cx, [di - mouse_column + mouse_x] shr cx, X_SENSITIVITY mov bx, [di - mouse_column + mouse_y] shr bx, Y_SENSITIVITY mov ch, bl call flip_mouse_cursor mov [di], cx call flip_mouse_cursor mov dl, [di - mouse_column + mouse_buttons] mov al, WM_MOUSE call window_event .mouse_change_end: mov ah, 1 int 0x16 jz .key_end .key: xor ah, ah int 0x16 mov cx, ax mov al, WM_KEYBOARD call window_event .key_end: hlt jmp mainloop ; requires: ; ds = 0 ; di = mouse_column ; in: ; al = event ; out: ; clobbers bx ; clobbers bp window_event: push cs ; Return segment push word draw_wallpaper.ret ; Return offset mov bx, [di - mouse_column + window_chain_head] mov bp, 0xf000 and bp, bx push bp ; Call segment ;push word 0 ; Call offset push cs ; Call offset retf ; ------------------------------------------------------------------ ; Drawing subroutines ; ------------------------------------------------------------------ ; in: ; bx = width of input buffer ; cx = width of rectangle ; dx = height of rectangle (must be at least 1) ; ds:si = beginning of source data ; di = X ; bp = Y ; [Far calls only] draw_rect: pusha push es push word 0xb800 pop es ; Calculate the starting address in the screen buffer ; Assumes COLUMNS is 80 ; X columns * 2 bytes / column shl di, 1 ; Y rows * 80 cells / row * 2 bytes / cell = Y * 160 bytes ; bp * 160 = bp * 128 + bp * 32 = (bp<<7) + (bp<<5) shl bp, 5 add di, bp shl bp, 2 add di, bp ; Convert widths to bytes shl bx, 1 .loop: ; Copy a row pusha rep movsw popa ; Move to the next row in the input buffer add si, bx ; Move to the next row in the screen buffer add di, 2*COLUMNS dec dx ; Any rows left to copy? jnz .loop pop es popa retf ; requires: ; ds = 0 ; es = 0xb800 draw_wallpaper: pusha mov si, GLOBAL_WALLPAPER xor di, di mov cx, 80*25 rep movsw popa .ret: ret ; window_event needs this ; requires: ; di = mouse_column ; ds = 0 ; es = 0xb800 flip_mouse_cursor: pusha mov bx, [di] mov al, bh ; Column xor bh, bh shl bx, 1 ; Row mov cl, COLUMNS*2 mul cl add bx, ax ; Swap foreground and background colours inc bx ror byte [es:bx], 4 popa ret ; ------------------------------------------------------------------ ; Disk subroutines ; ------------------------------------------------------------------ ; in: ; ax = LBA of first sector ;; bl = drive number ; cx = number of sectors to read (must be at least 1) ; es:bx = output buffer ; di = 0x0100 for write, 0x0000 for read ; [Far calls only] modify_sectors: pusha .loop: call modify_sector inc ax add bx, 512 loop .loop popa retf ; in: ; ax = LBA of first sector ;; bl = drive number, use [boot_disk] for now ; es:bx = output buffer ; di = 0x0100 for write, 0x0000 for read modify_sector: pusha mov cl, 18 div cl ; cl = sector (1…18) mov cl, ah inc cl ; dh = head (0…1) mov dh, 1 and dh, al ; ch = cylinder shr al, 1 mov ch, al ; dl = drive number mov dl, [cs:boot_disk] .retry: mov ax, 0x0201 ; read/write one sector add ax, di int 0x13 jc .error popa ret .error: ; Reset the disk system unconditionally, as we have no ; kernel panic handler to go to after 3 tries and proper ; error handling would take too much code xor ah, ah int 0x10 jmp .retry ; ------------------------------------------------------------------ ; Filesystem ; ------------------------------------------------------------------ shell_name db 'shell.bin', 0 ; in: ; ds:si = file name ; dx = non-zero => do not create new file ; out: ; ax = LBA of first sector, 0 if no space left or if dx non-zero and the ; specified file was not found ; cx = length in sectors ; di = dirent address (in GLOBAL_DIRENTS) ; [Far calls only] open_file: push si push bx push es ; Stolen from https://stackoverflow.com/a/72746473, get strlen (including ; null-termination) in cx mov cx, ds mov es, cx mov di, si mov cx, -1 xor ax, ax repne scasb not cx mov es, ax ;mov ax, 1 mov al, 1 mov bx, GLOBAL_DIRENTS xor di, di call modify_sector mov ax, 2 mov di, bx .loop: cmp word [es:di], 0 je .create_file pusha inc di inc di repe cmpsb popa je .success add ax, FS_FILE_MAX_SIZE add di, FS_DIRENT_SIZE cmp di, GLOBAL_DIRENTS + 0x200 jl .loop .error: xor ax, ax ; Return with mangled cx, di .success: mov cx, [es:di] .return: pop es pop bx pop si retf .create_file: test dx, dx jnz .error ; TODO: zero out the sector for this file? inc word [es:di] pusha inc di inc di rep movsb mov ax, 1 ;mov bx, GLOBAL_DIRENTS mov di, 0x0100 ; write call modify_sector popa jmp .success ; ------------------------------------------------------------------ ; Mouse callback ; ------------------------------------------------------------------ Y_OVERFLOW equ 0x80 X_OVERFLOW equ 0x40 BUTTONS equ 0x03 X_MAX_VALUE equ (1 << X_SENSITIVITY)*COLUMNS-1 Y_MAX_VALUE equ (1 << Y_SENSITIVITY)*ROWS-1 ; in: ; si = non-zero for Y ; di = &mouse_x/&mouse_y ; ax = X/Y ; bx = status ; cl = negative bit # in ah ; dx = MAX_VALUE ; zf = tested against overflow ; out ; [mouse_x]/[mouse_y] updated appropriately ; si = updated [mouse_x]/[mouse_y] ; di = di + 2 ; clobbers ax xy_handler: jnz .overflow_return ; X and Y coördinates are stored as 9-bit signed integers ; using two's complement notation. The high bits are called ; "X negative" and "Y negative". mov ah, bl shl ah, cl ; Shift negative bit to sign position sar ah, 7 ; Fill entire byte with sign bit's value test si, si jz .not_y neg ax .not_y: mov si, [cs:di] add si, ax ;cmp si, 0 jge .not_underflow xor si, si .not_underflow: cmp si, dx jle .not_overflow mov si, dx .not_overflow: mov [cs:di], si .overflow_return: inc di inc di ret mouse_handler: pusha mov bp, sp mov bx, [bp+2*8+10] ; status .x: xor si, si mov di, mouse_x mov dx, X_MAX_VALUE mov ax, [bp+2*8+8] mov cl, 3 test bl, X_OVERFLOW call xy_handler .y: inc si ; will be non-zero mov ax, [bp+2*8+6] mov cl, 2 mov dx, Y_MAX_VALUE test bl, Y_OVERFLOW call xy_handler mov bh, 1 ; Mark that mouse state has updated and bl, BUTTONS mov [cs:di], bx popa retf ; ------------------------------------------------------------------ ; Padding and boot sector signature ; ------------------------------------------------------------------ %ifndef SIZE times 510-($-$$) db 0 %endif memory_allocation_map: db 0x55 db 0xaa ; ------------------------------------------------------------------ ; Zero-initialized variables ; ------------------------------------------------------------------ section .bss _bss_start: resb 8 ; Rest of the memory allocation map mouse_x resw 1 mouse_y resw 1 ; mouse_x + 2, do not touch mouse_buttons resb 1 ; mouse_y + 2 mouse_change resb 1 ; mouse_buttons + 1 mouse_column resb 1 mouse_row resb 1 ; mouse_column + 1 window_chain_head resw 1 redraw resb 1 ; Last thing in bss boot_disk resb 1 _bss_end: