   	NAME	MSNUT1
; File MSNUT1.ASM
; Provides various Intel 8088 level operations, including
; display facilities (via msg buffer or DOS) for char, strings, etc.
;
; Copyright 1991, University of Waterloo.
;  Copyright (C) 1985, 1993, Trustees of Columbia University in the 
;  City of New York.  Permission is granted to any individual or institution
;  to use this software as long as it is not sold for profit.  This copyright
;  notice must be retained.  This software may not be included in commercial
;  products without written permission of Columbia University.
; 
; Written by Erick Engelke of the University of Waterloo, Waterloo, 
;  Ontario, Canada,
;  and by Joe R. Doupnik, Utah State University, 
;  jrd@cc.usu.edu, jrd@usu.Bitnet.
;
; Edit history
; 27 August 1992 version 3.13
; 6 Sept 1991 v3.11
; Last Edit
; 13 May 1993

cr	equ	0dh
lf	equ	0ah
conout	equ	2
dos	equ	21h
allocmem equ	48h
freemem	equ	49h

MSGBUFLEN equ	512			; coordinate with msntnd.c
BIOSCLK	equ	046ch
HI_MAX	equ	018h
LO_MAX	equ     0b0h

_TEXT	SEGMENT  WORD PUBLIC 'CODE'
_TEXT	ENDS
_DATA	SEGMENT  WORD PUBLIC 'DATA'
_DATA	ENDS
CONST	SEGMENT  WORD PUBLIC 'CONST'
CONST	ENDS
_BSS	SEGMENT  WORD PUBLIC 'BSS'
_BSS	ENDS
DGROUP	GROUP	CONST, _BSS, _DATA
	ASSUME  CS: _TEXT, DS: DGROUP, SS: DGROUP

_DATA	SEGMENT
	extrn	_doslevel:word, _msgcnt:word, _msgbuf:byte,_display_mode:byte
_DATA	ENDS

_TEXT	segment

        ; Compute 1's-complement sum of data buffer
        ; unsigned checksum( unsigned word *buf, unsigned cnt)

	public	_checksum
_checksum proc	near
	push	bp
	mov	bp,sp
	push	si
	push	bx
	push	cx
	push	dx
        mov     si,[bp+4+0]		; offset of data to be checked
	mov     cx,[bp+4+2]		; cx = cnt
	mov	bl,cl
	shr     cx,1			; group into words
	xor	dx,dx			; set checksum to 0
        cld
	jcxz	chk3
        clc
chk1:	lodsw
	adc	dx,ax
	loop	chk1
					; resolve remainder
chk2:	adc     dx,0
        jc	chk2
chk3:	and	bl,1
	jz	chk5
        xor     ah,ah
	lodsb
        clc
        add     dx,ax
chk4:	adc     dx,0
	jc	chk4
chk5:	mov	ax,dx			; result into ax
        or      ax,ax
	jnz	chk6
        mov     ax,0ffffh
chk6:	pop	dx
	pop	cx
	pop	bx
	pop	si
	pop	bp
	ret
_checksum endp

;  ntohl(longword x)	32 bit network (big endian) to host (little endian)
;  htonl(longword x)	32 bit host (little endian) to network (big endian)
;  intel(longword x)
;  Reverse order of 4 byte groups between big and little endian forms
;
	public	_intel, _ntohl, _htonl

_intel	proc	near
_ntohl	equ	this byte
_htonl	equ	this byte
	push	bp
	mov	bp,sp
	mov	ax,[bp+4+2]
	mov	dx,[bp+4+0]
	xchg	al,ah
	xchg	dl,dh
	pop	bp
	ret
_intel	endp

;  ntohs(word x)	16 bit network (big endian) to host (little endian)
;  htons(word x)	16 bit host (little endian) to network (big endian)
;  intel16(word x)
;  Reverse order of 2 byte groups between big and little endian forms
	public	_intel16, _ntohs, _htons
_intel16 proc	near
_ntohs	equ	this byte
_htons	equ	this byte
	push	bp
	mov	bp,sp
	mov	ax,[bp+4+0]
	xchg	al,ah
	pop	bp
	ret
_intel16 endp


; int ourmod(int top, int bottom)
; Perform modulo function on 16 bit quantities
	public	_ourmod
