The MASM Forum Archive 2004 to 2012

Miscellaneous Forums => 16 bit DOS Programming => Topic started by: veronicak5678 on October 15, 2009, 03:16:17 AM

Title: Counting Set Bits
Post by: veronicak5678 on October 15, 2009, 03:16:17 AM
I am trying to write a subprocedure that will count the number of buts set in a 16bit number, then send that number (bits set) back to the main procedure in AX. The main should display display the number of 1s and determine if the number is even or odd.

I am trying to count the 1s by shifting left and incrementing on carry. The problem seems to be that when I get back to 'main,' the original input number is still in AX rather than the count I got in 'parity.' I don't know why it isn't changing.


;===================================================================
;                   MAIN.ASM
;===================================================================
         EXTERN  GETDEC$:FAR
         EXTERN  NEWLINE:FAR
         EXTERN  PUTSTRNG:FAR
         EXTERN  PUTDEC$:FAR
         EXTERN  PUTBIN:FAR
         EXTERN  PARITY:FAR
;===================================================================
        .MODEL  LARGE
        .STACK  512
;===================================================================
; D A T A   S E G M E N T   D E F I N I T I O N
        .DATA
NUMBER     DW  ?
PROMPT DB 'Enter a number: '
BINDISPLAY    DB 'Number in binary: '
ONESDISPLAY    DB 'Number of 1s: '
ODDDISPLAY    DB 'The number of 1s is odd. '
EVENDISPLAY DB 'The number of 1s is even. ' 
;===================================================================
; C O D E   S E G M E N T   D E F I N I T I O N
        .CODE
        ASSUME  DS:DGROUP
;===================================================================

MAIN    PROC

        MOV     AX,DGROUP           ;SET DS-REGISTER TO POINT TO
        MOV     DS,AX               ;DATA SEGMENT
        MOV     ES,AX               ;AND ES ALSO

        CALL    NEWLINE
        MOV     DI, OFFSET PROMPT
        MOV     CX, SIZEOF PROMPT
        CALL    PUTSTRNG
        CALL    GETDEC$
        CALL    NEWLINE
        MOV     DI, OFFSET BINDISPLAY
        MOV     CX, SIZEOF BINDISPLAY
        CALL    PUTSTRNG
CALL PUTBIN
        PUSH    AX
        CALL    PARITY
CALL NEWLINE


        CALL    NEWLINE
        MOV     DI, OFFSET ONESDISPLAY
        MOV     CX, SIZEOF ONESDISPLAY
        CALL    PUTSTRNG
POP AX
        CALL    PUTDEC$
        CALL    NEWLINE
               
SUB DX, DX
        MOV     BX, 2
        DIV     BX
        CMP     DX, 0
        JNE     ODDS

        MOV     DI, OFFSET EVENDISPLAY
        MOV     CX, SIZEOF EVENDISPLAY
        CALL    NEWLINE
        CALL    PUTSTRNG
        JMP     EXIT_PROGRAM
ODDS:   
        MOV     DI, OFFSET ODDDISPLAY
        MOV     CX, SIZEOF ODDDISPLAY
        CALL    NEWLINE
        CALL    PUTSTRNG       
EXIT_PROGRAM:
.EXIT
        MOV     AX, 4C00H
        INT     21H
MAIN    ENDP
        END MAIN





;;===================================================================
;              Veronica Kaufman
;                 CISP 310
;                PARITY.ASM
;===================================================================
         .MODEL  LARGE     
;===================================================================
; D A T A   S E G M E N T   D E F I N I T I O N
        .DATA
ONES_COUNT     DW      0                           
;===================================================================
; C O D E   S E G M E N T   D E F I N I T I O N
        .CODE   
        ASSUME  DS:DGROUP
;===================================================================

PARITY  PROC    FAR PUBLIC USES CX DX DS
POP AX
NUM_LOOP:
CMP AX, 0
JE END_PROGRAM
SHL AX, 1
JC INCREMENT
JMP NUM_LOOP
INCREMENT:
INC ONES_COUNT
JMP NUM_LOOP
END_PROGRAM:
MOV AX, ONES_COUNT
PUSH AX
RET
PARITY  ENDP
        END PARITY
Title: Re: Counting Set Bits
Post by: dedndave on October 15, 2009, 05:25:38 AM
i dunno how it ever returns at all - lol
when you POP AX, you will get the procedure's return address into AX
this is a far procedure, so just above the return address is also the return segment
just put the value in AX and make the call - it will be in AX when you get there
also, i would avoid using names like PARITY, make it CkParity or something unique

