/* * In the DTE 5.1 editor, the end of file was marked with '\0'. I have * decided to use ^Z to mark the begin and end of files instead of '\0'. * That way, null characters are allowed as normal text characters. ^Z is used * to mark the end of strings in buffers instead of '\0'. The standard C * string library functions should not be used when dealing with text buffers. * * The often used string routines have been rewritten in assembly. When using * 16 bit processors, accessing memory by WORDs on WORD boundaries is twice * as fast as accessing memory by BYTEs. If a memory pointer is even then it * is WORD aligned. If a memory pointer is odd, do the first BYTE and then * the rest of the string is WORD aligned on an even boundary. * * Two routines were written to adjust the string pointers whenever they * approach the end of a segment, cpf() and cpb() (check pointer foward and * check pointer backward). With these two routines, the code may be * compiled without the huge memory model. Another assembly routine was * written to compare physical memory locations, ptoul(). For example, * all of these pointers point to same physical memory address: * * 59a1:9122 == 58a1:a122 == 62a1:0122 = physical address 404,274 * * An efficient way to compare far pointers is to convert them to either * unsigned long or long integers. Either one will do - their is no such * thing a negative physical memory address. A long int goes from * -2 billion to 2 billion, which leaves plenty of room to describe a physical * address, using a long, where the max is 1 MEG. I used unsigned long. When * adding or subtracting from the physical address of a pointer, we should * never, ever get a negative physical address. This is the concept behind the * function ptoul(), which is short for pointer to unsigned long. * * With these functions written in assembly, this editor is fairly fast. * I feel the need for speed. * * New editor name: tde, the Thomson-Davis Editor. * Author: Frank Davis * Date: June 5, 1991, version 1.0 * Date: July 29, 1991, version 1.1 * Date: October 5, 1991, version 1.2 * Date: January 20, 1992, version 1.3 * Date: February 17, 1992, version 1.4 * Date: April 1, 1992, version 1.5 * Date: June 5, 1992, version 2.0 * * This modification of Douglas Thomson's code is released into the * public domain, Frank Davis. You may distribute it freely. */ #include "tdestr.h" #include "common.h" #include "tdefunc.h" /* * Name: cpf - check_pointer_forward * Purpose: To adjust a pointer if it is nearing end of a segment (within 16k) * Date: June 5, 1991 * Passed: s: string pointer * Notes: To avoid a bunch of code generated for pointer arithmetic when using * the huge memory model, this routine adjusts a pointer when it * approaches the end of a segment. */ text_ptr cpf( text_ptr s ) { _asm { mov ax, WORD PTR s ; get OFFSET of s mov dx, WORD PTR s+2 ; get SEGMENT of s cmp ax, 0xc000 ; are we within 16k of top of segment? jb get_out ; no, get out sub ax, 0x8000 ; yes, subtract 32k from offset add dx, 0x0800 ; add 0x0800 paragraphs to segment == 32k ALIGN 2 get_out: } } /* * Name: cpb - check_pointer_backward * Purpose: To adjust a pointer if it is nearing beginning of a segment (16k) * Date: June 5, 1991 * Passed: s: string pointer * Notes: To avoid a bunch of code generated for pointer arithmetic when using * the huge memory model, this routine adjusts a pointer when it * approaches the beginning of a segment. Don't check NULL pointer. */ text_ptr cpb( text_ptr s ) { _asm { mov ax, WORD PTR s ; get OFFSET of s mov dx, WORD PTR s+2 ; get SEGMENT of s cmp ax, 0 ; is offset of s == NULL? jne not_null ; no, check pointer cmp dx, 0 ; is segment of s == NULL? je get_out ; yes, don't check NULL pointer ALIGN 2 not_null: cmp ax, 0x4000 ; are we within 16k of beginning of segment? jae get_out ; no, get out add ax, 0x8000 ; yes, add 32k to offset sub dx, 0x0800 ; sub 0x0800 paragraphs from segment == 32k ALIGN 2 get_out: } } /* * Name: ptoul - pointer to unsigned long * Purpose: convert a far pointer to unsigned long integer * Date: June 5, 1991 * Passed: s: a far pointer * Notes: combine the offset and segment like so: * offset 0000 * segment + 0000 * ======= * 00000 * result is returned in dx:ax */ unsigned long ptoul( void far *s ) { _asm { mov ax, WORD PTR s ; ax = OFFSET of s mov dx, WORD PTR s+2 ; dx = SEGMENT of s mov bx, dx ; put copy of segment in bx mov cl, 12 ; cl = decimal 12 - shift hi word 3 hex digits shr dx, cl ; convert to 'real segment' mov cl, 4 ; cl = 4 - shift hi word 1 hex digit left shl bx, cl ; shift bx - to add 3 digits of seg to 4 of off add ax, bx ; add low part of segment to offset adc dx, 0 ; if carry, bump to next 'real' segment } } /* * Name: nptos - normalize pointer to segment * Purpose: make the offset of a pointer no larger than a paragraph (16 bytes) * Date: June 5, 1991 * Passed: s: string pointer * Notes: move all but the paragraph from the offset of the pointer to the * segment. The offset will be no larger than 16 bytes. Why? because * we can add up to 0xFFF0 reliably to a pointer in small, compact or * large model and not worry about segment wrap. * * offset abcx * segment 0000 * ======= * offset 000x * segment 0abc * result is returned in dx:ax */ text_ptr nptos( text_ptr s ) { _asm { mov ax, WORD PTR s ; ax = OFFSET of s mov dx, WORD PTR s+2 ; dx = SEGMENT of s mov bx, ax ; put copy of offset in bx mov cl, 4 ; cl = 4 - shift lo word 1 hex digit shr bx, cl ; shift bx - line up on paragraph add dx, bx ; add hi part of offset to segment and ax, 0x000f ; mask out three hex digits in offset } } /* * Name: addltop - add long to pointer * Purpose: add long integer to a pointer * Date: June 5, 1991 * Passed: l: long * p: text pointer * Returns: pointer + long integer * Notes: A long integer takes two WORDs. A far pointer takes two WORDs. * A long integer cannot be added directly to a far pointer. * This diagram may help explain better than I can write. * * far pointer 0000 offset * 0xxx segment * + * long integer 00000000 throw away those three * ====== hex digits on long integer, * 0000 offset they have no effect * new far pointer 0xxx segment * * msw = Most Significant WORD * lsw = Least Significant WORD * * When working with the long integer, we don't need to worry about * the three x's on segment of the pointer. Add or subtract the lsw * of the long integer to/from the offset. If there is a carry, * it only affects the left most digit of the msw of the pointer. */ text_ptr addltop( long l, text_ptr p ) { if (l >= 0) { _asm { mov ax, WORD PTR p ; ax = OFFSET of p mov dx, WORD PTR p+2 ; dx = SEGMENT of p mov bx, WORD PTR l+2 ; msw of l in bx add ax, WORD PTR l ; add offset of p and lsw of l adc bx, 0 ; if carry, pointer in another segment mov cl, 12 ; cl = 12 - shift off 3 hex digits shl bx, cl ; consider the 1st hex digit of msw of l add dx, bx ; add segment of p and 1st digit of msw of l } } else { l = -l; /* convert l to positive and subtract from pointer p */ _asm { mov ax, WORD PTR p ; ax = OFFSET of p mov dx, WORD PTR p+2 ; dx = SEGMENT of p mov bx, WORD PTR l+2 ; msw of l in bx mov cl, 12 ; cl = 12 - shift off 3 hex digits sub ax, WORD PTR l ; subtract low part of pointer adc bx, 0 ; if we borrowed then add it back to bx shl bx, cl ; only handle 1st hex digit of msw of l sub dx, bx ; subtract msw from segment of p } } } /* * Name: find_CONTROL_Z - assembler version, see commented C at end * Purpose: To determine the length of a line up to ^Z * Date: June 5, 1991 * Passed: s: the line to be measured * Notes: DOS carried over ^Z to mark the end of files from CP/M. Since * it is the only character not allowed in regular text files. ^Z * can be used, instead of '\0', to mark the end of strings. All * ASCII characters, except ^Z, may be included in a text file. * However, none of the C string library functions should be used * when working with text. The string library functions can be used * on responses solicited from the user. * Returns: the length of the line */ unsigned int find_CONTROL_Z( text_ptr s ) { s = cpf( s ); _asm { mov dx, ds ; keep ds in dx, MUST save data segment push si ; put copy of si on stack xor cx, cx ; cx = 0 mov si, WORD PTR s ; put OFFSET of s in si mov ax, WORD PTR s+2 ; get SEGMENT of s mov ds, ax ; else, segment in ds cmp si, 0 ; is offset of s == NULL? jne not_null ; no, find length cmp ax, 0 ; is segment of s == NULL? je get_out ; yes, line length = 0 ALIGN 2 not_null: mov bl, CONTROL_Z ; keep Control Z in bl - eos marker mov ax, si ; pointer is ok, check for word align shr ax, 1 ; if [si] is odd, lsb is 1 - rotate to carry jnc top ; see if string is WORD aligned lodsb ; no, get a BYTE - now WORD aligned cmp al, bl ; is ds:[si] == ^Z? je get_out ; yes, have length, cx = 0 inc cx ; increment length variable ALIGN 2 top: lodsw ; string is WORD aligned cmp al, bl ; is lo BYTE == ^Z? je get_out ; yes, we have length inc cx ; no, increment counter cmp ah, bl ; now test higher BYTE, is it ^Z? je get_out ; yes, we have length inc cx ; no, increment length jmp SHORT top ; look at next two characters ALIGN 2 get_out: mov ax, cx ; put length in ax - as defined by Microsoft mov ds, dx ; get back data segment from dx pop si ; get back si from stack } /* int len = 0; while (*s != ^Z) { ++len; ++s; } return len; */ } /* * Name: linelen - assembler version, see commented C at end of routine * Purpose: To determine the length of a line, up to either a \n or a * ^Z, whichever comes first. * Date: June 5, 1991 * Passed: s: the line to be measured * Notes: Demonstrates 'lodsb' and 'lodsw'. Memory operations are most * efficient when working with WORDs. See if first BYTE in * string is WORD aligned. If it is then work with WORDs else * get the first BYTE and rest of string will be WORD aligned. * The 'mov' instruction could have been used, but 'lobsb' and * 'lodsw' automatically increment the memory pointer. * Returns: the length of the line */ unsigned int linelen( text_ptr s ) { s = cpf( s ); _asm { mov dx, ds ; keep ds in dx, MUST save data segment push si ; save si on stack xor cx, cx ; cx = 0 mov si, WORD PTR s ; put OFFSET of s in si mov ax, WORD PTR s+2 ; get SEGMENT of s mov ds, ax ; else, segment in ds cmp si, 0 ; is offset of s == NULL? jne not_null ; no, find length cmp ax, 0 ; is segment of s == NULL? je get_out ; yes, line length = 0 ALIGN 2 not_null: mov bl, '\n' ; keep new line character in bl mov bh, CONTROL_Z ; keep Control Z in bh - DOS eof marker mov ax, si ; pointer is ok, check for word align shr ax, 1 ; if [si] is odd, lsb is 1 - rotate to carry jnc top ; see if string is WORD aligned lodsb ; no, get a BYTE - now WORD aligned cmp al, bl ; is BYTE == '\n'? je get_out ; yes, have length, cx = 0 cmp al, bh ; is ds:[si] == ^Z? je get_out ; yes, have length, cx = 0 inc cx ; increment length variable ALIGN 2 top: lodsw ; string is WORD aligned cmp al, bl ; test lower BYTE, is it '\n' je get_out ; yes, we have length cmp al, bh ; no, test for ^Z je get_out ; yes, we have length inc cx ; no, not '\n' or ^Z so increment counter cmp ah, bl ; now test higher BYTE, is it '\n' je get_out ; yes, we have length cmp ah, bh ; is it ^Z je get_out ; yes, we have length inc cx ; no, not '\n' or ^Z so increment length jmp SHORT top ; look at next two characters ALIGN 2 get_out: mov ax, cx ; put length in ax - as defined by Microsoft mov ds, dx ; get back data segment from dx pop si ; get back si from stack } /* int len = 0; while (*s && *s != '\n') { ++len; ++s; } return len; */ } /************* prelinelen is not used, but left in for reference **********/ /* * Name: prelinelen * Purpose: To determine the length of a line, from the current position * backwards to either a \n or a ^Z, whichever comes first. * Date: June 5, 1991 * Passed: s: the line to be measured * Returns: the length of the line up to the current position * Notes: It is assumed there will be a "terminating" ^Z before the * start of the first line. */ /* unsigned int prelinelen( text_ptr s ) { s = cpb( s ); _asm { push di ; put copy of di on stack xor ax, ax ; ax = 0, keep string length in ax mov di, WORD PTR s ; get offset of string mov dx, WORD PTR s+2 ; get segment of string mov es, dx ; put segment in es cmp di, 0 ; is offset of string == NULL? jne not_null ; no, do string stuff cmp dx, 0 ; is, segment of string == NULL? je get_out ; yes, don't do NULL string not_null: dec di ; look at previous character ALWORD: dec di ; get ready to check for WORD align mov bl, '\n' ; keep '\n' in bl mov bh, CONTROL_Z ; keep ^Z in bh mov dx, di ; pointer is ok, check for WORD align shr dx, 1 ; if [di] is odd, lsb is 1 - rotate to carry jnc top ; string is WORD aligned inc di ; fix the second decrement - see ALWORD mov dl, BYTE PTR es:[di] ; get a BYTE - put in DL cmp dl, bl ; is it '\n' je get_out ; yes, get out - count = 0 cmp dl, bh ; is it ^Z je get_out ; yes, get out - count = 0 inc ax ; increment length counter dec di ; pointer was BYTE aligned, dec pointer dec di ; pointer is now WORD aligned ALIGN 2 top: mov dx, WORD PTR es:[di] ; load WORD - hi BYTE is next cmp dh, bl ; is hi BYTE (next char) '\n'? je get_out ; yes, get out - count already in ax cmp dh, bh ; is hi BYTE (next char) ^Z? je get_out ; yes, get out - count already in ax inc ax ; increment character counter cmp dl, bl ; now check lo BYTE, is it '\n'? je get_out ; yes, get out - count is in ax cmp dl, bh ; is lo BYTE ^Z? je get_out ; yes, get out - count is in ax inc ax ; increment character counter dec di ; decrement pointer dec di ; align pointer on WORD jmp SHORT top ; test next 2 characters get_out: pop di ; get back di from stack } int len = 0; while (*--s != CONTROL_Z && *s != '\n') ++len; return len; } */ /************************** prelinelen is not used ************************/ /* * Name: find_next * Purpose: To find the first character in the next line * Date: June 5, 1991 * Passed: s: the starting point * Returns: the first character in the next line * Notes: This function goes faster if machine works with WORDs. See if * first BYTE in string is WORD aligned. If it is not, get first * BYTE in string then the rest of string is WORD aligned. * Code added at end to adjust segment:offset if needed. */ text_ptr find_next( text_ptr s ) { _asm { push ds ; save ds on stack push si ; save si on stack mov si, WORD PTR s ; load OFFSET of s mov ax, WORD PTR s+2 ; load SEGMENT of s mov ds, ax cmp si, 0 ; is offset of string == NULL? jne not_null ; no, do string stuff cmp ax, 0 ; is segment of string == NULL? je return_null ; yes, return NULL if string is NULL ALIGN 2 not_null: mov bl, '\n' ; keep '\n' in bl mov bh, CONTROL_Z ; keep ^Z in bh mov ax, si ; move offset of si to ax shr ax, 1 ; shift right into carry flag jnc top ; is string WORD aligned? lodsb ; no, get a BYTE cmp al, bl ; is it '\n'? je next_even ; yes, si already incremented by lodsb cmp al, bh ; is it ^Z? je return_null ; yes, return NULL ALIGN 2 top: lodsw ; string is WORD aligned, get two BYTEs cmp al, bl ; is next BYTE == '\n'? je next_odd ; yes, since si inc for WORD (lodsw) - dec di cmp al, bh ; is next BYTE == ^Z? je return_null ; yes, return NULL cmp ah, bl ; is next BYTE in AH == '\n'? je next_even ; yes, si is OK - return pointer to next BYTE cmp ah, bh ; is next BYTE in AH == ^Z? je return_null ; yes, return NULL jmp SHORT top ; look at next WORD ALIGN 2 return_null: xor ax, ax ; clear ax - offset = NULL xor dx, dx ; clear dx - segment = NULL jmp SHORT get_out ; return text_ptr in dx:ax - see Microsoft ALIGN 2 next_odd: dec si ; 'lodsw' went one BYTE too far - so dec si next_even: mov ax, si ; ds:si now points to next line, load ax mov dx, ds ; load dx with segment of next BYTE cmp ax, 0xc000 ; are we within 16k of segment? jb get_out ; no, get out sub ax, 0x8000 ; yes, subtract 32k from offset add dx, 0x0800 ; add 0x0800 paragraphs to segment ALIGN 2 get_out: pop si ; get back si from stack pop ds ; get back ds from stack } /* while (*s && *s != '\n' && *s != CONTROL_Z) ++s; if (*s) return ++s; else return NULL; */ } /* * Name: find_prev * Purpose: To find the start of the previous line * Date: June 5, 1991 * Passed: current: the current line * Returns: the start if the previous line * Notes: current should be at the start of the current line. * There must be a ^Z preceding the first line. * This function goes faster if machine works with WORDs. See if * first BYTE in string is WORD aligned. If it is not, get first * BYTE in string then the rest of string is WORD aligned. * The test for '\n' will pass a lot more than the test for * ^Z. Set up the WORD align stuff first. * Since we are searching, by WORDs, backwards, the hi BYTE is the * prev BYTE and the al BYTE is two prev BYTEs (make sense?). * Code added at end to adjust segment:offset if needed. */ text_ptr find_prev( text_ptr current ) { _asm { push di ; save di on stack mov di, WORD PTR current ; load OFFSET of current DECR1: dec di ; decrement it mov ax, WORD PTR current+2 ; load SEGMENT of current mov es, ax cmp di, 0 ; is offset of string == NULL? jne not_null ; no, do string stuff cmp ax, 0 ; is segment of string == NULL? je return_null ; yes, return NULL if string NULL ALIGN 2 not_null: mov bl, '\n' ; keep '\n' in bl mov bh, CONTROL_Z ; keep ^Z in bh mov ax, di ; put copy of offset in ax shr ax, 1 ; shift right thru carry flag jnc on_boundary ; if no carry, string is WORD aligned ; ; if we were to dec the pointer twice, it would be WORD aligned with the ; '--current' BYTE in the AH register. if ^Z test fails, might as well ; test the BYTE in the AL register. ; DECR2: dec di ; dec offset one more so it is WORD aligned mov ax, WORD PTR es:[di] ; might as well load WORD cmp ah, bh ; is prev BYTE ^Z? je return_null ; yes, return NULL ; ; now we are in the for loop - see commented C code at bottom. ; 'on_boundary' is not part of the for loop so jump past it if needed. ; cmp al, bl ; is prev BYTE '\n'? je inc_pointer ; yes, increment the pointer and return cmp al, bh ; is it ^Z? je inc_pointer ; yes, increment the pointer and return jmp SHORT for_loop ;no, pointer is now WORD aligned - do for loop ALIGN 2 ; ; the string ended on an odd boundary and the DECR1 has now aligned the ; string on a WORD. if we load a WORD, the '--current' BYTE would be in the ; AL register. ; on_boundary: mov ax, WORD PTR es:[di] ; load --current, aligned on WORD cmp al, bh ; is --current ^Z? je return_null ; yes, return NULL ; ; now we are in the for loop and string is guaranteed WORD aligned. ; IMPORTANT: there are 2 cases if the test for '\n' or ^Z pass. ; 1) AH passed, so di must be increment twice for '++current' ; 2) AL passed, inc di once for '++current' ; ALIGN 2 for_loop: dec di ; decrement di twice so it will be dec di ; WORD aligned mov ax, WORD PTR es:[di] ; string is WORD aligned cmp ah, bl ; is --current '\n'? je next_even ; yes, increment di twice to return ++current cmp ah, bh ; is --current ^Z? je next_even ; yes, increment di twice to return ++current cmp al, bl ; look at low part of WORD, is it '\n'? je inc_pointer ; yes, increment di once to return ++current cmp al, bh ; is low part of WORD ^Z? je inc_pointer ; yes, increment di once to return ++current jmp SHORT for_loop ; get next WORD ALIGN 2 return_null: xor ax, ax ; clear ax - offset = NULL xor dx, dx ; clear dx - segment = NULL jmp SHORT get_out ; return text_ptr in dx:ax - see Microsoft ALIGN 2 next_even: inc di ; di is a WORD too far - inc di inc_pointer: inc di ; ++current mov ax, di ; put offset in ax mov dx, es ; put segment in dx, return dx:ax - Microsoft cmp ax, 0x4000 ; are we within 16k of segment? jae get_out ; no, get out add ax, 0x8000 ; yes, add 32k to offset sub dx, 0x0800 ; sub 0x0800 paragraphs to segment ALIGN 2 get_out: pop di ; get back di from stack } /* if (*--current == ^Z) return NULL; for (;;) { if (*--current == '\n' || *current == ^Z) return ++current; } */ } /* * Name: update_line * Purpose: Display the current line in window * Date: June 5, 1991 * Passed: window: pointer to current window * Notes: Show string starting at column zero and if needed blank rest * of line. Put max_col in cx and count down. When we run into * '\n', cx contains number of columns to blank out. Use the * fast 'rep stosw' to clear the end of line. * The C routine was probably fast enough, but let's do some * assembly because it's so fun. * To handle line lengths longer than 255 characters, * block begin and end columns were changed from real * to virtual columns in this display routine. */ void update_line( WINDOW *window ) { text_ptr text; /* current character of orig begin considered */ char far *screen_ptr; int off; int attr; int line; int col; int bcol; int bc, ec; int normal, block; int max_col; int block_line; int show_eol; int len; int c; long rline; file_infos *file; if (window->rline > window->file_info->length) return; file = window->file_info; max_col = window->end_col + 1 - window->start_col; line = window->cline; normal = g_display.text_color; block = g_display.block_color; show_eol = mode.show_eol; /* * set the screen pointer to physical screen memory. */ screen_ptr = g_display.display_address; /* 160 = 80 chars + 80 attr for each line */ off = line * 160 + window->start_col * 2; /* * figure which line to display. assume we are displaying window->cursor. * if the current line is in the line buffer, then text will be the * same as buff_line, which means we need to display the line_buff. */ text = cpf( window->cursor ); if (g_status.copied && ptoul( text ) == ptoul( g_status.buff_line )) text = g_status.line_buff; /* * lets look at the base column. if the line to display is shorter * than the base column, then set text to eol and we can't see the * eol either. */ bc = window->bcol; if (bc > 0) { if ((col = linelen( text )) < bc) { bc = col; show_eol = FALSE; } text += bc; } bcol = window->bcol; rline = window->rline; if (file->block_type && rline >= file->block_br && rline <= file->block_er) block_line = TRUE; else block_line = FALSE; /* * do this if 1) a box block is marked, or 2) a stream block begins * and ends on the same line. */ if (block_line == TRUE && (file->block_type == BOX || (file->block_type == STREAM && rline == file->block_br && rline == file->block_er))) { len = linelen( text ); /* * start with the bc and ec equal to physical block marker. */ bc = file->block_bc; ec = file->block_ec; if (ec < bcol || bc >= bcol + max_col) /* * we can't see block if ending column is less than the base col or * the beginning column is greater than max_col. */ ec = bc = max_col + 1; else if (ec < bcol + max_col) { /* * if the ec is less than the max column, make ec relative to * base column then figure the bc. */ ec = ec - bcol; if (bc < bcol) bc = 0; else bc = bc - bcol; } else if (bc < bcol + max_col) { /* * if the bc is less than the max column, make bc relative to * base column then figure the ec. */ bc = bc - bcol; if (ec > bcol + max_col) ec = max_col; else ec = ec - bcol; } else if (bc < bcol && ec >= bcol + max_col) { /* * if the block is wider than the screen, make bc start at the * logical begin and make ec end at the logical end of the * window. */ bc = 0; ec = max_col; } _asm { push ds ; MUST save ds - push it on stack push si ; save si on stack push di ; save di on stack ; on the stack so we can pop it when we're thru displaying line. mov ax, WORD PTR show_eol ; get the show_eol flag push ax ; push the flag ; ; set up local register variables ; mov ax, WORD PTR bc ; get beginning column mov bl, al ; keep it in bl mov ax, WORD PTR ec ; get ending column mov bh, al ; keep it in bh mov ax, WORD PTR normal ; get normal attribute mov dl, al ; keep it in dl mov ax, WORD PTR block ; get block attribute mov dh, al ; keep it in dh mov ax, WORD PTR max_col ; get max number columns on screen mov ch, al ; keep it in ch xor cl, cl ; col = 0, keep col in cl ; ; load screen and text pointer ; mov di, WORD PTR screen_ptr ; load OFFSET of screen ptr add di, WORD PTR off ; add offset of line mov ax, WORD PTR screen_ptr+2 ; load SEGMENT of screen ptr mov es, ax mov si, WORD PTR text ; load OFFSET of text ptr mov ax, WORD PTR text+2 ; load SEGMENT of text ptr mov ds, ax ; move segment of text in ds cmp si, 0 ; is offset of text ptr == NULL? jne not_null ; no, output string cmp ax, 0 ; is segment of text ptr == NULL? je block_eol ; yes, clear end of line not_null: ALIGN 2 top: cmp cl, ch ; is col == max_col 0? je getout ; yes, thru with line lodsb ; get next char in string cmp al, CONTROL_Z ; is it ^Z? je block_eol ; yes, must check block past ^Z cmp al, '\n' ; is it '\n'? je dspl_eol ; yes, must check block past '\n' mov ah, dl ; assume normal attribute cmp cl, bl ; is col < bc? (less than beginning col) jl ch_out1 ; yes, show char and normal attribute cmp cl, bh ; is col > ec? (greater than ending col) jg ch_out1 ; yes, show char and normal attribute mov ah, dh ; must be in a block - show block attribute ch_out1: stosw ; else show char on screen inc cl ; ++col jmp SHORT top ; get another character ALIGN 2 dspl_eol: pop ax ; look at the show_eol flag push ax ; push it back on stack or ax, ax ; or the flag - test for 0 je block_eol ; show_eol flag is FALSE, blank line mov al, EOL_CHAR ; load some eol indicator mov ah, dl ; assume normal attribute cmp cl, bl ; is col < bc? (less than beginning col) jl ch_out2 ; yes, show char and normal attribute cmp cl, bh ; is col > ec? (greater than ending col) jg ch_out2 ; yes, show char and normal attribute mov ah, dh ; must be in a block - show block attribute ALIGN 2 ch_out2: stosw ; write eol and attribute to screen inc cl ; ++col cmp cl, ch ; is col == max_col? je getout ; yes, we're done ALIGN 2 block_eol: mov al, ' ' ; clear rest of line w/ spaces b1: mov ah, dl ; assume normal attribute cmp cl, bl ; is col < bc? (less than beginning col) jl ch_out3 ; yes, show char and normal attribute cmp cl, bh ; is col > ec? (greater than ending col) jg ch_out3 ; yes, show char and normal attribute mov ah, dh ; must be in a block - show block attribute ALIGN 2 ch_out3: stosw ; write blank and attribute to screen inc cl ; ++col cmp cl, ch ; is col == max_col? jl b1 ; while less output block ALIGN 2 getout: add sp, 2 ; "pop" the show_eol flag pop di pop si pop ds } /* for (col=0; col < max_col; col++) { attr = normal; if (col >= bc && col <= ec) attr = block; if (col < len) c = text[col]; else c = ' '; update_char( c, col, line, attr ); } */ } else if (block_line == TRUE && file->block_type == STREAM && (rline == file->block_br || rline == file->block_er)) { len = linelen( text ); if (rline == file->block_br) bc = file->block_bc; else { bc = file->block_ec + 1; ec = normal; normal = block; block = ec; } if (bc < bcol) bc = 0; else if (bc < bcol + max_col) bc = bc - bcol; else bc = max_col + 1; _asm { push ds ; MUST save ds - push it on stack push si ; save si on stack push di ; save di on stack ; on the stack so we can pop it when we're thru displaying line. mov ax, WORD PTR show_eol ; get the show_eol flag push ax ; push the flag ; ; set up local register variables ; mov ax, WORD PTR bc ; get beginning column mov bl, al ; keep it in bl mov ax, WORD PTR normal ; get normal attribute mov dl, al ; keep it in dl mov ax, WORD PTR block ; get block attribute mov dh, al ; keep it in dh mov ax, WORD PTR max_col ; get max number columns on screen mov ch, al ; keep it in ch xor cl, cl ; col = 0, keep col in cl ; ; load screen and text pointer ; mov di, WORD PTR screen_ptr ; load OFFSET of screen ptr add di, WORD PTR off ; add offset of line mov ax, WORD PTR screen_ptr+2 ; load SEGMENT of screen ptr mov es, ax mov si, WORD PTR text ; load OFFSET of text ptr mov ax, WORD PTR text+2 ; load SEGMENT of text ptr mov ds, ax ; move segment of text in ds cmp si, 0 ; is offset of text ptr == NULL? jne nott_null ; no, output string cmp ax, 0 ; is segment of text ptr == NULL? je stream_eol ; yes, clear end of line nott_null: ALIGN 2 ttop: cmp cl, ch ; is col == max_col? je ggetout ; yes, thru with line lodsb ; get next char in string cmp al, CONTROL_Z ; is it ^Z? je stream_eol ; yes, must check block past ^Z cmp al, '\n' ; is it '\n'? je ddspl_eol ; yes, must check block past '\n' mov ah, dl ; assume normal attribute cmp cl, bl ; is col < bc? (less than beginning col) jl str_out1 ; yes, show char and normal attribute mov ah, dh ; must be in a block - show block attribute ALIGN 2 str_out1: stosw ; else show char on screen inc cl ; ++col jmp SHORT ttop ; get another character ALIGN 2 ddspl_eol: pop ax ; look at the show_eol flag push ax ; push it back on stack or ax, ax ; or the flag - test for 0 je stream_eol ; show_eol flag is FALSE, blank line mov al, EOL_CHAR ; load some eol indicator mov ah, dl ; assume normal attribute cmp cl, bl ; is col < bc? (less than beginning col) jl str_out2 ; yes, show char and normal attribute mov ah, dh ; must be in a block - show block attribute ALIGN 2 str_out2: stosw ; write blank and attribute to screen inc cl ; ++col cmp cl, ch ; is col == max_col? je ggetout ; yes, we're done ALIGN 2 stream_eol: mov al, ' ' ; clear rest of line w/ spaces ALIGN 2 c1: mov ah, dl ; assume normal attribute cmp cl, bl ; is col < bc? (less than beginning col) jl str_out3 ; yes, show char and normal attribute mov ah, dh ; must be in a block - show block attribute ALIGN 2 str_out3: stosw ; write blank and attribute to screen inc cl ; ++col cmp cl, ch ; is col == max_col? jl c1 ; while less output block ALIGN 2 ggetout: add sp, 2 ; "pop" show_eol pop di pop si pop ds } /* for (col=0; col < max_col; col++) { attr = normal; if (col >= bc && col <= ec) attr = block; if (col < len) c = text[col]; else c = ' '; update_char( c, col, line, attr ); } */ } else { if (block_line) attr = block; else attr = normal; _asm { mov dx, ds ; MUST save ds - keep it in dx push di ; save di on stack push si ; save si on stack ; on the stack so we can pop it when we're thru displaying line. mov ax, WORD PTR show_eol ; get the show_eol flag push ax ; push the flag mov bx, WORD PTR attr ; keep attribute in bl mov bh, '\n' ; keep '\n' in bh mov cx, WORD PTR max_col ; keep max_col in cx mov di, WORD PTR screen_ptr ; load OFFST of screen ptr add di, WORD PTR off ; add offset of line mov ax, WORD PTR screen_ptr+2 ; load SEGMENT of screen ptr mov es, ax mov si, WORD PTR text ; load OFFSET of text ptr mov ax, WORD PTR text+2 ; load SEGMENT of text ptr mov ds, ax ; move segment of text in ds cmp si, 0 ; is offset of pointer == NULL? jne nnot_null ; no, output string cmp ax, 0 ; is segment of pointer == NULL? je clreol ; yes, then clear rest of line nnot_null: mov ah, bl ; get attribute ALIGN 2 topp: or cx, cx ; col == 0 ? je getoutt ; yes, thru with line lodsb ; get next char in string cmp al, CONTROL_Z ; is it ^Z je clreol ; yes, clear end of line cmp al, bh ; is it '\n' je normeol ; yes, clear end of line stosw ; else show char on screen dec cx ; --col, count down from max_column jmp SHORT topp ; get another character ALIGN 2 normeol: pop ax ; look at the show_eol flag push ax ; push it back on stack or ax, ax ; or the flag - test for 0 je clreol ; show_eol flag is FALSE, blank line mov al, EOL_CHAR ; load some eol indicator mov ah, bl ; assume normal attribute stosw ; write blank and attribute to screen dec cl ; ++col or cl, cl ; is col == 0? je getoutt ; yes, we're done ALIGN 2 clreol: mov ah, bl ; get attribute mov al, ' ' ; clear eol with ' ' rep stosw ; count is in cx - set rest of line to ' ' ALIGN 2 getoutt: add sp, 2 ; "pop" show_eol pop si pop di mov ds, dx } } /* if (orig != NULL) { text = orig; screen_ptr = g_display.display_address + line * 160 + col * 2; for (; *text != '\n' && *text != ^Z && col < max_col; text++, col++) { *screen_ptr++ = *text; *screen_ptr++ = attr; } } if (col < max_col) eol_clear( col, line, attr ); */ } /* * Name: c_output * Purpose: Output one character on prompt lines * Date: June 5, 1991 * Passed: c: character to output to screen * col: col to display character * line: line number to display character * attr: attribute of character * Returns: none */ void c_output( int c, int col, int line, int attr ) { void far *screen_ptr; int off; screen_ptr = (void far *)g_display.display_address; off = line * 160 + col * 2; _asm { mov bx, WORD PTR screen_ptr ; load OFFSET of screen ptr add bx, WORD PTR off ; add offset of line:col mov ax, WORD PTR screen_ptr+2 ; load SEGMENT of screen ptr mov es, ax mov cx, WORD PTR attr ; get attribute mov ah, cl ; put in ah mov cx, WORD PTR c ; get character mov al, cl ; put in al mov WORD PTR es:[bx], ax ; show char on screen } /* screen_ptr = g_display.display_address + line * 160 + col * 2; *screen_ptr++ = c; *screen_ptr = attr; */ } /* * Name: s_output * Purpose: To output a string * Date: June 5, 1991 * Passed: s: string to output * line: line to display * col: column to begin display * attr: color to display string * Notes: This function is used to output most strings not part of file text. * * All strings in the SMALL memory model are in the default NEAR data * segment (used in production version of tde). Prototype the output * string as far because when compiling for debugging, you must * compile with the LARGE library since the /Zi code will not * fit in the SMALL model. */ void s_output( char far *s, int line, int col, int attr ) { void far *screen_ptr; int off; int max_col; max_col = g_display.ncols; screen_ptr = (void far *)g_display.display_address; off = line * 160 + col * 2; _asm { push ds ; save ds on stack push di ; save di on stack push si ; save si on stack mov bx, WORD PTR attr ; keep attribute in bx mov cx, WORD PTR col ; put cols in cx mov dx, WORD PTR max_col ; keep max_col in dx mov di, WORD PTR screen_ptr ; load OFFSET of screen ptr add di, WORD PTR off ; add offset of line:col mov ax, WORD PTR screen_ptr+2 ; load SEGMENT of screen ptr mov es, ax mov si, WORD PTR s ; load offset of string ptr or si, si ; is it == NULL? je getout ; yes, no output needed mov ax, WORD PTR s+2 ; load segment of string ptr or ax, ax ; is pointer == NULL? je getout ; yes, no output needed mov ds, ax ; load segment of text in ds mov ah, bl ; put attribute in AH ALIGN 2 top: cmp cx, dx ; col < max_cols? jge getout ; no, thru with line lodsb ; get next char in string - put in al or al, al ; is it '\0' je getout ; yes, end of string cmp al, '\n' ; is it '\n'? je getout ; yes, end of string stosw ; else show attr + char on screen (ah + al) inc cx ; col++ jmp SHORT top ; get another character ALIGN 2 getout: pop si ; get back si pop di ; get back di pop ds ; get back ds } /* screen_ptr = g_display.display_address + line * 160 + col * 2; max_col = g_display.ncols; while (*s && col < max) { *screen_ptr++ = *s++; *screen_ptr++ = attr; } */ } /* * Name: eol_clear * Purpose: To clear the line from col to max columns * Date: June 5, 1991 * Passed: col: column to begin clear * line: line to clear * attr: color to clear * Notes: Basic assembly */ void eol_clear( int col, int line, int attr ) { int max_col; void far *screen_ptr; int off; max_col = g_display.ncols; screen_ptr = (void far *)g_display.display_address; off = line * 160 + col * 2; _asm { push di ; save di on stack mov bx, WORD PTR attr ; keep attribute in bx mov dx, WORD PTR col ; put cols in dx mov cx, WORD PTR max_col ; put max_col in cx cmp dx, cx ; max_cols < cols? jge getout ; no, thru with line sub cx, dx ; number of column to clear mov di, WORD PTR screen_ptr ; load OFFSET of screen ptr add di, WORD PTR off ; add offset of line:col mov ax, WORD PTR screen_ptr+2 ; load SEGMENT of screen ptr mov es, ax mov ah, bl ; get attribute in ah mov al, ' ' ; store ' ' in al rep stosw ; clear to end of line getout: pop di ; get back di from stack } /* for (; col < g_display.ncols; col++) { *p++ = ' '; *p++ = attr; } */ } /* * Name: window_eol_clear * Purpose: To clear the line from start_col to end_col * Date: June 5, 1991 * Passed: col: column to begin clear * line: line to clear * attr: color to clear * Notes: Basic assembly */ void window_eol_clear( WINDOW *window, int attr ) { int max_col; void far *screen_ptr; int off; screen_ptr = (void far *)g_display.display_address; off = window->cline * 160 + window->start_col * 2; max_col = window->end_col + 1 - window->start_col; _asm { push di ; save di on stack mov bx, WORD PTR attr ; keep attribute in bx mov cx, WORD PTR max_col ; put max_col in cx mov di, WORD PTR screen_ptr ; load OFFSET of screen ptr add di, WORD PTR off ; add offset of line:col mov ax, WORD PTR screen_ptr+2 ; load SEGMENT of screen ptr mov es, ax mov ah, bl ; get attribute in ah mov al, ' ' ; store ' ' in al rep stosw ; clear to end of line getout: pop di ; get back di from stack } /* for (; col < g_display.ncols; col++) { *p++ = ' '; *p++ = attr; } */ } /* * Name: upper_asm * Purpose: To convert all lower case characters to upper characters * Date: June 5, 1991 * Passed: s: the starting point * count: number of characters to convert (unsigned) * Returns: none * Notes: This function goes faster if machine works with WORDs. See if * first BYTE in string is WORD aligned. If it is not, get first * BYTE in string then the rest of string is WORD aligned. * * The pointer should have been normalized, using the nptos, as * this function does not handle segment wrap. * * ax, ah, al = characters from string * bl = 'a' * bh = 'z' * cx = number of characters to look at * dl = 0x20, xor 0x20 with lower case to get upper case * ds:si = far pointer to character string * use ds:si so there is no segment override */ void upper_asm( text_ptr s, unsigned count ) { _asm { push ds ; save ds on stack push si ; save si on stack mov cx, WORD PTR count ; load count in cx jcxz get_out ; 1st, if count == 0 then do nothing mov si, WORD PTR s ; load OFFSET of s mov ax, WORD PTR s+2 ; load SEGMENT of s mov ds, ax or si, si ; is offset of string == NULL or 0? jne not_null ; no, do string stuff or ax, ax ; is segment of string == NULL or 0? je get_out ; yes, don't upper case a NULL string ALIGN 2 not_null: mov bh, 'z' ; keep 'z' in bh mov bl, 'a' ; keep 'a' in bl mov dl, 0x20 ; keep 0x20 in dl mov ax, si ; move offset of si to ax shr ax, 1 ; shift right into carry flag jnc top ; is string WORD aligned? mov al, BYTE PTR [si] ; no, get a BYTE cmp al, bl ; is al < 'a' (use unsigned test) jb word_align ; yes, dec count and align on WORD cmp al, bh ; is al > 'z' (use unsigned test) ja word_align ; yes, dec count and align on WORD xor al, dl ; convert lower case to upper mov BYTE PTR [si], al ; store the character or BYTE ALIGN 2 word_align: inc si ; inc the string pointer - now WORD aligned dec cx ; decrement the character count jcxz get_out ; if count or cx == 0 then we're done, get_out ALIGN 2 top: mov ax, WORD PTR [si] ; string is WORD aligned, get two BYTEs cmp al, bl ; is al < 'a'? jb upper_hi ; yes, dec count and check the hi byte cmp al, bh ; is al > 'z'? ja upper_hi ; yes, dec count and check the hi byte xor al, dl ; convert lower case to upper ALIGN 2 upper_hi: dec cx ; decrement the count jcxz clean_up ; if count or cx == 0 then we're done, clean_up cmp ah, bl ; is al < 'a'? jb save_word ; yes, dec count and do next word cmp ah, bh ; is al > 'z'? ja save_word ; yes, dec count and do next word xor ah, dl ; convert lower case to upper ALIGN 2 save_word: mov WORD PTR [si], ax ; else, save changes dec cx ; decrement the count jcxz get_out ; if count or cx == 0 then we're done, get_out inc si ; increment the pointer to next word inc si jmp SHORT top ; look at next WORD ALIGN 2 clean_up: mov WORD PTR [si], ax ; else, save changes then we're done ALIGN 2 get_out: pop si ; get back si from stack pop ds ; get back ds from stack } /* while (count-- > 0) { if (*s >= 'a' && *s <= 'z') *s++ &= 0xdf; } */ } /* * Name: lower_asm * Purpose: To convert all upper case characters to lower characters * Date: June 5, 1991 * Passed: s: the starting point * count: number of characters to convert (unsigned) * Returns: none * Notes: This function goes faster if machine works with WORDs. See if * first BYTE in string is WORD aligned. If it is not, get first * BYTE in string then the rest of string is WORD aligned. * * The pointer should have been normalized, using the nptos, as * this function does not handle segment wrap. One could safely * pass a count of 0xFFF0 if the pointer has been normalized to * a segment * * ax, ah, al = characters from string * bl = 'A' * bh = 'Z' * cx = number of characters to look at, unsigned * dl = 0x20, or upper case with 0x20 to get lower case * ds:si = far pointer to character string * use ds:si so there is no segment override */ void lower_asm( text_ptr s, unsigned count ) { _asm { push ds ; save ds on stack push si ; save si on stack mov cx, WORD PTR count ; load count in cx jcxz get_out ; 1st, if count == 0 then do nothing mov si, WORD PTR s ; load OFFSET of s mov ax, WORD PTR s+2 ; load SEGMENT of s mov ds, ax or si, si ; is offset of string == NULL or 0? jne not_null ; no, do string stuff or ax, ax ; is segment of string == NULL or 0? je get_out ; yes, don't upper case a NULL string ALIGN 2 not_null: mov bh, 'Z' ; keep 'z' in bh mov bl, 'A' ; keep 'a' in bl mov dl, 0x20 ; keep 0x20 in dl mov ax, si ; move offset of si to ax shr ax, 1 ; shift right into carry flag jnc top ; is string WORD aligned? mov al, BYTE PTR [si] ; no, get a BYTE cmp al, bl ; is al < 'A' (use unsigned test) jb word_align ; yes, dec count and align on WORD cmp al, bh ; is al > 'Z' (use unsigned test) ja word_align ; yes, dec count and align on WORD or al, dl ; convert upper case to lower mov BYTE PTR [si], al ; store the character or BYTE ALIGN 2 word_align: inc si ; inc the string pointer - now WORD aligned dec cx ; decrement the character count jcxz get_out ; if count or cx == 0 then we're done ALIGN 2 top: mov ax, WORD PTR [si] ; string is WORD aligned, get two BYTEs cmp al, bl ; is al < 'A'? jb lower_hi ; yes, dec count and check the hi byte cmp al, bh ; is al > 'Z'? ja lower_hi ; yes, dec count and check the hi byte or al, dl ; convert upper case to lower ALIGN 2 lower_hi: dec cx ; decrement the character count jcxz clean_up ; if count or cx == 0 then we're done, clean_up cmp ah, bl ; is al < 'A'? jb save_word ; yes, dec count and do next word cmp ah, bh ; is al > 'Z'? ja save_word ; yes, dec count and do next word or ah, dl ; convert upper case to lower ALIGN 2 save_word: mov WORD PTR [si], ax ; else, save changes dec cx ; decrement the count jcxz get_out ; if count or cx == 0 then we're done, get out inc si ; increment the pointer to next word inc si jmp SHORT top ; look at next WORD ALIGN 2 clean_up: mov WORD PTR [si], ax ; else, save changes then we're done ALIGN 2 get_out: pop si ; get back si from stack pop ds ; get back ds from stack } /* while (count-- > 0) { if (*s >= 'a' && *s <= 'z') *s++ &= 0xdf; } */ } /* * Name: strip_asm * Purpose: To strip bit 7 from characters * Date: June 5, 1991 * Passed: s: the starting point, which should be normalized to a segment * count: number of characters to strip (unsigned) * count should not be greater than 0xfff0 * Returns: none * Notes: This function goes faster if machine works with WORDs. See if * first BYTE in string is WORD aligned. If it is not, get first * BYTE in string then the rest of string is WORD aligned. * * The pointer should have been normalized, using the nptos, as * this function does not handle segment wrap. * * ax, ah, al = characters from string * bl = 01111111 or 0x7f to strip the hi bit from characters * cx = number of characters to look at * ds:si = far pointer to character string * use ds:si so there is no segment override */ void strip_asm( text_ptr s, unsigned count ) { _asm { push ds ; save ds on stack push si ; save si on stack mov cx, WORD PTR count ; load count in cx jcxz get_out ; 1st, if count == 0 then do nothing mov si, WORD PTR s ; load OFFSET of s mov ax, WORD PTR s+2 ; load SEGMENT of s mov ds, ax or si, si ; is offset of string == NULL or 0? jne not_null ; no, do string stuff or ax, ax ; is segment of string == NULL or 0? je get_out ; yes, don't upper case a NULL string ALIGN 2 not_null: mov bl, 0x7f ; turn all bits except high bit in bl mov ax, si ; move offset of si to ax shr ax, 1 ; shift right into carry flag jnc top ; is string WORD aligned? mov al, BYTE PTR [si] ; no, get a BYTE and al, bl ; strip the high bit mov BYTE PTR [si], al ; store the character or BYTE word_align: inc si ; inc the string pointer - now WORD aligned dec cx ; dec the character count jcxz get_out ; if count or cx == 0 then we're done ALIGN 2 top: mov ax, WORD PTR [si] ; string is WORD aligned, get two BYTEs and al, bl ; strip the hi bit from the lo byte dec cx ; decrement the count jcxz clean_up ; if count or cx == 0 then let's clean up and ah, bl ; strip the hi bit from the hi byte mov WORD PTR [si], ax ; save changes dec cx ; decrement the count jcxz get_out ; if count or cx == 0 then we're done inc si ; increment the pointer to next word inc si jmp SHORT top ; look at next WORD ALIGN 2 clean_up: mov WORD PTR [si], ax ; else, save changes then we're done ALIGN 2 get_out: pop si ; get back si from stack pop ds ; get back ds from stack } /* while (count-- > 0) { if (*s >= 'a' && *s <= 'z') *s++ &= 0xdf; } */ } /* * Name: get_fattr * Purpose: To get dos file attributes * Date: December 26, 1991 * Passed: fname: ASCIIZ file name. Null terminated file name * fattr: pointer to file attributes * Returns: 0 if successfull, non zero if not * Notes: Uses the DOS function to get file attributes. I really didn't * like the file attribute functions in the C library: fstat() and * stat() or access() and chmod(). * FYI, File Attributes: * 0x00 = Normal. Can be read or written w/o restriction * 0x01 = Read-only. Cannot be opened for write; a file with * the same name cannot be created. * 0x02 = Hidden. Not found by directory search. * 0x04 = System. Not found by directory search. * 0x08 = Volumn Label. * 0x10 = Directory. * 0x20 = Archive. Set whenever the file is changed, or * cleared by the Backup command. * Return codes: * 0 = No error * 1 = AL not 0 or 1 * 2 = file is invalid or does not exist * 3 = path is invalid or does not exist * 5 = Access denied */ int get_fattr( char far *fname, int *fattr ) { int rc; /* return code */ int attr; _asm { push ds mov dx, WORD PTR fname ; get OFFSET of filename string mov ax, WORD PTR fname+2 ; get SEGMENT of filename string mov ds, ax ; put SEGMENT in ds mov ax, 0x4300 ; function: get file attributes int 0x21 ; DOS interrupt pop ds jc an_error ; save the error code from get attr xor ax, ax ; if no carry, no error jmp SHORT get_out ; lets get out an_error: xor cx, cx ; if error, then zero out cx - attrs get_out: mov WORD PTR rc, ax ; ax contains error number on error mov WORD PTR attr, cx ; cx contains file attributes } *fattr = attr; if (ceh.flag == ERROR) rc = ERROR; return( rc ); } /* * Name: set_fattr * Purpose: To set dos file attributes * Date: December 26, 1991 * Passed: fname: ASCIIZ file name. Null terminated file name * fattr: file attributes * Returns: 0 if successfull, non zero if not * Notes: Uses the DOS function to get file attributes. * Return codes: * 0 = No error * 1 = AL not 0 or 1 * 2 = file is invalid or does not exist * 3 = path is invalid or does not exist * 5 = Access denied */ int set_fattr( char far *fname, int fattr ) { int rc; /* return code */ _asm { push ds mov dx, WORD PTR fname ; get OFFSET of filename string mov ax, WORD PTR fname+2 ; get SEGMENT of filename string mov ds, ax ; put SEGMENT in ds mov cx, WORD PTR fattr ; cx contains file attributes mov ax, 0x4301 ; function: get file attributes int 0x21 ; DOS interrupt pop ds jc get_out ; save the error code from get attr xor ax, ax ; if no carry, no error get_out: mov WORD PTR rc, ax ; ax contains error number on error } if (ceh.flag == ERROR) rc = ERROR; return( rc ); } /* * Name: get_current_directory * Purpose: get current directory * Date: February 13, 1992 * Passed: path: pointer to buffer to store path * drive: drive to get current directory * Notes: use simple DOS interrupt */ int get_current_directory( char far *path, int drive ) { int rc; _asm { push si ; save register vars if any push ds ; save ds mov dx, WORD PTR drive ; dl = drive, 0 = default, 1 = a, etc.. mov si, WORD PTR path ; get OFFSET of path mov ax, WORD PTR path+2 ; get SEGMENT of path mov ds, ax ; put it in ds mov ah, 0x47 ; function 0x47 == get current dir int 0x21 ; standard DOS interrupt xor ax, ax ; zero out ax, return OK if no error jnc no_error ; if carry set, then an error mov ax, -1 ; return -1 if error no_error: pop ds ; get back ds pop si ; get back si mov WORD PTR rc, ax ; save return code } if (ceh.flag == ERROR) rc = ERROR; return( rc ); } /* * Name: set_current_directory * Purpose: set current directory * Date: February 13, 1992 * Passed: new_path: directory path which may include drive letter * Notes: use simple DOS interrupt */ int set_current_directory( char far *new_path ) { int rc; _asm { push ds ; save ds mov dx, WORD PTR new_path ; get OFFSET of new_path mov ax, WORD PTR new_path+2 ; get SEGMENT of new_path mov ds, ax ; put it in ds mov ah, 0x3b ; function 0x3b == set current dir int 0x21 ; standard DOS interrupt xor ax, ax ; zero out ax, return OK if no error jnc no_error ; if carry set, then an error mov ax, -1 ; return -1 if error no_error: pop ds ; get back ds mov WORD PTR rc, ax ; save return code } if (ceh.flag == ERROR) rc = ERROR; return( rc ); } /* * Name: hlight_line * Date: July 21, 1991 * Passed: x: column to begin hi lite * y: line to begin hi lite * lgth: number of characters to hi lite * attr: attribute color * Notes: The attribute byte is the hi byte. */ void hlight_line( int x, int y, int lgth, int attr ) { int off; void far *screen_ptr; screen_ptr = (void far *)g_display.display_address; off = y * 160 + 2 * x + 1; /* add one - so it points to attribute byte */ _asm { push di ; save di mov cx, lgth ; number of characters to change color mov di, WORD PTR screen_ptr ; get destination - video memory add di, off ; add offset mov ax, WORD PTR screen_ptr+2 mov es, ax mov ax, attr ; attribute lite_len: stosb ; store a BYTE inc di ; skip over character to next attribute loop lite_len ; change next attribute pop di ; restore di } } /* * Name: cls * Purpose: clear screen * Date: June 5, 1991 * Notes: Call the video BIOS routine to clear the screen. */ void cls( void ) { int line; line = g_display.nlines+1; _asm { xor ch, ch ; starting row in ch = 0 xor cl, cl ; starting column in cl = 0 mov ax, WORD PTR line ; get ending row mov dh, al ; put it in dh mov dl, 79 ; ending column in dl = 79 mov bh, 7 ; attribute in bh = 7 (normal) mov al, 0 ; get number of lines mov ah, 6 ; get function number push bp ; some BIOS versions wipe out bp int 0x10 pop bp } } /* * Name: set_cursor_size * Purpose: To set cursor size according to insert mode. * Date: June 5, 1991 * Passed: csize: desired cursor size * Notes: use the global display structures to set the cursor size */ void set_cursor_size( int csize ) { _asm { mov ah, 1 ; function 1 - set cursor size mov cx, WORD PTR csize ; get cursor size ch:cl == top:bot int VIDEO_INT ; video interrupt = 10h } } /* * Name: findfirst * Purpose: find the first file matching a pattern using DOS interrupt * Date: January 6, 1992 * Passed: dta: disk transfer address * path: path to search for files * f_attr: attributes of files to search for * Notes: return codes for findfirst: * 0 no error * 2 file is invalid or does not exist * 3 path is invalid or does not exist * 18 no matching directory entry was found * -1 check the critical error flag for critical errors */ int findfirst( DTA far *dta, char far *path, int f_attr ) { void far *old_dta; void far *new_dta; int rc; new_dta = (void far *)dta; _asm { ; save the old dta mov ah, 0x2f ; DOS get dta int 0x21 ; DOS interrupt mov WORD PTR old_dta, bx ; save OFFSET of old DTA mov ax, es mov WORD PTR old_dta+2, ax ; save SEGMENT of old DTA ; set the new dta push ds ; save ds mov dx, WORD PTR new_dta ; get OFFSET of new dta mov ax, WORD PTR new_dta+2 ; get SEGMENT of new dta mov ds, ax ; put it in ds mov ah, 0x1a ; DOS set dta int 0x21 ; DOS interrupt pop ds ; get back ds ; find first matching file push ds ; save ds mov cx, WORD PTR f_attr ; file attributes to search for mov dx, WORD PTR path ; get OFFSET of path mov ax, WORD PTR path+2 ; get SEGMENT of path mov ds, ax ; put it in ds mov ah, 0x4e ; DOS find first file int 0x21 ; DOS interrupt pop ds ; get back ds ; save the return code jc an_error ; carry is set if an error occured xor ax, ax ; zero out ax, return OK if no error an_error: mov WORD PTR rc, ax ; save the return code ; get back old dta push ds ; save ds mov dx, WORD PTR old_dta ; get OFFSET of old dta mov ax, WORD PTR old_dta+2 ; get SEGMENT of old dta mov ds, ax ; put it in ds mov ah, 0x1a ; DOS set dta int 0x21 ; DOS interrupt pop ds ; get back ds } if (ceh.flag == ERROR) rc = ERROR; return( rc ); } /* * Name: findnext * Purpose: find the next file matching a pattern using DOS interrupt * Date: January 6, 1992 * Passed: dta: disk transfer address * Notes: findfirst() MUST be called before calling this function. * return codes for findnext: * 0 no error * 2 path is invalid or does not exist * 18 no matching directory entry was found * -1 check the critical error flag for critical errors */ int findnext( DTA far *dta ) { void far *old_dta; void far *new_dta; int rc; new_dta = (void far *)dta; _asm { ; save the old dta mov ah, 0x2f ; DOS get dta int 0x21 ; DOS interrupt mov WORD PTR old_dta, bx ; save OFFSET of old DTA mov ax, es mov WORD PTR old_dta+2, ax ; save SEGMENT of old DTA ; set the new dta push ds ; save ds mov dx, WORD PTR new_dta ; get OFFSET of new dta mov ax, WORD PTR new_dta+2 ; get SEGMENT of new dta mov ds, ax ; put it in ds mov ah, 0x1a ; DOS set dta int 0x21 ; DOS interrupt pop ds ; get back ds ; find next matching file mov ah, 0x4f ; DOS find first file int 0x21 ; DOS interrupt ; save the return code jc an_error ; carry is set if an error occured xor ax, ax ; zero out ax, return OK if no error an_error: mov WORD PTR rc, ax ; save the return code ; get back old dta push ds ; save ds mov dx, WORD PTR old_dta ; get OFFSET of old dta mov ax, WORD PTR old_dta+2 ; get SEGMENT of old dta mov ds, ax ; put it in ds mov ah, 0x1a ; DOS set dta int 0x21 ; DOS interrupt pop ds ; get back ds } if (ceh.flag == ERROR) rc = ERROR; return( rc ); }