_ourmod	proc	near
	push	bp
	mov	bp,sp
	push	bx
	mov	ax,[bp+4+0]		; top number
	mov	bx,[bp+4+2]		; bottom number (radix)
	xor	dx,dx
	or	bx,bx			; bottom is zero?
	jz	ourmod1			; z = yes, return zero
	div	bx
ourmod1:mov	ax,dx			; return remainder
	pop	bx
	pop	bp
	ret
_ourmod	endp

; int ourdiv(int top, int bottom)
; Perform 16 bit integer division
	public	_ourdiv
_ourdiv	proc	near
	push	bp
	mov	bp,sp
	push	bx
	mov	ax,[bp+4+0]		; top
	mov	bx,[bp+4+2]		; bottom
	xor	dx,dx
	or	bx,bx			; divide by zero?
	jz	outdiv1			; z = yes, divide by one
	div	bx
outdiv1:pop	bx
	pop	bp			; quotient is returned in ax
	ret
_ourdiv	endp

; int ourlmod(long top, int bottom)
; Perform 32 bit integer modulo function
	public	_ourlmod
_ourlmod proc	near
	push	bp
	mov	bp,sp
	push	bx
	mov	ax,[bp+4+0]		; top lower 16 bits
	mov	dx,[bp+4+2]		; top upper 16 bits
	mov	bx,[bp+4+4]		; bottom
	or	bx,bx			; zero?
	jz	outlmo1			; z = yes divide by 2^16 and quit
	div	bx
outlmo1:mov	ax,dx			; return remainder in ax
	pop	bx
	pop	bp
	ret
_ourlmod endp

; int ourldiv(long top, int bottom)
; Perform 32 bit integer division of 32 bit quotient by 16 bit divisor
	public	_ourldiv
_ourldiv proc	near
	push	bp
	mov	bp,sp
	push	bx
	mov	ax,[bp+4+0]		; top lower 16 bits
	mov	dx,[bp+4+2]		; top upper 16 bits
	mov	bx,[bp+4+4]		; bottom
	cmp	dx,bx			; about to overflow?
	jae	ourldiv1		; ae = yes, return 0xffffh
	div	bx
	xor	dx,dx			; clear remainder (high order ret)
	pop	bx
	pop	bp			; quotient is returned in dx:ax
	ret
ourldiv1:mov	ax,0ffffh		; overflow indication
	xor	dx,dx			; clear high order
	pop	bx
	pop	bp
	ret
_ourldiv endp

; long ourlmul(long top, int bottom)
; Perform 32 bit integer multiplication of 32 bit multiplicand by 16 bit mult
	public	_ourlmul
_ourlmul proc	near
	push	bp
	mov	bp,sp
	push	bx
	push	cx
	mov	ax,[bp+4+2]		; top upper 16 bits
	mov	bx,[bp+4+4]		; bottom
	mul	bx
	mov	cx,ax			; save product (no overflow noted)
	mov	ax,[bp+4+0]		; top lower 16 bits
	mul	bx
	adc	dx,cx			; new upper 16 bits
	pop	cx
	pop	bx
	mov	sp,bp
	pop	bp			; long product is returned in dx:ax
	ret
_ourlmul endp

; void * bcopy(src, dest, count)
; void *dest, *src;
; size_t count;
; copy count bytes from src to dest
	public	_bcopy
_bcopy proc	near
	push	bp
	mov	bp,sp
	push	es
	push	si
	push	di
	push	cx
	mov	ax,ds			; set to same data segment
	mov	es,ax
	mov	si,[bp+4+0]		; offset of source
	mov	di,[bp+4+2]		; offset of destination
	mov	cx,[bp+4+4]		; count
	cld
	push	di			; push dest address for return
	jcxz	bcopy2			; z = nothing to copy
	or	di,di			; is destination NULL?
	jz	bcopy2			; z = yes, don't do a thing
	cmp	si,di			; is source after destination?
	ja	bcopy1			; a = yes, no overlap problem
	je	bcopy2			; e = same place, do nothing
	add	di,cx			; start at the ends
	dec	di
	add	si,cx
	dec	si
	std				; work backward
bcopy1:	rep	movsb
bcopy2:	pop	ax			; recover return destination
	cld
	pop	cx
	pop	di
	pop	si
	pop	es
	mov	sp,bp
	pop	bp
	ret
