Metropoli BBS
VIEWER: sieve386.asm MODE: TEXT (ASCII)
	title	'Eratosthenes Sieve for 80386'
	name	sieve
	page	50,132

;
; Eratosthenes Sieve for 80386 32-bit protected mode
; Implemented by Ray Duncan, April 1987
; After Gilbreath, Byte September 1981, and January 1983
;
; Here is the MAKE file for this program:
;
; sieve386.obj : sieve386.asm
;   386asm sieve386
; 
; sieve386.exe : sieve386.obj
;   386link sieve386 start386 -exe sieve386 -map sieve386
;
; To run the program with PharLap DOS|EXTENDER:
;   C>RUN386 SIEVE386
;

niter	equ     100      		; number of iterations
asize   equ     8190    		; size of array "flags"

cr	equ	0dh			; ASCII carriage return
lf	equ	0ah			; ASCII line feed

stdin	equ 	0			; handle for standard input
stdout	equ	1			; handle for standard output


_TEXT	segment para public use32 'CODE'

	assume cs:_TEXT,ds:_DATA,es:_DATA

	public _start_			; magic name for RUN386 entry

_start_	proc    near

	xor	edx,edx			; convert number of iterations
	mov	eax,niter		; for output
	mov	ecx,10
	mov	esi,offset msg1a+3
	call	binasc

	mov	edx,offset msg1		; display message
	mov	ecx,msg1_len		; "Starting N iterations of sieve"
	call	pmsg

	call	getmsec			; get current time in msec
	push	eax			; and save it...

        mov     counter,niter		; initialize iterations counter

sieve1:					; a sieve iteration starts here...
	mov	edi,offset flags	; initialize flags array
	mov	ecx,asize		; to all bytes = TRUE
	mov	al,1
	cld
	rep stosb

 	xor	esi,esi			; ESI = index to flags array
	xor	edi,edi			; EDI = primes counter

sieve2:     				; main loop of sieve
        test	byte ptr flags[esi],1	; is this a prime?
	jnz	short sieve4		; jump if prime

sieve3:	inc     esi			; bump to next slot in "flags"
	cmp	esi,asize
	jle	sieve2			; loop until array exhausted

	dec	counter			; count off sieve iterations
	jnz	sieve1			; jump, another iteration needed.
	jmp	sieve7			; jump, all iterations finished.

sieve4:					; prime found, zap its multiples
	mov	ebx,esi			; copy i
 	mov     edx,ebx 		; EDX = prime = i + i + 3
        add	edx,edx
        add     edx,3
	xor	al,al
	jmp	short sieve6

sieve5:	mov	byte ptr flags[ebx],al	; zero this multiple

sieve6:	add     ebx,edx			; find next multiple of prime
        cmp     ebx,asize		; have we exhausted the array?
        jle	sieve5			; not yet, zap it
	inc     edi			; count primes and try next
	jmp	sieve3

sieve7:	call	getmsec			; all done, get current time
	push	eax

	mov	eax,edi			; convert number of primes 
	mov	edx,0			; found on last iteration
	mov	ecx,10
	mov	esi,offset msg2a+4
	call	binasc

	mov	edx,offset msg2		; display "Number of primes: "
	mov	ecx,msg2_len
	call	pmsg

	pop	eax			; calculate total elapsed msec.
	pop	ebx
	sub	eax,ebx

	mov	edx,0			; divide by number of iterations
	mov	ecx,niter		; to get msec per iteration
	idiv	ecx

	mov	edx,0			; convert msec to ASCII
	mov	ecx,10
	mov	esi,offset msg3a+4
	call	binasc

	mov	edx,offset msg3		; display "Elapsed time:"
	mov	ecx,msg3_len
	call	pmsg

 	mov     ax,04C00h		; final exit, return code = 0 
        int     21H

_start_	endp


