Errata copied from old board

Support for Randall Hyde's "The Art of 64-Bit Assembly" book
Post Reply
rhyde
Site Admin
Posts: 51
Joined: Sun Dec 04, 2022 5:36 pm

Errata copied from old board

Post by rhyde »

The first incarnation of this board got hacked and I had to delete it. Before doing so, I grabbed the following errata to repost here:

Example code in section 12-12
-----------------------------

The example code in Section 12-12 ("Merging Bit Strings" ) is incorrect.
Currently the code is:
CODE: SELECT ALL
; Merge two 16-bit strings into a single 32-bit string.
; AX - Source for even numbered bits.
; BX - Source for odd numbered bits.
; CL - Scratch register.
; EDX- Destination register.

mov cl, 16
MergeLp: shrd edx, eax, 1 ; Shift a bit from EAX into EDX.
shrd edx, ebx, 1 ; Shift a bit from EBX into EDX.
dec cl
jne MergeLp;


It should be:

Code: Select all

CODE: SELECT ALL
; Merge two 16-bit strings into a single 32-bit string.
; AX - Source for even numbered bits.
; BX - Source for odd numbered bits.
; CL - Scratch register.
; EDX- Destination register.

          mov cl, 16 
MergeLp:  shrd edx, eax, 1     ; Shift a bit from EAX into EDX. 
          shrd edx, ebx, 1     ; Shift a bit from EBX into EDX.
          shr  eax, 1
          shr  ebx, 1 
          dec  cl 
          jne  MergeLp; 
The shrd instruction does not modify the second operand. For this code to work properly, EAX and EBX must also be shifted to the right one bit on each iteration of the loop.

The second block of instructions in this section suffers from the same problem:

Code: Select all

CODE: SELECT ALL
shrd edx, eax, 6
shrd edx, ebx, 5
shrd edx, eax, 6
shrd edx, ebx, 11
shrd edx, eax, 4
It should be:

Code: Select all

CODE: SELECT ALL
shrd edx, eax, 6
shr  eax, 6
shrd edx, ebx, 5
shr  ebx, 5
shrd edx, eax, 6
shr  eax, 6
shrd edx, ebx, 11
shrd edx, eax, 4
Cheers,
Randy Hyde


Sections 9.2.1 and 9.2.3 are redundant
--------------------------------------
I just discovered that sections 9.2.1 and 9.2.3 cover the exact same material. Interesting boo-boo. Sorry for confusing anyone if you're wonder what the difference was.
Cheers,
Randy Hyde

Errata: Pointers
----------------
On page 162:
A MASM pointer is a 64-bit value that may contain the address of another
variable. If you have a dword variable p that contains 1000_0000h, then p
“points” at memory location 1000_0000h. To access the dword that p points
at, you could use code like the following:

Code: Select all

mov rbx, p
mov rax, [rbx]
Isn't this completely wrong?
First, "p" is not a qword (64-bit),
Second, "mov rbx,p" is completely wrong because p is a dword and we are trying to put a dword into a qword register.

Re: Errata: Pointers
Report Quote
Unread post by randyhyde » Sun Feb 13, 2022 6:55 pm

Yes, p should have been a qword.
cheers,
Randy Hyde

Listing 5-15 QuickSort
Report Quote
Unread post by sevilla.larry » Sun Nov 14, 2021 12:12 am

Hi tried the quicksort and add more data.

Modification:

Code: Select all

numElements = 20

theArray dword 1,10,2,9,3,8,4,7,5,6
dword 99, 7, 2, 88, 5, 77, 66, 44, 3, 55
and got this:

Data after sorting:
1
2
2
3
3
4
5
5
6
7
7
8
9
10
55
44
66
77
88
99

I'm not familiar with the algorithm, but (seems) failed...

Re: Listing 5-15 QuickSort
Report Quote
Unread post by randyhyde » Sun Feb 13, 2022 6:53 pm