_bcopy endp

; void * bcopy(src, dest, count)
; void * FAR dest, * FAR src;
; size_t count;
; copy count bytes from src to dest
	public	_bcopyff
_bcopyff proc	near
	push	bp
	mov	bp,sp
	push	es
	push	ds
	push	si
	push	di
	push	cx
	push	dx
	lds	si,dword ptr [bp+4+0]	; source
	les	di,dword ptr [bp+4+4]	; destination
	mov	cx,[bp+4+8]		; count
	cld
	jcxz	bcopyff2		; z = nothing to copy
	mov	ax,ds
	mov	dx,es
	or	ax,ax			; is destination NULL?
	jz	bcopyff2		; z = yes, don't do a thing
	cmp	ax,dx			; is source seg after destination?
	ja	bcopyff1		; a = yes, no overlap problem
	jb	bcopyff3		; b = no, no overlap the other way
	cmp	si,di			; is source offset after destination?
	ja	bcopyff1		; a = yes, no overlap problem
	je	bcopyff2		; e = same place, do nothing
bcopyff3:add	di,cx			; start at the ends
	dec	di
	add	si,cx
	dec	si
	std				; work backward
bcopyff1:rep	movsb
bcopyff2:xor	ax,ax			; say null destination
	cld
	pop	dx
	pop	cx
	pop	di
	pop	si
	pop	ds
	pop	es
	mov	sp,bp
	pop	bp
	ret
_bcopyff endp


; void * memset(dest, c, count)
; void *dest;
; char c;
; size_t count;
; Store count copies of byte c in destination area dest
	public	_memset
_memset	proc	near
	push	bp
	mov	bp,sp
	push	es
	push	di
	push	cx
	mov	ax,ds			; setup data segment
	mov	es,ax
	mov	di,[bp+4+0]		; offset of destination
	or	di,di			; is it NULL?
	jz	memset1			; z = yes, don't do a thing
	push	di			; save dest for return
	mov	al,[bp+4+2]		; byte of character c
	mov	ah,al
	mov	cx,[bp+4+4]		; count
	jcxz	memset1			; z = do nothing
	cld
	shr	cx,1
	jnc	memset2
	stosb
memset2:rep	stosw
	pop	ax			; return pointer to destination
memset1:pop	cx
	pop	di
	pop	es
	mov	sp,bp
	pop	bp
	ret
_memset	endp

; Allocate size bytes from DOS free memory.
; void FAR * malloc(size_t size)
; Returns FAR pointer in dx:ax, or 0L if failure. Size is an unsigned int.
	public	_malloc
_malloc	proc	near
	push	bp
	mov	bp,sp
	push	bx
	push	cx
	mov	bx,[bp+4+0]		; bytes wanted
	add	bx,15			; round up
	mov	cl,4
	shr	bx,cl			; convert to # of paragraphs
	mov	cx,bx			; remember quantity wanted
	mov	ah,allocmem		; DOS memory allocator
	int	dos			; returns segment in ax
	jc	malloc1			; c = fatal error
	cmp	cx,bx			; paragraphs wanted vs delivered
	je	malloc2			; e = got the block
	push	es			; insufficient, return it
	mov	es,ax			; identify the block
	mov	ah,freemem		; free the unwanted block
	int	dos
	pop	es
malloc1:xor	ax,ax			; return 0L on failure
malloc2:mov	dx,ax			; segment
	xor	ax,ax			; offset
	pop	cx
	pop	bx
	pop	bp
	ret
_malloc	endp

; Free a block of memory allocated from the DOS memory pool.
; void free(FAR * memblock);
	public	_free
_free	proc	near
	push	bp
	mov	bp,sp
	push	ax
	push	es
	mov	ax,[bp+4+2]		; get high order (segment) arg
	or	ax,ax			; NULL?
	jz	free1			; z = yes, leave it alone
	mov	es,ax			; identify the block
	mov	ah,freemem		; free the unwanted block
	int	dos
free1:	pop	es
	pop	ax
	pop	bp
	ret
_free	endp

; int fstchr(const char FAR * p, word len, byte c)
; finds first occurence of unsigned byte in low part of c and returns
; the number of bytes preceeding it in the buffer, or len if not found.
	public	_fstchr
