Category : C++ Source Code
Archive   : VCCRT2.ZIP
Filename : FREAD.ASM

 
Output of file : FREAD.ASM contained in archive : VCCRT2.ZIP
page ,132
title fread - read from a stream
;***
;fread.asm - read from a stream
;
; Copyright (c) 1988-1992, Microsoft Corporation. All rights reserved.
;
;Purpose:
; Read from the specified stream into the user's buffer.
;
;*******************************************************************************

.xlist
include version.inc
include cmacros.inc
include stdio.inc
.list

if sizeD
extrn __AHINCR:ABS
endif ;sizeD

externP _filbuf ; fill stream buffer
externP memcpy ; buffer copy
externP _read ; LOWIO's read function


sBegin data
assumes ds,data

externW _iob
externW _iob2

sEnd data

sBegin code
assumes cs,code
assumes ds,data

;***
;size_t fread ( void *buffer, size_t size, size_t count, FILE *stream ) -
; read from specified stream into the specified buffer.
;
;Purpose:
; Read 'count' items of size 'size' from the specified stream into
; the specified buffer. Return when 'count' items have been read in
; or no more items can be read from the stream.
;
;Entry:
; buffer - pointer to user's buffer
; size - size of the item to read in
; count - number of items to read
; stream - stream to read from
;
;Exit:
; Returns the number of (whole) items that were read into the buffer.
; This may be less than 'count' if an error or eof occurred. In this
; case, ferror() or feof() should be used to distinguish between the
; two conditions.
;
;Notes:
; fread will attempt to buffer the stream (side effect of the _filbuf
; call) if necessary.
;
; No more than 0xFFFE bytes may be read in at a time by a call to
; read(). Further, read() does not handle huge buffers. Therefore,
; in large data models, the read request is broken down into chunks
; that do not violate these considerations. Each of these chunks is
; processed much like an fread() call in a small data model (by a
; call to _nfread()).
;
; This code depends on _iob[] and _iob2[] both being near arrays
; and having the same element size.
;
; MTHREAD/DLL - Handled in three layers. fread() handles the locking
; and DS saving/loading/restoring (if required) and calls _fread_lk()
; to do the work. _fread_lk() is the same as the single-thread,
; large data model version of fread(). It breaks up the read request
; into digestible chunks and calls nfread() to do the actual work.
;
;*******************************************************************************

if sizeD


;**
; Single thread version.

cProc fread,,


parmDP buffer
parmW itemsize
parmW count
parmDP stream

localD ltotal

cBegin

;**
; Set:
; ltotal = dx:ax = number of chars to be read

mov ax,[itemsize]
mul [count]
mov cx,ax
or cx,dx
jz farfinish
mov word ptr [ltotal],ax
mov word ptr [ltotal + 2],dx

;**
; Set:
; es:bx = pointer to user's buffer
; si = pointer to _iob entry

les bx,[buffer]
mov si,word ptr [stream] ; _iob[] is a near array!

loopstart:

;**
; If we're reading 64 Kb or more, or the target portion of the user's buffer
; crosses a segment boundary, then we must do a partial read.

or dx,dx
jnz partialrd
cmp ax,0FFFFh
je partialrd
mov cx,bx
add cx,ax
jcxz fullrd ; to the very last byte of the segment
; but not beyond!
jc partialrd

;**
; We have less that 0xFFFF bytes to read and it all fits into one segment
; in the user's buffer. One (more) call to _nfread and we're done!

fullrd:
push ax ; save regs
push bx
push dx
mov cx,ax ; cx = num of chars to read
call _nfread
mov cx,ax ; cx = return value
pop dx ; restore saved regs
pop bx
pop ax
sub ax,cx ; update num of chars to be read
sbb dx,0
jmp short setupret ; we're finished, go set up return

;**
; Do a partial read. Read enough to fill the current segment of the user's
; buffer, if no more than 0FFFEh bytes are required. Otherwise, read in
; 08000h bytes (32 Kb).

partialrd:
cmp bx,1 ; more than 0FFFEh to fill seg?
ja fillseg ; no, go read in 64 Kb - bx chars
mov cx,08000h ; ask for 32 Kb
jmp short donfread

