I need to find out how many times does the sequence 101b shows in a 16 bit number. But I need to find also the ones that are far away.
For example: in the number 01010101 it appears 4 times. Because 3 are part of it and the fourth one (if index 0 is the left bit) is composed of the 3 bits at index 1, 4, and 7.
Because you can relate to it as symmetrical 101b sequence.
It seems really complicated, but does it really? I think it might just be a little tricky.
I managed to find out how many times it shows regularly, like the 3 that you can see in the example number. But I don't know how can I find the symmetrical ones. EDIT:My teacher did mean rotation and I misunderstood the problem, thanks for everyone for the help though
mov cx,15
Check:
push dx;the number that I need to check
and dx,0111b
cmp dx,101b
jne Again
Again:
pop dx
shr dx,1
loop check
somthing like this (not tested):
mov dx, yourNumber
mov cx, 16
xor ax, ax
count:
mov bx, dx
and bx, 0b111
cmp bx, 0b101
jne nope
inc ax
nope:
rol dx, 1
dec cx
jnz count
so basically, you rotate the register 16 times and on each iteration to test, if the lowest 3 bits by masking the register with 0b111 are equal to 0b101. after this calculation, the result is in ax and the number you tested should be still in dx
The logic:
If you XOR the value with 1010101010101010b (0xAA), any 4-bit group that was 0101b will become 1111b, and any group that wasn't won't.
If you add 1 to 1111b it will overflow, and if you add 1 to any other number it won't overflow. You can arrange this so when the addition overflows it causes the carry flag to be set.
You can use the adc instruction to add the carry flag to a count/sum to get the total number of groups that overflowed (the number of groups that were 0101b originally).
This will only find 0101b that is aligned to the start of the group. To make it work for "0101b anywhere" you'd need to do it 4 times, with a rotate between each time.
The code:
mov cx,4 ;Number of times to rotate/repeat
xor ax,ax ;ax = current sum of 4-bit groups that were 0101b = 0
.nextRotation:
mov bx,dx ;bx = the value
xor bx,0xAAAA ;dx = any 4-bit group that was 0101b is now 1111b
add bx,0x1000 ;Carry set if fourth/highest group was originally 0101
adc ax,0 ;Add carry to the sum
add bl,0x10 ;Carry set if second group was originally 0101b
adc ax,0 ;Add carry to the sum
shl bx,4
add bx,0x1000 ;Carry set if third group was originally 0101b
adc ax,0 ;Add carry to the sum
add bl,0x10 ;Carry set if first/lowest group of 4 bits was originally 0101b
adc ax,0 ;Add carry to the sum
;ax = number of 4-bit groups that were originally 0101b
rol dx,1
loop .nextRotation
I'm not sure I've understood where the 4th copy is. I (and @sivizius) were thinking it was composed of bits 7, 0, and 1 (wrapping around to the top bit). But apparently that's not what you want.
(Also your bit numbering makes no sense. You say the top bit of your 8-bit number is bit 0, but in comments you insist you want to work with 16-bit numbers. So what bit-number is 1<<15?)
Anyway, I think you want to find patterns like 1.0.1 and 1..0..1? (Where . is a don't-care placeholder in the pattern that can match either a 0 or 1).
You can look for those with something like this (inside a shr dx, 1 loop).
; dumb brute force method.
countloop: ; do {
mov ax, dx
and al, 111b ; select the bits that matter in 101
cmp al, 101b ; check that it's the pattern we want
; do count += ZF somehow
mov ax, dx
and al, 10101b ; select the bits that matter in 1.0.1
cmp al, 10001b ; check that it's the pattern we want
; do count += ZF somehow
mov ax, dx
and al, 1001001b ; select the bits that matter in 1..0..1
cmp al, 1000001b ; check that it's the pattern we want
; do count += ZF somehow
... for all the rest of the patterns.
; Wider patterns will need to use AX instead of AL
shr dx, 1
cmp dx, 101b
jae countloop ; }while(x >= 0b101);
;;; exit the loop as soon as DX contains no bits high enough to possibly match
;;; with a big loop, this is worth the extra cmp
;;; instead of just using jnz on the shr dx,1 result
So exactly like you're doing for 101b except you force the don't-care bits to zero.
I haven't thought of anything better than checking separately for every possible pattern separately inside the loop body.
The "count += ZF" could be (386) setz al / add cl, al, or a simple jnz over inc cx. (I think the max possible count is less than 255, so an 8-bit counter is fine.)
Note that I'm not using cx as a loop counter, instead ending the loop after shifting all the bits out of dx
Another way to do it branchlessly without SETCC would be
mov ax, dx
and al, 10101b
xor al, 10001b
sub al, 1 ; set CF only if AL==0
adc cx, 0 ; cx += (al == 0b10001)
It seems really complicated, but does it really? I think it might just be a little tricky.
Yes, it is really complicated. Here's how far I've got:
Step 1: Do "run-length encoding", ignoring leading and trailing zeros. Examples (for 8-bit numbers):
01010101 = 1(1s), 1(0s), 1(1s), 1(0s), 1(1s), 1(0s), 1(1s)
11001011 = 2(1s), 2(0s), 1(1s), 1(0s), 2(1s)
11111101 = 6(1s), 1(0s), 1(1s)
Step 2: Find permutations of "run of ones, run of zeros, run of ones" and multiply each run's count. Examples (for 8-bit numbers):
01010101 = 1(1s), 1(0s), 1(1s), 1(0s), 1(1s), 1(0s), 1(1s)
= 1 *1 *1
+ 1 *1 *1
+ 1 *1 *1
+ 1 *1 *1
+ 1 *1 *1
+ 1 *1 *1
+ 1 *1 *1
+ 1 *1 *1
+ 1 *1 *1
= 1+1+1+1+1+1+1+1 = 9
11001011 = 2(1s), 2(0s), 1(1s), 1(0s), 2(1s)
= 2 *2 *1
+ 2 *2 *2
+ 2 *1 *2
+ 1 *1 *2
= 4+8+4+2 = 18
11111101 = 6(1s), 1(0s), 1(1s)
= 6 *1 *1
= 6
At this point; it's easy to conclude that an efficient implementation would involve lists, and would probably be easier to implement if recursion is used (find permutations that involve the first run of ones in the list, then discard the first run of ones and the first run of zeros and recurse).
来源:https://stackoverflow.com/questions/56608545/how-can-i-find-how-many-times-does-101b-show-in-the-number