_fstchr proc	near
	push	bp
	mov	bp,sp
	push	es
	push	bx
	push	cx
	push	di
	les	di,dword ptr [bp+4+0]	; Far string address
	mov	bx,es			; check for NULL
	mov	ax,-1			; error return value
	or	bx,bx			; NULL?
	jz	fstchr1			; z = yes, return -1
	mov	bx,di			; remember starting offset
	mov	cx,[bp+4+4]		; number of bytes to examine
	jcxz	fstchr1			; z = empty string, return -1
	mov	ax,[bp+4+6]		; pattern is in al
	cld
	repne	scasb
	mov	ax,di			; ending place
	dec	ax			; minus auto inc of rep
	sub	ax,bx			; minus starting offset
fstchr1:pop	di
	pop	cx
	pop	bx
	pop	es
	pop	bp
	ret
_fstchr endp

;  Timer routines - use set_*timeout to receive a clock value which
;                   can later be tested by calling chk_timeout with
;                   that clock value to determine if a timeout has
;                   occurred.  chk_timeout returns 0 if NOT timed out.
;  Usage:   long set_timeout( int seconds );
;           long set_ttimeout( int bios_ticks );
;           int chk_timeout( long value );
;
;  (c) 1990 University of Waterloo,
;           Faculty of Engineering,
;           Engineering Microcomputer Network Development Office
;  version
;    0.1    7-Nov -1990   E. P. Engelke

	public	_set_ttimeout
_set_ttimeout proc	near
	push	bp
	mov	bp,sp
	push	es
        xor     ax,ax
        cwd
        mov     es,ax
        mov     ax,[bp+4+0]		; initial Bios clock ticks
	pushf				; critical section
	cli
        add     ax,es:[BIOSCLK+0];
        adc     dx,es:[BIOSCLK+2];
	popf				; end critical section
	pop	es
	pop	bp
	ret
_set_ttimeout endp

	public	_set_timeout
_set_timeout proc near
	push	bp
	mov	bp,sp
	push	es
	push	cx
	xor	ax,ax			; reference low memory
	mov	es,ax
	mov	ax,[bp+4+0]		; seconds
	xor	dx,dx
	mov	cx,1165
	mul	cx			; 1165/64 = 18.203...
	mov	cx,6
tmp:	shr	dx,1
	rcr	ax,1
	loop	tmp
	pushf				; critical section
	cli
	add	ax,es:[BIOSCLK+0]
	adc	dx,es:[BIOSCLK+2]
	popf				; end critical section
	pop	cx
	pop	es
	pop	bp
	ret
_set_timeout	endp

	public	_chk_timeout
_chk_timeout	proc near
	push	bp
	mov	bp,sp
	push	cx
	push	es
	xor	ax,ax
	mov	es,ax
	pushf				; critical section
	cli
	mov	cx,es:[BIOSCLK+0]
	mov	bx,es:[BIOSCLK+2]
	popf				; end critical section
	pop	es
	mov	ax,[bp+4+0]		; timeout value
	mov	dx,[bp+4+2]
	cmp	dx,bx			; if timeout < clock, has expired
        jb      ret_tru
	cmp	ax,cx
        jbe	ret_tru
					; may have gone over by one day
	sub	ax,LO_MAX
	sbb	dx,HI_MAX
	jc	ret_fal			; c = nope, timeout is today
					; test timeout new values
	cmp	dx,bx
	jb	ret_tru
	cmp	ax,cx
	ja	ret_fal
ret_tru:mov	ax,1			; say have timed out
	pop	cx
	pop	bp
	ret

ret_fal:xor	ax,ax			; say have not timed out
	pop	cx
	pop	bp
	ret
_chk_timeout	endp

; unsigned long realclock(void)
	public	_realclock
_realclock	proc near		; get Bios time of day dword
	push	es
	xor	ax,ax			; reference low memory
	mov	es,ax
	pushf				; critical section
	cli
	mov	ax,es:[BIOSCLK+0]	; return Bios time of day tick count
	mov	dx,es:[BIOSCLK+2]
	popf				; end critical section
	pop	es
	ret
_realclock endp

; void _chkstk()
; Stack checker
; {
;  ;
; }
	public	__chkstk, __aNchkstk