There was a missing push and pop (needed to preserve R9 around the swap).
Cheers,
Randy Hyde

Working code:
CODE: SELECT ALL

Code: Select all

; Listing 5-15
;
; Recursive quicksort

        option  casemap:none

nl          =       10
numElements =       20


            .const
ttlStr      byte    "Listing 5-15", 0
fmtStr1     byte    "Data before sorting: ", nl, 0
fmtStr2     byte    "%d "   ;Use nl and 0 from fmtStr3
fmtStr3     byte    nl, 0
fmtStr4     byte    "Data after sorting: ", nl, 0

        
            .data
theArray    dword   1,10,2,9,3,8,4,7,5,6
            dword   99, 7, 2, 88, 5, 77, 66, 44, 3, 55
        
            .code
            externdef printf:proc
            
; Return program title to C++ program:

            public  getTitle
getTitle    proc
            lea     rax, ttlStr
            ret
getTitle    endp


; quicksort-
;
;  Sorts an array using the quicksort algorithm.
;
; Here's the algorithm in C, so you can follow along:
;
; void quicksort(int a[], int low, int high)
; {
;     int i,j,Middle;
;     if( low < high)
;     {
;         Middle = a[(low+high)/2];
;         i = low;
;         j = high;
;         do
;         {
;             while(a[i] <= Middle) i++;
;             while(a[j] > Middle) j--;
;             if( i <= j)
;             {
;                 swap(a[i],a[j]);
;                 i++;
;                 j--;
;             }
;         } while( i <= j );
;  
;         // recursively sort the two sub arrays
;
;         if( low < j ) quicksort(a,low,j-1);
;         if( i < high) quicksort(a,j+1,high);
;     }
; }
;
; Args:
;    RCX (_a):      Pointer to array to sort
;    RDX (_lowBnd): Index to low bound of array to sort
;    R8 (_highBnd): Index to high bound of array to sort    

_a          equ     [rbp+16]        ;Ptr to array
_lowBnd     equ     [rbp+24]        ;Low bounds of array
_highBnd    equ     [rbp+32]        ;High bounds of array

; Local variables (register save area)

saveR9      equ     [rbp+40]        ;Shadow storage for R9
saveRDI     equ     [rbp-8]
saveRSI     equ     [rbp-16]
saveRBX     equ     [rbp-24]
saveRAX     equ     [rbp-32]

; Within the procedure body, these registers
; have the following meaning:
;
; RCX: Pointer to base address of array to sort
; EDX: Lower bound of array (32-bit index).
; r8d: Higher bound of array (32-bit index).
;
; edi: index (i) into array.
; esi: index (j) into array.
; r9d: Middle element to compare against

quicksort   proc
            push    rbp
            mov     rbp, rsp
            sub     rsp, 32
                        
; This code doesn't mess with RCX. No
; need to save it. When it does  mess
; with RDX and R8, it saves those registers
; at that point.

; Preserve other registers we use:

            mov     saveRAX, rax
            mov     saveRBX, rbx
            mov     saveRSI, rsi
            mov     saveRDI, rdi
            mov     saveR9, r9
            
            mov     edi, edx        ;i=low
            mov     esi, r8d        ;j=high

; Compute a pivotal element by selecting the
; physical middle element of the array.
        
            lea     rax, [rdx+r8*1]   ; RAX=low+high
            shr     rax, 1            ; (low+high)/2
            mov     r9d, [rcx][rax*4] ;Middle = ary[(i+j)/2]
                    

; Repeat until the edi and esi indexes cross one
; another (edi works from the start towards the end
; of the array, esi works from the end towards the
; start of the array).

rptUntil:   cmp     edi, esi        ;while( i <= j)
            jnle    sortPartitions
        
; Scan from the start of the array forward
; looking for the first element greater or equal
; to the middle element).
            
            dec     edi     ;to counteract inc, below
while1:     inc     edi     ;i = i + 1
            cmp     [rcx][rdi*4], r9d ;While ary[i] < middle 
            jl      while1

