Chapter 7.1, 7.4 Notes and Visual C++

Document last modified: 

Overview

The question of how to compute arithmetic results that are beyond the capacity of the processor has a simple and easy to implement solution for the problem of large number of bits in addition or subtraction.

Use of the 32-bit register set of the 386 and above processor will be examined along with minor changes to the way that we have been writing Assembler programs. Use of the Visual C++ environment for debugging 32-bit Assembler programs will also be examined in the following.
 

Addition and Subtraction

As we have seen, when addition exceeds the size of the destination a carry of 1 is generated. Suppose that we were adding either one set of 3 digit numbers or three sets of 1 digit numbers (and add the carry from the lower digit addition). The result is the same.
 010 Carry                0   1   0   Carry
 627                     +6  +2  +7 
+246                     +2  +4  +6
 873                      8   7   3
This idea could be extended for any number of digit addition. Subtraction works much the same way except that instead of carry when the result is too large, on subtraction the carry flag of 1 indicates a borrow is required from the higher digit(s).
 011 Borrow               0   1   1   Borrow
 647                      6   4   7 
-478                     -4  -7  -8
 169                      1   6   9
Adc and Sbb - To implement 48-bit addition or subtraction requires propagating the carry or borrow from lower to higher additions or subtractions as we did above. The Adc is just an Add instruction that adds the carry flag value, Sbb is just a Sub that subtracts the carry flag value.
 
Adc and Sbb
Adc  Al, Bh Sbb  Al, Bh
   Al
  +CF
  +Bh
   Al
   Al
  -CF
  -Bh
   Al

Examples of adding 12341234567856789ABC9ABCx + CBA9CBA98765876543214321x and subtracting CBA9CBA98765876543214321x - 12341234567856789ABC9ABCx are below. In both cases the result is in the combined three registers eAx:eBx:eCx.
 

Mov  eAx, 12341234h
Mov  eBx, 56785678h
Mov  eCx, 9ABC9ABCh

Mov  eDx, 0CBA9CBA9h
Mov  eSi, 87658765h
Mov  eDi, 43214321h

Add  eCx, eDi
Adc  eBx, eSi
Adc  eAx, eDx
Mov  eAx, 12341234h
Mov  eBx, 56785678h
Mov  eCx, 9ABC9ABCh

Mov  eDx, 0CBA9CBA9h
Mov  eSi, 87658765h
Mov  eDi, 43214321h

Sub  eCx, eDi
Sbb  eBx, eSi
Sbb  eAx, eDx
 eAx:eBx:eCx
+eDx:eSi:eDi
 eAx:eBx:eCx
 eAx:eBx:eCx
-eDx:eSi:eDi
 eAx:eBx:eCx

386+ vs. earlier processors

The 386 processor has considerably greater capabilities than the 8086 processor. The key added features of the 386 architecture are: compatibility with previous 80x86 processors, virtual memory, larger registers. However, for the purposes of writing C callable assembler functions, we are only interested in one aspect of the 386, the 32-bit register set. The key point is all registers except the segment registers have 32-bit versions, the segment registers are still 16-bit. The format of the X-registers and others are illustrated at right. For example, the eAx register is 32-bit with the low 16 bits being Ax. Ax is still 16-bit, Ah and Al are 8-bit. eBp, eSp, eDi, and eSi are 32-bit versions with the16-bit version in the low bits (i.e. Bp. Sp, Di, and Si). The segment registers still serve the same purpose as before except now the segments are 232 rather than 216 memory bytes.

Real and Protected Mode - Most 16-bit instructions are also 32-bit, addition, subtraction, move, etc. Multiplication of two 32-bit operands, one in eAx, produces a 64-bit result in eDx:eAx; division uses eDx:eAx for the dividend, eAx is the quotient and eDx the remainder.

 
    eAx 
   *eBx
eDx:eAx
 
         eAx   
eBx /  eDx:eAx
         eDx
Push and Pop instructions behave differently whether for real or 16-bit mode (a 16-bit value is pushed or popped) or protected or 32-bit mode (a 32-bit value is pushed or popped). For example:
16-bit
Push Ax
Pop  Ax
PushA
PopA
PushF
PopF
 
