;**************************************************************************** ; UMASCAN scans the PC's address space from 640K to 1MB and lists the ROM, ; RAM, Video RAM, EMS pages, and unused areas that it finds. Its syntax is: ; ; UMASCAN [/M] ; ; where /M forces it to run in monochrome mode (useful on laptops). The ; ROM it reports may be adapter ROM or system ROM. The RAM it reports may ; be adapter RAM or UMB RAM. Any unused areas it identifies are candidates ; to be converted to UMBs using DOS's EMM386.EXE driver. ;**************************************************************************** code segment assume cs:code,ds:code org 100h begin: jmp main header1 db "UMASCAN 1.1 Copyright (c) 1993 Jeff Prosise",0 header2 db "From: PC Magazine DOS 6 Memory Management with " db "Utilities",0 helpmsg db "Draws a map profiling the upper memory area and " db "identifies unused",13,10 db "address space that may be converted to upper memory " db "blocks.",13,10,13,10 db "UMASCAN [/M]",13,10,13,10 db " /M Use monochrome video attributes.",13,10,13,10 db "Run UMASCAN when EMM386.EXE is not loaded to " db "identify adapter RAM.",13,10,"$" errmsg1 db "Syntax: UMASCAN [/M]",13,10,"$" map db 192 dup (32) ;Upper memory map colors label byte ;Color video attributes border_color db 1Fh back_color db 3Fh fore_color db 1Fh rom_color db 5Bh ram_color db 4Fh video_color db 2Eh ems_color db 3Fh unk_color db 7Fh note_color db 07h title_color db 0Fh shadow_color db 07h mono_colors db 70h,07h,7Fh,0Fh,0Fh,0Fh ;Monochrome attributes db 0Fh,0Fh,07h,0Fh,07h devname db "EMMXXXX0" ;EMM device name video_segment dw 0B800h ;Current video segment video_offset dw ? ;Video buffer start address line_length dw ? ;Bytes per video line maxrow db 24 ;Highest row number text1 db "F000",0 text2 db "E000",0 text3 db "D000",0 text4 db "C000",0 text5 db "B000",0 text6 db "A000",0 text7 db 0Ah,"0",0Ch,"1",0Eh,"2",10h,"3" db 13h,"4",15h,"5",17h,"6",19h,"7" db 1Ch,"8",1Eh,"9",20h,"A",22h,"B" db 25h,"C",27h,"D",29h,"E",2Bh,"F" text8 db " LEGEND ",0 text9 db "RRRR",0 text10 db "++++",0 text11 db "VVVV",0 text12 db "EEEE",0 text12x db "UUUU",0 text13 db "ROM",0 text14 db "RAM",0 text15 db "Video Buffer",0 text16 db "EMS Page Frame",0 text17 db "Unknown",0 text18 db "1. RRRR indicates adapter ROM or system board " db "ROM.",0 text19 db "2. ++++ indicates adapter RAM or UMB RAM created by " db "a 386 memory manager.",0 text20 db "3. Areas marked UUUU usually contain a mixture of " db "ROM, RAM, and unused space.",0 text21 db " These areas generally should not be converted to " db "upper memory blocks.",0 text22 db "4. Unmarked areas may safely be converted to upper " db "memory blocks.",0 text23 db " Press Esc to exit ",0 ;**************************************************************************** ; Procedure MAIN ;**************************************************************************** main proc near cld ;Clear direction flag mov si,81h ;Point SI to command line call scanhelp ;Scan for "/?" switch jnc main1 ;Branch if not found mov ah,09h ;Display help text and exit mov dx,offset helpmsg ;with ERRORLEVEL=0 int 21h mov ax,4C00h int 21h ; ; Modify the color palette if a /M switch was included. ; main1: call findchar ;Advance to next character jc main3 ;Branch if there is none lodsw ;Get the next two characters and ah,0DFh ;Capitalize the second one cmp ax,4D2Fh ;Error if other than /M je main2 mov ah,9 ;Display error message mov dx,offset errmsg1 int 21h mov ax,4C01h ;Exit with ERRORLEVEL=1 int 21h main2: call change_colors ;Change color palette to mono ; ; Draw the screen and terminate when a key is pressed. ; main3: call init_video ;Initialize video mov ah,03h ;Get the cursor type mov bh,00h ;from the BIOS int 10h push cx ;Save it mov ah,01h ;Hide the cursor mov ch,20h int 10h call draw_screen ;Paint the screen call build_table ;Build the map table call draw_map ;Display the memory map main4: mov ah,00h ;Pause until Esc is pressed int 16h cmp al,27 jne main4 call clear_screen ;Clear the screen mov ah,0Fh ;Get the active page number int 10h mov ah,02h ;Home the cursor to the upper mov dx,0000h ;left corner of the screen int 10h mov ah,01h ;Make the cursor visible pop cx ;again int 10h mov ax,4C00h ;Exit with ERRORLEVEL=0 int 21h main endp ;**************************************************************************** ; SCANHELP scans the command line for a /? switch. If found, carry returns ; set and SI contains its offset. If not found, carry returns clear. ;**************************************************************************** scanhelp proc near push si ;Save SI scanloop: lodsb ;Get a character cmp al,0Dh ;Exit if end of line je scan_exit cmp al,"?" ;Loop if not "?" jne scanloop cmp byte ptr [si-2],"/" ;Loop if not "/" jne scanloop add sp,2 ;Clear the stack sub si,2 ;Adjust SI stc ;Set carry and exit ret scan_exit: pop si ;Restore SI clc ;Clear carry and exit ret scanhelp endp ;**************************************************************************** ; FINDCHAR advances SI to the next non-white space character. On return, ; carry set indicates EOL was encountered; carry clear indicates it was not. ;**************************************************************************** findchar proc near lodsb ;Get the next character cmp al,09h ;Loop if tab je findchar cmp al,20h ;Loop if space je findchar cmp al,2Ch ;Loop if comma je findchar dec si ;Point SI to the character cmp al,0Dh ;Exit with carry set if end je eol ;of line is reached clc ;Clear carry and exit ret eol: stc ;Set carry and exit ret findchar endp ;**************************************************************************** ; CHANGE_COLORS copies monochrome attribute values to the color table. ; On entry, both DS and ES must point to the code segment. ;**************************************************************************** change_colors proc near mov si,offset mono_colors mov di,offset colors mov cx,11 rep movsb ret change_colors endp ;**************************************************************************** ; INIT_VIDEO initializes the variables used by the program's video output ; routines and makes sure we're in an 80-column text mode. ;**************************************************************************** init_video proc near mov ax,40h ;Point ES to the BIOS data mov es,ax ;area mov al,es:[49h] ;Get video mode in AL cmp al,0Fh ;Branch if it's other than jne init1 ;0Fh (EGA mono graphics) mov ax,0007h ;Switch to mode 7 (80-column int 10h ;monochrome text) jmp short init2 ;Branch and change colors init1: cmp al,7 ;Branch if it's other than jne init3 ;mode 7 (monochrome text) init2: mov ax,cs ;Point ES back to the code mov es,ax ;segment call change_colors ;Change color palette to mono mov ax,40h ;Point ES back to the BIOS mov es,ax ;data area mov video_segment,0B000h ;Change to monochrome segment jmp short init5 ;Branch past mode check init3: cmp al,2 ;Reset the video mode if the jb init4 ;current video mode number cmp al,3 ;is less than 2 or greater jbe init5 ;than 3 init4: mov ax,0003h ;Switch to mode 3 (80-column int 10h ;color text) init5: mov ax,es:[4Ah] ;Get number of columns in AX shl ax,1 ;Compute bytes per video line mov line_length,ax ;Store it in LINE_LENGTH mov ax,es:[4Eh] ;Save the starting address mov video_offset,ax ;of the video buffer mov ax,1A00h ;Branch if the system int 10h ;contains a VGA video cmp al,1Ah ;adapter je init6 mov ah,12h ;Branch if the system does mov bl,10h ;not contain an EGA video int 10h ;adapter cmp bl,10h je init7 init6: mov al,es:[84h] ;Read the highest row number mov maxrow,al ;from the BIOS data area init7: ret init_video endp ;**************************************************************************** ; BUILD_TABLE fills in the MAP array by performing a series of tests to ; determine what type of video hardware is installed, where physical EMS ; pages are located, and what regions of upper memory contain RAM and ROM. ;**************************************************************************** build_table proc near mov ax,cs ;Point ES to the code mov es,ax ;segment ; ; Check for a VGA video adapter and fill in the A000 and B000 segments ; if a VGA is detected. ; mov ax,1A00h ;Check for a VGA by calling int 10h ;function 1A00h in the cmp al,1Ah ;video BIOS jne check_ega ;Branch if no VGA mov di,offset map ;Fill the first 64 bytes of mov al,"V" ;the MAP array with "V"s mov cx,64 rep stosb jmp check_ems ;Branch to EMS check ; ; If there's an EGA installed, fill in the MAP array based on whether the ; EGA is attached to a monochrome or color monitor and how much video RAM ; it contains. ; check_ega: mov ah,12h ;Check for an EGA adapter mov bl,10h ;and branch if there's not int 10h ;one installed cmp bl,10h je check_cga mov ax,40h ;Point ES to the BIOS data mov es,ax ;area cmp byte ptr es:[49h],7 ;Branch if mode number is jne ega_color ;other than 7 mov ax,cs ;Point ES back to the code mov es,ax ;segment mov di,offset map+32 ;For an EGA attached to a mov al,"V" ;monochrome monitor, fill mov cx,16 ;in B000 through B3FF if cmp bl,0 ;the EGA contains 64K of jne ega1 ;RAM, or B000 through mov cx,8 ;B7FF if there's more ega1: rep stosb ;than 64K on board mov di,offset map ;For an EGA attached to a mov al,"V" ;monochrome monitor, fill mov cx,32 ;in A000 through A7FF if cmp bl,0 ;the EGA contains 64K of jne ega2 ;RAM, or A000 through mov cx,16 ;AFFF if there's more ega2: rep stosb ;than 64K on board jmp short check_cga ;Check for a CGA, too ega_color: mov ax,cs ;Point ES back to the code mov es,ax ;segment mov di,offset map+48 ;For an EGA attached to a mov al,"V" ;color monitor, fill in mov cx,16 ;B800 through BBFF if the cmp bl,0 ;EGA contains 64K of RAM, jne ega3 ;or B800 through BFFF if mov cx,8 ;there's more than 64K ega3: rep stosb ;on board mov di,offset map ;For an EGA attached to a mov al,"V" ;color monitor, fill in mov cx,32 ;A000 through A7FF if the cmp bl,0 ;EGA contains 64K of RAM, jne ega4 ;or A000 through AFFF if mov cx,16 ;there's more than 64K ega4: rep stosb ;on board ; ; Check for a CGA, MDA, or Hercules card and fill the MAP array accordingly. ; check_cga: mov dx,3D4h ;Write a "V" to bytes 48 call test_crtc ;through 55 of the MAP jc check_mda ;array if there's a mov di,offset map+48 ;CGA installed mov al,"V" mov cx,8 rep stosb check_mda: mov dx,3B4h ;Write a "V" to bytes 32 call test_crtc ;through 33 of the MAP jc check_ems ;array if there's an mov di,offset map+32 ;MDA installed mov al,"V" mov cx,2 rep stosb mov dx,3BAh ;Determine if a Hercules in al,dx ;video adapter is installed mov ah,al ;by seeing if bit 7 of the and ah,80h ;CRTC's Status Register mov cx,8000h ;changes hgc1: in al,dx and al,80h cmp ah,al jne hgc2 ;Exit loop if value changed loop hgc1 ;Try again if it didn't jmp short check_ems ;Branch if test was negative hgc2: mov di,offset map+32 ;Fill the entire B000 area mov al,"V" ;of the MAP array with "V"s mov cx,32 ;if a Hercules adapter was rep stosb ;detected ; ; Check for the presence of an expanded memory manager (EMM) and locate ; the EMS page frame if an EMM is installed. ; check_ems: mov ax,3567h ;See if there is an EMM int 21h ;installed by checking mov di,10 ;for the string "EMMXXXX0" mov si,offset devname ;10 bytes past where the mov cx,8 ;interrupt 67h vector repe cmpsb ;points to jne check_arom ;Branch if no EMM detected mov ax,cs ;Point ES back to the code mov es,ax ;segment mov ah,40h ;Now make sure the EMM int 67h ;hardware is present cmp ah,00h ;and operational jne check_arom ;Branch if it's not ; ; Get EMS page frame information. ; mov ah,41h ;Determine segment address int 67h ;of the EMS page frame by cmp ah,00h ;calling function 41h jne check_arom cmp bx,0A000h ;Branch if page frame is jb check_arom ;below A000h sub bx,0A000h ;Compute corresponding mov cl,7 ;offset into MAP array shr bx,cl mov di,offset map add di,bx mov al,"E" ;Write "E" to 32 consecutive mov cx,32 ;blocks to identify the rep stosb ;page frame ; ; Check for adapter ROM by inspecting the first two bytes of every 2K ; block between segments C000 and F400 for an adapter ROM signature. ; check_arom: mov si,64 ;SI holds index into MAP arom1: cmp byte ptr [si+offset map],20h ;Skip this block if jne next_block ;already checked mov ax,si ;Compute next segment address mov cl,7 ;by shifting SI 7 bits left shl ax,cl ;and adding A000h add ax,0A000h mov es,ax ;Transfer result to ES cmp word ptr es:[0],0AA55h ;Branch if no signature is jne next_block ;found mov al,es:[2] ;Get number of blocks cbw ;Convert byte to word mov cl,9 ;Compute length of adapter shl ax,cl ;ROM in bytes mov cx,ax ;Transfer result to CX xor al,al ;Zero AL and DI xor di,di arom3: add al,es:[di] ;Validate the ROM by summing inc di ;all the bytes in it, loop arom3 ;modulo 100h or al,al ;Not a valid ROM module if jnz next_block ;the result isn't zero mov al,es:[2] ;Get number of blocks add al,3 ;Compute number of 2K shr al,1 ;blocks the module shr al,1 ;comprises mov cl,al ;Transfer result to CL xor ch,ch ;Byte to word in CX push cx ;Save the result mov ax,cs ;Write "R"s to affected mov es,ax ;areas of the MAP array mov di,si add di,offset map mov al,"R" rep stosb pop cx ;Retrieve block count add si,cx ;Add block count to SI dec si ;Decrement before proceeding next_block: cmp si,168 ;Increment SI and loop back jae check_ram ;if it's less than 168, inc si ;which corresponds to jmp arom1 ;segment F400 ; ; Check all 2K blocks that haven't been analyzed yet for ROM or RAM. ; check_ram: call disable_nmi ;Disable NMI xor cx,cx ;Initialize counter mov dx,cs ;DX holds segment address ram1: push cx ;Save count mov si,cx ;Transfer count to SI cmp byte ptr [si+offset map],20h ;Skip this block if je ram2 ;already checked jmp next_region ram2: mov cl,7 ;Compute next segment address shl si,cl ;by shifting SI 7 bits left add si,0A000h ;and adding A000h mov bx,si ;Save result in BX mov ds,bx ;Copy the block to local assume ds:nothing ;memory xor si,si mov di,offset lastbyte mov es,dx mov cx,1024 cli ;Interrupts off!!! rep movsw mov ds,dx assume ds:code mov es,bx ;Copy test data to the xor di,di ;block in upper memory mov si,0100h mov cx,1024 rep movsw mov ds,bx ;Copy the block to local assume ds:nothing ;memory again xor si,si mov di,offset lastbyte+2048 mov es,dx mov cx,1024 rep movsw mov ds,dx assume ds:code mov es,bx ;Restore the block's xor di,di ;original contents mov si,offset lastbyte mov cx,1024 rep movsw sti ;Interrupts on!!! mov si,0100h ;Compare what was written mov di,offset lastbyte+2048 ;to what was read back to mov es,dx ;determine if this block mov cx,1024 ;is populated with RAM repe cmpsw jne ram3 ;Branch if they're not equal pop si ;Retrieve count from stack push si ;Push it back on for later mov byte ptr [si+offset map],"+" ;Mark block as RAM jmp short next_region ;Branch and continue ram3: mov si,offset lastbyte ;Compare the two sets of mov di,offset lastbyte+2048 ;data read from the block mov cx,1024 ;to determine if the block repe cmpsw ;is populated with ROM jne ram4 ;Branch if they're not equal mov di,offset lastbyte ;See if all the bytes that mov al,[di] ;were read have the same mov cx,2048 ;value. If they do, then repe scasb ;this probably isn't ROM. je next_region pop si ;Retrieve count from stack push si ;Push it back on for later mov byte ptr [si+offset map],"R" ;Mark block as ROM jmp short next_region ;Branch and continue ram4: pop si ;Retrieve count from stack push si ;Push it back on for later mov byte ptr [si+offset map],"U" ;Mark block with "U" next_region: pop cx ;Retrieve count inc cx ;Increment count cmp cx,192 ;Loop until done je build_exit jmp ram1 build_exit: call enable_nmi ;Enable NMI ret build_table endp ;**************************************************************************** ; DISABLE_NMI disables NMI. ;**************************************************************************** disable_nmi proc near mov ax,0C400h ;Find out if this is a PS/2 int 15h jc dnmi1 xor al,al ;Disable NMI on a PS/2 out 70h,al jmp short $+2 jmp short $+2 in al,71h jmp short dnmi_exit dnmi1: in al,0A0h ;Disable NMI on a non- jmp short $+2 ;PS/2 jmp short $+2 and al,7Fh out 0A0h,al dnmi_exit: ret disable_nmi endp ;**************************************************************************** ; ENABLE_NMI enables NMI. ;**************************************************************************** enable_nmi proc near mov ax,0C400h ;Find out if this is a PS/2 int 15h jc enmi1 mov al,80h ;Enable NMI on a PS/2 out 70h,al jmp short $+2 jmp short $+2 in al,71h jmp short enmi_exit enmi1: in al,0A0h ;Enable NMI on a non- jmp short $+2 ;PS/2 jmp short $+2 or al,80h out 0A0h,al enmi_exit: ret enable_nmi endp ;**************************************************************************** ; DRAW_SCREEN paints the screen. ;**************************************************************************** draw_screen proc near call clear_screen ;Clear the screen mov ah,title_color ;Write line 1 of the mov dx,0012h ;header at the top mov si,offset header1 ;of the screen call write_string mov ah,title_color ;Write line 2 of the mov dx,010Ch ;header mov si,offset header2 call write_string mov ah,border_color ;Draw the border around mov cx,0300h ;the window at the top mov dx,104Fh ;of the screen call drawbox mov ax,0600h ;Clear the interior of mov bh,back_color ;the window mov cx,0401h mov dx,0F4Eh int 10h mov ax,0600h ;Draw the shadow behind mov bh,shadow_color ;the first subwindow mov cx,0605h mov dx,0E2Eh int 10h mov ax,0600h ;Draw the shadow behind mov bh,shadow_color ;the second subwindow mov cx,0635h mov dx,0E4Bh int 10h mov ax,0600h ;Draw the first subwindow mov bh,fore_color mov cx,0504h mov dx,0D2Dh int 10h mov ax,0600h ;Draw the second subwindow mov bh,fore_color mov cx,0534h mov dx,0D4Ah int 10h mov ah,fore_color ;Display segment addresses mov dx,0705h mov si,offset text1 call write_string mov ah,fore_color mov dx,0805h mov si,offset text2 call write_string mov ah,fore_color mov dx,0905h mov si,offset text3 call write_string mov ah,fore_color mov dx,0A05h mov si,offset text4 call write_string mov ah,fore_color mov dx,0B05h mov si,offset text5 call write_string mov ah,fore_color mov dx,0C05h mov si,offset text6 call write_string mov si,offset text7 ;Display "0" thru "F" mov cx,16 ;above the first dscr1: push cx ;subwindow lodsb mov dl,al mov dh,06h lodsb mov ah,fore_color call write_char pop cx loop dscr1 mov ah,rom_color ;Display "LEGEND" mov dx,0635h mov si,offset text8 call write_string mov ah,rom_color ;Display legend labels mov dx,0835h mov si,offset text9 call write_string mov ah,ram_color mov dx,0935h mov si,offset text10 call write_string mov ah,video_color mov dx,0A35h mov si,offset text11 call write_string mov ah,ems_color mov dx,0B35h mov si,offset text12 call write_string mov ah,unk_color mov dx,0C35h mov si,offset text12x call write_string mov ah,fore_color ;Display legend notes mov dx,083Bh mov si,offset text13 call write_string mov ah,fore_color mov dx,093Bh mov si,offset text14 call write_string mov ah,fore_color mov dx,0A3Bh mov si,offset text15 call write_string mov ah,fore_color mov dx,0B3Bh mov si,offset text16 call write_string mov ah,fore_color mov dx,0C3Bh mov si,offset text17 call write_string mov ah,note_color ;Display notes at the mov dx,1200h ;bottom of the screen mov si,offset text18 call write_string mov ah,note_color mov dx,1300h mov si,offset text19 call write_string mov ah,note_color mov dx,1400h mov si,offset text20 call write_string mov ah,note_color mov dx,1500h mov si,offset text21 call write_string mov ah,note_color mov dx,1600h mov si,offset text22 call write_string mov ah,rom_color ;Highlight text in the mov dx,1203h ;notes area mov si,offset text9 call write_string mov ah,ram_color mov dx,1303h mov si,offset text10 call write_string mov ah,unk_color mov dx,1410h mov si,offset text12x call write_string mov ah,title_color ;Display "Press Esc to mov dx,181Eh ;exit" mov si,offset text23 call write_string ret draw_screen endp ;**************************************************************************** ; DRAW_MAP draws the memory map stored in the MAP array. ;**************************************************************************** draw_map proc near mov dx,0C0Ah ;Initialize DX mov si,offset map ;Point SI to MAP array mov cx,6 ;Do six rows, saving CX and dmap1: push cx ;and DX prior to each pass push dx ;through the loop mov cx,4 ;Do four sets of columns in dmap2: push cx ;each row, each time saving push dx ;CX and DX at the beginning mov cx,8 ;Eight characters per set dmap3: push cx ;Save CX and DX push dx lodsb ;Get the MAP character mov ah,fore_color ;Load the appropriate video cmp al,"R" ;attribute in AH jne dmap4 mov ah,rom_color jmp short dmap8 dmap4: cmp al,"+" jne dmap5 mov ah,ram_color jmp short dmap8 dmap5: cmp al,"V" jne dmap6 mov ah,video_color jmp short dmap8 dmap6: cmp al,"E" jne dmap7 mov ah,ems_color jmp short dmap8 dmap7: cmp al,"U" jne dmap8 mov ah,unk_color dmap8: call write_char ;Output the character pop dx ;Retrieve row and column inc dl ;Increment the column number pop cx ;Retrieve count in CX loop dmap3 ;Loop until done pop dx ;Retrieve row and column add dl,9 ;Add 9 to the column number pop cx ;Retrieve set count in CX loop dmap2 ;Loop until done pop dx ;Retrieve row and column dec dh ;Decrement the row number mov dl,0Ah ;Reinitialize column number pop cx ;Retrieve row count in CX loop dmap1 ;Loop until done ret draw_map endp ;**************************************************************************** ; TEST_CRTC tests for the presence of a 6845-style CRT controller at the ; port address specified in DX. On return, carry is clear if a CRTC was ; detected, or set if no CRTC was found. ;**************************************************************************** test_crtc proc near mov al,0Fh ;Set CRTC address register to out dx,al ;0Fh (Cursor Address Low) jmp short $+2 ;I/O delay jmp short $+2 inc dx ;Point DX to data register in al,dx ;Read Cursor Address Low mov ah,al ;Save result in AH not al ;Flip the bits in AL mov bl,al ;Save this result in BL out dx,al ;Write the new value back jmp short $+2 ;I/O delay jmp short $+2 in al,dx ;Read Cursor Address Low jmp short $+2 ;I/O delay jmp short $+2 xchg al,ah ;Swap AH and AL out dx,al ;Restore original value cmp ah,bl ;No CRTC here is AH is not jne test1 ;equal to BL clc ;Clear the carry flag ret ;and return test1: stc ;Set the carry flag ret ;and return test_crtc endp ;**************************************************************************** ; CLEAR_SCREEN clears the screen. Set MAXROW equal to the number of rows ; displayed minus 1 before calling this procedure. ;**************************************************************************** clear_screen proc near mov ax,0600h mov bh,07h mov cx,0000h mov dh,maxrow mov dl,4Fh int 10h ret clear_screen endp ;**************************************************************************** ; DRAWBOX draws a box in text mode using single-line graphics characters. ; On entry, CX holds the row and column address of the upper left corner, ; and DX holds the row and column address of the lower right corner. AH ; holds the video attribute used to draw the box. ;**************************************************************************** rows dw 0 ;Number of rows columns dw 0 ;Number of columns drawbox proc near sub dh,ch ;Compute the number of rows dec dh ;inside the box mov byte ptr rows,dh sub dl,cl ;Then compute the number of dec dl ;columns mov byte ptr columns,dl push ax ;Save video attribute mov dx,cx ;Place starting address in DX call compute_address ;Compute the memory address mov di,ax ;Transfer result to DI mov es,video_segment ;Point ES to the video buffer pop ax ;Retrieve video attribute push di ;Save video buffer address mov al,0DAh ;Draw the upper left corner stosw ;of the box mov al,0C4h ;Draw the upper horizontal mov cx,columns rep stosw mov al,0BFh ;Draw the upper right corner stosw ;of the box sub di,2 ;Point DI to the end of the add di,line_length ;second row of the box mov cx,rows ;Draw right vertical call draw_vertical mov al,0D9h ;Draw the lower right corner stosw ;of the box pop di ;Retrieve address add di,line_length ;Point DI to the second row mov cx,rows ;Draw left vertical call draw_vertical mov al,0C0h ;Draw the lower left corner stosw ;of the box mov al,0C4h ;Draw the lower horizontal mov cx,columns rep stosw ret drawbox endp ;**************************************************************************** ; DRAW_VERTICAL draws a vertical line. On entry, ES:DI points to the ; location in the video buffer, AH holds the video attribute, and CX ; holds the length of the line in rows. ;**************************************************************************** draw_vertical proc near mov al,0B3h ;Load AL with ASCII code dv_loop: stosw ;Write one character sub di,2 ;Point DI to the character add di,line_length ;cell on the next row loop dv_loop ;Loop until done ret draw_vertical endp ;**************************************************************************** ; WRITE_STRING writes an ASCIIZ string at the row and column address ; specified in DH and DL. On entry, AH contains the video attribute and ; DS:SI points to the string. ;**************************************************************************** write_string proc near push ax ;Save video attribute call compute_address ;Compute the memory address mov di,ax ;Transfer result to DI mov es,video_segment ;Point ES to the video buffer pop ax ;Retrieve video attribute ws_loop: lodsb ;Get a character or al,al ;Exit if it's zero jz ws_exit stosw ;Display it jmp ws_loop ;Loop back for more ws_exit: ret write_string endp ;**************************************************************************** ; WRITE_CHAR writes a character and attribute at the row and column ; address specified in DH and DL. On entry, AH holds the video attribute ; and AL holds the character's ASCII code. ;**************************************************************************** write_char proc near push ax ;Save video attribute call compute_address ;Compute the memory address mov di,ax ;Transfer result to DI mov es,video_segment ;Point ES to the video buffer pop ax ;Retrieve video attribute stosw ;Write the character and ret ;attribute and exit write_char endp ;**************************************************************************** ; COMPUTE_ADDRESS returns in AX the video buffer address that corresponds ; to the row and column number passed in DH and DL. Before this procedure ; is called, LINE_LENGTH must contain the number of bytes per video line ; and VIDEO_OFFSET must contain the offset within the video buffer of the ; current video page. ;**************************************************************************** compute_address proc near mov cl,dl ;Save DL in CL mov al,dh ;Get starting row in AL cbw ;Convert byte to word in AX mul line_length ;Multiply by bytes per row mov dl,cl ;Load DL with column number shl dx,1 ;Multiply starting column by 2 add ax,dx ;Add it to AX add ax,video_offset ;Add video buffer offset ret compute_address endp lastbyte label byte code ends end begin