; Scan from the end of the array backwards looking
; for the first element that is less than or equal
; to the middle element.

            inc     esi     ;To counteract dec, below
while2:     dec     esi     ;j = j - 1
            cmp     [rcx][rsi*4], r9d       ;while ary[j] < Middle
            jg      while2            
            
            
; If we've stopped before the two pointers have
; passed over one another, then we've got two
; elements that are out of order with respect
; to the middle element, so swap these two elements.
            
            cmp     edi, esi ;If i <= j
            jnle    sortPartitions            
           
            push    r9
            mov     eax, [rcx][rdi*4] ;Swap ary[i] and ary[j]
            mov     r9d, [rcx][rsi*4]
            mov     [rcx][rsi*4], eax
            mov     [rcx][rdi*4], r9d
            pop     r9
            
            inc     edi     ;i = i + 1
            dec     esi     ;j = j - 1
                
endif1:     cmp     edi, esi ;Until i > j
            jng     rptUntil
        
; We have just placed all elements in the array in
; their correct positions with respect to the middle
; element of the array. So all elements at indexes
; greater than the middle element are also numerically
; greater than this element. Likewise, elements at
; indexes less than the middle (pivotal) element are
; now less than that element. Unfortunately, the
; two halves of the array on either side of the pivotal
; element are not yet sorted. Call quicksort recursively
; to sort these two halves if they have more than one
; element in them (if they have zero or one elements, then
; they are already sorted).
        
sortPartitions:
            cmp     edx, esi ;if lowBnd < j
            jnl     endif2

            ; Note: a is still in RCX,
            ; Low is still in RDX
            ; Need to preserve R8 (High)
            ; Note: quicksort doesn't require stack alignment
                

            cmp     edx, esi        ;if( low < j ) 
            jnl     endif2

            push    rcx 
            push    r8
            mov     r8d, esi    
            call    quicksort ;( a, low, j )
            pop     r8
            pop     rcx
            
            ; Note: a is still in RCX,
            ; High is still in R8d
            ; Need to preserve RDX (low)
            ; Note: quicksort doesn't require stack alignment
              
;call printArray

endif2:     cmp     edi, r8d        ;if( i < high )
            jnl     endif3

            push    rcx
            push    rdx
            mov     edx, edi  
            call    quicksort ;( a, i, High )
            pop     rdx
            pop     rcx

; Restore registers and leave:
            
endif3:
            mov     rax, saveRAX 
            mov     rbx, saveRBX 
            mov     rsi, saveRSI 
            mov     rdi, saveRDI 
            mov     r9, saveR9
            leave
            ret  
quicksort   endp
    


; Little utility to print the array elements:

printArray  proc
            push    r15
            push    rbp
            mov     rbp, rsp
            sub     rsp, 40 ;Shadow parameters
            
            lea     r9, theArray
            mov     r15d, 0
whileLT10:  cmp     r15d, numElements
            jnl     endwhile1
            
            lea     rcx, fmtStr2
            lea     r9, theArray
            mov     edx, [r9][r15*4]
            call    printf
            
            inc     r15d
            jmp     whileLT10

endwhile1:  lea     rcx, fmtStr3
            call    printf
            leave
            pop     r15
            ret
printArray  endp

; Here is the "asmMain" function.

        
            public  asmMain
asmMain     proc
            push    rbp
            mov     rbp, rsp
            sub     rsp, 32   ;Shadow storage
        
; Display unsorted array:

            lea     rcx, fmtStr1
            call    printf
            call    printArray
            

; Sort the array

            lea     rcx, theArray
            xor     rdx, rdx                ;low = 0
            mov     r8d, numElements-1      ;high= 9
            call    quicksort ;(theArray, 0, 9)
            
; Display sorted results:

            lea     rcx, fmtStr4
            call    printf
            call    printArray   
            
            leave
            ret     ;Returns to caller
        
asmMain     endp
            end