32-bit
Push eAx
Pop  eAx
PushAD
PopAD
PushFD
PopFD

32-bit Assembly Programming and Input/Output Routines

32-bit programming requires some changes to programs and input/output routines. The following compares a 16-bit versus on the left with a 32-bit program on the right. The changes are given in bold. The following is an example of assembling the HelloWorld.asm program:

16-bit vs. 32-bit Assembler Hello World
Extrn	PutStrng:Far

data  Segment
      HelloWorld db  'Hello World'
data  Ends

code  Segment

Main proc Near
        Assume  Cs:code, Ds:data

        Mov     Bx, Seg Data       
        Mov     Ds, Bx
	Mov	Es, Bx             
        
	Lea	Di, HelloWorld
	Mov	Cx, 11
	Call	PutStrng

      	Mov	Ah, 4ch   ;Stop
      	Int	21h

Main endp

code Ends

	end 	Main
.386
      .model flat, stdcall

      include kernel32.inc
      include masm32.inc
      includelib kernel32.lib
      includelib masm32.lib

      WriteString	proto

.data
      HelloWorld	db	'Hello World',0

.code

Main proc Near
	Lea	eDX, HelloWorld
	
	Call	WriteString

      	

Main endp

	end 	Main

A more complex example in C++ and Assembler to compute the maximum of two unsigned numbers.

C++ and Assembler Maximum
// maximum.cpp
#include <iostream.h>

int X, Y;
char *IsMaximum = " is maximum\r\n"; 
char *PromptX = Enter X:\r\n";
char *PromptY = Enter Y:\r\n";

void main( void ) {
    cout << PromptX;
    cin >> X;
    cout << PromptY;
    cin >> Y;

    if (X > Y) 
        cout << X;
    else
        cout << Y;

    cout << IsMaximum;
}

















; Maximum.Asm
.386
      .model flat, stdcall

      include kernel32.inc
      include masm32.inc
      includelib kernel32.lib
      includelib masm32.lib

      WriteString	proto
      ReadDec		proto
      WriteDec		proto

.data
	X		dd 	?
	Y		dd	?
	IsMaximum	db 	' is maximum',0, 10, 13
	PromptX		db	'Enter X:',0, 10, 13
	PromptY		db	'Enter Y:',0, 10, 13

.code

Main proc Near
	Lea	eDX, PromptX
	Call	WriteString

	Call	ReadDec
	Mov	X, eAx

	Lea	eDX, PromptY
	Call	WriteString

	Call	ReadDec
	Mov	Y, eAx

   ifA: 
	Mov	eAx, X
	Cmp	eAx, Y
	Jg	thenA
	Jmp	elseA
   thenA:
	Mov	eAx, X
	Call	WriteDec
	Jmp	endifA
   elseA:
	Mov	eAx, Y
	Call	PutDec
   endifA:
	Lea	eDx, IsMaximum
	Call	WriteString

      
Main endp

	end 	Main

Input/Output in Assembler

For now there are five basic input/output routines similar to those for 16-bit programs supplied by the text. See the above Maximum.asm program for a complete example. Note that these are case sensitive. The 32-bit routines are:

  1. WriteDec - Outputs a 32-bit signed number in eAx to the console.
  2. ReadDec - Inputs a 32-bit signed number from the console to eAx.
  3. WriteString - Outputs a string to the console.  The string to be written is referenced by eDx.
  4. WriteChar -  Outputs a character in Al to the console.
  5. NewLine - Outputs a new line to the console.

Using Visual Studio Debugger with Assembler

The Visual Studio debugger can be used to debug 32-bit Assembler executable programs.

  1. Start Visual Studio .NET
  2. File | Open | Project
  3. HelloWorld.exe
  4. Press F10 key to open the HelloWorld.asm file and start execution.
  5. Save the solution. Required before .NET will continue.
  6. In the figure at right, the arrow point to the next instruction to execute. Passing the mouse pointer over eCx in the assembly code displays the value of eCx=11.
Execute Stop - Necessary before assembling the program again. Debug Commands
Document last modified: