INTERRUPTS SEGMENT AT 0H ;This is where the keyboard interrupt ORG 9H*4 ;holds the address of its service routine KEYBOARD_INT LABEL DWORD INTERRUPTS ENDS SCREEN SEGMENT AT 0B000H ;A dummy segment to use as the SCREEN ENDS ;Extra Segment ROM_BIOS_DATA SEGMENT AT 40H ;BIOS statuses held here, also keyboard buffer ORG 1AH HEAD DW ? ;Unread chars go from Head to Tail TAIL DW ? BUFFER DW 16 DUP (?) ;The buffer itself BUFFER_END LABEL WORD ROM_BIOS_DATA ENDS CODE_SEG SEGMENT ASSUME CS:CODE_SEG ORG 100H ;ORG = 100H to make this into a .COM file FIRST: JMP LOAD_PAD ;First time through jump to initialize routine CNTRL_N_FLAG DW 0 ;Cntrl-N on or off PAD DB '_',499 DUP(' ') ;Memory storage for pad PAD_CURSOR DW 0 ;Current position in pad PAD_OFFSET DW 0 ;Chooses 1st 250 bytes or 2nd FIRST_POSITION DW ? ;Position of 1st char on screen ATTRIBUTE DB 112 ;Pad Attribute -- reverse video SCREEN_SEG_OFFSET DW 0 ;0 for mono, 8000H for graphics IO_CHAR DW ? ;Holds addr of Put or Get_Char STATUS_PORT DW ? ;Video controller status port OLD_KEYBOARD_INT DD ? ;Location of old kbd interrupt N_PAD PROC NEAR ;The keyboard interrupt will now come here. ASSUME CS:CODE_SEG PUSH AX ;Save the used registers for good form PUSH BX PUSH CX PUSH DX PUSH DI PUSH SI PUSH DS PUSH ES PUSHF ;First, call old keyboard interrupt CALL OLD_KEYBOARD_INT ASSUME DS:ROM_BIOS_DATA ;Examine the char just put in MOV BX,ROM_BIOS_DATA MOV DS,BX MOV BX,TAIL ;Point to current tail CMP BX,HEAD ;If at head, kbd int has deleted char JE IN ;So leave SUB BX,2 ;Point to just read in character CMP BX,OFFSET BUFFER ;Did we undershoot buffer? JAE NO_WRAP ;Nope MOV BX,OFFSET BUFFER_END ;Yes -- move to buffer top SUB BX,2 NO_WRAP:MOV DX,[BX] ;Char in DX now CMP DX,310EH ;Is the char a Cntrl-N? JNE NOT_CNTRL_N ;No MOV TAIL,BX ;Yes -- delete it from buffer NOT CNTRL_N_FLAG ;Switch Modes CMP CNTRL_N_FLAG,0 ;Cntrl-N off? JNE CNTRL_N_ON ;No, only other choice is on CNTRL_N_OFF: MOV ATTRIBUTE,7 ;Set up for normal video MOV PAD_OFFSET,250 ;Point to 2nd half of pad LEA AX,PUT_CHAR ;Make IO call Put_Char as it scans MOV IO_CHAR,AX ;over all locations in pad on screen CALL IO ;Restore screen IN: JMP OUT ;Done CNTRL_N_ON: MOV PAD_OFFSET,250 ;Point to screen stroage part of pad LEA AX,GET_CHAR ;Make IO use Get_char so current screen MOV IO_CHAR,AX ;is stored CALL IO ;Store Screen CALL DISPLAY ;And put up the pad JMP OUT ;Done here. NOT_CNTRL_N: TEST CNTRL_N_FLAG,1 ;Is Cntrl-N on? JZ IN ;No -- leave MOV TAIL,BX ;Yes, delete this char from buffer CMP DX,5300H ;Decide what to do -- is it a Delete? JNE RUBOUT_TEST ;No -- try Rubout MOV BX,249 ;Yes -- fill pad with spaces DEL_LOOP: MOV PAD[BX],' ' ;Move space to current pad position DEC BX ;and go back one JNZ DEL_LOOP ;until done. MOV PAD,'_' ;Put the cursor at the beginning MOV PAD_CURSOR,0 ;And start cursor over CALL DISPLAY ;Put up the new pad on screen JMP OUT ;And take our leave RUBOUT_TEST: CMP DX,0E08H ;Is it a Rubout? JNE CRLF_TEST ;No -- try carriage return-line feed MOV BX,PAD_CURSOR ;Yes -- get current pad location CMP BX,0 ;Are we at beginning? JLE NEVER_MIND ;Yes -- can't rubout past beginning MOV PAD[BX],' ' ;No -- move space to current position MOV PAD[BX-1],'_' ;And move cursor back one DEC PAD_CURSOR ;Set the pad location straight NEVER_MIND: CALL DISPLAY ;And put the result on the screen JMP OUT ;Done here. CRLF_TEST: CMP DX,1C0DH ;Is it a carriage return-line feed? JNE CHAR_TEST ;No -- put it in the pad CALL CRLF ;Yes -- move to next line CALL DISPLAY ;And display result on screen JMP OUT ;Done. CHAR_TEST: MOV BX,PAD_CURSOR ;Get current pad location CMP BX,249 ;Are we past the end of the pad? JGE PAST_END ;Yes -- throw away char MOV PAD[BX],DL ;No -- move ASCII code into pad MOV PAD[BX+1],'_' ;Advance cursor INC PAD_CURSOR ;Increment pad location PAST_END: CALL DISPLAY ;Put result on screen OUT: POP ES ;Having done Pushes, here are the Pops POP DS POP SI POP DI POP DX POP CX POP BX POP AX IRET ;An interrupt needs an IRET N_PAD ENDP DISPLAY PROC NEAR ;Puts the whole pad on the screen PUSH AX MOV ATTRIBUTE,112 ;Use reverse video MOV PAD_OFFSET,0 ;Use 1st 250 bytes of pad memory LEA AX,PUT_CHAR ;Make IO use Put-Char so it does MOV IO_CHAR,AX CALL IO ;Put result on screen POP AX RET ;Leave DISPLAY ENDP CRLF PROC NEAR ;This handles carriage returns CMP PAD_CURSOR,225 ;Are we on last line? JGE DONE ;Yes, can't do a carriage return, exit NEXT_CHAR: MOV BX,PAD_CURSOR ;Get pad location MOV AX,BX ;Get another copy for destructive tests EDGE_TEST: CMP AX,24 ;Are we at the edge of the pad display? JE AT_EDGE ;Yes -- fill pad with new cursor JL ADD_SPACE ;No -- Advance another space SUB AX,25 ;Subtract another line-width JMP EDGE_TEST ;Check if at edge now ADD_SPACE: MOV PAD[BX],' ' ;Add a space INC PAD_CURSOR ;Update pad location JMP NEXT_CHAR ;Check if at edge now AT_EDGE: MOV PAD[BX+1],'_' ;Put cursor in next location INC PAD_CURSOR ;Update pad location to new cursor DONE: RET ;And out. CRLF ENDP GET_CHAR PROC NEAR ;Gets a char from screen and advances position PUSH DX MOV SI,2 ;Loop twice, once for char, once for attribute MOV DX,STATUS_PORT ;Get ready to read video controller status G_WAIT_LOW: ;Start waiting for a new horizontal scan - IN AL,DX ;Make sure the video controller scan status TEST AL,1 ;is low JNZ G_WAIT_LOW G_WAIT_HIGH: ;After port has gone low, it must go high IN AL,DX ;before it is safe to read directly from TEST AL,1 ;the screen buffer in memory JZ G_WAIT_HIGH MOV AH,ES:[DI] ;Do the move from the screen, one byte at a time INC DI ;Move to next screen location DEC SI ;Decrement loop counter CMP SI,0 ;Are we done? JE LEAVE ;Yes MOV PAD[BX],AH ;No -- put char we got into the pad JMP G_WAIT_LOW ;Do it again LEAVE: INC BX ;Update pad location POP DX RET GET_CHAR ENDP PUT_CHAR PROC NEAR ;Puts one char on screen and advances position PUSH DX MOV AH,PAD[BX] ;Get the char to be put onto the screen MOV SI,2 ;Loop twice, once for char, once for attribute MOV DX,STATUS_PORT ;Get ready to read video controller status P_WAIT_LOW: ;Start waiting for a new horizontal scan - IN AL,DX ;Make sure the video controller scan status TEST AL,1 ;is low JNZ P_WAIT_LOW P_WAIT_HIGH: ;After port has gone low, it must go high IN AL,DX ;before it is safe to write directly to TEST AL,1 ;the screen buffer in memory JZ P_WAIT_HIGH MOV ES:[DI],AH ;Move to screen, one byte at a time MOV AH,ATTRIBUTE ;Load attribute byte for second pass INC DI ;Point to next screen postion DEC SI ;Decrement loop counter JNZ P_WAIT_LOW ;If not zero, do it one more time INC BX ;Point to next char in pad POP DX RET ;Exeunt PUT_CHAR ENDP IO PROC NEAR ;This scans over all screen positions of the pad ASSUME ES:SCREEN ;Use screen as extra segment MOV BX,SCREEN MOV ES,BX MOV DI,SCREEN_SEG_OFFSET ;DI will be pointer to screen postion ADD DI,FIRST_POSITION ;Add width of screen minus pad width MOV BX,PAD_OFFSET ;BX will be pad location pointer MOV CX,10 ;There will be 10 lines LINE_LOOP: MOV DX,25 ;And 25 spaces across CHAR_LOOP: CALL IO_CHAR ;Call Put-Char or Get-Char DEC DX ;Decrement character loop counter JNZ CHAR_LOOP ;If not zero, scan over next character ADD DI,FIRST_POSITION ;Add width of screen minus pad width LOOP LINE_LOOP ;And now go back to do next line RET ;Finished IO ENDP LOAD_PAD PROC NEAR ;This procedure intializes everything ASSUME DS:INTERRUPTS ;The data segment will be the Interrupt area MOV AX,INTERRUPTS MOV DS,AX MOV AX,KEYBOARD_INT ;Get the old interrupt service routine MOV OLD_KEYBOARD_INT,AX ;address and put it into our location MOV AX,KEYBOARD_INT[2] ;OLD_KEYBOARD_INT so we can call it. MOV OLD_KEYBOARD_INT[2],AX MOV KEYBOARD_INT,OFFSET N_PAD ;Now load the address of our notepad MOV KEYBOARD_INT[2],CS ;routine into the keyboard interrupt MOV AH,15 ;Ask for service 15 of INT 10H INT 10H ;This tells us how display is set up SUB AH,25 ;Move to twenty places before edge SHL AH,1 ;Mult by two (char & attribute bytes) MOV BYTE PTR FIRST_POSITION,AH ;Set screen cursor MOV STATUS_PORT,03BAH ;Assume this is a monochrome display TEST AL,4 ;Is it? JNZ EXIT ;Yes - jump out MOV SCREEN_SEG_OFFSET,8000H ;No - set up for graphics display MOV STATUS_PORT,03DAH EXIT: MOV DX,OFFSET LOAD_PAD ;Set up everything but LOAD_PAD to INT 27H ;stay and attach itself to DOS LOAD_PAD ENDP CODE_SEG ENDS END FIRST ;END "FIRST" so 8088 will go to FIRST first.