Category : Assembly Language Source Code
Archive   : EMAC15ES.ZIP
Filename : BUFFERS.ASM

 
Output of file : BUFFERS.ASM contained in archive : EMAC15ES.ZIP
;History:56,1
;10-10-88 22:10:05 add inverse_seg to adjust_list.
;06-03-88 23:47:57 improve buffer_check
;05-26-88 23:25:49 added assumes in init_all_buffers
;05-26-88 23:24:13 remove external reference to adjust_buffers
;04-19-88 22:23:33 restore ax after returning from a buffer_free.
;03-29-88 20:59:24 store ' c' or ' e' after expand or compress.
;03-13-88 23:01:42 after doing our work, store ' ' to debug.
;12-11-87 06:52:36 add buffer_check
;12-08-87 22:52:36 add calls to store_debug
;12-06-87 00:32:51 add support for four formSegs.
;12-06-87 00:18:16 let init_forms allocate as many buffers as it wants.
;11-16-87 23:17:35 make memsize private to buffers.asm
;11-10-87 23:02:55 more work to do....
;11-10-87 21:46:38 ensure that we can treat 'data' as if it were a 'bufseg'.
;11-09-87 23:12:03 start writing buffer_free.
.xlist
include memory.def

bufseg segment public

extrn prev_buffer: word ;= segment of prev buffer (0 if none)
extrn next_buffer: word ;= segment of next buffer (0 if none)

extrn toptop: word
extrn topbot: word
extrn bottop: word
extrn botbot: word
extrn new_size: word

memsize dw ?

extrn bufseg_size: word

bufseg ends


data segment byte public

extrn lomem: byte, lomem_end: byte

public textseg
textseg dw ? ;bufseg of current text buffer.
current_seg dw ? ;bufseg of buffer_free caller.
saveDS dw ?
saveES dw ?

last_para dw ? ;segment after highest buffer
memory_end dw ? ;segment after highest buffer right now.
num_buffers dw ?
amount_needed dw ? ;amount that we need to be free.
free_paras dw ? ;number of free paragraphs left.
insert_number dw ? ;number of buffer being inserted from.
insert_mark db ? ;mark in buffer being inserted from.

extrn w1_windseg: word, w2_windseg: word ;from redisp.asm
extrn inverse_seg: word ;from redisp.asm
extrn syntax_seg: word ;from mintform.asm
extrn formSeg0: word ;from mintform.asm
extrn formSeg1: word ;from mintform.asm
extrn formSeg2: word ;from mintform.asm
extrn formSeg3: word ;from mintform.asm

adjust_list label word
dw textseg ;have to adjust this one.
dw current_seg
dw w1_windseg ;from redisp.asm, window one's buffer.
dw w2_windseg ;from redisp.asm, window two's buffer.
dw inverse_seg
dw syntax_seg
dw formSeg0 ;from mintform.asm
dw formSeg1 ;from mintform.asm
dw formSeg2 ;from mintform.asm
dw formSeg3 ;from mintform.asm
dw saveDS
dw saveES
dw 0

data ends


code segment byte public
;all the routines in this segment are entered with ds=data, es=data
assume cs:code, ds:data, es:data, ss:data

extrn init_forms: near

movmem macro
local x
shr cx,1
jnc x
movsb
x: rep movsw
endm


comment /

buffers:

data
mint string
text
...

a buffer is initially set with bottop=botbot=end of buffer. Then the buffer
is initialized.
/

public init_all_buffers
init_all_buffers:
;enter with bx=> first paragraph of unavailable memory.
;exit with cy if no buffer available.
mov memory_end,bx ;remember where memory ends.
mov last_para,bx ;remember where memory ends.
assume ds:bufseg
mov prev_buffer,0 ;only one buffer so far.
mov next_buffer,0
mov ax,offset lomem
mov topbot,ax
mov bottop,ax
mov botbot,offset lomem_end
assume ds:data
call init_forms ;create as many buffers as init_forms needs.
jc init__all_buffers_1
mov cx,offset bufseg_size
call new_buffer ;create a new buffer.
jc init__all_buffers_1
assume ds:bufseg
call select_buffer ;make this the current buffer.
call init_vars$ ;init most everything
call init_marks ;init the rest.
clc
push es
pop ds
assume ds:data
init__all_buffers_1:
ret