fillseg:
mov cx,bx
neg cx ; ask for 64 Kb - bx

donfread:
push cx ; save amount being requested
push ax ; save regs
push bx
push dx
call _nfread
mov cx,ax ; cx = return value
pop dx ; restore saved regs
pop bx
pop ax
pop di ; di = request amount given to _nfread
sub ax,cx ; update number of chars to be read
sbb dx,0
cmp cx,di ; did we get less than we requested?
jb setupret ; yep, we're kaput

; Update es:bx. Note that it is a huge pointer.

add bx,cx ; update es:bx
jnc looptest
mov cx,es
add cx,__AHINCR
mov es,cx
jmp short looptest

;**
; Do a short jump to finish. This has nothing whatsoever to do with the
; surrounding loop and does not lie in its control flow. It is just a
; bridge for the conditional jump near the beginning of this function.

farfinish:
jmp short finish

;**
; Test whether or not there are more characters to be read. If so, jump
; to the beginning of the loop.

looptest:
mov cx,ax
or cx,dx

jnz loopstart
jmp short setupret

;** We're finished, set up the return value and return.

setupret:
mov cx,ax
or cx,dx ; did we fulfill user's request?
jz easyret ; yes, go set ax = count
mov cx,word ptr [ltotal]
sub cx,ax
mov ax,cx
mov cx,word ptr [ltotal] + 2
sbb cx,dx
mov dx,cx ; dx:ax = number of chars read in
div [itemsize] ; ax = number of items read in
jmp short finish

easyret:
mov ax,[count]

finish:
cEnd

else ;not sizeD (near data)

cProc fread,,

parmDP buffer
parmW itemsize
parmW count
parmDP stream

localW total
localW bufsize

cBegin

; Set:
; total = cx = number of bytes to be read

mov ax,itemsize
mul count
mov cx,ax
jcxz fardone ; do we have anything to do?
mov total,ax

; Set:
; bx = pointer to user's buffer
; si = pointer to _iob entry

mov bx,buffer
mov si,stream

endif ;sizeD

if sizeD

;***
;_nfread - core routine that reads from a stream in large data models
;
;Purpose:
; This is the core routine that reads data from a stream in large
; data models. It is basically the same as the small data model
; version of fread, though some of the details (e.g., computing
; the number of bytes to read) are handled by the high level, large
; data model fread code.
;
;Entry:
; cx = number of bytes to read
; ds:si = stream pointer = pointer to _iob entry
; es:bx = pointer to user's buffer
;
;Exit:
; ax = number of bytes read
; ds:bx = pointer to the first byte after the data read into the user's
; buffer
;
;Uses:
; ax, cx, dx, di
;
;Preserves:
; si, ds, es
;
;*******************************************************************************

cProc _nfread,,<>

localW total
localW bufsize

cBegin

;**
; Set:
; total = cx = number of bytes to be read

mov total,cx

endif ;sizeD

;**
; Set:
; di = pointer to _iob2 entry

mov di,dataOFFSET _iob2
mov ax,si
sub ax,dataOFFSET _iob
add di,ax

;**
; Set bufsize to the proper value. Use _bufsiz field from _iob2 entry if
; there is a buffer. Otherwise, use BUFSIZ (i.e., assume that when a buffer
; is attached, its size will be BUFSIZ).

test [si]._flag,_IOMYBUF OR _IONBF
jnz havebuf
test [di]._flag2,_IOYOURBUF
jz nobuf

havebuf:
mov ax,[di]._bufsiz
jmp short setbufsize
nobuf:
mov ax,BUFSIZ
setbufsize:
mov bufsize,ax

loopbegin:

;**
; Check if the stream buffer exists and has characters in it. If so, copy them
; to the user's buffer

test [si]._flag,_IOMYBUF OR _IONBF
jnz testcnt
test [di]._flag2,_IOYOURBUF
jz doread
testcnt:
mov ax,[si]._cnt
or ax,ax
jz doread

cmp ax,cx
jbe copybuf
mov ax,cx

copybuf:
push ax ; save regs
push bx
push cx