Errata Table 2-9
Report Quote
Unread post by alfredmyers » Fri Oct 29, 2021 4:28 pm

I wasn't able to find a place to submit errata, so I hope it's OK here.

On page 71, Table 2-9, item "Carry" where read
Note that subtracting 1 from 0 will also clear the carry flag (that is, 0 – 1 is equivalent to 0 + (–1), and –1 is 0FFh in two’s complement form).
should read:
Note that subtracting 1 from 0 will also set the carry flag

Also of note, I found the setence a little bit confusing, because despite 0-1 and 0 + (-1) resulting in the same bit pattern, the first will set the carry flag, while the second will clear the carry flag.



Re: Errata Table 2-9
Report Quote
Unread post by alfredmyers » Mon Nov 01, 2021 4:55 am

On page 937, item 8

where answer c reads
65,636
should read:

65,536



Re: Errata Table 2-9
Report Quote
Unread post by alfredmyers » Mon Nov 01, 2021 4:10 pm

On page 78, second paragraph, where read:
If the shift count is 1, these two instructions copy the bit shifted out of
the destination operand into the carry flag,
should read:

If the shift count is non-zero, these two instructions copy the last bit shifted out of
the destination operand into the carry flag,

Re: Errata Table 2-9
Report Quote
Unread post by alfredmyers » Fri Nov 05, 2021 3:04 am

On page 112, Figure 3-2, where read:
Offset 0FFFh
in page xxxx + 1
should read:

"Offset 0
in page xxxx + 1"

Re: Errata Table 2-9
Report Quote
Unread post by alfredmyers » Fri Nov 05, 2021 3:15 pm

On page 122, item 3.7.1, where read:
mov ch, cl ; Copies the value from CL into DH
The destination register in comment doesn't match the destination register in the code.

Re: Errata Table 2-9
Report Quote
Unread post by alfredmyers » Sat Nov 06, 2021 4:07 am

On page 125, the operation described in Figure 3-9

mov al, [rbx + 1100h]

is inverted in relation to the text preceding it:

mov [rbx + 1100h], al


Re: Errata Table 2-9
Report Quote
Unread post by alfredmyers » Sat Nov 06, 2021 4:42 am

On page 128, 2nd paragraph where read:
To turn off the large address–aware flag, you need to add an extra command
line option to the ml64 command.
should read:

To turn off the large address–aware flag, you need to add an extra command
line option to the cl command.

Re: Errata Table 2-9
Report Quote
Unread post by randyhyde » Tue Nov 16, 2021 3:04 pm

This is as good a place as any to post erata.
Soon, I will start collecting these notes and adding them to the Ao64A website.
Thanks for the updates.
Cheers,
Randy Hyde

Re: Errata Table 2-9
Report Quote
Unread post by alfredmyers » Wed Nov 24, 2021 3:10 am

On page 123, second to last paragraph where read:
For example, if the next
instruction’s opcode is sitting in memory at location 8000h (the end of the
current instruction), then MASM will encode the value 88h as a 32-bit signed
constant for j in the instruction opcode.
Will only be true if the address for j is 8088h (RIP + 88h).
But this can be a little bit confusing given that the address for j in the preceeding text and diagram was RIP + 8088h.

Errata: Unions
Report Quote
Unread post by iuavvz » Fri Jan 07, 2022 3:19 am

On page 208:
...With a declaration like this, you can manipulate an uns32 object by accessing ...
What is a uns32?! We never mentioned that before.

Re: Errata: Unions
Report Quote
Unread post by randyhyde » Sun Feb 13, 2022 6:58 am

Sorry, that was a lack from translation from the original 32-bit edition (HLA has an uns32 type, which is just dword in MASM).
Cheers,
Randy Hyde


Errata: Redundant jz test
Report Quote
Post by stack2170 » Wed Jan 12, 2022 1:33 pm

In section 14.1.3, page 832, there's the following listing:
CODE: SELECT ALL