oops! - lol

you are using
PARITY  PROC    FAR PUBLIC USES CX DX DS
so, besides the return address and segment, the assembler also pushes cx, dx, and ds
i have no idea what order they are saved in, but one of those values winds up in ax
i would do something like this...

mov  ax,TestVal
call CkParity
.
.
.
CkParity PROC

xor  dx,dx
mov  cx,16

CkParity0:
shr  ax,1
jnc  BitCounted

inc  dx          ;note - INC does not alter the carry flag

BitCounted:
rcr  bx,1
loop CkParity0

xchg ax,bx       ;original value back in ax
or   ax,ax       ;sets parity flag if odd parity

it isn't necessary to use the parity flag at the end - the count in dx (specifically, bit 0 of dx) tells you even or odd
Title: Re: Counting Set Bits
Post by: jj2007 on October 15, 2009, 06:55:32 AM
ADC is also a pretty useful opcode:

include \masm32\include\masm32rt.inc

.code
start:
push 0
call CtBits
print str$(eax), " bits set", 13, 10

push -1
call CtBits
print str$(eax), " bits set", 13, 10

push 01110111000101000111011100010100b
call CtBits
print str$(eax), " bits set", 13, 10

getkey
exit

CtBits proc uses ecx edx arg
mov edx, arg
xor eax, eax
mov ecx, 32 ; put 16 for testing 16 bits only
@@: shl edx, 1
adc eax, 0
dec ecx
jne @B
  ret
CtBits endp
end start
Title: Re: Counting Set Bits
Post by: dedndave on October 15, 2009, 07:08:31 AM
hiyas Jochen - good idea
just - needs to be in 16-bit form - lol    :P
(16-bit forum)

    mov dx, arg
    xor ax, ax
    mov cx, 16

@@: shl dx, 1
    adc ax, 0
    dec cx
    jne @B
Title: Re: Counting Set Bits
Post by: sinsi on October 15, 2009, 07:13:08 AM

mov ecx, 32 ; put 16 for testing 16 bits only
@@: shl edx, 1

If the count is 16, you should use SHR to count the bottom 16 bits.
Title: Re: Counting Set Bits
Post by: japheth on October 15, 2009, 09:49:01 AM
Quote from: sinsi on October 15, 2009, 07:13:08 AM

mov ecx, 32 ; put 16 for testing 16 bits only
@@: shl edx, 1

If the count is 16, you should use SHR to count the bottom 16 bits.

or simply use no count at all:

CtBits proc uses edx arg
mov edx, arg
xor eax, eax
@@: shr dx, 1
adc eax, 0
and dx, dx
jne @B
ret
CtBits endp

Title: Re: Counting Set Bits
Post by: dedndave on October 15, 2009, 11:36:59 AM
even better Japheth
but, this is still the 16-bit forum - lol

CtBits  proc uses bx dx arg

        mov     dx,arg
        xor     ax,ax
        mov     bx,ax

@@:     shr     dx,1
        adc     ax,bx
        and     dx,dx
        jne     @B

        ret

CtBits  endp
Title: Re: Counting Set Bits
Post by: jj2007 on October 15, 2009, 01:00:57 PM
Quote from: sinsi on October 15, 2009, 07:13:08 AM

mov ecx, 32 ; put 16 for testing 16 bits only
@@: shl edx, 1

If the count is 16, you should use SHR to count the bottom 16 bits.

You are right, as usual :U

Here is an example how it could be done with a macro. Very flexible but 32 bit code.

include \masm32\include\masm32rt.inc

PopCount MACRO arg
LOCAL testReg, ctReg, size
size = 32
if @SizeStr(<arg>) eq 2
size = 16
endif
ctReg equ ecx
if @InStr(1, <arg>, <ax>)
testReg equ <edx>
else
testReg equ <eax>
if @InStr(1, <arg>, <cx>)
ctReg equ edx
endif
endif
; int 3
push testReg
xor testReg, testReg
push ctReg
push size
pop ctReg
@@: rol arg, 1
adc testReg, 0
dec ctReg
jne @B
pop ctReg
xchg dword ptr testReg, [esp]
pop eax
; int 3
EXITM <eax>
ENDM

.code
start:
print "Counting bits in a proc: ", 13, 10
push 0
call CtBits
print str$(eax), " bits set", 13, 10

