;----------------------------------------------------------------------- ; NOTE.ASM Public Domain 1992 Ralf Brown ; You may do with this software whatever you want, but ; common courtesy dictates that you not remove my name ; from it. ; ; Popup to append one or more lines to a text file. Demonstration of the ; use of DOS from within an AMIS-compliant TSR. ; Note: popup may be done from the commandline or via a hotkey; however, ; the hotkey support requires a newer BIOS which has the INT 15/4F ; keyboard intercept ; ; Version 0.81 ; LastEdit: 4/20/92 ;----------------------------------------------------------------------- __TINY__ equ 1 ; using Tiny model INCLUDE AMIS.MAC ;----------------------------------------------------------------------- ; VERSION_NUM equ 0051h ; v0.81 VERSION_STR equ "0.81" WINDOW_TOP equ 0 ; topmost row of TSR's popup window WINDOW_LEFT equ 5 ; leftmost column of TSR's popup window WINDOW_HEIGHT equ 3 ; height (including frame) of popup window WINDOW_WIDTH equ 70 ; width (including frame) of popup window LOCAL_STACK_SIZE equ 128 ; size of local stack in bytes HOTKEY_SCAN equ 31h ; scan code for 'N' key HOTKEY_NAME equ "N" ;----------------------------------------------------------------------- ; Declare our segments in the order we want them in the executable. ; _TEXT SEGMENT PUBLIC BYTE 'CODE' _TEXT ENDS TSRgroup@ ;----------------------------------------------------------------------- ; Put the resident code into its own segment so that all the offsets are ; proper for the new location after copying it into a UMB or down into ; the PSP. ; TSRcode@ start_TSRcode label byte ;----------------------------------------------------------------------- ; Since we need a TSR, but might be loaded into a UMB or at the top of ; conventional memory, we make a copy of the all-important first 64 bytes ; of the PSP here. After relocation, this copy will start at offset 0 ; TSR_PSP db 64 dup (?) ;----------------------------------------------------------------------- ; TSR's initialized data storage ; TSRdata@ TSR_name db "NOTE",0 int13_busy db 0 want_popup db 0 want_shutdown db 0 TSR_activated db 0 popup_INT28 db 0 ;;; add TSR-specific initialized data below CRLF_buffer db 13,10 TSRdataEnd@ ;----------------------------------------------------------------------- ; TSR's uninitialized data storage ; TSRbss@ INDOS_ptr dd ? CRITERR_ptr dd ? interrupted_DTA dd ? interrupted_PSP dw ? interrupted_SP dw ? interrupted_SS dw ? interrupted_cursorpos dw ? display_page_attr label word display_attr db ? display_page db ? screen_width db ? cursor_pos label word cursor_x db ? cursor_y db ? screen_buffer db (WINDOW_HEIGHT*WINDOW_WIDTH*2) dup (?) local_stack db LOCAL_STACK_SIZE dup (?) local_stack_bottom label byte ;;; add TSR-specific uninitialized data below notefile_handle dw ? edit_buffer db WINDOW_WIDTH-2 dup (?) TSRbssEnd@ ;----------------------------------------------------------------------- TSR_main proc near ASSUME DS:TGROUP,ES:NOTHING xor si,si ; SI stores line length TSR_main_loop: mov dx,256*(WINDOW_TOP+1) + (WINDOW_LEFT+1) add dx,si call TSR_move_cursor call TSR_getkey cmp al,0Dh ; Enter pressed? je TSR_main_line_end cmp al,27 ; Esc pressed? je TSR_main_done cmp al,8 je backspace cmp al,0 ; extended ASCII? je TSR_main_loop ; if yes, ignore cmp al,0E0h jne got_char cmp ah,0 jne TSR_main_loop got_char: cmp si,WINDOW_WIDTH-2 jb store_char beep: mov ax,0E07h ; beep int 10h jmp TSR_main_loop store_char: mov edit_buffer[si],al inc si ; remember that we got another char call TSR_put_char jmp TSR_main_loop backspace: or si,si jz beep dec si mov dx,256*(WINDOW_TOP+1) + (WINDOW_LEFT+1) add dx,si call TSR_move_cursor mov al,' ' call TSR_put_char jmp TSR_main_loop TSR_main_line_end: mov ah,40h mov bx,notefile_handle mov cx,si mov dx,offset TGROUP:edit_buffer int 21h mov ah,40h mov cx,2 mov dx,offset TGROUP:CRLF_buffer int 21h call TSR_clear_window jmp TSR_main ; restart for next line TSR_main_done: mov bx,notefile_handle mov ah,45h ; DUP handle int 21h jc TSR_main_exit ; quit now if unable to duplicate mov bx,ax mov ah,3Eh ; close duplicate int 21h TSR_main_exit: ret TSR_main endp ;----------------------------------------------------------------------- ; Function that performs any necessary cleanup prior to the TSR being ; removed from memory. At the time it is called, the TSR is effectively ; popped up, though it has not modified the screen. If this routine needs ; to write on the screen, it must save and restore the screen contents ; itself ; TSR_shutdown proc near mov bx,notefile_handle mov ah,3Eh ; close the file int 21h ret TSR_shutdown endp ;----------------------------------------------------------------------- TSR_INT24_handler: mov al,03h ; FAIL, for now iret ;----------------------------------------------------------------------- ; Simply ignore Ctrl-Break and Ctrl-C interrupts ; TSR_INT1B_handler: TSR_INT23_handler: iret ;======================================================================= ; It should not be necessary to make any changes between here and the ; end of the resident portion in order to modify this code for a different ; purpose. ;======================================================================= ;----------------------------------------------------------------------- ; TSR_getkey proc near mov ah,11h ; keystroke available? int 16h jnz TSR_getkey_got_one ; if yes, get it, otherwise int 28h ; give other TSRs a chance to do work jmp TSR_getkey TSR_getkey_got_one: mov ah,10h ; get the keystroke int 16h ret TSR_getkey endp ;----------------------------------------------------------------------- ; entry: DH = row, DL = column ; TSR_move_cursor proc near ASSUME DS:TGROUP,ES:NOTHING mov cursor_pos,dx mov bh,display_page mov ah,2 ; BIOS move-cursor function int 10h ret TSR_move_cursor endp ;----------------------------------------------------------------------- ; entry: AL = char ; exit: AH,BX,CX,DX destroyed ; TSR_put_char proc near mov cx,1 ;; fall through to TSR_put_line TSR_put_char endp ;----------------------------------------------------------------------- ; entry: AL = char, CX = repeat count ; exit: AX,BX,CX,DX destroyed ; TSR_put_line proc near ASSUME DS:TGROUP,ES:NOTHING add cursor_x,cl mov bx,display_page_attr mov ah,9 int 10h mov al,cursor_x cmp al,screen_width jb TSR_put_line_done mov cursor_x,0 inc cursor_y ; ; need to handle case of falling off the bottom ; TSR_put_line_done: mov dx,cursor_pos mov ah,2 ; set cursor position int 10h ret TSR_put_line endp ;----------------------------------------------------------------------- save_screen proc near ASSUME DS:TGROUP,ES:NOTHING mov ah,0Fh int 10h ; get video mode and active page mov display_page,bh mov screen_width,ah mov ah,3 ; get cursor position on page BH int 10h mov interrupted_cursorpos,dx push ds pop es ASSUME ES:TGROUP mov di,offset TGROUP:screen_buffer mov dh,WINDOW_TOP save_screen_loop1: mov dl,WINDOW_LEFT save_screen_loop2: mov ah,2 ; set cursor position on page BH int 10h mov ah,8 ; read character&attribute on page BH int 10h stosw ; and remember them for later restore inc dl cmp dl,WINDOW_LEFT+WINDOW_WIDTH jb save_screen_loop2 inc dh cmp dh,WINDOW_TOP+WINDOW_HEIGHT jb save_screen_loop1 ret save_screen endp ;----------------------------------------------------------------------- framed_window proc near ASSUME DS:TGROUP,ES:NOTHING mov dx,256*WINDOW_TOP + WINDOW_LEFT call TSR_move_cursor mov display_attr,0Fh ; bright white on black mov al,201 ; double upper left corner call TSR_put_char mov cx,WINDOW_WIDTH-2 mov al,205 call TSR_put_line mov al,187 ; double upper right corner call TSR_put_char push si mov dx,256*(WINDOW_TOP+1) + WINDOW_LEFT frame_loop: mov si,dx call TSR_move_cursor mov al,186 ; double vertical bar call TSR_put_char mov dx,si mov dl,WINDOW_LEFT+WINDOW_WIDTH-1 call TSR_move_cursor mov al,186 ; double vertical bar call TSR_put_char mov dx,si inc dh cmp dh,WINDOW_TOP+WINDOW_HEIGHT-1 jb frame_loop pop si mov dx,256*(WINDOW_TOP+WINDOW_HEIGHT-1) + WINDOW_LEFT call TSR_move_cursor mov display_attr,0Fh ; bright white on black mov al,200 ; double lower left corner call TSR_put_char mov cx,WINDOW_WIDTH-2 mov al,205 call TSR_put_line mov al,188 ; double lower right corner call TSR_put_char ; ; frame is done, now add the title ; mov dx,256*WINDOW_TOP + (WINDOW_LEFT+2) call TSR_move_cursor mov si,offset TGROUP:TSR_name frame_name_loop: lodsb or al,al jz frame_name_done call TSR_put_char jmp frame_name_loop frame_name_done: mov dx,256*(WINDOW_TOP+1) + (WINDOW_LEFT+1) call TSR_move_cursor mov display_attr,07h ; dim white on black ;; fall through to TSR_clear_window ;; framed_window endp ;----------------------------------------------------------------------- TSR_clear_window proc near mov bh,display_attr mov cx,256*(WINDOW_TOP+1) + (WINDOW_LEFT+1) mov dx,256*(WINDOW_TOP+WINDOW_HEIGHT-2) + (WINDOW_LEFT+WINDOW_WIDTH-2) mov ax,0600h ; clear popup window area int 10h ret TSR_clear_window endp ;----------------------------------------------------------------------- restore_screen proc near ASSUME DS:TGROUP,ES:NOTHING mov si,offset TGROUP:screen_buffer mov dh,WINDOW_TOP rest_screen_loop1: mov dl,WINDOW_LEFT rest_screen_loop2: mov ah,2 mov bh,display_page int 10h ; set cursor position lodsw ; get character and attribute to restore mov bl,ah ; BL <- attribute mov cx,1 mov ah,9 ; write character&attribute int 10h inc dl cmp dl,WINDOW_LEFT+WINDOW_WIDTH jb rest_screen_loop2 inc dh cmp dh,WINDOW_TOP+WINDOW_HEIGHT jb rest_screen_loop1 mov dx,interrupted_cursorpos mov ah,2 ; restore cursor position int 10h ret restore_screen endp ;----------------------------------------------------------------------- ; requires DS = TGROUP and interrupts enabled on entry; may destroy BX ; popup proc near mov want_popup,0 ; we are finally popping up mov TSR_activated,1 ; ; switch to a local stack so that we are assured of enough stack space ; push ax mov interrupted_SS,ss mov interrupted_SP,sp mov ax,cs cli mov ss,ax ASSUME SS:TGROUP mov sp,offset RESIDENT_CODE:local_stack_bottom sti push es push di push si push bp push dx push cx push bx ; ; switch to our own PSP and store current DTA ; mov ah,51h int 21h mov interrupted_PSP,bx mov bx,cs mov ah,50h int 21h mov ah,2Fh ; get DTA int 21h ASSUME ES:NOTHING mov word ptr interrupted_DTA,bx mov word ptr interrupted_DTA+2,es GRAB_INTERRUPT 1Bh,TSR_INT1B_handler GRAB_INTERRUPT 23h,TSR_INT23_handler GRAB_INTERRUPT 24h,TSR_INT24_handler mov al,0 xchg al,want_shutdown ; get and clear shutdown flag or al,al ; was it set? jz do_popup ; if not, regular popup call TSR_shutdown jmp short popup_done do_popup: call save_screen call framed_window call TSR_main ; the actual guts of the TSR call restore_screen popup_done: RESTORE_INTERRUPT 1Bh RESTORE_INTERRUPT 23h RESTORE_INTERRUPT 24h ; ; restore the original PSP and DTA ; mov bx,interrupted_PSP mov ah,50h int 21h push ds lds dx,interrupted_DTA ASSUME DS:NOTHING mov ah,1Ah int 21h pop ds ASSUME DS:TGROUP pop bx pop cx pop dx pop bp pop si pop di pop es ; ; finally, switch back to original stack ; cli mov ss,interrupted_SS ASSUME SS:NOTHING mov sp,interrupted_SP sti mov TSR_activated,0 ; no longer popped up, so OK to pop again pop ax ret popup endp ;----------------------------------------------------------------------- attempt_popup proc near ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING mov want_popup,1 ; remember that a popup was requested ;; fall through to try_popup ;; attempt_popup endp try_popup proc near pushf sti ; OK to interrupt cmp int13_busy,0 jnz try_popup_done cmp TSR_activated,0 jnz try_popup_done push ds push bx lds bx,INDOS_ptr ASSUME DS:NOTHING cmp byte ptr [bx],1 jb try_popup_1 ja try_popup_2 cmp popup_INT28,0 ; if activated via INT 28, INDOS flag is jz try_popup_2 ; allowed to be 1 rather than 0 try_popup_1: lds bx,CRITERR_ptr ASSUME DS:NOTHING cmp byte ptr [bx],0 jne try_popup_2 push cs pop ds ASSUME DS:TGROUP call popup try_popup_2: ASSUME DS:NOTHING pop bx pop ds ASSUME DS:NOTHING try_popup_done: popf ret try_popup endp ;----------------------------------------------------------------------- API_popup proc near ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING call attempt_popup mov al,AMIS_POPUP_WILLDO ; can't pop up now, will do so when able cmp want_popup,1 ; did we manage to pop up? je API_popup_done mov al,AMIS_SUCCESSFUL ; successful xor bx,bx ; no return code API_popup_done: ret API_popup endp ;----------------------------------------------------------------------- remov proc near ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING inc want_shutdown call try_popup mov al,0 xchg al,want_shutdown ; get and clear shutdown flag cmp al,0 ; if no longer set, shutdown successful je remov_successful mov al,AMIS_UNINST_TRYLATER ret remov_successful: mov al,AMIS_UNINST_SAFE_OFF ; no resident remover, now disabled mov bx,0 ; seg of block to free (will be patched) ALTMPX$PSP equ word ptr ($-2) ; magic name of word to be patched with ; actual memory block segment by TSR ; installation code ret remov endp ;----------------------------------------------------------------------- ; Declare the interrupt vectors hooked by the program, then set up the ; Alternate Multiplex Interrupt Spec handler ; HOOKED_INTS 13h,15h,21h,28h ALTMPX 'Ralf B','NOTE',VERSION_NUM,"Append notes to a file",,,API_popup,remov,Y ;----------------------------------------------------------------------- ; Can't pop up when disk is busy, so try to pop up on return ; We can save one byte by specifying the hardware reset handler set up by ; the ALTMPX macro above ; int13_handler proc far ISP_HEADER 13h,hw_reset_2Dh pushf inc int13_busy call ORIG_INT13h pushf dec int13_busy cmp want_popup,0 je int13_done call try_popup int13_done: popf ret 2 int13_handler endp ;----------------------------------------------------------------------- ; Hotkey checker. Hotkey is Shift+Shift+Key, where Key is patched in at ; installation. ; int15_handler proc far ISP_HEADER 15h jnc int15_done pushf sti cmp ax,4F00h+HOTKEY_SCAN ; can also patch with actual scan code hotkey_scancode equ byte ptr ($-2) ; at installation time jne not_hotkey cmp TSR_activated,0 jne not_hotkey ; ignore hotkey if already popped up push ds push ax xor ax,ax mov ds,ax mov al,ds:[417h] ; get shift states and al,3 ; Shift+Shift cmp al,3 ; both pressed? pop ax pop ds jne not_hotkey ; ; yes, we got our hotkey ; call attempt_popup popf clc ; throw out scan code ret 2 not_hotkey: popf int15_done: jmp ORIG_INT15h int15_handler endp ;----------------------------------------------------------------------- ; Can't pop up when DOS is busy, so try to pop up on return ; int21_handler proc far ISP_HEADER 21h pushf call ORIG_INT21h pushf sti cmp want_popup,0 je int21_done call try_popup int21_done: popf ret 2 int21_handler endp ;----------------------------------------------------------------------- ; Can't pop up when DOS is busy, but can do so during an INT 28h ; ISP_HEADER 28h sti cmp want_popup,0 je int28_done inc popup_INT28 call try_popup dec popup_INT28 int28_done: jmp ORIG_INT28h ;----------------------------------------------------------------------- resident_code_end label byte TSRcodeEnd@ TSRdata@ resident_data_end label byte TSRdataEnd@ TSRbss@ resident_bss_end label byte TSRbssEnd@ resident_code_size equ offset TGROUP:resident_bss_end ;----------------------------------------------------------------------- _TEXT SEGMENT 'CODE' ASSUME cs:_TEXT,ds:_TEXT,es:_TEXT,ss:_TEXT ORG 100h NOTE: DISPLAY_STRING banner CHECK_DOS_VER 3,00 ; we use features not available in 2.x mov bx,1000h ; set memory block to 64K mov ah,4Ah int 21h mov si,81h ; SI -> command line cld ; ensure proper direction for string ops cmdline_loop: lodsb cmp al,' ' ; skip blanks and tabs on commandline je cmdline_loop cmp al,9 je cmdline_loop cmp al,'-' je got_cmdline_switch bad_cmdline: jmp usage got_cmdline_switch: lodsb ; get next character and al,0DFh ; force to uppercase cmp al,'R' jne not_removing jmp removing not_removing: cmp al,'I' jne bad_cmdline installing: ; ; place any necessary pre-initialization here ; mov dx,si ; point at filename mov filename_start,si scan_filename_loop: lodsb cmp al,' ' ja scan_filename_loop mov byte ptr [si-1],0 ; turn filename into ASCIZ mov ax,3DC1h ; open, no-inherit/DENYNONE/write-only int 21h jnc open_successful mov ax,3C00h ; if unable to open, try creating file int 21h jnc create_successful jmp open_failed open_successful: create_successful: mov bx,ax mov ah,3Eh ; close the file again; we now know int 21h ; we can access it ; ; find out whether keyboard intercept is available ; stc mov ah,0C0h int 15h ; get ROM BIOS configuration data ASSUME ES:NOTHING mov dx,offset _TEXT:no_hotkey_msg jc no_kbd_intercept test byte ptr es:[bx+5],10h ; have keyboard intercept? jz no_kbd_intercept mov dx,offset _TEXT:hotkey_msg no_kbd_intercept: mov ah,9 int 21h ; ; get and store pointers to DOS busy flags ; mov ah,34h int 21h ASSUME ES:NOTHING mov word ptr _TEXT:INDOS_ptr,bx mov word ptr _TEXT:INDOS_ptr+2,es push ds mov ax,5D06h int 21h ASSUME DS:NOTHING mov word ptr _TEXT:CRITERR_ptr+2,ds pop ds ASSUME DS:_TEXT mov word ptr _TEXT:CRITERR_ptr,si ; ; now go install the TSR ; INSTALL_TSR ,RELBYTE,resident_code_size,BYTE,,BEST,TOPMEM,inst_patch,already_installed removing: UNINSTALL ,RELBYTE,cant_uninstall push cs pop ds DISPLAY_STRING uninstalled_msg mov ax,4C00h int 21h already_installed: mov dx,offset _TEXT:already_inst_msg jmp short exit_with_error open_failed: mov dx,offset _TEXT:cant_access_msg jmp short exit_with_error usage: mov dx,offset _TEXT:usage_msg jmp short exit_with_error cant_uninstall: mov dx,offset _TEXT:cant_remove_msg exit_with_error: mov ah,9 int 21h mov ax,4C01h int 21h inst_patch: push ax ; remember resident segment DISPLAY_STRING installed_msg pop ax ; get back resident segment push es mov es,ax ASSUME ES:TGROUP ; ; close all files which will not be used by the TSR ; mov bx,0 ; for this TSR, don't need handles 0-4 close_file_loop: mov ah,3Eh int 21h inc bx cmp bx,4 jbe close_file_loop ; ; now copy the PSP into the resident portion ; xor si,si xor di,di mov cx,size TSR_PSP cld rep movsb mov es:[36h],es ; adjust JFT pointer in copied PSP mov bx,es mov ah,50h ; set PSP segment so TSR owns file int 21h push cs pop ds ASSUME DS:_TEXT mov dx,filename_start mov ax,3DC1h ; open, no-inherit/DENYNONE/write-only int 21h jnc reopen_successful xor ax,ax ; point at a closed handle reopen_successful: mov TGROUP:notefile_handle,ax mov bx,ax xor cx,cx xor dx,dx mov ax,4202h ; position to end of file int 21h mov ah,50h ; restore PSP segment mov bx,cs int 21h ; ; finally, zero out the JFT in our PSP so that the exit won't close ; the files that the TSR does need ; les di,cs:[0034h] ; pointer to JFT mov cx,cs:[0032h] ; size of JFT mov al,0FFh ; closed-file flag rep stosb pop es ASSUME ES:NOTHING ret banner db 'NOTE v',VERSION_STR,' Public Domain 1992 Ralf Brown',13,10,'$' usage_msg db "Usage:",9,"NOTE -Ifile",9,"Install using as notepad",13,10 db 9,"NOTE -R",9,9,"Remove from memory",13,10 db "$" hotkey_msg db "Press Shift-Shift-",HOTKEY_NAME," to pop up",13,10,"$" no_hotkey_msg db "Hotkey is not available on this machine",13,10,"$" installed_msg db "Installed.",13,10,"$" already_inst_msg db 13,10,"Already installed.",13,10,"$" cant_remove_msg db "Can't remove from memory.",13,10,"$" uninstalled_msg db "Removed.",13,10,"$" cant_access_msg db "Unable to open or create notepad file",13,10,"$" filename_start dw ? _TEXT ENDS end NOTE