public percent_full
percent_full:
;return the percent full amount in ax.
;destroy ax,cx,dx.
push ds
mov ds,textseg
assume ds:bufseg
mov ax,100
mov cx,memsize
jcxz percent_full_1
mov ax,botbot ;compute the size of the buffer
sub ax,bottop
add ax,topbot
sub ax,toptop
mov dx,0
div cx
cmp dx,0
je percent_full_1
inc ax
percent_full_1:
pop ds
assume ds:data
ret


public buffer_allocate
buffer_allocate:
;entry:
; case cx of
; -1..-32768: report the current buffer number.
; exit: ax=current buffer number.
; 0: create a new buffer.
; exit: ax=new buffer number if enough memory, ax=0 otherwise.
; 1..32767:
; entry: cx=buffer number to select, ax=0 for read/write buffer.
; exit: ax=buffer number if it exists, ax=0 otherwise.
jcxz buffer_allocate_2
or cx,cx ;if cx<0, return buffer number.
js buffer_allocate_4
call find_buffer
jc buffer_allocate_5 ;buffer not found.

push ds
mov ds,dx ;get the current buffer back.
call select_buffer
pop ds

jmp buffer_allocate_4

buffer_allocate_5:
mov ax,0 ;buffer not found.
jmp short buffer_allocate_1
buffer_allocate_2:
;create a new buffer.
mov cx,offset bufseg_size
call new_buffer
mov ax,0 ;failed to create buffer - report it.
jc buffer_allocate_1
assume ds:bufseg
call select_buffer ;make this the current buffer.
call init_vars$ ;init most everything
call init_marks ;init the rest.
push es
pop ds
assume ds:data
buffer_allocate_4:
;return the current buffer number.
mov bx,textseg
call buffer_number ;return number in ax.
buffer_allocate_1:
push es
pop ds
assume ds:data
ret


public buffer_insert
buffer_insert:
;enter with al=mark, cx=buffer number.
;insert the text between point and mark from the given buffer.
;exit with nc if ok, cy if the given buffer doesn't exist, or the specified
; text won't fit.
mov insert_number,cx
mov insert_mark,al

call find_buffer ;find their buffer.
jc buffer_insert_1 ;not found.

;get the size of the inserted text, and make sure we have that much free
;space.
push ds
mov ds,dx
assume ds:bufseg
mov al,insert_mark
call read_mark$ ;we're just interested in the count.
mov ax,textseg ;now free that many bytes.
call buffer_free
pop ds
assume ds:data
jc buffer_insert_1 ;go if it won't fit.

;now find the buffer again, get the mark, and insert it.
mov cx,insert_number
call find_buffer ;this should ALWAYS return nc.
mov al,insert_mark
mov ds,dx
assume ds:bufseg
call read_mark$
mov ax,ds
mov ds,textseg
call insert_string$ ;this should ALWAYS return nc.
jmp short buffer_insert_2
buffer_insert_1:
stc
buffer_insert_2:
push es ;restore ds.
pop ds
assume ds:data
ret


public compact_buffers
compact_buffers:
;exit with bx=first unused paragraph.
call compress
push ds
mov ds,textseg
assume ds:bufseg
; mov cx,1000h ;give the current buffer all that
; call adjust_new_size ; we can.
call expand
call get_last_buffer ;get ds = paragraph of last buffer.
call buffer_paragraphs ;get the size of it.
mov bx,ds
add bx,cx
pop ds
mov last_para,bx
ret


public uncompact_buffers
uncompact_buffers:
mov ax,memory_end
mov last_para,ax
ret


compress:
;move all the buffers as low in memory as they'll go.
;exit with bx=first unused paragraph, num_buffers set to the number of buffers.
mov ax,'*c'
call store_debug

push ds
assume ds:bufseg

mov num_buffers,0

mov ax,ds ;get set to compress data.
mov es,ax
mov di,topbot ;put the bottom at the end of the top.
mov si,bottop ;have to get the copy, just in case.
mov bottop,di ;save the new bottop.
mov cx,botbot
sub cx,si ;same as sub cx,bottop
movmem
mov botbot,di