__chkstk proc	near			; MSC v5.1
__aNchkstk equ	this byte		; MSC v6.00
	pop	bx			; pop return address
	sub	sp,ax			; down adjust stack pointer
	jmp	bx			; return the no-stack way
__chkstk endp

; Microsoft C v7.0 direct support. Shift left dx:ax by cx. No C calling conv.
; Long shift left
	public	__aNlshl
__aNlshl proc	near
	jcxz	lshift2			; z = no shift
lshift1:shl	ax,1
	rcl	dx,1
	loop	lshift1
lshift2:ret
__aNlshl endp

; Microsoft C v7.0 direct support. Shift rgt dx:ax by cx. No C calling conv.
; Unsigned long shift right
	public	__aNulshr
__aNulshr proc	near
	jcxz	rshift2			; z = no shift
rshift1:shr	dx,1
	rcr	ax,1
	loop	rshift1
rshift2:ret
__aNulshr endp

; void 
; outch(char ch)
; Sends character to the screen via the msgbuf buffer if operating at
; interrupt level, or via DOS if operating at task level.
	public	_outch
_outch	proc	near
	push	bp
	mov	bp,sp
	push	ax
	push	bx
	cmp	_display_mode,0		; quiet screen?
	je	outch3			; e = yes
	mov	al,[bp+4]		; get the character
	cmp	_doslevel,0		; at DOS task level?
	je	outch1			; e = no
	mov	ah,conout		; use DOS
	push	dx
	mov	dl,al
	int	dos
	pop	dx
	jmp	short outch3
outch1:	cmp	_msgcnt,MSGBUFLEN	; is buffer filled?
	ja	outch3			; a = yes, discard this byte
	mov	bx,_msgcnt
	mov	_msgbuf[bx],al
	inc	_msgcnt
outch3:	pop	bx
	pop	ax
	mov	sp,bp
	pop	bp
	ret
_outch	endp


; void 
; outsn(char * string, int count)
; display counted string
	public	_outsn
_outsn	proc	near
	push	bp
	mov	bp,sp
	push	ax
	push	si
	mov	si,[bp+4]		; string address
	mov	cx,[bp+4+2]		; string length
	cld
outsn1:	lodsb
	or	al,al
        jz      outsn2
	push	ax			; push arg
        call    _outch
	pop	ax			; clean stack
	loop	outsn1
outsn2:	pop	si
	pop	ax
	mov	sp,bp
	pop	bp
	ret
_outsn	endp

; void 
; outs(char * string)
; display asciiz string
	public	_outs
_outs	proc	near
	push	bp
	mov	bp,sp
	push	ax
	push	si
	mov	si,[bp+4]		; asciiz string address
	cld
outs1:	lodsb
        or      al,al			; terminator ?
	jz 	outs2			; z = yes
	push	ax			; push arg
	call    _outch
	pop	ax			; clean stack
	jmp	short outs1
outs2:	pop	si
	pop	ax
	mov	sp,bp
	pop	bp
	ret
_outs	endp

; void
; outhex(char c)
; display char in hex
	public _outhex
_outhex	proc	near
	push	bp
	mov	bp,sp
	push	ax
        mov     ax,[bp+4]		; incoming character
        mov     cl,4
        shr     al,cl
        call    outh
        mov     ax,[bp+4]
        call    outh
	pop	ax
	mov	sp,bp
	pop	bp
	ret

; worker for outhex
outh	proc	near
	and     al,0fh
        cmp     al,9
        jbe     outh1
        add     al,'A' - '9' - 1
outh1:	add     al,'0'
        push    ax
        call    _outch
        pop     ax
        ret
outh	endp
_outhex	endp

; output a string of hex chars
; void
; outhexes(char * string, int count )
	public	_outhexes
_outhexes proc	near
	push	bp
	mov	bp,sp
	push	ax
	push	si			; preserve such things
	mov	si,[bp+4]		; get string pointer
	mov	cx,[bp+4+2]		; get char count
	jcxz	outhexs2		; z = nothing
	cld
outhexs1:lodsb				; read a byte
	push	cx			; save loop counter
	push	ax
	call	_outhex			; display as hex pair
	add	sp,2			; clean stack
	pop	cx
	loop	outhexs1		; do count's worth
outhexs2:pop	si
	pop	ax
	mov	sp,bp
	pop	bp
	ret
_outhexes endp
_TEXT	ends
        end