push -1
call CtBits
print str$(eax), " bits set in AX", 13, 10

push 0111011100010100b
call CtBits
print str$(eax), " bits set", 13, 10, 10

print "Counting bits in a macro: ", 13, 10

mov eax, 0
print str$(PopCount(ax)), " bits set", 13, 10

mov eax, -1
print str$(PopCount(ax)), " bits set in AX", 13, 10

mov eax, -1
print str$(PopCount(eax)), " bits set - this is EAX, 32 bits", 13, 10

mov eax, 0111011100010100b
print str$(PopCount(ax)), " bits set", 13, 10, 10

mov ecx, 0
print str$(PopCount(cx)), " bits set", 13, 10

mov ecx, -1
print str$(PopCount(cx)), " bits set in CX", 13, 10

mov ecx, -1
print str$(PopCount(ecx)), " bits set - this is EAX, 32 bits", 13, 10

mov ecx, 0111011100010100b
print str$(PopCount(cx)), " bits set", 13, 10

getkey
exit

CtBits proc uses ecx edx arg
mov edx, arg
xor eax, eax
mov ecx, 16 ; 16 bits only - put 32 if needed
@@: rol dx, 1
adc eax, 0
dec ecx
jne @B
  ret
CtBits endp
end start
Title: Re: Counting Set Bits
Post by: dedndave on October 15, 2009, 02:01:34 PM
here's another twist on Jochen's/Japheth's code

CtBits  proc uses bx dx arg

        mov     dx,arg
        xor     ax,ax
        mov     bx,ax

@@:     shr     dx,1
        jz      @F

        adc     ax,bx
        jmp     @B

@@:     adc     ax,bx
        ret

CtBits  endp

even faster is not to use a stack frame - also - eliminate the jmp...

        mov     dx,TestVal
        call    CBits

;bit count is in AX - bit 0 will tell you if odd/even parity
.
.
CBits   PROC

        push    bx
        xor     ax,ax       ;XOR always clears the carry flag
        push    dx
        mov     bx,ax

CBits0: adc     ax,bx
        shr     dx,1        ;the zero flag is set if DX = 0
        jnz     CBits0      ;JNZ does not look at the carry flag

        pop     dx
        adc     ax,bx       ;don't forget the last carry bit
        pop     bx
        ret

CBits   ENDP
Title: Re: Counting Set Bits
Post by: FORTRANS on October 15, 2009, 02:12:09 PM
Hi Veronica,

  I don't have your library, so take this with a grain of salt.  When you
call GETDEC$ where is your number stored?  If it is in AX it may be
destroyed when you call other routines.  You should ensure that
it is used by PUTBIN as expected.  If it ends up in NUMBER, and that
is used properly by PUTBIN, then:


       MOV     AX,[NUMBER]
       CALL    PARITY


and don't POP AX (or push it later) _should_ make your code work.
Pushing and popping stuff should be done with care, check in
DEBUG the first time around if necessary.

  Look at the other suggestions to see what else you can do.
They are interesting, and do show better ways to do things, but
your code should work with the few fixes, unless I've missed
something as usual.  If you want to do more than one number,
don't forget to zero ONES_COUNT when you enter PARITY.

HTH,

Steve N.
Title: Re: Counting Set Bits
Post by: ilian007 on October 16, 2009, 03:57:00 PM
Veronica what I did is right before calling parity subprocedure in main I moved ax to cx : MOV CX,AX then in parity.asm I dit: MOV AX,CX and everything works. sure u should have USES AX CX .. etc in parity.asm

Title: Re: Counting Set Bits
Post by: dedndave on October 16, 2009, 04:10:13 PM
        mov     dx,TestVal
        call    CBits

;ax = bit count - bit 0 will tell you if odd/even parity
;dx = TestVal
.
.
CBits   PROC

        push    bx
        xor     ax,ax       ;XOR always clears the carry flag
        push    dx
        mov     bx,ax

CBits0: adc     ax,bx
        shr     dx,1        ;the zero flag is set if DX = 0
        jnz     CBits0      ;JNZ does not look at the carry flag

        pop     dx
        adc     ax,bx       ;don't forget the last carry bit
        pop     bx
        ret

CBits   ENDP
Title: Re: Counting Set Bits
Post by: veronicak5678 on October 16, 2009, 05:48:48 PM
Thanks to everyone for answering. I wanted to stick with my original method, really just because I understood it, but the pops and pushes were screwing it up. My teacher made it seem like the thing to do, but I guess it's not so simple.