compress_4:
inc num_buffers
call buffer_paragraphs
mov new_size,cx ;remember it for later.
cmp next_buffer,0 ;was this the last buffer in memory?
je compress_3 ;yes - we're done.

call move_buffer_lower
jmp compress_4
compress_3:
mov bx,ds ;get the para of the last buffer.

call buffer_paragraphs
add bx,cx ;compute the first free segment.

pop ds
assume ds:data

mov ax,last_para ;get the end of memory.
sub ax,bx ;compute the amount free.
mov free_paras,ax ;remember it.

mov ax,' c'
call store_debug

ret


public find_buffer
find_buffer:
;enter with cx=buffer number.
;exit with nc, dx set to that buffer if it exists, cy otherwise.
push ds
assume ds:bufseg
add cx,4 ;allow for the forms buffer(s).
find_buffer_1:
mov dx,next_buffer
cmp dx,0 ;at the end?
je find_buffer_2
mov ds,dx
loop find_buffer_1
mov dx,ds ;get the current buffer back.
pop ds
clc
ret
find_buffer_2:
pop ds
stc
ret
assume ds:data


public new_buffer
new_buffer:
;create a new buffer of size cx.
;exit with cy if there's not enough memory for a new buffer, or else return
; with ds = new buffer.
push cx

call compress
new_buffer_2:
assume ds:bufseg
mov dx,ds ;remember the current buffer.
mov ax,next_buffer ;keep going until we have the
mov ds,ax ; last buffer.
or ax,ax
jne new_buffer_2

mov ds,dx ;get the para of the last buffer.
call buffer_paragraphs ;compute the size of it.
add dx,cx ;find the end of it.

pop cx
mov ax,cx
add ax,0fh ;get the size rounded up.
shr ax,1
shr ax,1
shr ax,1
shr ax,1
add ax,dx
cmp ax,last_para ;is there enough memory for new buffer?
jae new_buffer_1 ;no.
mov next_buffer,dx ;link to the new buffer.
mov ax,ds
mov ds,dx
mov prev_buffer,ax ;link back to the old buffer.
mov next_buffer,0 ;and no link to a new buffer.
mov topbot,cx ;start with an empty buffer.
mov bottop,cx
mov botbot,cx
clc
ret
new_buffer_1:
push es
pop ds
assume ds:data
stc
ret


code ends


code segment byte public
;all the code in this segment is entered with ds=bufseg, es=data
assume cs:code, ds:bufseg, es:data

;the following externs are in 'memory'
extrn init_vars$: near
extrn insert_string$: near
extrn read_mark$: near


;the following externs are in 'marks'
extrn init_marks: near

;the following externs are in machine dependent.
extrn store_debug: near

public buffer_free, expand, adjust_all, adjust_new_size
public adjust_segments, select_buffer, move_buffer_higher
public move_buffer_lower, buffer_paragraphs


public buffer_check
buffer_check:
;exit with zr if all is ok, nz if we have a bad link.
push ds
mov ax,ss
mov ds,ax
mov cx,256
buffer_check_1:
cmp next_buffer,0 ;if we're done, exit.
je buffer_check_2
mov ax,ds ;remember this buffer.

if 0
mov bx,botbot
add bx,10h ;round up to next paragraph.
rcr bx,1 ;ensure that 65536 bytes becomes
shr bx,1 ; 1000h paragraphs.
shr bx,1
shr bx,1
add bx,ax ;compute the next possible location
cmp bx,next_buffer ; for the next buffer.
ja buffer_check_2 ;go if we somehow overlap.
endif

mov ds,next_buffer ;go to the next buffer.
cmp ax,prev_buffer ;does the next buffer point to us?
loope buffer_check_1 ;keep going if we're still ok.
buffer_check_2:
pop ds
ret



public buffer_number
buffer_number:
;enter with bx=paragraph of buffer.
;exit with ax=number of buffer.
push ds
mov ax,ss
mov ds,ax
xor ax,ax
buffer_number_1:
mov dx,ds
cmp dx,bx ;is this the one we're looking for.
je buffer_number_2 ;yes - we've got its number.
mov ds,next_buffer ;get the next buffer
inc ax
jmp buffer_number_1
buffer_number_2:
pop ds
sub ax,4 ;allow for the forms buffer(s).
ret