Code: Select all

   mov rcx, Length  ; Compute (Length mod 8)
    and rcx, 111b    ; Execute movsb only if # of bytes/8 <> 0
    jz divisibleBy8  ; Copy the remaining 1 to 7 bytes
    rep movsb
divisibleBy8:
The jz test is redundant - the rep instruction doesn't perform any operation if the counter (ecx) is zero, so there's no need to treat such case as special.

Re: Errata: Redundant jz test
Report Quote
Post by randyhyde » Sun Feb 13, 2022 6:57 am

Noted.
Cheers,
Randy Hyde

Errata: Arithmetic Idioms
Report Quote
Unread post by iuavvz » Mon Jan 17, 2022 6:54 pm

Page 311:
CODE: SELECT ALL

Code: Select all

lea eax, [eax][eax * 2] ; EAX = ECX * 3
lea eax, [eax * 4]      ; EAX = ECX * 4
should be:
CODE: SELECT ALL

Code: Select all

lea eax, [eax][eax * 2] ; EAX = EAX * 3
lea eax, [eax * 4]      ; EAX = EAX * 4
Re: Errata: Arithmetic Idioms
Report Quote
Unread post by randyhyde » Sun Feb 13, 2022 6:54 am

Noted, will update in the next printing.
Cheers,
Randy Hyde

Errata: SSE Floating-Point Comparisons
Report Quote
Unread post by iuavvz » Tue Feb 01, 2022 9:38 pm

Page 372:
cmpne qss
should be:
cmpneqss

Re: Errata: SSE Floating-Point Comparisons
Report Quote
Unread post by randyhyde » Sun Feb 13, 2022 6:49 am

Noted.
Cheers,
Randy Hyde

Errata: FPU Data Types
Report Quote
Unread post by iuavvz » Mon Jan 31, 2022 11:48 pm

Page 324:
First figure: 32-bit single-precision floating-point format has 7 bits for the exponent instead of 8.
Top
randyhyde
Site Admin
Posts: 68
Joined: Wed Jan 13, 2021 11:01 am
Re: Errata: FPU Data Types
Report Quote
Unread post by randyhyde » Sun Feb 13, 2022 6:48 am

Yes indeed.
I will note that for the next printing.
Cheers,
Randy Hyde

Errata: Mantissa
Report Quote
Unread post by iuavvz » Sat Jan 22, 2022 12:08 am

Page 313:
For example, in the number 3.456e+12, the mantissa consists of 3.456, and the exponent digits are 12.
Isn't mantissa 0.456, and not 3.456?

From American Heritage Dictionary:
man·tis·sa (măn-tĭs'ə)
n.
The decimal part of a logarithm. In the logarithm 2.95424, the mantissa is 0.95424.
Top
randyhyde
Site Admin
Posts: 68
Joined: Wed Jan 13, 2021 11:01 am
Re: Errata: Mantissa
Report Quote
Unread post by randyhyde » Sun Feb 13, 2022 6:43 am

The American Heritage Dictionary is describing the original (English/mathematic) definition. In binary machine code, the mantissa and exponent fields are defined by their bit position in the floating-point format. In that context, the book is correct.
Cheers,
Randy Hyde

Errata: Answers to Questions in Chapter 6
Report Quote
Unread post by iuavvz » Tue Feb 01, 2022 10:40 pm

Page 945:

Answer to this question:
x = -((x*y)/z)
is:

Code: Select all

mov eax, x
imul y ; Note: Sign-extends into EDX
idiv z
mov x, eax
Where is the
CODE: SELECT ALL

Code: Select all

neg eax
?
Top
randyhyde
Site Admin
Posts: 68
Joined: Wed Jan 13, 2021 11:01 am
Re: Errata: Answers to Questions in Chapter 6
Report Quote
Unread post by randyhyde » Sun Feb 13, 2022 6:41 am

Obviously, should be before the "mov x, eax" instruction.
Post Reply