;-----------------------------------------------------------------------
;   PRIME3.ASM
;
; Program that tests values between 0 and some upper limit displaying
; each prime number and total primes detected.
;
;
;-----------------------------------------------------------------------

org 100h            ;Code starts at offset 0100h.
use16               ;Use 16-bit code.
jmp Start           ;Go to start of program.


;=======================================================================
;                     Program Data & Constants.
;=======================================================================
;                          C O N S T A N T S
;-----------------------------------------------------------------------
;
;DOS Kernel Int 21h function codes.
;

CharIn       = 01h    ;Read character from STDIN to AL (with echo).
CharOut      = 02h    ;Write character to STDOUT from DL.
AuxIn        = 03h    ;Read character from STDAUX to AL.*
AuxOut       = 04h    ;Write character to STDAUX from DL.
LPOut        = 05h    ;Write character to printer from DL.*
DirConOut    = 06h    ;Write character directly to STDOUT from DL.*
DirConIn     = 06h    ;Read character directly from STDIN to AL.*
DCINoEcho    = 07h    ;Read character (no echo) from STDIN to AL.*
WaitByte     = 08h    ;Console input (no echo) AL holds character.
StrOut       = 09h    ;Output '$' terminated string addressed by DS:DX.

SINStatus    = 0Bh    ;Return STDIN status to AL.
FlshSTDIN    = 0Ch    ;Flush STDIN and read character as per AL.*



;
;ASCII codes to 1Fh.
;
Null         = 00h    ;Null character.
SOH          = 01h    ;Start of heading.
STX          = 02h    ;Start of text.
ETX          = 03h    ;End of text.
EOT          = 04h    ;End of transmission.
ENQ          = 05h    ;Enquiry.
ACK          = 06h    ;Acknowledge.
Bell         = 07h    ;Bell sound.
BckSpc       = 08h    ;Backspace.
Tab          = 09h    ;Horizontal tab.
LinF         = 0Ah    ;Line feed.
VTab         = 0Bh    ;Vertical tab.
FFeed        = 0Ch    ;Form Feed.
CRet         = 0Dh    ;Carriage Return.
ShOut        = 0Eh    ;Shift Out.
ShIn         = 0Fh    ;Shift In.
StrTrm       = 24h    ;String terminator.

;-----------------------------------------------------------------------
;
;Program Constants
;
;For NumOut16 and Asc2Bin16.
;
NSLen        = 06     ;Length of numeric strings.
Dvsr         = 10     ;Divisor for ASCII-Binary conversion.

;
;For IsPrime.
;
MinDvsr      = 03     ;Minimum divisor for trial division.
RootLim      = 255    ;Maximum allowed root for trial division.


MaxVal       = 700    ;Limit of prime testing.

;
;-----------------------------------------------------------------------
;                               D A T A
;-----------------------------------------------------------------------
;
;Data for NumOut16.
;
NumStr          db NSLen dup 0  ;Space for string after conversion.
TstVal          dw 0            ;Value to be converted to string.
Top             dw 0            ;Pointer to current end-of-string.

;
;Data for Asc2Bin16.
;
GI16Str         db NSLen dup 0  ;Space for String before conversion.
BinVal          dw 0            ;Holds converted binary value.

;
;Data for IsPrime.
;
NumIn           dw 0            ;Space to save number to be tested.

;
;Data for RunPgm.
;
NxtVal          dw 0            ;Value to be tested.

TotPrm          dw 0            ;Total Primes Detected.

;
;-----------------------------------------------------------------------
;
;                            General Texts.
;

;Program name string (includes newlines).
;
PgmTxt          db 'Prime Number Finder'
                db CRet, LinF
                db '----- ------ ------'
                db CRet, LinF
                db StrTrm

Txt01           db 'List of primes between 0 and '
                db StrTrm

TotTxt          db 'Total Primes = '
                db StrTrm

CTxt            db ', '
                db StrTrm

;
;Prime message;
;
StrtMsg         db 'Press any key to start...'
                db StrTrm

ClrMsg          db CRet
                db '                         '
                db StrTrm

FinMsg          db '*** RUN COMPLETE ***'
                db StrTrm


;
;Continuation message.
;
ConMsg          db 'Run Again? (Y/N): '
                db StrTrm
;
;Quit Message.
;
QMsg            db 'Press any key...'
                db StrTrm

;-----------------------------------------------------------------------
;                          E N D  O F  D A T A
;-----------------------------------------------------------------------




;-----------------------------------------------------------------------
;                          S U B R O U T I N E S
;-----------------------------------------------------------------------
;
;RingBell - Sound the bell!!!!!
;

RingBell:
        pusha                   ;Save all regs.
        mov  ah, CharOut        ;Function code to AH.
        mov  dl, Bell           ;Bell code to DL.
        int  21h                ;DING DONG
        popa                    ;Restore regs.
        ret                     ;Return to caller.

;
;-----------------------------------------------------------------------
;
;NewLine:- Display line feed on STDOUT.
;

NewLine:
        pusha           ;Preserve all regs.

        mov dl, LinF    ;Get ASCII code to DL.
        mov ah, CharOut ;Function code to AH.
        int 21h         ;Output character.

        mov dl, CRet    ;Get ASCII code to DL.
        mov ah, CharOut ;Function code to AH.
        int 21h         ;Output character.

        popa            ;Restore all regs.
        ret             ;Return to caller.

;
;-----------------------------------------------------------------------
;
;GetYN - Ask if user wishes to Run Again (Y/N)?
;
;      Returns: CX = 0 -> No
;               CX = 1 -> Yes.
;

GetYN:
        mov  cx, 0              ;Set return flag to No.
        mov  dx, ConMsg         ;Addr. of continue message to DX.
        mov  ah, StrOut         ;Display message.
        int  21h

GY01:
        mov  ah, WaitByte       ;Code to get char. from STDIN.
        int  21h                ;Get character to AL.

        cmp  al, 'Y'            ;Is it 'Y'?
        je   SetY               ;Yes. Set flag to yes.

        cmp  al, 'y'            ;Is it 'y'?
        je   SetY               ;Yes. Set flag to yes.

        cmp  al, 'N'            ;Is it 'N'?
        je   GYXit              ;Yes. Just quit.

        cmp  al, 'n'            ;Is it 'n'?
        je   GYXit              ;Yes. Just quit.
        ;
        ;Here when character invalid.
        ;
        call RingBell           ;Ring the bell!
        jmp  GY01               ;And try again.

SetY:
        mov  cx, 1              ;Set return flag to Yes.

GYXit:  
        ;
        ; Echo character entered, get carriage return, and quit
        ; with CX = 1 - YES; or CX = 0 - NO.
        ;
        mov  dl, al             ;Copy response to DL.
        mov  ah, CharOut        ;Code to AH.
        int  21h                ;Display character.

GYX01:
        ;
        ; Allow ONLY carriage return.
        ;
        mov  ah, WaitByte       ;Code to Ah.
        int  21h                ;Wait for next byte.
        cmp  al, CRet           ;Is it carriage return?
        je   GYX02              ;Yes. Go to exit.

        call RingBell           ;No. Ring the bell.
        jmp  GYX01              ;And try again.

GYX02:
        call NewLine            ;Move to new line.
        ret                     ;Return to caller.

;
;-----------------------------------------------------------------------
;
;QWait - Display quit message and await keystroke.
;

QWait:
        mov  ah, CharOut        ;Output line feed.
        mov  dl, LinF
        int  21h

        mov  ah, CharOut        ;Output <CR>.
        mov  dl, CRet
        int  21h

        mov  dx, QMsg           ;Address of text to DX.
        mov  ah, StrOut         ;Code to AH.
        int  21h                ;Display message.

KWait:
        mov  ah, WaitByte       ;Code to AH.
        int  21h                ;Await keystroke.
        ret                     ;Return to caller.
;
;-----------------------------------------------------------------------
;
;ShftStr - Shift characters in string one byte right.
;        - On Entry - AL holds byte to be added to head of string.
;                     Target string should have...
;                     (i) '$' in byte [0] of string.
;                     (ii)'Top' = Base address of string.
;
;           Registers preserved on exit.
;

ShftStr:
        pusha                   ;Save all registers. AL = new character.
        mov  bx, [Top]          ;Get pointer to current end-of-string.
        mov  cx, bx             ;Save in CX.

        mov  al, [bx]           ;Get byte at end of string.
        inc  bx                 ;Advance to next byte.
        mov  [bx], al           ;And shuffle byte one place right.
        mov  [Top], bx          ;Save new 'Top' pointer.

SSLoop:
        cmp  cx, NumStr         ;Have we shuffled all characters?
        je   SSExit             ;Yes. Go to exit.

        dec  cx                 ;No. Decrement source pointer.
        mov  bx, cx             ;Copy to BX.
        mov  al, [bx]           ;Get next byte to be moved.
        inc  bx                 ;Advance pointer to destination address.
        mov  [bx], al           ;Place byte in destination address.
        jmp  SSLoop             ;And check for end.

SSExit:
        popa                    ;Restore all registers.
        mov  [NumStr], al       ;Add new byte to first byte of string.

        ret                     ;Return to caller
;
;-----------------------------------------------------------------------
;
;NumOut16 - Display word in AX as decimal string.
;
;Associated data items are:
;
;         TstVal = Value to be displayed.
;         NumStr = Decimal string equivalent.
;         Top    = Pointer to current end-of-string.
;

NumOut16:
        mov  cx, NumStr         ;String addr. to CX.
        mov  [Top], cx          ;Save as buffer limit.
        mov  cl, StrTrm         ;String terminator to CL.
        mov  [NumStr], cl       ;Place in first byte of string.

NxtDig:
        xor  dx, dx             ;Clear DX.
        mov  bx, Dvsr           ;Divisor to BX
        div  bx                 ;Divide number by ten.AX = Quo., DX = Mod.
        or   dl, 30h            ;Convert modulus to ASCII.
        xchg cx, ax             ;Save quotient to CX.
        mov  al, dl             ;Transfer byte to AL.
        call ShftStr            ;Shuffle chars and add new one to string.

        xchg ax, cx             ;Restore quotient.
        cmp  ax, 0              ;Are we at the end?
        je   N8Xit              ;Yes. Quit.
        jmp  NxtDig             ;No. Do next digit.

N8Xit:
        mov  ah, StrOut         ;All bytes done...
        mov  dx, NumStr         ;...so display string.
        int  21h

        ret                     ;Return to caller.

;
;-----------------------------------------------------------------------
;
; IsPrime - Determine whether 16-bit value in AX is prime or composite.
;           Uses trial division. Maximum divisor is limited to 255. If
;           required divisor exceeds this value, IsPrime returns CX = 2.
;
;           On Entry: AX = Value to be tested.
;
;           On Exit : CX = 0 when composite.
;                     CX = 1 when prime.
;                     CX = 2 when limit of division exceeded.
;
IsPrime:
        mov  [NumIn], ax        ;Save number to NumIn.
        ;
        ; Check whether number is 0, 1, or 2.
        ;
        cmp  ax, 2              ;Is it 2?
        ja   TstOdd             ;No. Greater, see if odd.
        je   Prime              ;Yes. Flag as prime and quit.
        jmp  Composite          ;No. Less, must be 0 or 1.

TstOdd:
        cmp  ax, MinDvsr        ;Is it 3?
        jz   Prime              ;Yes. Flag as prime.

        mov  dx, ax             ;Copy number to DX.
        and  dx, 1              ;No. Mask to bit 0.
        jnz  OddVal             ;If NOT clear must be odd.
        mov  bx, 2              ;else even so set divisor to 2.
        shr  ax, 1              ;Halve the value being tested.
        jmp  Composite          ;and got to composite exit.

OddVal:
        ;
        ; Find limit for trial division as root AX.
        ;
        mov  bx, MinDvsr        ;Get lowest divisor to BX.

NxtNum:
        mov  ax, bx             ;Copy divisor to AX.
        mul  bx                 ;Calculate AX squared to DX:AX
        cmp  ax, [NumIn]        ;Is result >= value to be tested?
        jae  GotLim             ;Yes. Perform trial division.

        inc  bx                 ;No. Increment to next number.
        cmp  bx, RootLim        ;Is root beyond limit?
        ja   NoLimit            ;Yes. Flag unable to process.
        jmp  NxtNum             ;No. Try again.

GotLim:
        mov  cx, bx             ;Save limit of trial to CX.
        mov  bx, MinDvsr        ;Try lowest divisor first.

