
; Find primes
;  by definition primes starting sequence is 1,2,3,5,....
;  code is example only and does not indicate the best or fastest logic.
;  uses a window form for parameter and control input.
;  uses a prime factor sieve to search for primes.
;  <Esc> or right mouse click interrupts run
;  file outputs can be hex, ascii, or factors, or none
;       hex output is binary output
;       ascii and factor output have CR/LF terminated lines.

format PE GUI 4.0
entry start

 include '%fasminc%\win32a.inc'

;===================================================

ID_OK		   = 101
ID_CANCEL	   = 102
ID_START	   = 103
ID_CURTEST	   = 104
ID_DEPTH	   = 105
ID_PRIMES	   = 106
ID_SPRIMEDESC	   = 151
ID_LPRIMEDESC	   = 152
ID_LSTOP	   = 153

ID_NUMFROM	   = 201
ID_NUMTO	   = 202

ID_NONE 	   = 301
ID_ASCII	   = 302
ID_HEX		   = 303
ID_FACTOR	   = 304


PSELSTART	  = 1
;PSELSTART         = 10          ; for debug
PSELEND 	  = 100000
;PSELEND           = 1000000
PSEARCHMAX	  = 65535
;PSEARCHMAX        = 128         ; for debug
PFILEMAX	  = 100000
;PFILEMAX          = 1000000

;================================================

section '.data' data readable writeable
  svhwnd	dd 0
  svhitem1	dd 0
  svhitem2	dd 0
  svdc		dd 0
  btnflag	dd 0
  testmess	rb 11
  testnum	dd 0
  testlvl	dd 0
  filebtn	dw 1
  pmax		dd 6600       ; max number of entries allowed in prime filter table
  plist 	rd 6600
  ptop		dd 0
  primes	dd 0
  pcheckOK	dd 0
  pstart	dd PSELSTART
  pend		dd PSELEND
  xnum		dd 0
  usefile	dd 0
  pfilename	rb 32
  byteswritten	dd 0
  buffptr	dd 0
  writebuff	rb 133

  virtual at writebuff
      ddbuff  dd ?
  end virtual

  decfile	db 'd%8.8lu.txt',0
  decfilef	db 'f%8.8lu.txt',0
  hexfile	db 'x%8.8lx.hex',0
  decformat	db '%lu',0
  decformatf	db '%lu = ',0
  hexformat	db '%8.8lx',0
  facformat1	db '%ld',0
  facformat2	db '%ld^%ld',0
  facformatt	db ' * ',0
  priformat	db 'Prime',0
  wrcount	dd 0
  wrflag	dd 0
  idnumflg	dd 0
  ws_border	dd 0
  textcolor	dd 0

  brushblack	dd 0
  brushwhite	dd 0
  brushred	dd 0
  brushgreen	dd 0
  brushblue	dd 0
  brushyellow	dd 0
  brushcyan	dd 0
  brushmagenta	dd 0
  dispfont1	dd 0
  dispfont2	dd 0

		db '-----end---------'

 ;===========================================

section '.code' code readable executable
	      ; setup basic brushes --- may or may not be used.
  start:invoke CreateSolidBrush,0x00ffffff	    ; bbggrr    white
	mov [brushwhite],eax
	invoke CreateSolidBrush,0x0000ffff	    ; bbggrr     yellow
	mov [brushyellow],eax

	invoke	GetModuleHandle,0
	mov	[svhwnd],eax
	invoke	DialogBoxParam,eax,37,HWND_DESKTOP,DialogProc,0
	or	eax,eax
	jz	exit

  exit: invoke DeleteObject,brushwhite		 ;   cleanup
	invoke DeleteObject,brushyellow
	invoke DeleteObject,[dispfont1]
	invoke DeleteObject,[dispfont2]

	invoke	ExitProcess,0


;---------------------------------------------------
proc DialogProc,hwnddlg,msg,wparam,lparam

	push	ebx ecx edi edx esi	; save regs
	cmp	[msg],WM_INITDIALOG
	je	wminitdialog
	cmp	[msg],WM_COMMAND
	je	wmcommand
	cmp	[msg],WM_CLOSE
	je	wmclose
	cmp	[msg],WM_CTLCOLOREDIT
	je	wmctlcoloredit

	xor	eax,eax
	jmp	finish

  wminitdialog:
	invoke	CheckRadioButton,[hwnddlg],ID_NONE,ID_HEX,ID_NONE	 ; init radio buttons
	mov [idnumflg],0
	invoke	SetDlgItemInt,[hwnddlg],ID_NUMFROM,[pstart],FALSE
	invoke	SetDlgItemInt,[hwnddlg],ID_NUMTO,[pend],FALSE
	mov [idnumflg],1

	invoke	GetDlgItem,[hwnddlg],ID_SPRIMEDESC
	mov	[svhitem1],eax			 ; store entered start number
	invoke	CreateFont,14,0,0,0,0,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_TT_ONLY_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,VARIABLE_PITCH+FF_SCRIPT,NULL
	or	eax,eax
	jz	failed
	mov	[dispfont1],eax
	invoke	SendMessage,[svhitem1],WM_SETFONT,eax,FALSE

	invoke	GetDlgItem,[hwnddlg],ID_LPRIMEDESC
	mov	[svhitem2],eax			 ; store entered start number
	invoke	CreateFont,14,0,0,0,0,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_TT_ONLY_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,VARIABLE_PITCH+FF_SCRIPT,NULL
	or	eax,eax
	jz	failed
	mov	[dispfont2],eax
	invoke	SendMessage,[svhitem2],WM_SETFONT,eax,FALSE

	jmp	processed
  failed:
	jmp	processed

  wmcommand:
	cmp	[wparam],BN_CLICKED shl 16 + ID_CANCEL
	je	wmclose
	cmp	[wparam],BN_CLICKED shl 16 + ID_START
	je	btnnone
	cmp	[wparam],EN_CHANGE shl 16 + ID_NUMFROM
	je	fromchgd
	cmp	[wparam],EN_CHANGE shl 16 + ID_NUMTO
	je	tochgd
	cmp	[wparam],BN_CLICKED shl 16 + ID_OK
	jne	processed

      fromchgd:
	cmp [idnumflg],0
	je  exitfromchg
	push eax ebx
	invoke	GetDlgItemInt,[hwnddlg],ID_NUMFROM,0,FALSE
	mov ebx,0xFFFFFFFF		  ; figure out default number span
	sub ebx,PSELEND
	cmp eax,ebx
	ja  toadj
       add eax,PSELEND
	jmp  setto
      toadj:
	mov	eax,0xFFFFFFFF
      setto:
	invoke	SetDlgItemInt,[hwnddlg],ID_NUMTO,eax,FALSE	  ; set to number
	pop ebx eax
       exitfromchg:
	jmp processed
      tochgd:
	jmp processed

      btnnone:
	invoke	IsDlgButtonChecked,[hwnddlg],ID_NONE
	cmp	eax,BST_CHECKED
	jne	btnascii
	mov	[btnflag],0
      btnascii:
	invoke	IsDlgButtonChecked,[hwnddlg],ID_ASCII
	cmp	eax,BST_CHECKED
	jne	btnhex
	mov	[btnflag],1
      btnhex:
	invoke	IsDlgButtonChecked,[hwnddlg],ID_HEX
	cmp	eax,BST_CHECKED
	jne	btnfactor
	mov	[btnflag],2
      btnfactor:
	invoke	IsDlgButtonChecked,[hwnddlg],ID_FACTOR
	cmp	eax,BST_CHECKED
	jne	btnsdo
	mov	[btnflag],3

      btnsdo:
	cmp	[ptop],0		;check for lookup table creation
	jne	L0
	call	SetUpPrimes		; setup prime lookup table
       L0:
	invoke	GetDlgItemInt,[hwnddlg],ID_NUMFROM,0,FALSE
	mov	[pstart],eax		       ; store entered start number
	invoke	GetDlgItemInt,[hwnddlg],ID_NUMTO,0,FALSE
	mov	[pend],eax		; store entered end number
	cmp	eax,[pstart]		; check to see if range exists
	jb	processed		; must use jb, jl is for signed number
	push	[pstart]
	pop	[testnum]
	mov	ecx,[pend]		; number start moved to ecx for looping
	sub	ecx,[pstart]		; adjust range
	inc	ecx			; set up for loop
	mov	[primes],0		; zero counts
	mov	[testlvl],0		; zero count
       L1:
	mov	eax,[testnum]
	call	Check4Prime
	cmp	eax,0
	jz	L2
	inc	[primes]
	mov	edx,[btnflag]
	or	edx,edx
	jz	L2
	call	Write2File
	jmp	L3
       L2:
	cmp [btnflag],3
	jne L3		     ; print factors?
	call	Write2File
       L3:
	cmp	cx,0
	jne	L4
	push ecx   ; be sure to keep ecx and pop val back after invoke calls
		   ;   or loop will not terminate
	invoke	SetDlgItemInt,[hwnddlg],ID_CURTEST,[testnum],FALSE
	invoke	SetDlgItemInt,[hwnddlg],ID_DEPTH,[testlvl],FALSE
	invoke	SetDlgItemInt,[hwnddlg],ID_PRIMES,[primes],FALSE
	invoke	GetAsyncKeyState,VK_ESCAPE
	pop	ecx
	or	eax,eax
	jnz	processed     ; check if escape key is pressed to exit
	push	ecx
	invoke	GetAsyncKeyState,VK_RBUTTON
	pop	ecx
	or	eax,eax
	jnz	processed     ; check if right mouse button is pressed to exit
       L4:
	inc	[testnum]
	dec	ecx
	jnz	L1	     ; do loop again if not done

      L5:
	dec [testnum]	     ; adjust for the extra inc on loop exit
	invoke	SetDlgItemInt,[hwnddlg],ID_CURTEST,[testnum],FALSE
	invoke	SetDlgItemInt,[hwnddlg],ID_DEPTH,[testlvl],FALSE
	invoke	SetDlgItemInt,[hwnddlg],ID_PRIMES,[primes],FALSE
	jmp	processed

  wmctlcoloredit:	; set color extra to long digits
			;  this routine passes back background paint brush for edit box
			; color control. valid brush must be passed back or digit color is ignored.
	invoke GetDlgCtrlID,[lparam]
	cmp eax,ID_NUMFROM
	jne	ce_to
	invoke	GetDlgItemInt,[hwnddlg],ID_NUMFROM,pcheckOK,FALSE
	cmp [pcheckOK],TRUE
	je  ce_contfrom 	      ;00bbggrr
	invoke SetTextColor,[wparam],0x0000ffff      ; yellow on red if nuumber error
	invoke SetBkColor,[wparam],0x000000ff
     ce_contfrom:
	mov eax,[brushwhite]
	;mov eax,[brushyellow]
	ret

     ce_to:
	invoke GetDlgCtrlID,[lparam]
	cmp eax,ID_NUMTO
	jne	ce_contto
	invoke	GetDlgItemInt,[hwnddlg],ID_NUMTO,pcheckOK,FALSE
	cmp [pcheckOK],TRUE
	je  ce_contto		      ;00bbggrr     (color map for line below))
	invoke SetTextColor,[wparam],0x0000ffff       ;yellow on red if number error
	invoke SetBkColor,[wparam],0x000000ff
     ce_contto:
	mov eax,[brushwhite]
	ret

  wmclose:
	invoke	EndDialog,[hwnddlg],0
  processed:
	call	Close2File
	mov	eax,1
  finish:
	pop	esi edx edi ecx ebx   ; restore regs
     ret
 endp
;-------------------------------------------------------------------
 proc SetUpPrimes
      ; setup prime table with primes to 65536.
      ; This table will be used to check for primes to 4G.
      ; plist table has room for only 6600 entries since there are fewer primes than this under 65K
      ; after this table is setup it is used to check all other numbers up to 4G for prime,
      ;   any number does not have a factor in this sieve table is prime.

	pushad				;save regs
	mov	[plist],dword 2
	mov	[ptop],dword 1
	mov	ecx,dword 2
     SUP_L1:
	inc	ecx			; in combo with inc ecx below, so eax incs twice. no need to check even numbers
	mov	eax,ecx
	call	Check4Prime
	or	eax,eax
	jz	SUP_L2			; not prime if zero returned in eax
	push	eax

	mov	eax,[ptop]
	pop	[plist+eax*4]		; prime number returned in eax and was pushed on stack
	mov	eax,[ptop]
	cmp	eax,[pmax]		; increment top of current prime table list
	jge	SUP_L2
	inc	[ptop]
     SUP_L2:
	inc	ecx
	cmp	ecx,dword PSEARCHMAX
	jl	SUP_L1			; end after 65535.
	popad				;restore regs
     ret
 endp
;------------------------------------------------------------------------
proc Check4Prime

	push	ebx ecx edi edx 	; save regs
	mov	ecx,0			; set to start looping from zero
	mov	ebx,eax 		; save eax
	cmp	eax,2
	jg	C4P_L1			; 1, 2 are prime, 0 is used as a 'no prime' flag, but if entry is <= 2 then pass back number.
	je	C4P_L5
	cmp	eax,1
	je	C4P_L5
	cmp	eax,0
	je	C4P_L5

     C4P_L1:

	mov	eax,ebx 		; restore number to check back to eax
	mov	edx,0			; clear high part of  divisor
	mov	edi,[plist+ecx*4]	; for testing number using prime number table
	div	edi			; check prime item
	inc	ecx			; next
	or	edx,edx 		; if not remainder,number not prime, exit loop
	jz	C4P_L4
	cmp	eax,edi 		; exit loop if what ends up being sqrt of eax is < check item
	jb	C4P_L2
	cmp	ecx,[ptop]		; only loop as long as less than items in list
	jb	C4P_L1
     C4P_L2:
	cmp	ecx,[testlvl]
	jb	C4P_L3
	mov	[testlvl],ecx
     C4P_L3:
	dec	ecx
	mov	eax,ebx 		; restore eax (number being checked)
	cmp	ecx,[ptop]		; if not all primes checked in list, then not prime
	jne	C4P_L5
     C4P_L4:
	mov	eax, 0			; pass back zero if not prime
     C4P_L5:
	pop	edx edi ecx ebx 	; restore regs  (only 1 of this or previous popad run per pass)
     ret
 endp
;---------------------------------------------------------------------
proc Factors
	; factor list in form  number= x[^y] * x[^y] ....      (^y only prints if > 1)
	;                              or Prime

	pushad				; save regs
	mov	[wrflag],0		; turn off '*' in print
	mov	ecx,0			; set to start looping from zero
	mov	ebx,[testnum]		; save eax
	cmp	ebx,2
	ja	F_nextnum		   ; 1, 2 are prime, 0 is no prime flag, but if entry is <= 2 then pass back number.
	jmp	F_endsqr
     F_nextnum:
	mov	eax,ebx 		; restore eax
	mov	esi,0			; zero exponent
	mov	edx,0			; clear high part of  divisor
	mov	edi,[plist+ecx*4]	; for testing number using prime number table
	jmp	F_checknum

     F_samenum:
	inc	esi			; increment exponent
	mov	ebx,eax 		; save new value
     F_checknum:
	mov	edx,0			; clear high order
	or	edi,edi 		; check for divide by zero that can happen at this point when checking large unsigned numbers
	jz	F_skip
	div	edi			; check prime item
	or     edx,edx
	jz	F_samenum		; loop back and try same divisor again

	or	esi,esi
	jz	F_skip

     F_addtoline:			;print/setup  base^exponent here
	pushad
	cmp	[wrflag],0
	je	F_setnum
	cinvoke wsprintf,[buffptr],facformatt		      ; seperator
	add	[buffptr],eax
     F_setnum :
	mov	[wrflag],1		; print the '*' from now on on line
	cmp	[testnum],edi
	je	F_prime
	cmp	esi,1
	je	F_use1
     F_use2:
	cinvoke wsprintf,[buffptr],facformat2,edi,esi	      ; move number in eax to buffer
	jmp	F_addcont
     F_use1:
	cinvoke wsprintf,[buffptr],facformat1,edi	     ; move number in eax to buffer
	jmp	F_addcont
     F_prime:
	cinvoke wsprintf,[buffptr],priformat		     ; Prime indication to buffer
     F_addcont:
	add	[buffptr],eax
	popad

     F_skip:
	or	eax,eax
	jz	F_end			; end if zero
	cmp	eax,edi
	jl	F_endsqr
	inc	ecx			; next
	mov	eax,ebx 		; restore value to last evenly divisable number
	jmp	F_nextnum		     ; run next prime number pass on number

     F_endsqr:
	mov	esi,1
	mov	edi,ebx
	mov	eax,0
	jmp	F_addtoline

     F_end:

     F_printlast:
	mov	eax,[buffptr]
	mov	[eax],byte 0Dh		; end line
	inc	eax
	mov	[eax],byte 0Ah
	inc	eax
	mov	[buffptr],eax
     F_exit:
	popad				; restore regs
      ret