buffer_free:
;ensure that the buffer in ax has cx bytes free. If it doesn't, reallocate
; memory between the buffers. Return with cy if we cannot get enough memory.
; return with nc, ax = new location of buffer.
mov saveDS,ds
mov ds,ax
assume ds:bufseg, es:nothing
mov ax,bottop ;get the free space size.
sub ax,topbot
cmp cx,ax ;go if we have that much space.
ja buffer_free_1
mov ax,ds
mov ds,saveDS
clc
ret
buffer_free_1:

push bx
push cx
push dx
push si
push di
mov saveES,es

mov amount_needed,cx ;remember how much we need.

mov current_seg,ds
mov ax,ss
mov ds,ax
call compress ;move them down all the way.
mov ds,current_seg

;compact buffers also sets new_size to the new total size of the buffer,
; and sets num_buffers to the number of buffers, and sets free_paras to
; the number of free paragraphs.
mov cx,amount_needed ;compute the amount for this buffer.
add cx,0fh
rcr cx,1 ;ensure that 65536 bytes becomes
shr cx,1 ; 1000h paragraphs.
shr cx,1
shr cx,1
cmp cx,ax ;do we have that much?
ja buffer_free_2 ; no - it's hopeless.
mov ax,new_size ;get the current size of the buffer.
add ax,cx ; add in the size that we need.
cmp ax,1000h ;more than a buffer can possibly hold?
jae buffer_free_2 ; yes - it's hopeless

;now give our buffer the amount that it needs.
call adjust_new_size ;if we don't have enough memory,

mov ax,free_paras ;get the free paragraphs, and divide it
mov dx,0 ; evenly among all the buffers.
div num_buffers

mov cx,ax ;allocate the memory evenly.
call adjust_all ;adjust all the buffers.

cmp free_paras,0 ;is there any memory left?
je buffer_free_3 ;no - we're done allocating.

mov cx,65535 ;now allocate the rest of the memory.
call adjust_all
buffer_free_3:
call expand
clc
jmp short buffer_free_4
buffer_free_2:
stc
buffer_free_4:
mov es,saveES
pop di
pop si
pop dx
pop cx
pop bx
mov ax,ds
mov ds,saveDS
ret
assume es:data


expand:
;exit with ds=data.
mov ax,'*e'
call store_debug

call get_last_buffer
;now we have ds=paragraph of the last buffer, dx->new location for this buffer.
expand_2:
mov ax,dx ;get the destination paragraph.
call move_buffer_higher ;move the buffer up.
mov ax,prev_buffer ;get the previous buffer.
or ax,ax
je expand_1 ;go if we're done.
mov dx,ds ;remember where we are now.
mov ds,ax
sub dx,new_size ;find out where we'll be next.
jmp expand_2
expand_1:
mov ax,' e'
call store_debug

ret


get_last_buffer:
;exit with dx = paragraph of new last buffer,
; ds = paragraph of current last buffer.
mov dx,ss ;add up the new sizes of the buffers.
mov ds,dx
get_last_buffer_1:
cmp next_buffer,0 ;is this the last buffer?
je get_last_buffer_2 ;yes - we're done.
add dx,new_size ;no - add in the size of this buffer,
mov ds,next_buffer ;and go to the next buffer.
jmp get_last_buffer_1
get_last_buffer_2:
ret


adjust_all:
;enter with cx = amount of memory to allocate to each buffer.
;add this amount to the new size of each buffer.
push ds
mov ax,ss ;get the first buffer.
mov ds,ax
adjust_all_1:
call adjust_new_size
mov ax,next_buffer ;is this the last buffer?
cmp ax,0
mov ds,ax
jne adjust_all_1 ;no - go do another.
pop ds
ret