if sizeD
push es ; save es
push ax ; push args
push word ptr [si]._ptr + 2
push word ptr [si]._ptr
push es
push bx
else
push ax ; push args
push word ptr [si]._ptr
push bx
endif

callcrt memcpy ; memcpy(bx, [si]._ptr, ax)

if sizeD
add sp,10 ; clean off args
pop es ; restore es
else ;not sizeD
add sp,6 ; clean off args
endif ;sizeD

pop cx ; restore saved regs
pop bx
pop ax

sub cx,ax ; update num of chars to be read
sub [si]._cnt,ax ; update num of chars in stream buffer
add bx,ax ; advance pointer to user buffer
add word ptr [si]._ptr,ax ; advance stream buffer pointer
jmp short loopcond

ife sizeD

;**
; Jump to done. This has nothing to do with the surrounding loop and does not
; lie in its control flow. It is just a bridge for the conditional jump near
; the beginning of this function.

fardone:
jmp short done

endif ;sizeD

;**
; Test cx to see if we have any more characters to read in. If so, jump to
; the start of the loop. Otherwise, go set up the return to the caller.

loopcond:
jcxz doret
jmp short loopbegin


;**
; Check if a call to lowio's read() is appropriate and, if so, do it

doread:
cmp cx,bufsize ; more than bufsize chars to be read?
jb dofilbuf ; no, go fill the stream buffer

; Compute (cx/bufsize)*bufsize

xor dx,dx ; zero out dx
mov ax,cx
div bufsize
mov ax,cx
sub ax,dx ; ax = (cx/bufsize)*bufsize

; Set up and issue call to lowio's read()

push bx ; save regs
push cx

if sizeD
push es ; save es
push ax ; push args
push es
push bx
xor ax,ax
mov al,[si]._file
push ax
else ;not sizeD
push ax ; push args
push bx
xor ax,ax
mov al,[si]._file
push ax
endif ;sizeD

callcrt _read ; read(fileno(stream), bx, ax)

if sizeD
add sp,8 ; clean off args
pop es ; restore es
else ;not sizeD
add sp,6 ; clean off args
endif ;sizeD

pop cx ; restore saved regs
pop bx

or ax,ax
jz readerr
cmp ax,0FFFFH
je readerr2
sub cx,ax ; update num of chars to be read
add bx,ax ; advance pointer to user buffer
jmp short loopcond

;**
; Stream buffer is empty (or stream is not yet buffered) and they're not
; enough characters to be read to make a direct read call legit. Therefore,
; call _filbuf to fill up the stream buffer.

dofilbuf:
push bx ; save regs
push cx

if sizeD
push es ; save es
push ds ; push arg
push si
else ;not sizeD
push si ; push arg
endif ;sizeD

callcrt _filbuf ; _filbuf(stream)

if sizeD
add sp,4 ; clean off arg
pop es ; restore es
else ;not sizeD
pop cx ; clean off arg
endif ;sizeD

pop cx ; restore saved args
pop bx

cmp ax,EOF ; did we hit eof or error?
je doret ; yep, we're kaput

if sizeD
mov byte ptr es:[bx],al ; store the char returned by _filbuf
else ;not sizeD
mov byte ptr [bx],al ; store the char returned by _filbuf
endif ;sizeD
inc bx ; advance pointer to user's buffer
dec cx ; update num of chars to be read
mov ax,[di]._bufsiz ; update bufsize
mov bufsize,ax
jmp short loopcond

;**
; An error occurred or eof was encountered during a read. Set the proper
; bit in stream->_flag and return.

readerr:
or [si]._flag,_IOEOF
jmp short doret

readerr2:
or [si]._flag,_IOERR

;**
; We're finished, compute the return value. Note that if we have completely
; fulfilled the user's request, we need only return count.

doret:

ife sizeD ; small data model
jcxz quickret
endif ;sizeD

mov ax,total
sub ax,cx

ife sizeD ; small data model
xor dx,dx ; zero out dx
div itemsize
jmp short done

quickret:
mov ax,count
endif ;sizeD

done:
cEnd

sEnd

end


  3 Responses to “Category : C++ Source Code
Archive   : VCCRT2.ZIP
Filename : FREAD.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/