NxtDiv:
        mov  ax, [NumIn]        ;Restore number to be tested to AX.
        xor  dx, dx             ;Clear DX for division.
        div  bx                 ;Calculate AX DIV BX. Modulus to DX.
        cmp  dx, 0              ;Is modulus = zero?
        je   Composite          ;Yes. Flag as composite.

        inc  bx                 ;No. Move to next divisor.
        cmp  bx, cx             ;Have we reached limit of trial?
        ja   Prime              ;Yes. Value is prime.
        jmp  NxtDiv             ;And perform next division.

NoLimit:
        mov  cx, 2              ;Set "Unable to process" flag.
        jmp  Return             ;And quit.

Composite:
        mov  cx, 0              ;Clear return flag.
        jmp  Return             ;And go to exit.

Prime:
        mov  cx, 1              ;Set return flag.

Return:
        ret                     ;Return to caller.

;
;-----------------------------------------------------------------------
;
; Comma - display comma between values..
;
Comma:
      mov  dx, CTxt             ;Address of text to DX.
      mov  ah, StrOut           ;Code to AH.
      int  21h                  ;Display it.
      ret                       ;Return to caller.
;
;----------------------------------------------------------------------
;
; InitPgm - Initialise program by displaying program name, clears
;           value to be tested and total primes detected.
;

InitPgm:
        mov  dx, PgmTxt         ;Address of heading to DX.
        mov  ah, StrOut         ;Code to display string.
        int  21h                ;Display it.

        mov  dx, Txt01          ;Address of text to DX.
        mov  ah, StrOut         ;Code to AH,
        int  21h                ;Output start message.

        mov  ax, MaxVal         ;Get limit of test.
        call NumOut16           ;...and output.
        call NewLine            ;Move to new line.


        mov  ax, 0              ;Clear accumulator...
        mov  [NxtVal], ax       ;...Value to be tested...
        mov  [TotPrm], ax       ;...and total primes.

        ret                     ;Return to caller.

;
;-----------------------------------------------------------------------
;
; RunPgm -
;

RunPgm:
        mov  ax, [NxtVal]       ;Restore next value to AX.
        call  IsPrime           ;Perform prime test.

        cmp  cx, 0              ;Is result composite?
        je   TstEnd             ;Yes. Look for end of test.

        cmp  cx, 2              ;No. Is result out of range?
        je   TstEnd             ;Yes. Test for end.
        ;
        ; Here when result is prime.
        ;
        mov  ax, [NxtVal]       ;Get next value.
        call NumOut16           ;Display it.
        call Comma              ;and a comma.
        inc  [TotPrm]           ;Increment total primes.

TstEnd:
        inc  [NxtVal]           ;Increment value to be tested.
        mov  ax, [NxtVal]       ;Get it to AX.
        cmp  ax, MaxVal         ;Is it end?
        ja   RPXit              ;Yes.
        jmp  RunPgm             ;No.

RPXit:
        call NewLine            ;Finished. Move to new line for total.
        ret                     ;Return to caller.

;
;-----------------------------------------------------------------------
;
; ClosePgm -
;

ClosePgm:
        mov  dx, TotTxt         ;Address of text to DX.
        mov  ah, StrOut         ;Code to AH.
        int  21h                ;Display it.

        mov  ax, [TotPrm]       ;Get total to AX.
        call NumOut16           ;Display it.
        call NewLine            ;Move to next line.

        mov  dx, FinMsg         ;Display "*** RUN COMPLETE ***.
        mov  ah, StrOut
        int  21h

        ret                     ;Return to caller.

;
;-----------------------------------------------------------------------
;                   E N D  O F  S U B R O U T I N E S
;-----------------------------------------------------------------------



;=======================================================================
;                         M A I N  P R O G R A M
;=======================================================================
;
;
; Program that tests values between 0 and some upper limit displaying
; rolling fields for value being tested and total primes detected.
;

Start:
        call InitPgm            ;Initialise program.
        call RunPgm             ;Run it...
        call ClosePgm           ;...and closedown.
        ;
        ;See if user wants to run again.
        ;
        call NewLine            ;Move to new line.

        call GetYN              ;Run Again (Y/N)?.
        cmp  cx, 1              ;Is reply 'y'?
        je   Start              ;Yes. Loop.

        call  QWait             ;Display quit message.

        int 20h                 ;Terminate Program!
;
;=======================================================================
;                         E N D   O F   C O D E
;=======================================================================
;