adjust_new_size:
;enter with ds = segment whose new_size we're adjusting.
; free_paras = the number of free paragraphs to allocate,
; cx = number of paragraphs to allocate to the buffer in ds.
;return with cy if we don't have enough memory to allocate that many.
mov ax,1000h ;get the max buffer size.
sub ax,new_size ;subtract off the new size.
cmp ax,cx ;more than we want?
jb adjust_new_size_1 ; no - we can only give it this much.
mov ax,cx ; yes - only give it as much as we want.
adjust_new_size_1:
cmp ax,free_paras ;more than we have?
jb adjust_new_size_2 ; no - we can give it this much.
mov ax,free_paras ; yes - only give it as much as we have.
adjust_new_size_2:
add new_size,ax
sub free_paras,ax ;say that we have that many fewer.
ret


adjust_segments:
;enter with bx = current bufseg, ax = new location of that bufseg.
push si
push di
mov si,offset adjust_list
adjust_segments_1:
mov di,data:[si] ;get the next pointer.
add si,2
or di,di ;exit on null.
je adjust_segments_2
cmp data:[di],bx ;is this the old one?
jne adjust_segments_1 ;no.
mov data:[di],ax ;yes - update with the new.
jmp adjust_segments_1
adjust_segments_2:
pop di
pop si
ret


select_buffer:
;enter with ds=buffer to select.
mov textseg,ds ;save the new current buffer.
mov ax,botbot
sub ax,toptop
mov dx,0
mov cx,100
div cx
mov memsize,ax
ret


move_buffer_higher:
;move a buffer higher in memory.
;enter with ax = new buffer location, new_size = new buffer size (in paras).
push es
mov bx,ds
call adjust_segments
mov es,ax
assume es:bufseg

std ;moving from low to high, go backwards.

mov si,botbot ;get the last location used in a buffer,
dec si

mov di,new_size ;compute the new last location in the
shl di,1 ; buffer.
shl di,1
shl di,1
shl di,1
dec di
dec di

mov cx,botbot ;compute the amount to move.
sub cx,bottop

mov botbot,di
inc botbot
rep movsb
mov bottop,di
inc bottop

mov si,topbot ;now move the bottom part of the buffer.
dec si
mov di,si
mov cx,topbot
rep movsb

cld ;now update the previous and next ptrs.
mov si,es:prev_buffer
mov di,es:next_buffer

or si,si ;is there a previous buffer?
je move_buffer_higher_1 ;no.
mov ds,si
mov next_buffer,ax ;tell it where we are now.
move_buffer_higher_1:

or di,di ;is there a next buffer?
je move_buffer_higher_2 ;no.
mov ds,di ;get the next buffer.
mov prev_buffer,ax ;tell it where we are now.
move_buffer_higher_2:

mov ds,ax ;and return ds = us.
pop es
assume es:data
ret


move_buffer_lower:
;move a buffer lower in memory.
;enter with ds=buffer before the one to be lowered.
;exit with ds=new location of lowered buffer.
call buffer_paragraphs

mov ax,ds ;get the base of this buffer.
add ax,cx ;get the end of this buffer.
mov bx,next_buffer
mov next_buffer,ax ;save the pointer to the next buffer.
mov cx,ds
mov ds,bx ;get paragraph of buffer to move.
mov prev_buffer,cx ;now remember where the previous buffer is.

call adjust_segments

push es
mov es,ax ;move the bottom down to the new buffer.
assume es:bufseg
mov si,0
mov di,si
mov cx,topbot
movmem

mov si,es:bottop ;have to get the copy, just in case.
mov es:bottop,di ;save the new bottop.
mov cx,es:botbot
sub cx,si ;same as sub cx,bottop
movmem
mov es:botbot,di

pop es
assume es:data
mov ds,ax ;get new para of just moved buffer.
ret


buffer_paragraphs:
;compute the number of paragraphs used by a buffer.
;enter with ds=buffer
;exit with cx=number of paragraphs.
mov cx,botbot
add cx,10h ;round up to next paragraph.
rcr cx,1 ;ensure that 65536 bytes becomes
shr cx,1 ; 1000h paragraphs.
shr cx,1
shr cx,1
ret


code ends

end


  3 Responses to “Category : Assembly Language Source Code
Archive   : EMAC15ES.ZIP
Filename : BUFFERS.ASM

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/