endp
;---------------------------------------
proc Create2File
	      ; create type of file to output
	pushad
	cmp	[btnflag],2
	je	C2F_Hex
	cmp	[btnflag],3
	je	C2F_Fac
     C2F_Dec:
	cinvoke  wsprintf,pfilename,decfile,[testnum]
	jmp	C2F_cont
     C2F_Fac:
	cinvoke  wsprintf,pfilename,decfilef,[testnum]
	jmp	C2F_cont
     C2F_Hex:
	 cinvoke  wsprintf,pfilename,hexfile,[testnum]
     C2F_cont:
	or	eax,eax
	jz	skipfile
	invoke CreateFile,pfilename,GENERIC_WRITE,0,0,CREATE_ALWAYS,\
	FILE_ATTRIBUTE_ARCHIVE,0
	mov	[usefile],eax
	mov	[wrcount],0
     skipfile:
       popad
    ret
endp
;---------------------------------------------------------
proc Write2File

	pushad
	cmp	[usefile],0
	jnz	W2F_1
	call	Create2File
       W2F_1:
	cmp	[usefile],0
	je	W2F_skip
	cmp	[btnflag],1
	je	W2F_Dec
	cmp	[btnflag],2
	je	W2F_Hex
	cmp	[btnflag],3
	je	W2F_factor
	jmp	W2F_skip

     W2F_factor:
	mov    eax,writebuff
	mov    [buffptr],eax
	mov    eax,[testnum]
	cinvoke wsprintf,writebuff,decformatf,eax	  ; move number in eax to buffer
	add    [buffptr],eax
	call  Factors
	mov eax,[buffptr]
	sub eax,writebuff
	jmp    W2F_cont
     W2F_Dec:
	cinvoke wsprintf,writebuff,decformat,eax	 ; move number in eax to buffer
	mov    [writebuff+eax],0Dh			 ; add cr/lf code to buffer
	inc    eax
	mov    [writebuff+eax],0Ah
	inc    eax
	jmp    W2F_cont
     W2F_Hex:
	mov    [ddbuff],eax
	mov    eax,4
     W2F_cont:
	invoke	WriteFile,[usefile],writebuff,eax,byteswritten,0    ; eax contains bytes to write
	inc    [wrcount]
	cmp    [wrcount],PFILEMAX	       ; check for number of numbers allowed in a file
	jl     W2F_skip
	call   Close2File
     W2F_skip:
	popad
    ret
endp
;---------------------------------------------------
proc Close2File

	cmp   [usefile],0
	jz    C2F_1
	invoke FlushFileBuffers,[usefile]
	invoke CloseHandle,[usefile]
	mov   [usefile],0
     C2F_1:
     ret
endp

;===================================================

section '.idata' import data readable writeable

  library kernel32,'KERNEL32.DLL',\
	  user32,'USER32.DLL',\
	  gdi32,'GDI32.DLL'

  include '%fasminc%\apia\kernel32.inc'
  include '%fasminc%\apia\user32.inc'
  include '%fasminc%\apia\gdi32.inc'

;=====================================================