getmsec	proc	near			; Return EAX = current time in msec.

	mov	ah,2ch			; read time	
	int	21h
	movzx	eax,ch			; EAX := hours
	imul	eax,60			; hours -> minutes
	and	ecx,0ffh		; isolate system minutes
	add	eax,ecx			; and find total minutes
	imul	eax,60			; minutes -> seconds
	movzx	ecx,dh			; isolate system seconds
	add	eax,ecx			; and find total seconds
	and	edx,0ffh		; isolate hundredths
	imul	eax,100			; seconds -> hundredths
	add	eax,edx			; find total hundredths
	imul	eax,10			; hundredths -> msec
	ret

getmsec	endp

;
; BINASC: Convert 64 bit binary value to ASCII string.
;
; Call with  EDX:EAX  = signed 64 bit value
;	     ECX      = radix
;            DS:ESI   = last byte of area to store resulting string
;	                (make sure enough room is available to store
;		        the string in the radix you have selected.)
;
; Destroys EAX, EBX, ECX, EDX, and ESI.
;
binasc 	proc	near			; convert EDX:EAX to ASCII.

	mov	byte ptr [esi],'0' 	; force storage of at least 1 digit.
	or	edx,edx			; test sign of 64 bit value,
	pushf				; and save sign on stack.
	jns	bin1			; jump if it was positive.
	not	edx			; negative, take 2's complement
	not	eax			; of the value. 
	add	eax,1
	adc	edx,0
bin1:					; divide 64 bit value by the radix 
					; to extract next digit for the
					; forming string.
	mov	ebx,eax			; is the value zero yet?
	or	ebx,edx
	jz	bin3			; yes, we are done converting.
	call	divide			; no, divide by radix.
	add	bl,'0'			; convert remainder to ASCII digit.
	cmp	bl,'9'			; might be converting hex ASCII,
	jle	bin2			; jump if in range 0-9,
	add	bl,'A'-'9'-1		; correct it if in range A-F.
bin2:	mov	[esi],bl		; store this character into string.
	dec	esi			; back up through string,
	jmp	bin1			; and do it again.
bin3:					; restore sign flag,
	popf				; was original value negative?
	jns	bin4			; no, jump
	mov	byte ptr [esi],'-'	; yes,store sign into output string.
bin4:	ret				; back to caller.

binasc	endp

;
; General purpose 64 bit by 32 bit unsigned divide.
; This must be used instead of the plain machine unsigned divide
; for cases where the quotient may overflow 32 bits.  If called with
; zero divisor, this routine returns the dividend unchanged and gives 
; no warning.
;
; Call with EDX:EAX = 64 bit dividend
;           ECX     = divisor
;
; Returns   EDX:EAX = quotient
;           EBX     = remainder
;	    ECX     = divisor (unchanged)
;
divide	proc	near			; Divide EDX:EAX by ECX

	jecxz	div1			; exit if divide by zero
	push	eax			; 0:dividend_upper/divisor
	mov	eax,edx
	xor	edx,edx
	div	ecx
	mov	ebx,eax			; EBX = quotient1
	pop	eax			; remainder1:dividend_lower/divisor
	div	ecx
	xchg	ebx,edx			; EDX:EAX = quotient1:quotient2

div1:	ret				; EBX = remainder2

divide	endp


pmsg	proc	near			; print a message on std output
					; call with DS:EDX = address 
					;           ECX    = length 
	mov	ah,40h
	mov	bx,stdout
	int	21h
	ret
pmsg	endp

_TEXT	ends


_DATA	segment para public use32 'DATA'


flags	db      asize+1 dup (?)
counter	dd      ?			; sieve iteration counter

msg1	db	cr,lf,'Starting '
msg1a	db	'     iterations of Sieve...',cr,lf
msg1_len equ $-msg1

msg2	db	cr,lf,'Primes found:  '
msg2a	db	'     ',cr,lf
msg2_len equ $-msg2

msg3	db	cr,lf,'Elapsed time:  '
msg3a	db	'       msec. per iteration',cr,lf
msg3_len equ $-msg3
	

_DATA	ends


_STACK	segment byte stack use32 'stack'
      	db      4096 dup (?)
_STACK	ends

	end

[ RETURN TO DIRECTORY ]