;FILENAME: UNIT18.TXT ; ; ASSEMBLY LANGUAGE COURSE, UNIT 18 ; By Don Stoner Revision: 2008.07.20 ; ORG 100h ; Code Starts at 100h ; ; We've used BIOS interrupts to perform ; character I/O, but the computer's ; interrups can be used for many other ; things as well. Interrupt 08h, for ; example, is called by one of the ; computer's timers which (for historic ; reasons) ticks about 18.2 times/sec ; (3579545/3/65536). This timer is used ; to keep the system clock updated and ; to time mechanical functions like the ; shutdown of floppy disk drive motors. ; By patching into the chain of system ; programs which are normally triggered ; by this interrupt, we can add timer ; functionality to our programs. Once ; we have written our new timer routine ; (below), there are two steps to patch ; it into the chain. First we redirect ; our routine's exit to where interrupt ; 08h would normally have began; then ; we redirect interrupy 08h to our ; routine. The order of these steps is ; important because, otherwise, our ; routine could be called before it ; would know where to return. ; ; All 256 interrupt vectors are kept in ; a table at the bottom of memory. Each ; vector uses 4 bytes (2-byte offset ; first then 2-byte segment) so vector ; 08h's offset is at address 32 (8x4) ; and its segment is at 34 (32+2). To ; read from or write to this page, we ; need to use a segment register which ; contains a zero. (We will use ES.) ; ; Step 1: Redirect the new routine's ; exit to where the old chain starts: XOR AX,AX ;zero out the ES MOV ES,AX ;segment register MOV AX,[ES:32] ;fetch offset ;for vector 8, then patch it ;into the offset part of the ;address of the FAR jump at ;the end of our new routine: MOV [FIX_OFFSET],AX ;then do the same for the MOV AX,[ES:34] ;segment part: MOV [FIX_SEGMENT],AX ; ; Step 2: Redirect the interrupt 08h ; vector so that timer ticks are sent ; straight to our routine. Don't allow ; any interrupts while changing it. CLI ;Disable all interrupts MOV AX,NEW_INTERRUPT_8 ;Patch MOV [ES:32],AX ;our routine MOV AX,CS ;into the old MOV [ES:34],AX ;vector 08h STI ;re-enable interrupts ; ; Running this types 18.2 periods (.) ; every second. Ignore them; DOS will ; still work. Try runing the program ; again (a double TSR). Rebooting will ; make everything run normally again. ; ; Terminate and Stay Resident: MOV AH,31H ;31h = "TSR" Code MOV AL,0 ;0 means no error MOV DX,20H ;reserve 20h pages ;(100h PSP + 100h code) INT 21H ;DOS call (TSR exit) ;-------------------------------------- ; This is the new timer routine that ; we will patch into the interrupt 08h ; "chain." Each time the computer's ; timer activates interrupt 08h, this ; routine will be executed along with ; the other normal chain of routines. ; NEW_INTERRUPT_8: PUSHA ;Save all registers ;with one instruction! ;An interrupt might happen when ;the computer is doing anything ;so we must be careful to put ;everything back where we found ;it. ;All it will do is MOV AL,"." ;Output one "." CALL ASCOUT ;for each timer ;tick (about 18 ;per second) POPA ;Restore all registers ;with one instruction! ;Finally we do a jump to put us back ;to where this interrupt normally ;would have gone. We won't know where ;this will be until we run the code, ;so the code above will ask DOS, then ;our program will use the information ;to change this final instruction. ;All we can write now at present is ;the framework for that instruction: ; DB 0EAh ;0EAh is the operation ;code for a FAR jump. "FAR" means the ;address will have both an offset and ;a segment which we will patch here: FIX_OFFSET: DW 0 ;chain to this offset FIX_SEGMENT: DW 0 ;within this segment ;-------------------------------------- ; I/O Subroutine: ASCOUT: MOV AH,0EH ;ASCII out, TTY mode MOV BH,0 ;Page MOV BL,6 ;Graphics color INT 10H ;BIOS Call RET