Here's what I ended up with:

;===================================================================
;                   MAIN.ASM
;===================================================================
         EXTERN  GETDEC$:FAR
         EXTERN  NEWLINE:FAR
         EXTERN  PUTSTRNG:FAR
         EXTERN  PUTDEC$:FAR
         EXTERN  PUTBIN:FAR
         EXTERN  PARITY:FAR
;===================================================================
        .MODEL  LARGE
        .STACK  512
;===================================================================
; D A T A   S E G M E N T   D E F I N I T I O N
        .DATA
NUMBER      DW  ?
PROMPT DB  'Enter a number: '
BINDISPLAY    DB  'Number in binary: '
ONESDISPLAY     DB  'Number of 1s: '
ODDDISPLAY      DB  'The number of 1s is odd. '
EVENDISPLAY     DB  'The number of 1s is even. '
NUMDISPLAY     DB  'The number IS '
;===================================================================
; C O D E   S E G M E N T   D E F I N I T I O N
        .CODE
        ASSUME  DS:DGROUP
;===================================================================

MAIN    PROC

        MOV     AX,DGROUP           ;SET DS-REGISTER TO POINT TO
        MOV     DS,AX               ;DATA SEGMENT
        MOV     ES,AX               ;AND ES ALSO

        CALL    NEWLINE
        MOV     DI, OFFSET PROMPT
        MOV     CX, SIZEOF PROMPT
        CALL    PUTSTRNG
        CALL    GETDEC$
MOV NUMBER, AX
        CALL    NEWLINE
        MOV     DI, OFFSET BINDISPLAY
        MOV     CX, SIZEOF BINDISPLAY
        CALL    PUTSTRNG
MOV BL, 1
CALL    PUTBIN






        MOV AX, NUMBER
        CALL    PARITY
CALL    NEWLINE
        CALL    NEWLINE
        MOV     DI, OFFSET ONESDISPLAY
        MOV     CX, SIZEOF ONESDISPLAY
        CALL    PUTSTRNG
        CALL    PUTDEC$
        CALL    NEWLINE 
SUB     DX, DX
        MOV     BX, 2
        DIV     BX
        CMP     DX, 0
        JNE     ODDS
        MOV     DI, OFFSET EVENDISPLAY
        MOV     CX, SIZEOF EVENDISPLAY
        CALL    NEWLINE
        CALL    PUTSTRNG
        CALL    NEWLINE
CALL    NEWLINE
        JMP     EXIT_PROGRAM
ODDS:   
        MOV     DI, OFFSET ODDDISPLAY
        MOV     CX, SIZEOF ODDDISPLAY
        CALL    NEWLINE
        CALL    PUTSTRNG 
CALL    NEWLINE
        CALL    NEWLINE     
EXIT_PROGRAM:
.EXIT
        MOV     AX, 4C00H
        INT     21H
MAIN    ENDP
        END MAIN


;;===================================================================
;              Veronica Kaufman
;                 CISP 310
;                PARITY.ASM
;===================================================================
         .MODEL  LARGE     
;===================================================================
; D A T A   S E G M E N T   D E F I N I T I O N
        .DATA
ONES_COUNT     DW      0                           
;===================================================================
; C O D E   S E G M E N T   D E F I N I T I O N
        .CODE   
        ASSUME  DS:DGROUP
;===================================================================

PARITY  PROC    FAR PUBLIC USES CX DX DS
MOV ONES_COUNT, 0
NUM_LOOP:
CMP AX, 0
JE  END_PROGRAM
SHL AX, 1
JC  INCREMENT
JMP NUM_LOOP
INCREMENT:
INC ONES_COUNT
JMP NUM_LOOP
END_PROGRAM:
MOV AX, ONES_COUNT
RET
PARITY  ENDP
        END PARITY
Title: Re: Counting Set Bits
Post by: dedndave on October 16, 2009, 06:17:15 PM
that's ok, Veronica
your prof would know the above routine was not written by a student - lol
it is the result of 3 fairly experienced asm programmers bumping ideas together
Title: Re: Counting Set Bits
Post by: FORTRANS on October 16, 2009, 06:18:05 PM
Hi Dave,

   Doing any timing tests?


; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; 0 = 1, 1 = 8, 2 = 28, 3 = 56, 4 = 70, 5 = 56, 6 = 28, 7 = 8, 8 = 1
CntTable DB     0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4
        DB      1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5
        DB      1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5
        DB      2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6
        DB      1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5
        DB      2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6
        DB      2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6
        DB      3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7
        DB      1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5
        DB      2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6
        DB      2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6
        DB      3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7
        DB      2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6
        DB      3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7
        DB      3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7
        DB      4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; Counts bits set in AX.  Returns count of bits in AX, original
; AX destroyed.
PARITY  PROC
        MOV BX, OFFSET CntTable
        XLATB
        XCHG    AL,AH
        XLATB
        ADD     AL,AH
        XOR     AH,AH
        RET
PARITY  ENDP


Cheers,

Steve N.
Title: Re: Counting Set Bits
Post by: dedndave on October 16, 2009, 06:20:32 PM
Hutch tells me that XLAT is slow on P4's - that may only apply to 32-bit code - i dunno
you could probably make that zing with mov al,CntTable[bx] though -  :bg
i don't think we can use Michael's timing macros in 16-bit world and get meaningful readings
i could be wrong, there, but i think we need a 16-bit timer of some sort
Title: Re: Counting Set Bits
Post by: ilian007 on October 16, 2009, 06:41:56 PM
hey Veronica why you have the varible "number" it seems it doesnt do anything just printing statement is between the two number,ax  ax,number ?
Title: Re: Counting Set Bits
Post by: FORTRANS on October 16, 2009, 09:39:18 PM
Hi Dave,

   I found some timer code from Abrash's book(s).  But
the souce was apparently OCRed somewhere along the line,
And there will be some clean-up involved.  Do you have any
interest in seeing timing results?

Regards,

Steve N.
Title: Re: Counting Set Bits
Post by: dedndave on October 16, 2009, 10:07:05 PM
sure - although - i dunno what "OCR'ed" means - lol
but i am sure if they have workable code, we can make it fly
it would be good to have some 16-bit timing code around
it shouldn't be very large
and - all we really need is a concept
i suppose we could use the counter-timer, but that means long tests because it has poor resolution
Title: Re: Counting Set Bits
Post by: sinsi on October 16, 2009, 11:07:28 PM
Is that the zen timer? Have a look at chapter 22 here (http://www.gamedev.net/reference/articles/article1698.asp)
Title: Re: Counting Set Bits
Post by: dedndave on October 17, 2009, 12:21:36 AM
hiyas Sinsi
i finally found the zen timer in ch 3 - lol
that looks like a good resource - thanks
let me have a look at the timer.....
Title: Re: Counting Set Bits
Post by: dedndave on October 17, 2009, 12:37:36 AM
i found the lib with source...
http://www.programmersheaven.com/download/13855/download.aspx
at first glance, it appears to use the counter/timer chip


yah - it uses the 8253 - which means no clock cycle readings like we have come to love from Michael's macros
what would be really cool would be to learn to load and run a 16-bit app under a 32-bit console using NTVDM.EXE
then, we could use Michael's macros and subtract the load-time overhead
using the counter/timer means we need some way to measure the CPU clock frequency under 16-bit
i.e. the zen timer will yield times to the nearest 18.2 ms - not clock cycles
Title: Re: Counting Set Bits
Post by: sinsi on October 17, 2009, 03:21:59 AM
I don't see why michael's macros can't be used in 16-bit real mode with a bit of adjustment. We can still use rdtsc, which is the guts of measuring cycles.
Title: Re: Counting Set Bits
Post by: dedndave on October 17, 2009, 09:59:39 AM
Michael wrote a nice set of macros for us   :U

http://www.masm32.com/board/index.php?topic=12540.msg96548#msg96548
Title: Re: Counting Set Bits
Post by: FORTRANS on October 17, 2009, 12:11:28 PM
Hi,

Quotei dunno what "OCR'ed" means

   Sorry.  Optical Character Recognition, scan in a printed
copy, and make a guess as to what the text really is.
Error prone if the hard copy is poor.  Plus in the version I
tracked down, all the "'" got changed to something out
of M$ Word.

QuoteIs that the zen timer? Have a look at chapter 22 here

   Well, another source, but chapter 3 of that book.

QuoteMichael wrote a nice set of macros for us

   Goodie!  Saves me some effort, though it won't work
on my 80186.

Thanks.

Steve N.
Title: Re: Counting Set Bits
Post by: MichaelW on October 17, 2009, 12:40:41 PM
Quotei.e. the zen timer will yield times to the nearest 18.2 ms - not clock cycles

In theory it will yield times to the nearest 838ns (1 / 1193182Hz).