section '.rsrc' resource data readable

  directory RT_DIALOG,dialogs

  resource dialogs,\
	   37,LANG_ENGLISH+SUBLANG_DEFAULT,prime

  dialog prime,'Small Prime Finder',70,70,190,175,WS_CAPTION+WS_POPUP+WS_SYSMENU+DS_MODALFRAME ;+WS_MINIMIZEBOX+WS_MAXIMIZEBOX
    dialogitem 'STATIC','Smallest prime = 1',ID_SPRIMEDESC,40,10,120,18,WS_VISIBLE
    dialogitem 'STATIC','&From:',-1,10,20,30,8,WS_VISIBLE
    dialogitem 'EDIT','',ID_NUMFROM,40,20,80,13,WS_VISIBLE+WS_BORDER+WS_TABSTOP+ES_NUMBER
    dialogitem 'STATIC','&To:',-1,10,35,30,8,WS_VISIBLE
    dialogitem 'EDIT','',ID_NUMTO,40,35,80,13,WS_VISIBLE+WS_BORDER+WS_TABSTOP+ES_AUTOHSCROLL+ES_NUMBER
    dialogitem 'STATIC','Largest testable = 4,294,967,295',ID_LPRIMEDESC,40,50,140,8,WS_VISIBLE
    dialogitem 'BUTTON','&Start',ID_START,130,26,45,15,WS_VISIBLE+WS_TABSTOP+BS_PUSHBUTTON


    dialogitem 'BUTTON','&Output',-1,10,70,50,60,WS_VISIBLE+BS_GROUPBOX
    dialogitem 'BUTTON','&None',ID_NONE,20,82,35,13,WS_VISIBLE+BS_AUTORADIOBUTTON+WS_TABSTOP+WS_GROUP
    dialogitem 'BUTTON','&Ascii',ID_ASCII,20,92,35,13,WS_VISIBLE+BS_AUTORADIOBUTTON
    dialogitem 'BUTTON','&Hex',ID_HEX,20,102,35,13,WS_VISIBLE+BS_AUTORADIOBUTTON
    dialogitem 'BUTTON','&Factors',ID_FACTOR,20,112,35,13,WS_VISIBLE+BS_AUTORADIOBUTTON

    dialogitem 'BUTTON','&Cancel',ID_CANCEL,135,150,45,15,WS_VISIBLE+WS_TABSTOP+BS_PUSHBUTTON

    dialogitem 'STATIC','Testing',-1,150,80,140,8,WS_VISIBLE
    dialogitem 'STATIC','Test Depth',-1,150,90,140,8,WS_VISIBLE
    dialogitem 'STATIC','Primes',-1,150,100,140,8,WS_VISIBLE
    dialogitem 'STATIC','',ID_CURTEST,70,80,70,8,WS_VISIBLE+SS_RIGHT
    dialogitem 'STATIC','',ID_DEPTH,70,90,70,8,WS_VISIBLE+SS_RIGHT
    dialogitem 'STATIC','',ID_PRIMES,70,100,70,8,WS_VISIBLE+SS_RIGHT
    dialogitem 'STATIC','(Press <Esc> key to stop)',ID_LSTOP,10,155,80,8,WS_VISIBLE

  enddialog

;===================================================

Section 'export' export
      ; this section can be deleted and not affect the program.
      ; this section used to export values for the Ollydbg debugger (this may also work on others)
      ;  entries should be in alphabetical order for best export results.
      ;   (Note that uppercase is numerically less than lower case in ASCII
      ;      so the following list is in the correct sorted order.)
      ;
  export 'PRIME32.EXE',\
	DialogProc  ,	    'DialogProc',\
	L0,		    'L0',\
	L1,		    'L1',\
	L2,		    'L2',\
	L3,		    'L3',\
	L4,		    'L4',\
	L5,		    'L5',\
	btnflag ,	    'btnflag',\
	byteswritten,	    'byteswritten',\
	exit,		    'exit',\
	filebtn ,	    'filebtn',\
	pcheckOK,	    'pcheckOK',\
	pend	,	    'pend',\
	pfilename,	    'pfilename',\
	plist	,	    'plist',\
	pmax	,	    'pmax',\
	primes	,	    'primes',\
	pstart	,	    'pstart',\
	ptop	,	    'ptop',\
	svdc	,	    'svdc',\
	svhitem1,	    'svhitem1',\
	svhitem2,	    'svhitem2',\
	testlvl ,	    'testlvl',\
	testmess,	    'testmess',\
	testnum ,	    'testnum',\
	usefile ,	    'usefile',\
	xnum	,	    'xnum'



