Assembly Language Programming

October 6, 2012

FASM – Big Hex Number Calculator

This program is a programmable big hexadecimal number calculator, written in x86 assembly language using FASM (the flat assembler). It works with unsigned integers that can be up to 400 bytes (800 hex digits) in length. It looks like this:

Usage

Up to 14 parameters are available to the user of the program, all of which are 400 byte hex numbers. Ten of these parameters can be defined by the user, while the other 4 are built-in.

The edit control on the left allows the user to enter up to ten 400 byte input parameters. Curly brackets are used to define the name of a parameter. A parameter name can be up to 8 characters in length and can include letters, numbers and an underscore. It is case sensitive. The parameter name is followed by a hex value which can be up to 400 bytes (or 800 hex digits) in length. If nothing follows a parameter name definition, the value of that parameter will be initialised to zero. For example:

Define the parameters A = 2, B = 3 and a = 4:

{A} 2
{B} 3
{a} 4

Initialise a parameter to a 400 byte value:

{Wxyz_123}

01234567 89abcdef 01234567 89abcdef 01234567 89abcdef 01234567 89abcdef 01234567 89abcdef
01234567 89abcdef 01234567 89abcdef 01234567 89abcdef 01234567 89abcdef 01234567 89abcdef
01234567 89abcdef 01234567 89abcdef 01234567 89abcdef 01234567 89abcdef 01234567 89abcdef
01234567 89abcdef 01234567 89abcdef 01234567 89abcdef 01234567 89abcdef 01234567 89abcdef
01234567 89abcdef 01234567 89abcdef 01234567 89abcdef 01234567 89abcdef 01234567 89abcdef
01234567 89abcdef 01234567 89abcdef 01234567 89abcdef 01234567 89abcdef 01234567 89abcdef
01234567 89abcdef 01234567 89abcdef 01234567 89abcdef 01234567 89abcdef 01234567 89abcdef
01234567 89abcdef 01234567 89abcdef 01234567 89abcdef 01234567 89abcdef 01234567 89abcdef
01234567 89abcdef 01234567 89abcdef 01234567 89abcdef 01234567 89abcdef 01234567 89abcdef
01234567 89abcdef 01234567 89abcdef 01234567 89abcdef 01234567 89abcdef 01234567 89abcdef

Define the parameters a, b, c, d, e ,f, g, h ,i, j and initialise to zero:

{a}{b}{c}{d}{e}{f}{g}{h}{i}{j}

Once the parameters and their values have been entered, press the [Load Parameters] button to read them into memory. To clear all user defined parameters, press the [Clear Parameters] button.

There are also 4 built-in parameters: reg_1, reg_2, reg_3 and reg_4. These are displayed in the edit control on the lower right.

Calculations are implemented by entering instructions into the edit control on the upper right. Up to 40 instructions can be entered and executed at any one time. Instructions are executed by pressing the [Run Instructions] button. Press the [Clear Instructions] button to clear the instructions edit control.

Press the [Reset All] button to clear everything.

Instructions

The instructions used to perform arithmetic operations are like x86 assembly language instructions. But instead of the arguments being registers or memory locations, they are the 400 byte hex number parameters as described above. Some of the instructions also take an immediate value as one of their arguments. Depending on the instruction, the immediate value will be read either as a hex number or a decimal number. The hex number immediate values can be up to 16 bytes (32 hex digits) in length and must not contain any white space or non hex digit characters. The decimal number immediate values can only be up to 4 decimal digits in length and must not be greater than 3200.

The instruction are not case sensitive. So for example, mov can be written as: mov, Mov or MOV. The arguments (the parameter names) are case sensitive.

The instructions and their arguments are white space delimited, and there can also be any amount of leading and trailing white spaces.

A simple example:

Movi reg_1 1234567890abcdef
Movi reg_2 abcdef0987654321
Movi reg_3 1234abcd

Add reg_2 reg_1

Not reg_1

Mul reg_3 reg_1

Xor reg_3 reg_2

Rotr reg_2 127

In other words: move some immediate hex values into reg_1, reg_2 and reg_3. Then add reg_1 to reg_2. Not reg_1 and then multiply reg_3 by reg_1. Xor reg_2 into reg_3 and then right rotate reg_2 by 127 bits.

Single Argument Instructions

Dest is one of the 400 byte hex values.

Not dest NOT the destination value: dest = not dest
Neg dest Negate the destination value: dest = -dest = 1 + not dest
Clr dest Clear the destination value: dest = 0

Double Argument Instructions

Both dest and src are one of the 400 byte hex values.

Mov dest src Copy the source value to the destination: dest = src
Add dest src Add the source value to the destination: dest = dest + src
Sub dest src Subtract the source value from the destination: dest = dest – src
Mul dest src Multiply the destination by the source: dest = dest x src
Div dest src Divide the destination by the source: dest = dest / src
Mod dest src Compute destination modulo source: dest = dest mod src
And dest src Bitwise AND of the destination and source: dest = dest AND src
Or dest src Bitwise OR of the destination and source: dest = dest OR src
Xor dest src Bitwise XOR of the destination and source: dest = dest XOR src
Xchg dest src Exchange the destination and source values: swap(dest,src)

Double Argument Instructions

Dest is one of the 400 byte hex values. The second argument is a 16 byte (128 bit) hex value. There must be no spaces or non hex digits in this argument.

Movi dest immed Copy the immediate value to the destination: dest = immed
Addi dest immed Add the immediate value to the destination: dest = dest + immed
Subi dest immed Subtract the immediate value from the destination: dest = dest – immed
Muli dest immed Multiply the destination by the immediate value: dest = dest x immed
Divi dest immed Divide the destination by the immediate value: dest = dest / immed
Modi dest immed Compute the modulo of the destination with the immediate value: dest = dest mod immed
Andi dest immed Bitwise AND the immediate value into the destination: dest = dest AND immed
Ori dest immed Bitwise OR the immediate value into the destination: dest = dest OR immed
Xori dest immed Bitwise XOR the immediate value into the destination: dest = dest XOR immed

Double Argument Instructions

Dest is one of the 400 byte hex values. The second argument is a decimal number of up to 4 digits. It cannot be greater than 3200.

Shl dest immed Bitwise left shift of the destination value by the number of bits given by the immediate value.
Shr dest immed Bitwise right shift of the destination value by the number of bits given by the immediate value.
Rotl dest immed Bitwise left rotation of the destination value by the number of bits given by the immediate value.
Rotr dest immed Bitwise right rotation of the destination value by the number of bits given by the immediate value.
Trunc dest immed Truncate the destination value to the number of bits given by the immediate value.
Bset dest immed Set the number of bits in the destination value as given by the immediate value.

Single Argument Instructions

The argument is an immediate value which is read as a decimal number of up to 4 digits. It cannot be greater than 3200.

Nbytes immed When a parameter is shifted or rotated, first truncate it to nbytes as given by the immediate value.
Repeat immed Repeat the preceding instructions by the number of times given by the immediate value.

The Nbytes instruction affects the SHL, SHR, ROTL and ROTR instructions. It sets the word size that these instructions operate on. For example Nbytes 4 sets the word size to 4 bytes, and any parameter which is being shifted or rotated will be treated as a 4 byte value by these operations.

Nbytes must be in the range 1 – 400. Any value outside of this range will cause the nBytes value to be set to the default of 400.

Calculations

Here are some examples showing the sort of calculations that can be done with the program.

GCD – Euclidean Algorithm

To find the greatest common divisor (GCD) of two numbers A and B, the Euclidean Algorithm repeatedly applies the identity:

GCD(A, B) = GCD(B, A mod B)

– until A mod B becomes zero.

To use the calculator to find the GCD of a pair of numbers, first enter the numbers (for example):

{A}

BBA56355353BBBF4ADCE523423A

{B}

13423234242AC5EFDA3145FCDE3141

Then enter the instructions:

Mod A B
Xchg A B

Repeatedly press the [Run Instructions] button to execute these two instructions again and again until B has been reduced to zero. At this point, A will hold the value of the GCD (which in this case is 1).

AES – xtime Function

From the Advanced Encryption Standard, the finite field multiplication function xtime():

=========================================================================

xtime - finite field multiplication of the byte b by 2:

b' = 2 x b = shl(b,1) and mask

mask = 1B or ((shr(b,7) * FF) xor FF)

shr(b,7) == 0: mask = 1B or ((0 * FF) xor FF) = FF

shr(b,7) == 1: mask = 1B or ((1 * FF) xor FF) = 1B

=========================================================================

Enter these parameters and press [Load Parameters]:

{bytes}

{xtime}

{b}

{mask}

Then enter the following instructions and press [Run Instructions]:

shl bytes 8
or bytes b

mov reg_1 b
shl reg_1 1

mov mask b
shr mask 7
muli mask ff
xori mask ff
ori mask 1b

and reg_1 mask

shl xtime 8
or xtime reg_1

addi b 1

repeat 255

These instructions fill an array with the byte values 0 – FF, and a second array with the byte values after finite field multiplication by 2.

TEA – The Tiny Encryption Algorithm

The algorithm for TEA is:

=========================================================================

Input vector (64 bits): v0 v1

Input key (128 bits): k0 k1 k2 k3

sum = 0

delta = 9e3779b9

loop x 32
{
 sum += delta 

 v0 += (shl(v1,4) + k1) xor (v1 + sum) xor (shr(v1,5) + k1)

 v1 += (shl(v0,4) + k2) xor (v0 + sum) xor (shr(v0,5) + k3)
}

=========================================================================

Test Vector:

The Key: k0 k1 k2 k3 = 11223344 55667788 9900aabb ccddeeff

Plain Text: v0 v1 = 12345678 90abcdef

Cipher Text: v0' v1' = 8df5d71f 9c3be43f

=========================================================================

To encrypt using the above test vector, enter the following parameters and press [Load Parameters]:

{v0} 12345678

{v1} 90abcdef

{k0} 11223344

{k1} 55667788

{k2} 9900aabb

{k3} ccddeeff

{sum}

{delta} 9e3779b9

Then enter the following instructions and press [Run Instructions]:

nbytes 4

add sum delta

mov reg_1 v1
shl reg_1 4
add reg_1 k0

mov reg_2 v1
add reg_2 sum

mov reg_3 v1
shr reg_3 5
add reg_3 k1

xor reg_1 reg_2
xor reg_1 reg_3

add v0 reg_1

mov reg_1 v0
shl reg_1 4
add reg_1 k2

mov reg_2 v0
add reg_2 sum

mov reg_3 v0
shr reg_3 5
add reg_3 k3

xor reg_1 reg_2
xor reg_1 reg_3

add v1 reg_1

repeat 31

trunc v0 32
trunc v1 32

Note the first instruction: nbytes 4. This instruction tells the program that when the SHL and SHR operations are applied, the parameters being shifted are to be treated as 4 byte values (as required by the TEA algorithm).

Executing these instructions should give the result: v0 = 8df5d71f and v1 = 9c3be43f. Note that at the end of the calculation v0 and v1 are truncated to 32 bit (4 byte) values.

SHA-256 Message Schedule

The specifications for the SHA-256 message schedule are:

=========================================================================

M = the message = 512 bits = 16 x 32 bit words

Wt = the message schedule = 64 x 32 bit words

t = 0 to t = 15:

W[t] = M[t] 

t = 16 to t = 63:

W[t] = sigma_256_1(W[t-2]) + W[t-7] + sigma_256_0(W[t-15]) + W[t-16]

Where:

sigma_256_0(W) = rotr(W,7) xor rotr(W,18) xor shr(W,3)

sigma_256_1(W) = rotr(W,17) xor rotr(W,19) xor shr(W,10)

=========================================================================

Test Vector:

The ascii input message 'abc' generates the 512 bit message block M:

61626380 00000000 00000000 00000000 
00000000 00000000 00000000 00000000 
00000000 00000000 00000000 00000000 
00000000 00000000 00000000 00000018

The message schedule for this M is:

61626380 00000000 00000000 00000000 
00000000 00000000 00000000 00000000 
00000000 00000000 00000000 00000000 
00000000 00000000 00000000 00000018 

61626380 000F0000 7DA86405 600003C6 
3E9D7B78 0183FC00 12DCBFDB E2E2C38E 
C8215C1A B73679A2 E5BC3909 32663C5B 
9D209D67 EC8726CB 702138A4 D3B7973B 

93F5997F 3B68BA73 AFF4FFC1 F10A5C62 
0A8B3996 72AF830A 9409E33E 24641522 
9F47BF94 F0A64F5A 3E246A79 27333BA3 
0C4763F2 840ABF27 7A290D5D 065C43DA 

FB3E89CB CC7617DB B9E66C34 A9993667 
84BADEDD C21462BC 1487472C B20F7A99 
EF57B9CD EBE6B238 9FE3095E 78BC8D4B 
A43FCF15 668B2FF8 EEABA2CC 12B1EDEB

=========================================================================

In the calculator program, enter these parameters and press [Load Parameters]:

{Wt}

61626380 00000000 00000000 00000000 
00000000 00000000 00000000 00000000 
00000000 00000000 00000000 00000000 
00000000 00000000 00000000 00000018

{W_temp}

{Wt_2}

{Wt_7}

{Wt_15}

{Wt_16}

{s_256_0}

{s_256_1}

Then enter the following instructions and press [Run Instructions]:

nbytes 400

mov Wt_2 Wt
shr Wt_2 32

mov Wt_7 Wt
shr Wt_7 192

mov Wt_15 Wt
shr Wt_15 448

mov Wt_16 Wt
shr Wt_16 480

nbytes 4

mov reg_1 Wt_2
rotr reg_1 17

mov reg_2 Wt_2
rotr reg_2 19

mov reg_3 Wt_2
shr reg_3 10

mov s_256_1 reg_1
xor s_256_1 reg_2
xor s_256_1 reg_3

mov reg_1 Wt_15
rotr reg_1 7

mov reg_2 Wt_15
rotr reg_2 18

mov reg_3 Wt_15
shr reg_3 3

mov s_256_0 reg_1
xor s_256_0 reg_2
xor s_256_0 reg_3

mov W_temp Wt_7
add W_temp Wt_16
add W_temp s_256_0
add W_temp s_256_1

trunc W_temp 32

nbytes 400

shl Wt 32
or Wt W_temp

repeat 47

PRNG – Middle Square Method

Use an adaptation of the middle square method to generate a pseudo random 400 byte value:

=========================================================================

Middle Square Method

Start with a 32 bit hex number:

- Square it to get a 64 bit number.

- Right shift by 16 bits.

- Truncate to 32 bits.

- Repeat.

=========================================================================

Enter these parameters and press [Load Parameters]:

{seed} 12345678

{rand}

Then enter the following instructions and press [Run Instructions]:

mov reg_1 seed
mul reg_1 reg_1
shr reg_1 16
trunc reg_1 32

mov seed reg_1

shl rand 32
or rand seed

repeat 99

PRNG – Linear Congruential Generator

Use the Linear Congruential Generator to generate a pseudo random 400 byte value:

=========================================================================

Linear Congruential Generator

X(n+1) = (aX(n) + c) mod m

Use these values:

m = 100000000
a = 343FD
c = 269EC3

=========================================================================

Enter these parameters and press [Load Parameters]:

{a} 343FD

{c} 269EC3

{Xn} 12345678

{rand}

Then enter the following instructions and press [Run Instructions]:

mov reg_1 Xn
mul reg_1 a
add reg_1 c
trunc reg_1 32

mov Xn reg_1 

shl rand 32
or rand Xn

repeat 99

The modulus value m (0x100000000) in this case is just 232, which is equivalent to truncating to 32 bits.

PRNG – Galois LFSR

Use the Galois Linear Feedback Shift Register to generate a pseudo random 400 byte value:

=========================================================================

Galois LFSR (Linear Feedback Shift Register)

Feedback polynomial = x32 + x31 + x29 + x + 1

Update the LFSR:

lfsr = shr(lfsr,1) xor (neg(lfsr and 1) and D0000001)

=========================================================================

Enter these parameters and press [Load Parameters]:

{lfsr} 12345678

{poly} D0000001

{rand}

Then enter the following instructions and press [Run Instructions]:

mov reg_1 lfsr
shr reg_1 1

mov reg_2 lfsr
andi reg_2 1
neg reg_2
and reg_2 poly

xor reg_1 reg_2
mov lfsr reg_1

shl rand 32
or rand lfsr

repeat 99

Modular Exponentiation

Enter instructions to do the modular exponentiation calculation. Note that each of A and C should not be greater than 200 bytes.

=========================================================================

Modular Exponentiation - Left To Right Binary Method:

mod_exp = AB mod C

Initialise: mod_exp = 1

For each bit in B starting from the high order bit:

- mod_exp = (mod_exp x mod_exp) mod C

Then:

- if(bit == 0) mod_exp = (1 x mod_exp) mod C

- if(bit == 1) mod_exp = (A x mod_exp) mod C

=========================================================================

To turn a bit value of 0 or 1 into a multiplier value of 1 or A:

M = (neg(bit) and A) + (bit xor 1)

=========================================================================

Enter these parameters and press [Load Parameters]:

{A} 12345678 90abcdef

{B} 90abcdef

{C} 12345678

{mod_exp} 1

Then enter the following instructions and press [Run Instructions]:

mul mod_exp mod_exp
mod mod_exp C

rotl B 1

movi reg_1 1
and reg_1 B 

movi reg_2 1
xor reg_2 reg_1

neg reg_1
and reg_1 A

add reg_1 reg_2

mul mod_exp reg_1
mod mod_exp C

repeat 3199

Note that for the larger input numbers, it can take a couple of minutes for this calculation to execute.

The FASM x86 Code

To grab this code, select it with the mouse and copy it. It can then be pasted directly into the FASM IDE.

; -------------------------------------------------------------------------------------

 format PE GUI 4.0

 entry start

 include 'win32a.inc'

; -------------------------------------------------------------------------------------

 IDD_THE_DIALOG = 102

 IDC_INPUT = 1000
 IDC_CALCS = 1001
 IDC_OUTPUT = 1002

 IDC_BTN_LOAD_DATA = 1003
 IDC_BTN_CALC = 1004
 IDC_BTN_CLR_DATA = 1005
 IDC_BTN_CLR_INSTR = 1006
 IDC_BTN_RESET_ALL = 1007

; -------------------------------------------------------------------------------------

 HEX_LEN = 400

 MAX_HEX_DIGITS = 2*HEX_LEN

 N_CALCS = 40

 CALCS_BUFFER_SZ = 20*N_CALCS

; -------------------------------------------------------------------------------------

 section '.code' code readable executable

  start:

    invoke GetModuleHandle,0
    invoke DialogBoxParam,eax,IDD_THE_DIALOG,0,DialogProc,0

  exit:

    invoke  ExitProcess,0 

; -------------------------------------------------------------------------------------

proc DialogProc uses esi edi ebx,hwnddlg,msg,wparam,lparam

	cmp [msg],WM_INITDIALOG
	je .wminitdialog

	cmp [msg],WM_COMMAND
	je .wmcommand

	cmp [msg],WM_CLOSE
	je .wmclose

	xor eax,eax
	jmp .quit

  .wminitdialog:

	invoke SetDlgItemText,[hwnddlg],IDC_INPUT,szInputText
	invoke SetDlgItemText,[hwnddlg],IDC_CALCS,szExprText

	stdcall ClearRegisterValues

	stdcall PrintRegisterValues

	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,bfDisplay

	jmp .done

  .wmcommand:

	cmp [wparam], BN_CLICKED shl 16 + IDC_BTN_LOAD_DATA
	je .LOAD

	cmp [wparam], BN_CLICKED shl 16 + IDC_BTN_CALC
	je .CALC

	cmp [wparam], BN_CLICKED shl 16 + IDC_BTN_CLR_DATA
	je .CLR_DATA

	cmp [wparam], BN_CLICKED shl 16 + IDC_BTN_CLR_INSTR
	je .CLR_INSTR

	cmp  [wparam], BN_CLICKED shl 16 + IDC_BTN_RESET_ALL
	je .RESET

    jmp .done

  .LOAD:

	invoke GetDlgItemText,[hwnddlg],IDC_INPUT,bfDisplay,12000

	stdcall ValidateParamsBuffer

	cmp al,0xff

	je .ERR_8

	stdcall ProcessDisplayBuffer

	stdcall PrintSymbolsParams

	invoke SetDlgItemText,[hwnddlg],IDC_INPUT,bfDisplay

	stdcall ClearRegisterValues

	stdcall PrintRegisterValues

	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,bfDisplay

	jmp .done

  .ERR_8:

	mov [nErrorCode],byte 8

	jmp .ERROR

  .CALC:

	invoke GetDlgItemText,[hwnddlg],IDC_CALCS,bfDisplay,12000

	stdcall ValidateCalcsBuffer

	cmp al,0xff

	je .ERROR

	stdcall LoadCalculations

	cmp al,0xff

	je .ERROR

	stdcall ExecuteCalculations

	cmp eax,0xffffffff

	je .CALC_ERROR

	stdcall PrintSymbolsParams

	invoke SetDlgItemText,[hwnddlg],IDC_INPUT,bfDisplay

	stdcall PrintRegisterValues

	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,bfDisplay

	jmp .done

  .CALC_ERROR:

	stdcall PrintExeError

	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,bfDisplay

	stdcall PrintSymbolsParams

	invoke SetDlgItemText,[hwnddlg],IDC_INPUT,bfDisplay

	jmp .done

  .ERROR:

	stdcall PrintErrorMessage

	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,bfDisplay

	jmp .done

  .CLR_DATA:

	invoke SetDlgItemText,[hwnddlg],IDC_INPUT,""

	stdcall ClearSymbolTable

	stdcall ClearRegisterValues

	stdcall PrintRegisterValues

	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,bfDisplay

	jmp .done

  .CLR_INSTR:

	invoke SetDlgItemText,[hwnddlg],IDC_CALCS,""

	jmp .done

  .RESET:

	invoke SetDlgItemText,[hwnddlg],IDC_INPUT,szInputText
	invoke SetDlgItemText,[hwnddlg],IDC_CALCS,szExprText

	mov [nBytes],HEX_LEN

	stdcall ClearSymbolTable

	stdcall ClearRegisterValues

	stdcall PrintRegisterValues

	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,bfDisplay

	jmp .done

  .wmclose:

	invoke EndDialog,[hwnddlg],0

  .done:

	mov eax,1

  .quit:

	ret

endp

; -------------------------------------------------------------------------------------

proc ClearRegisterValues

	lea edi,[Reg_1]

	stdcall ClearParam

	lea edi,[Reg_2]

	stdcall ClearParam

	lea edi,[Reg_3]

	stdcall ClearParam

	lea edi,[Reg_4]

	stdcall ClearParam

	ret

endp

; -------------------------------------------------------------------------------------

macro PRINT_CRLF
{
  mov [edi],byte 13
  inc edi

  mov [edi],byte 10
  inc edi
}

; -------------------------------------------------------------------------------------

proc PrintRegisterValues

  ; print reg_1, reg_2, reg_3, reg_4

	lea edi,[bfDisplay]

	; reg_1

	mov [edi],dword "reg_"
	add edi,4

	mov [edi],dword "1=  "
	add edi,4

	PRINT_CRLF
	PRINT_CRLF

	lea esi,[Reg_1]

	stdcall PrintHexValue

	; reg_2

	mov [edi],dword "reg_"
	add edi,4

	mov [edi],dword "2=  "
	add edi,4

	PRINT_CRLF
	PRINT_CRLF

	lea esi,[Reg_2]

	stdcall PrintHexValue

	; reg_3

	mov [edi],dword "reg_"
	add edi,4

	mov [edi],dword "3=  "
	add edi,4

	PRINT_CRLF
	PRINT_CRLF

	lea esi,[Reg_3]

	stdcall PrintHexValue

	; reg_4

	mov [edi],dword "reg_"
	add edi,4

	mov [edi],dword "4=  "
	add edi,4

	PRINT_CRLF
	PRINT_CRLF

	lea esi,[Reg_4]

	stdcall PrintHexValue

	; null terminate

	mov [edi],byte 0

	ret

endp

; -------------------------------------------------------------------------------------

proc PrintSymbolsParams

  ; print the parameter names and their values to the bfDisplay buffer

	lea edi,[bfDisplay]

	; Symbol_1 and Param_1

	lea esi,[Symbol_1]

	cmp [esi],byte 0

	je .DONE

	stdcall PrintSymbol

	lea esi,[Param_1]

	stdcall PrintHexValue

	; Symbol_2 and Param_2

	lea esi,[Symbol_2]

	cmp [esi],byte 0

	je .DONE

	stdcall PrintSymbol

	lea esi,[Param_2]

	stdcall PrintHexValue

	; Symbol_3 and Param_3

	lea esi,[Symbol_3]

	cmp [esi],byte 0

	je .DONE

	stdcall PrintSymbol

	lea esi,[Param_3]

	stdcall PrintHexValue

	; Symbol_4 and Param_4

	lea esi,[Symbol_4]

	cmp [esi],byte 0

	je .DONE

	stdcall PrintSymbol

	lea esi,[Param_4]

	stdcall PrintHexValue

	; Symbol_5 and Param_5

	lea esi,[Symbol_5]

	cmp [esi],byte 0

	je .DONE

	stdcall PrintSymbol

	lea esi,[Param_5]

	stdcall PrintHexValue

	; Symbol_6 and Param_6

	lea esi,[Symbol_6]

	cmp [esi],byte 0

	je .DONE

	stdcall PrintSymbol

	lea esi,[Param_6]

	stdcall PrintHexValue

	; Symbol_7 and Param_7

	lea esi,[Symbol_7]

	cmp [esi],byte 0

	je .DONE

	stdcall PrintSymbol

	lea esi,[Param_7]

	stdcall PrintHexValue

	; Symbol_8 and Param_8

	lea esi,[Symbol_8]

	cmp [esi],byte 0

	je .DONE

	stdcall PrintSymbol

	lea esi,[Param_8]

	stdcall PrintHexValue

	; Symbol_9 and Param_9

	lea esi,[Symbol_9]

	cmp [esi],byte 0

	je .DONE

	stdcall PrintSymbol

	lea esi,[Param_9]

	stdcall PrintHexValue

	; Symbol_10 and Param_10

	lea esi,[Symbol_10]

	cmp [esi],byte 0

	je .DONE

	stdcall PrintSymbol

	lea esi,[Param_10]

	stdcall PrintHexValue

  .DONE:

	; null terminate

	mov [edi],byte 0

	ret

endp

; -------------------------------------------------------------------------------------

proc PrintHexValue uses ecx eax

  ; ESI and EDI set outside of this procedure

	locals

	  count db 0

	endl

	; find the first non zero dword in the hex value

	; starting from the high dword

	mov cx,HEX_LEN-4

	add esi,HEX_LEN-4

  .FIND:

	mov eax,[esi]

	cmp eax,0

	jne .PRINT

	sub esi,4

	sub cx,4

	jcxz .PRINT

	jmp .FIND

  .PRINT:

	mov eax,[esi]

	; dword to 8 digits

	stdcall DWordToStr

	jcxz .DONE

	sub cx,4

	sub esi,4

	inc [count]

	; add a CRLF to the output string after every 6 dwords

	cmp [count],byte 6

	je .ADD_CRLF

	jmp .PRINT

  .ADD_CRLF:

	mov [count],byte 0

	PRINT_CRLF

	jmp .PRINT

  .DONE:

	PRINT_CRLF
	PRINT_CRLF

	ret

endp

; -------------------------------------------------------------------------------------

proc WordToStr uses edx

  ; print a word (2 bytes) to a string

  ; word passed in AX
  ; EDI set outside of this function

	mov dx,ax
	mov al,ah

	stdcall ByteToDigits

	mov [edi],ah
	inc edi

	mov [edi],al
	inc edi

	mov al,dl

	stdcall ByteToDigits

	mov [edi],ah
	inc edi

	mov [edi],al
	inc edi

	ret

endp

; -------------------------------------------------------------------------------------

proc DWordToStr

  ; print a dword (4 bytes) to a string

  ; word passed in EAX
  ; EDI set outside of this function

	locals

	  tmp dw 0

	endl

	mov word [tmp],ax

	shr eax,16

	stdcall WordToStr

	mov ax,word [tmp]

	stdcall WordToStr

	; add a space

	mov [edi],byte 32
	inc edi

	ret

endp

; -------------------------------------------------------------------------------------

proc PrintSymbol uses ecx

  ; ESI and EDI set outside of this procedure

	mov cx,0

  .PRINT:

	mov al,[esi]

	cmp al,0

	je .DONE

	mov [edi],al

	inc esi
	inc edi

	inc cx

	cmp cx,8

	jge .DONE

	jmp .PRINT

  .DONE:

	; '='

	mov [edi],byte 61
	inc edi

	PRINT_CRLF
	PRINT_CRLF

	ret

endp

; -------------------------------------------------------------------------------------

proc ProcessDisplayBuffer

  ; extract the symbols and hex values from bfDisplay

	stdcall ClearSymbolTable

	lea esi,[bfDisplay]

	; get Symbol_1 and Param_1

	stdcall FindSymbol

	cmp al,0

	je .DONE

	lea edi,[Symbol_1]

	stdcall GetSymbol

	stdcall HexStrToInputBuffer

	lea edi,[Param_1]

	stdcall InputBufferToParam

	; get Symbol_2 and Param_2

	stdcall FindSymbol

	cmp al,0

	je .DONE

	lea edi,[Symbol_2]

	stdcall GetSymbol

	stdcall HexStrToInputBuffer

	lea edi,[Param_2]

	stdcall InputBufferToParam

	; get Symbol_3 and Param_3

	stdcall FindSymbol

	cmp al,0

	je .DONE

	lea edi,[Symbol_3]

	stdcall GetSymbol

	stdcall HexStrToInputBuffer

	lea edi,[Param_3]

	stdcall InputBufferToParam

	; get Symbol_4 and Param_4

	stdcall FindSymbol

	cmp al,0

	je .DONE

	lea edi,[Symbol_4]

	stdcall GetSymbol

	stdcall HexStrToInputBuffer

	lea edi,[Param_4]

	stdcall InputBufferToParam

	; get Symbol_5 and Param_5

	stdcall FindSymbol

	cmp al,0

	je .DONE

	lea edi,[Symbol_5]

	stdcall GetSymbol

	stdcall HexStrToInputBuffer

	lea edi,[Param_5]

	stdcall InputBufferToParam

	; get Symbol_6 and Param_6

	stdcall FindSymbol

	cmp al,0

	je .DONE

	lea edi,[Symbol_6]

	stdcall GetSymbol

	stdcall HexStrToInputBuffer

	lea edi,[Param_6]

	stdcall InputBufferToParam

	; get Symbol_7 and Param_7

	stdcall FindSymbol

	cmp al,0

	je .DONE

	lea edi,[Symbol_7]

	stdcall GetSymbol

	stdcall HexStrToInputBuffer

	lea edi,[Param_7]

	stdcall InputBufferToParam

	; get Symbol_8 and Param_8

	stdcall FindSymbol

	cmp al,0

	je .DONE

	lea edi,[Symbol_8]

	stdcall GetSymbol

	stdcall HexStrToInputBuffer

	lea edi,[Param_8]

	stdcall InputBufferToParam

	; get Symbol_9 and Param_9

	stdcall FindSymbol

	cmp al,0

	je .DONE

	lea edi,[Symbol_9]

	stdcall GetSymbol

	stdcall HexStrToInputBuffer

	lea edi,[Param_9]

	stdcall InputBufferToParam

	; get Symbol_10 and Param_10

	stdcall FindSymbol

	cmp al,0

	je .DONE

	lea edi,[Symbol_10]

	stdcall GetSymbol

	stdcall HexStrToInputBuffer

	lea edi,[Param_10]

	stdcall InputBufferToParam

 .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc FindSymbol

  ; look for the opening brace { (ascii = 123) in bfDisplay

  ; ESI is set outside of this procedure

  ; set AL to indicate success

	mov al,0

  .FIND:

	mov dl,[esi]

	cmp dl,0

	je .DONE

	cmp dl,123

	je .FOUND

	inc esi

	jmp .FIND

  .FOUND:

	mov al,1

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc GetSymbol uses ecx

  ; store the symbol in the symbol table

  ; delimiters = { and } = 123 and 125

  ; ESI and EDI set outside of this procedure

	xor ecx,ecx

	; ESI already points to the '{'

	inc esi

  .STORE:

	mov al,[esi]

	cmp al,0

	je .DONE

	; '}'

	cmp al,125

	je .DONE

	mov [edi],al

	inc esi
	inc edi

	inc cx

	; read up to 8 chars

	cmp cx,8

	jge .CLOSE

	jmp .STORE

  .CLOSE:

	; if 8 chars were read, find the closing brace

	mov al,[esi]

	cmp al,0

	je .DONE

	cmp al,125

	je .DONE

	inc esi

	jmp .CLOSE

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc ClearSymbolTable uses edi ecx

  ; clear the symbols

	lea edi,[Symbol_1]

	mov cx,0

  .CLEAR:

	mov [edi],byte 0

	inc edi
	inc cx

	cmp cx,100

	jl .CLEAR

	ret

endp

; -------------------------------------------------------------------------------------

proc InputBufferToParam uses esi ecx

  ; convert the string in InputBuffer to a hex value

  ; EDI is set outside of this procedure

  ; EDI points to one of: Param_1, Param_2, etc

	lea esi,[InputBuffer]

	; clear the buffer pointed to by EDI

	mov edx,edi

	stdcall ClearParam

	; restore EDI

	mov edi,edx

	; count the digits in InputBuffer and save to AX

	stdcall CountDigits

	; check if the input buffer is empty

	cmp ax,0

	je .DONE

	; calculate the offset in bytes from the start of EDI

	; based on the number of digits in the input buffer (given by AX)

	xor ecx,ecx

	mov cx,ax

	shr cx,1

	test ax,1

	jnz .ODD

  .EVEN:

	; the input buffer contains an even number of hex digits

	; offset = (nDigits/2) - 1

	dec cx

	add edi,ecx

	jmp .GET_DIGITS

  .ODD:

	; the input buffer contains an odd number of hex digits

	; offset = nDigits/2

	; convert the first digit

	add edi,ecx

	mov al,[esi]

	stdcall HexDigitToValue

	mov [edi],al

	inc esi

	dec edi

	dec cx

	cmp cx,0

	jl .DONE

  .GET_DIGITS:

	; convert 2 hex digits to 1 byte

	mov al,[esi]

	stdcall HexDigitToValue

	shl al,4

	mov [edi],al

	inc esi

	mov al,[esi]

	stdcall HexDigitToValue

	or [edi],al

	inc esi

	dec cx

	cmp cx,0

	jl .DONE

	dec edi

	jmp .GET_DIGITS

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc HexStrToInputBuffer uses edi ecx

  ; copy a string containing a hex number to the InputBuffer

  ; filter out any non hex digit characters

  ; ESI is set outside of this procedure

	lea edi,[InputBuffer]

	xor ecx,ecx

  .FILTER:

	mov al,[esi]

	; 0 = null terminator

	cmp al,0

	je .DONE

	; 123 = {

	cmp al,123

	je .DONE

	cmp al,48

	jl .NOT_HEX

	cmp al,57

	jle .COPY

	cmp al,65

	jl .NOT_HEX

	cmp al,70

	jle .COPY

	cmp al,97

	jl .NOT_HEX

	cmp al,102

	jg .NOT_HEX

  .COPY:

	mov [edi],al

	inc edi

	inc ecx

	cmp ecx,MAX_HEX_DIGITS

	jge .DONE

  .NOT_HEX:

	inc esi

	jmp .FILTER

  .DONE:

	; null terminate the new string

	mov [edi],byte 0

	ret

endp

; -------------------------------------------------------------------------------------

proc ClearParam uses ecx

  ; clear the input parameter

  ; EDI is set outside of this procedure

	xor ecx,ecx

  .CLEAR:

	mov [edi],byte 0

	inc edi
	inc cx

	cmp cx,HEX_LEN

	jl .CLEAR

	ret

endp

; -------------------------------------------------------------------------------------

proc CountDigits uses esi ecx

  ; count the number of hex digits in the InputBuffer string

	mov ax,0

	xor ecx,ecx

	lea esi,[InputBuffer]

  .COUNT:

	mov dl,[esi]

	cmp dl,0

	je .DONE

	inc cx

	inc esi

	jmp .COUNT

  .DONE:

	; return the digit count in AX

	mov ax,cx

	ret

endp

; -------------------------------------------------------------------------------------

proc HexDigitToValue

  ; convert a hex digit to a 4 bit value

  ; input in AL - output in AL

	; is AL in the range: 48 - 57 (0 - 9) ?

	cmp al,48

	jl .NOT_HEX

	cmp al,57

	jg .A_Z

	sub al,48

	jmp .DONE

  .A_Z:

	; is AL in the range: 65 - 70 (A - B) ?

	cmp al,65

	jl .NOT_HEX

	cmp al,70

	jg .a_z

	sub al,55

	jmp .DONE

  .a_z:

	; is AL in the range: 97 - 102 (a - b) ?

	cmp al,97

	jl .NOT_HEX

	cmp al,102

	jg .NOT_HEX

	sub al,87

	jmp .DONE

  .NOT_HEX:

	; set EAX to 0xffff if input is not a valid hex digit

	mov eax,0xffff

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc ByteToDigits

  ; convert 1 byte to 2 hex digits

  ; input in AL - output in AX

	; copy AL to AH

	mov ah,al

	; AH: get the upper 4 bits of the byte

	shr ah,4

	; nibble to hex digit

	add ah,48

	cmp ah,57

	jle .NEXT

	add ah,7

  .NEXT:

	; AL: get the lower 4 bits of the byte

	and al,0xf

	; nibble to hex digit

	add al,48

	cmp al,57

	jle .DONE

	add al,7

  .DONE:

	; output is in AX

	ret

endp

; -------------------------------------------------------------------------------------

proc LoadCalculations

  ; load the calculations from the bfDisplay string

	stdcall ResetCalculations

	mov [nErrorCode],byte 0

	lea esi,[bfDisplay]

  .NEXT:

	; get the next line

	lea edi,[CurrentLine]

	mov al,[esi]

	cmp al,0

	je .DONE

  .GET_LINE:

	; read the line into EDI (don't include CRLF)

	mov al,[esi]

	cmp al,13

	je .CRLF

	cmp al,10

	je .CRLF

	cmp al,0

	je .PROCESS

	mov [edi],al

	inc esi
	inc edi

	jmp .GET_LINE

  .CRLF:

	; advance ESI to the first non-CRLF (ready for the next line)

	inc esi

	mov al,[esi]

	cmp al,13

	je .CRLF

	cmp al,10

	je .CRLF

  .PROCESS:

	; null terminate EDI

	mov [edi],byte 0

	; process the line in EDI

	stdcall ProcessLine

	cmp al,0xff

	je .DONE

	inc [nCalculation]

	cmp [nCalculation],N_CALCS

	jge .DONE

	jmp .NEXT

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc ProcessLine uses esi edi

  ; the current calculation (line) is in the string CurrentLine

  ; convert to instr and param codes, and store in the Calculations buffer

  ; structure of Calculations buffer:

  ; 1 calculation = 20 bytes

  ; byte 1 = operation code
  ; byte 2 = arg 1 code
  ; byte 3 = arg 2 code
  ; byte 4 = arg 3 code
  ; byte 5-20 = 16 byte immediate value

  ; total size of Calculations buffer = 800 bytes = 40 calculations

  ; set via N_CALCS and CALCS_BUFFER_SZ

	locals

	  instrcode db 0

	endl

	lea esi,[CurrentLine]

	lea edi,[Calculations]

	; adjust EDI to point to the correct location in the Calculations buffer

	xor edx,edx

	; multiply by 20 = shl(DL,4) + shl(DL,2)

	mov dl,[nCalculation]

	shl edx,4

	xor eax,eax

	mov al,[nCalculation]

	shl eax,2

	add edx,eax

	; nCalculation has been multiplied by 20, now add to EDI

	add edi,edx

	; skip any leading spaces in the line

	stdcall SkipSpaces

	; get the instruction

	stdcall GetInstrCode

	cmp al,0xff

	je .DONE

	; store the instr code in the Calculations buffer

	mov [edi],al

	inc edi

	; also save to instrcode

	mov [instrcode],al

  ; instr codes 1 to 31 - 2 arguments

  ; instr codes 32 to 63 - 1 argument + 1 immediate

  ; instr codes 64 to 95 - 1 argument

  ; instr code 96 to 127 - 1 immediate

	cmp [instrcode],95

	jg .96_127

	; get the first argument

	stdcall SkipSpaces

	cmp al,0xff

	je .DONE

	stdcall GetNextArgument

	cmp al,0xff

	je .DONE

	; store the arg code in the Calculations buffer

	mov [edi],al

	inc edi

  .1_31:

	cmp [instrcode],31

	jg .32_63

	; get the second argument

	stdcall SkipSpaces

	cmp al,0xff

	je .DONE

	stdcall GetNextArgument

	cmp al,0xff

	je .DONE

	; store the arg code in the Calculations buffer

	mov [edi],al

	inc edi

	jmp .EXTRA_CHARS

  .32_63:

	cmp [instrcode],63

	jg .64_95

	; EDI points to the location in the Calculations buffer
	; where the 128 bit immediate value will be stored

	inc edi
	inc edi

	; get the immediate value

	stdcall SkipSpaces

	cmp al,0xff

	je .DONE

	stdcall GetImmediate

	; is the Immediate argument in decimal or hex

	cmp [instrcode],38

	jl .HEX_IMMED

	cmp [instrcode],43

	jg .HEX_IMMED

  .DEC_IMMED:

	stdcall DecImmediateToHex

	jmp .NEXT

  .HEX_IMMED:

	stdcall ImmediateToHex

  .NEXT:

	cmp al,0xff

	je .DONE

	stdcall ImmediateToCalcsBuffer

	jmp .EXTRA_CHARS

  .64_95:

	cmp [instrcode],95

	jg .96_127

	jmp .EXTRA_CHARS

  .96_127:

	; EDI points to the location in the Calculations buffer
	; where the 128 bit immediate value will be stored

	inc edi
	inc edi
	inc edi

	; get the immediate value

	stdcall SkipSpaces

	cmp al,0xff

	je .DONE

	stdcall GetImmediate

	; immediate value is in decimal

	stdcall DecImmediateToHex

	cmp al,0xff

	je .DONE

	stdcall ImmediateToCalcsBuffer

  .EXTRA_CHARS:

	stdcall CheckExtraChars

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc CheckExtraChars

  ; the instr and args have been read

  ; are there still unread chars on the line ?

  ; spaces are OK

  ; ESI is set outside of this procedure

	mov al,0

  .CHECK:

	mov dl,[esi]

	cmp dl,0

	je .DONE

	cmp dl,32

	je .NEXT

	mov al,0xff

	mov [nErrorCode],byte 1

	jmp .DONE

  .NEXT:

	inc esi

	jmp .CHECK

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc GetImmediate uses edi

  ; read the immediate hex value in ESI

  ; ESI is set outside of this procedure

	; read up to 32 hex digits

	lea edi,[bfImmediate]

	mov cx,0

	; read until either a space or a NULL is found

  .READ:

	mov dl,[esi]

	cmp dl,0

	je .DONE

	cmp dl,32

	je .DONE

	mov [edi],dl

	inc esi

	inc edi

	inc cx

	; read up to 32 digits

	cmp cx,32

	jl .READ

  .DONE:

	; null terminate

	mov [edi],byte 0

	ret

endp

; -------------------------------------------------------------------------------------

proc ImmediateToHex uses esi edi

  ; convert the string in bfImmediate to a 16 byte hex value

	lea edi,[Immediate]

	mov cx,0

	; zero the dest value

  .CLEAR:

	mov [edi],byte 0

	inc edi

	inc cx

	cmp cx,16

	jl .CLEAR

	; find the end of the bfImmediate string

	xor ecx,ecx

	lea esi,[bfImmediate]

  .EOS:

	; seek the null terminator, or count 32 digits

	mov dl,[esi]

	cmp dl,0

	je .NEXT

	inc esi

	inc cx

	cmp cx,32

	jl .EOS

  .NEXT:

	cmp cx,0

	je .DONE

	dec esi

	lea edi,[Immediate]

	; CX contains the digit count

  .CONVERT:

	; read the digits from right to left (in pairs)

	mov al,[esi]

	stdcall HexDigitToValue

	cmp eax,0xffff

	je .ERROR

	; put the first digit into EDI

	mov [edi],al

	dec cx

	cmp cx,0

	je .DONE

	dec esi

	mov al,[esi]

	stdcall HexDigitToValue

	cmp eax,0xffff

	je .ERROR

	shl al,4

	; OR the next digit into EDI

	or [edi],al

	dec cx

	cmp cx,0

	je .DONE

	dec esi

	inc edi

	jmp .CONVERT

	mov al,0

	jmp .DONE

  .ERROR:

	mov [nErrorCode],byte 2

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc DecImmediateToHex uses esi edi

  ; convert the string in bfImmediate to a hex value

  ; the value in the string is read as a decimal number

  ; it cannot be greater than 3200 (4 decimal digits)

	; count the number of digits in bfImmediate

	lea esi,[bfImmediate]

	mov cx,0

	xor eax,eax

  .COUNT:

	mov dl,[esi]

	cmp dl,0

	je .NEXT

	; check for non decimal digits

	cmp dl,48

	jl .ERROR

	cmp dl,57

	jg .ERROR

	; copy to EAX

	shl eax,8

	mov al,dl

	inc esi
	inc cx

	; check the count

	cmp cx,4

	jg .ERROR

	jmp .COUNT

  .NEXT:

	lea edi,[Immediate]

	mov cx,0

	; zero the dest value

  .CLEAR:

	mov [edi],byte 0

	inc edi

	inc cx

	cmp cx,16

	jl .CLEAR

	; decimal digits are in EAX - convert to a hex number

	stdcall DecToHex

	cmp eax,3200

	jg .ERROR

	; save the result to Immediate

	lea edi,[Immediate]

	mov [edi],eax

	; don't want to accidently return AL = 0xff

	xor eax,eax

	jmp .DONE

  .ERROR:

	mov al,0xff

	mov [nErrorCode],byte 6

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc DecToHex

  ; convert the decimal digit chars in EAX to a hex value

  ; result returned in EAX

	; swap the bytes

	bswap eax

	; find the most significant digit

	cmp al,0

	jne .NEXT

	shr eax,8

	cmp al,0

	jne .NEXT

	shr eax,8

	cmp al,0

	jne .NEXT

	shr eax,8

	; if AL is zero here, there were no digits in EAX

	cmp al,0

	je .DONE

  .NEXT:

	; the most significant digit is now in AL

	xor edx,edx

  .CONVERT:

	; if AL is zero, there are no more digits

	cmp al,0

	je .HEX

	; multiply the existing value in EDX by 10

	mov ebx,edx

	shl edx,3

	shl ebx,1

	add edx,ebx

	; put the next digit into EBX

	xor ebx,ebx

	mov bl,al

	sub bl,48

	add edx,ebx

	shr eax,8

	jmp .CONVERT

  .HEX:

	mov eax,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc ImmediateToCalcsBuffer uses esi

  ; copy the immediate hex value to the Calculations buffer

  ; EDI is set outside of this procedure

	lea esi,[Immediate]

	mov cx,0

  .COPY:

	mov dl,[esi]

	mov [edi],dl

	inc esi
	inc edi

	inc cx

	cmp cx,16

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc GetNextArgument uses edi

  ; get the next argument from the string in ESI

  ; ESI set outside of this procedure

  ; code for argument returned in AL:

  ; Param_1 = 1
  ; Param_2 = 2
  ; Param_3 = 3
  ; Param_4 = 4
  ; Param_5 = 5
  ; Param_6 = 6
  ; Param_7 = 7
  ; Param_8 = 8
  ; Param_9 = 9
  ; Param_10 = 10

  ; Reg_1 = 11
  ; Reg_2 = 12
  ; Reg_3 = 13
  ; Reg_4 = 14

	lea edi,[CurrentArg]

	mov cx,0

  .GET_ARG:

	; read into EDI until a space or null is found

	mov al,[esi]

	cmp al,0

	je .GET_CODE

	cmp al,32

	je .GET_CODE

	mov [edi],al

	inc cx

	cmp cx,8

	jge .GET_CODE

	inc esi
	inc edi

	jmp .GET_ARG

  .GET_CODE:

	; null terminate EDI

	mov [edi],byte 0

	cmp cx,0

	jl .ERROR

	; test the string in EDI to get the argument code

	stdcall Test_Argument

	jmp .DONE

  .ERROR:

	; error code

	mov al,0xff

	mov [nErrorCode],byte 3

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Test_Argument uses esi edi

  ; test the argument against the symbols

	; symbol 1

	lea esi,[CurrentArg]

	lea edi,[Symbol_1]

	stdcall MatchSymbol

	mov dl,1

	cmp al,0

	je .SET_CODE

	; symbol 2

	lea esi,[CurrentArg]

	lea edi,[Symbol_2]

	stdcall MatchSymbol

	mov dl,2

	cmp al,0

	je .SET_CODE

	; symbol 3

	lea esi,[CurrentArg]

	lea edi,[Symbol_3]

	stdcall MatchSymbol

	mov dl,3

	cmp al,0

	je .SET_CODE

	; symbol 4

	lea esi,[CurrentArg]

	lea edi,[Symbol_4]

	stdcall MatchSymbol

	mov dl,4

	cmp al,0

	je .SET_CODE

	; symbol 5

	lea esi,[CurrentArg]

	lea edi,[Symbol_5]

	stdcall MatchSymbol

	mov dl,5

	cmp al,0

	je .SET_CODE

	; symbol 6

	lea esi,[CurrentArg]

	lea edi,[Symbol_6]

	stdcall MatchSymbol

	mov dl,6

	cmp al,0

	je .SET_CODE

	; symbol 7

	lea esi,[CurrentArg]

	lea edi,[Symbol_7]

	stdcall MatchSymbol

	mov dl,7

	cmp al,0

	je .SET_CODE

	; symbol 8

	lea esi,[CurrentArg]

	lea edi,[Symbol_8]

	stdcall MatchSymbol

	mov dl,8

	cmp al,0

	je .SET_CODE

	; symbol 9

	lea esi,[CurrentArg]

	lea edi,[Symbol_9]

	stdcall MatchSymbol

	mov dl,9

	cmp al,0

	je .SET_CODE

	; symbol 10

	lea esi,[CurrentArg]

	lea edi,[Symbol_10]

	stdcall MatchSymbol

	mov dl,10

	cmp al,0

	je .SET_CODE

	; symbol 11

	lea esi,[CurrentArg]

	lea edi,[Symbol_11]

	stdcall MatchSymbol

	mov dl,11

	cmp al,0

	je .SET_CODE

	; symbol 12

	lea esi,[CurrentArg]

	lea edi,[Symbol_12]

	stdcall MatchSymbol

	mov dl,12

	cmp al,0

	je .SET_CODE

	; symbol 13

	lea esi,[CurrentArg]

	lea edi,[Symbol_13]

	stdcall MatchSymbol

	mov dl,13

	cmp al,0

	je .SET_CODE

	; symbol 14

	lea esi,[CurrentArg]

	lea edi,[Symbol_14]

	stdcall MatchSymbol

	mov dl,14

	cmp al,0

	je .SET_CODE

	; match not found

	mov dl,0xff

	mov [nErrorCode],byte 4

  .SET_CODE:

	; return the code in AL

	mov al,dl

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc MatchSymbol

  ; test the name of the argument in ESI against the symbol name in EDI

  ; ESI and EDI are set outside of this procedure

  ; result returned in AL

  ; AL = 0 is a match

	mov al,0xff

	mov cx,0

  .TEST:

	mov dl,[esi]

	cmp [edi],dl

	jne .DONE

	cmp [edi],byte 0

	je .MATCH

	inc esi
	inc edi

	inc cx

	cmp cx,8

	jl .TEST

  .MATCH:

	mov al,0

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc GetInstrCode uses edi

  ; get the instr code from the string in ESI

  ; ESI set outside of this procedure

  ; instr code returned in AL

	lea edi,[CurrentInstr]

	mov cx,0

  .GET_INSTR:

	; read the chars into EDI, stop when a space or null is found

	mov al,[esi]

	cmp al,0

	je .GET_CODE

	mov [edi],al

	inc cx

	cmp al,32

	je .GET_CODE

	cmp cx,8

	jge .GET_CODE

	inc esi
	inc edi

	jmp .GET_INSTR

  .GET_CODE:

	cmp cx,3

	jl .ERROR

	; convert letters in EDI to upper case

	stdcall InstrToUpper

	; test the string in EDI to get an instr code

	stdcall Test_Instr

	cmp al,0xff

	jne .DONE

  .ERROR:

	mov [nErrorCode],byte 5

	mov al,0xff

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Test_Instr uses edi

  ; find the instr code for the instr in CurrentInstr

  ; return in AL

	; not found code is FF

	mov al,0xff

	lea edi,[CurrentInstr]

	;  =========================================================================

	;  2 ARGS:

	;  MOV = 1  ADD = 2  SUB = 3  MUL = 4  DIV = 5

	;  MOD = 6  AND = 7  OR = 8   XOR = 9  XCHG = 10

	;  =========================================================================

	; MOV = 1

	cmp [edi],byte 'M'
	jne .ADD
	cmp [edi+1],byte 'O'
	jne .ADD
	cmp [edi+2],byte 'V'
	jne .ADD
	cmp [edi+3],byte 32
	jne .ADD

	mov al,1
	jmp .DONE

  .ADD:

	; ADD = 2

	cmp [edi],byte 'A'
	jne .SUB
	cmp [edi+1],byte 'D'
	jne .SUB
	cmp [edi+2],byte 'D'
	jne .SUB
	cmp [edi+3],byte 32
	jne .SUB

	mov al,2
	jmp .DONE

  .SUB:

	; SUB = 3

	cmp [edi],byte 'S'
	jne .MUL
	cmp [edi+1],byte 'U'
	jne .MUL
	cmp [edi+2],byte 'B'
	jne .MUL
	cmp [edi+3],byte 32
	jne .MUL

	mov al,3
	jmp .DONE

  .MUL:

	; MUL = 4

	cmp [edi],byte 'M'
	jne .DIV
	cmp [edi+1],byte 'U'
	jne .DIV
	cmp [edi+2],byte 'L'
	jne .DIV
	cmp [edi+3],byte 32
	jne .DIV

	mov al,4
	jmp .DONE

  .DIV:

	; DIV = 5

	cmp [edi],byte 'D'
	jne .MOD
	cmp [edi+1],byte 'I'
	jne .MOD
	cmp [edi+2],byte 'V'
	jne .MOD
	cmp [edi+3],byte 32
	jne .MOD

	mov al,5
	jmp .DONE

  .MOD:

	; MOD = 6

	cmp [edi],byte 'M'
	jne .AND
	cmp [edi+1],byte 'O'
	jne .AND
	cmp [edi+2],byte 'D'
	jne .AND
	cmp [edi+3],byte 32
	jne .AND

	mov al,6
	jmp .DONE

  .AND:

	; AND = 7

	cmp [edi],byte 'A'
	jne .OR
	cmp [edi+1],byte 'N'
	jne .OR
	cmp [edi+2],byte 'D'
	jne .OR
	cmp [edi+3],byte 32
	jne .OR

	mov al,7
	jmp .DONE

  .OR:

	; OR = 8

	cmp [edi],byte 'O'
	jne .XOR
	cmp [edi+1],byte 'R'
	jne .XOR
	cmp [edi+2],byte 32
	jne .XOR

	mov al,8
	jmp .DONE

  .XOR:

	; XOR = 9

	cmp [edi],byte 'X'
	jne .XCHG
	cmp [edi+1],byte 'O'
	jne .XCHG
	cmp [edi+2],byte 'R'
	jne .XCHG
	cmp [edi+3],byte 32
	jne .XCHG

	mov al,9
	jmp .DONE

  .XCHG:

	; XCHG = 10

	cmp [edi],byte 'X'
	jne .MOVI
	cmp [edi+1],byte 'C'
	jne .MOVI
	cmp [edi+2],byte 'H'
	jne .MOVI
	cmp [edi+3],byte 'G'
	jne .MOVI
	cmp [edi+4],byte 32
	jne .MOVI

	mov al,10
	jmp .DONE

	;  =========================================================================

	;  1 ARG + 1 IMMED:

	;  MOVI = 32  ADDI = 33  SUBI = 34  ANDI = 35  ORI = 36  XORI = 37

	;  SHL = 38   SHR = 39   ROTL = 40  ROTR = 41

	;  TRUNC = 42  BSET = 43

	;  MULI = 44  DIVI = 45  MODI = 46

	;  =========================================================================

  .MOVI:

	; MOVI = 32

	cmp [edi],byte 'M'
	jne .ADDI
	cmp [edi+1],byte 'O'
	jne .ADDI
	cmp [edi+2],byte 'V'
	jne .ADDI
	cmp [edi+3],byte 'I'
	jne .ADDI
	cmp [edi+4],byte 32
	jne .ADDI

	mov al,32
	jmp .DONE

  .ADDI:

	; ADDI = 33

	cmp [edi],byte 'A'
	jne .SUBI
	cmp [edi+1],byte 'D'
	jne .SUBI
	cmp [edi+2],byte 'D'
	jne .SUBI
	cmp [edi+3],byte 'I'
	jne .SUBI
	cmp [edi+4],byte 32
	jne .SUBI

	mov al,33
	jmp .DONE

  .SUBI:

	; SUBI = 34

	cmp [edi],byte 'S'
	jne .ANDI
	cmp [edi+1],byte 'U'
	jne .ANDI
	cmp [edi+2],byte 'B'
	jne .ANDI
	cmp [edi+3],byte 'I'
	jne .ANDI
	cmp [edi+4],byte 32
	jne .ANDI

	mov al,34
	jmp .DONE

  .ANDI:

	; ANDI = 35

	cmp [edi],byte 'A'
	jne .ORI
	cmp [edi+1],byte 'N'
	jne .ORI
	cmp [edi+2],byte 'D'
	jne .ORI
	cmp [edi+3],byte 'I'
	jne .ORI
	cmp [edi+4],byte 32
	jne .ORI

	mov al,35
	jmp .DONE

  .ORI:

	; ORI = 36

	cmp [edi],byte 'O'
	jne .XORI
	cmp [edi+1],byte 'R'
	jne .XORI
	cmp [edi+2],byte 'I'
	jne .XORI
	cmp [edi+3],byte 32
	jne .XORI

	mov al,36
	jmp .DONE

  .XORI:

	; XORI = 37

	cmp [edi],byte 'X'
	jne .SHL
	cmp [edi+1],byte 'O'
	jne .SHL
	cmp [edi+2],byte 'R'
	jne .SHL
	cmp [edi+3],byte 'I'
	jne .SHL
	cmp [edi+4],byte 32
	jne .SHL

	mov al,37
	jmp .DONE

  .SHL:

	; SHL = 38

	cmp [edi],byte 'S'
	jne .SHR
	cmp [edi+1],byte 'H'
	jne .SHR
	cmp [edi+2],byte 'L'
	jne .SHR
	cmp [edi+3],byte 32
	jne .SHR

	mov al,38
	jmp .DONE

  .SHR:

	; SHR = 39

	cmp [edi],byte 'S'
	jne .ROTL
	cmp [edi+1],byte 'H'
	jne .ROTL
	cmp [edi+2],byte 'R'
	jne .ROTL
	cmp [edi+3],byte 32
	jne .ROTL

	mov al,39
	jmp .DONE

  .ROTL:

	; ROTL = 40

	cmp [edi],byte 'R'
	jne .ROTR
	cmp [edi+1],byte 'O'
	jne .ROTR
	cmp [edi+2],byte 'T'
	jne .ROTR
	cmp [edi+3],byte 'L'
	jne .ROTR
	cmp [edi+4],byte 32
	jne .ROTR

	mov al,40
	jmp .DONE

  .ROTR:

	; ROTR = 41

	cmp [edi],byte 'R'
	jne .TRUNC
	cmp [edi+1],byte 'O'
	jne .TRUNC
	cmp [edi+2],byte 'T'
	jne .TRUNC
	cmp [edi+3],byte 'R'
	jne .TRUNC
	cmp [edi+4],byte 32
	jne .TRUNC

	mov al,41
	jmp .DONE

  .TRUNC:

	; TRUNC = 42

	cmp [edi],byte 'T'
	jne .BSET
	cmp [edi+1],byte 'R'
	jne .BSET
	cmp [edi+2],byte 'U'
	jne .BSET
	cmp [edi+3],byte 'N'
	jne .BSET
	cmp [edi+4],byte 'C'
	jne .BSET
	cmp [edi+5],byte 32
	jne .BSET

	mov al,42
	jmp .DONE

  .BSET:

	; BSET = 43

	cmp [edi],byte 'B'
	jne .MULI
	cmp [edi+1],byte 'S'
	jne .MULI
	cmp [edi+2],byte 'E'
	jne .MULI
	cmp [edi+3],byte 'T'
	jne .MULI
	cmp [edi+4],byte 32
	jne .MULI

	mov al,43
	jmp .DONE

  .MULI:

	; MULI = 44

	cmp [edi],byte 'M'
	jne .DIVI
	cmp [edi+1],byte 'U'
	jne .DIVI
	cmp [edi+2],byte 'L'
	jne .DIVI
	cmp [edi+3],byte 'I'
	jne .DIVI
	cmp [edi+4],byte 32
	jne .DIVI

	mov al,44
	jmp .DONE

  .DIVI:

	; DIVI = 45

	cmp [edi],byte 'D'
	jne .MODI
	cmp [edi+1],byte 'I'
	jne .MODI
	cmp [edi+2],byte 'V'
	jne .MODI
	cmp [edi+3],byte 'I'
	jne .MODI
	cmp [edi+4],byte 32
	jne .MODI

	mov al,45
	jmp .DONE

  .MODI:

	; MODI = 46

	cmp [edi],byte 'M'
	jne .NOT
	cmp [edi+1],byte 'O'
	jne .NOT
	cmp [edi+2],byte 'D'
	jne .NOT
	cmp [edi+3],byte 'I'
	jne .NOT
	cmp [edi+4],byte 32
	jne .NOT

	mov al,46
	jmp .DONE

	;  =========================================================================

	;  1 ARG:

	;  NOT = 64  NEG = 65  CLR = 67

	;  =========================================================================

  .NOT:

	; NOT = 64

	cmp [edi],byte 'N'
	jne .NEG
	cmp [edi+1],byte 'O'
	jne .NEG
	cmp [edi+2],byte 'T'
	jne .NEG
	cmp [edi+3],byte 32
	jne .NEG

	mov al,64
	jmp .DONE

  .NEG:

	; NEG = 65

	cmp [edi],byte 'N'
	jne .CLR
	cmp [edi+1],byte 'E'
	jne .CLR
	cmp [edi+2],byte 'G'
	jne .CLR
	cmp [edi+3],byte 32
	jne .CLR

	mov al,65
	jmp .DONE

  .CLR:

	; CLR = 66

	cmp [edi],byte 'C'
	jne .NBYTES
	cmp [edi+1],byte 'L'
	jne .NBYTES
	cmp [edi+2],byte 'R'
	jne .NBYTES
	cmp [edi+3],byte 32
	jne .NBYTES

	mov al,66

	;  =========================================================================

	;  1 IMMED:

	;  NBYTES = 96  REPEAT = 97

	;  =========================================================================

  .NBYTES:

	; NBYTES = 96

	cmp [edi],byte 'N'
	jne .REPEAT
	cmp [edi+1],byte 'B'
	jne .REPEAT
	cmp [edi+2],byte 'Y'
	jne .REPEAT
	cmp [edi+3],byte 'T'
	jne .REPEAT
	cmp [edi+4],byte 'E'
	jne .REPEAT
	cmp [edi+5],byte 'S'
	jne .REPEAT
	cmp [edi+6],byte 32
	jne .REPEAT

	mov al,96

  .REPEAT:

	; REPEAT = 97

	cmp [edi],byte 'R'
	jne .DONE
	cmp [edi+1],byte 'E'
	jne .DONE
	cmp [edi+2],byte 'P'
	jne .DONE
	cmp [edi+3],byte 'E'
	jne .DONE
	cmp [edi+4],byte 'A'
	jne .DONE
	cmp [edi+5],byte 'T'
	jne .DONE
	cmp [edi+6],byte 32
	jne .DONE

	mov al,97

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc InstrToUpper uses edi

  ; convert all letters in the instr to upper case

	lea edi,[CurrentInstr]

	mov cx,0

  .CONVERT:

	mov al,[edi]

	cmp al,97

	jl .NEXT

	cmp al,122

	jg .NEXT

	sub al,32

	mov [edi],al

  .NEXT:

	inc edi
	inc cx

	cmp cx,8

	jl .CONVERT

	ret

endp

; -------------------------------------------------------------------------------------

proc SkipSpaces

  ; ESI is set outside of this procedure

  ; advance ESI to the first non space char

	mov al,0

  .NEXT:

	mov dl,[esi]

	cmp dl,32

	jne .TEST

	inc esi

	jmp .NEXT

  .TEST:

	cmp dl,byte 0

	jne .DONE

	; a non space char was not found

	mov al,0xff

	mov [nErrorCode],byte 3

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc ValidateParamsBuffer uses esi

  ; make sure that bfDisplay - when used as the input buffer
  ; for the parameters - only contains valid chars

  ; 0-9 = ascii 48-57
  ; A-Z = ascii 65-90
  ; a-z = ascii 97-122
  ; space = ascii 32
  ; underscore = ascii 95
  ; crlf = 13,10
  ; {} = 123,125

  ; return the result in AL

	mov al,0

	lea esi,[bfDisplay]

  .SCAN:

	mov dl,[esi]

	cmp dl,0

	je .DONE

	cmp dl,123

	je .NEXT

	cmp dl,125

	je .NEXT

	stdcall ValidateChar

	cmp al,0xff

	je .DONE

  .NEXT:

	inc esi

	jmp .SCAN

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc ValidateCalcsBuffer uses esi

  ; make sure that bfDisplay - when used as the input buffer
  ; for the calculations - only contains valid chars

  ; 0-9 = ascii 48-57
  ; A-Z = ascii 65-90
  ; a-z = ascii 97-122
  ; space = ascii 32
  ; underscore = ascii 95
  ; crlf = 13,10

  ; return the result in AL

	mov al,0

	lea esi,[bfDisplay]

  .SCAN:

	mov dl,[esi]

	cmp dl,0

	je .DONE

	stdcall ValidateChar

	cmp al,0xff

	je .DONE

	inc esi

	jmp .SCAN

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc ValidateChar

  ; test the char passed in DL

  ; set AL to 0xff if invalid

  ; 0-9 = ascii 48-57
  ; A-Z = ascii 65-90
  ; a-z = ascii 97-122
  ; space = ascii 32
  ; underscore = ascii 95

	cmp dl,32

	je .DONE

	cmp dl,10

	je .DONE

	cmp dl,13

	je .DONE

	cmp dl,95

	je .DONE

	cmp dl,48

	jl .INVALID

	cmp dl,122

	jg .INVALID

	cmp dl,58

	jl .DONE

	cmp dl,65

	jl .INVALID

	cmp dl,91

	jl .DONE

	cmp dl,96

	jg .DONE

  .INVALID:

	mov al,0xff

	mov [nErrorCode],byte 7

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc ResetCalculations uses edi ecx

  ; clear the Calculations buffer

	mov [nCalculation],byte 0

	lea edi,[Calculations]

	mov cx,0

  .CLEAR:

	mov [edi],byte 0

	inc edi
	inc cx

	cmp cx,CALCS_BUFFER_SZ

	jl .CLEAR

	ret

endp

; -------------------------------------------------------------------------------------

proc PrintErrorMessage uses esi edi

  ; print the error message to bfDisplay

	lea edi,[bfDisplay]

	mov dl,[nErrorCode]

  .ERR_1:

	cmp dl,1

	jne .ERR_2

	lea esi,[szError_1]

	jmp .PRINT

  .ERR_2:

	cmp dl,2

	jne .ERR_3

	lea esi,[szError_2]

	jmp .PRINT

  .ERR_3:

	cmp dl,3

	jne .ERR_4

	lea esi,[szError_3]

	jmp .PRINT

  .ERR_4:

	cmp dl,4

	jne .ERR_5

	lea esi,[szError_4]

	jmp .PRINT

  .ERR_5:

	cmp dl,5

	jne .ERR_6

	lea esi,[szError_5]

	jmp .PRINT

  .ERR_6:

	cmp dl,6

	jne .ERR_7

	lea esi,[szError_6]

	jmp .PRINT

  .ERR_7:

	cmp dl,7

	jne .ERR_8

	lea esi,[szError_7]

	jmp .PRINT

  .ERR_8:

	cmp dl,8

	jne .ERR_0

	lea esi,[szError_8]

	jmp .PRINT

  .ERR_0:

	lea esi,[szError_0]

  .PRINT:

	mov dl,[esi]

	cmp dl,0

	je .MORE

	mov [edi],dl

	inc esi
	inc edi

	jmp .PRINT

  .MORE:

	mov dl,[nErrorCode]

	cmp dl,1

	jl .DONE

	cmp dl,6

	jg .DONE

	; CRLF x 2

	PRINT_CRLF
	PRINT_CRLF

	lea esi,[CurrentLine]

  .PRINTL:

	mov dl,[esi]

	cmp dl,0

	je .DONE

	mov [edi],dl

	inc esi
	inc edi

	jmp .PRINTL

  .DONE:

	mov [edi],byte 0

	ret

endp

; -------------------------------------------------------------------------------------

proc PrintExeError uses esi edi

  ; print the execution error message to the bfDisplay string

	locals

	  count db 0

	endl

	lea edi,[bfDisplay]

	; execution error codes:

	; 0 = unknown error

	; 1 = unknown instruction or argument code

	; 2 = divide by zero error

	cmp [nExeErrorCode],byte 1

	je .EXE_ERR_1

	cmp [nExeErrorCode],byte 2

	je .EXE_ERR_2

  .EXE_ERR_0:

	lea esi,[szExeError_0]

  .SZ_0:

	mov al,[esi]

	cmp al,0

	je .BF_CALCS

	mov [edi],al

	inc esi
	inc edi

	jmp .SZ_0

  .EXE_ERR_1:

	lea esi,[szExeError_1]

  .SZ_1:

	mov al,[esi]

	cmp al,0

	je .BF_CALCS

	mov [edi],al

	inc esi
	inc edi

	jmp .SZ_1

  .EXE_ERR_2:

	lea esi,[szExeError_2]

  .SZ_2:

	mov al,[esi]

	cmp al,0

	je .DONE

	mov [edi],al

	inc esi
	inc edi

	jmp .SZ_2

  .BF_CALCS:

	; print the calculations buffer

	lea esi,[Calculations]

	PRINT_CRLF
	PRINT_CRLF

	mov cx,CALCS_BUFFER_SZ

  .PRINT:

	; byte to digits

	mov al,[esi]

	stdcall ByteToDigits

	; add the 2 digits to the string

	mov [edi],ah
	inc edi

	mov [edi],al
	inc edi

	inc [count]

	cmp [count],20

	je .CRLF

	jmp .NEXT

  .CRLF:

	mov [count],byte 0

	PRINT_CRLF

  .NEXT:

	inc esi

	dec cx

	cmp cx,0

	jg .PRINT

  .DONE:

	; NULL terminate

	mov [edi],byte 0

	; reset the error flag

	mov [nExeErrorCode],byte 0

	ret

endp

; -------------------------------------------------------------------------------------

proc ExecuteCalculations uses esi edi

  ; execute the instructions in the Calculations buffer

	lea esi,[Calculations]

	mov cx,0

  .INSTR:

	mov [nExeErrorCode],byte 0

	; get the next instruction

	xor eax,eax

	; put instr code, arg 1 index, arg 2 index into EAX

	mov eax,[esi]

	; instr code is in AL

	; instr code = 0, no more instructions

	cmp al,0

	je .DONE

	cmp al,32

	jge .32_63

  .1_31:

	cmp al,1
	je .1
	cmp al,2
	je .2
	cmp al,3
	je .3
	cmp al,4
	je .4
	cmp al,5
	je .5
	cmp al,6
	je .6
	cmp al,7
	je .7
	cmp al,8
	je .8
	cmp al,9
	je .9
	cmp al,10
	je .10

	jmp .UI_ERROR

  .1:

	; MOV

	stdcall Mov_Dest_Src

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .2:

	; ADD

	stdcall Add_Dest_Src

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .3:

	; SUB

	stdcall Sub_Dest_Src

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .4:

	; MUL

	stdcall Mul_Dest_Src

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .5:

	; DIV

	stdcall Div_Dest_Src

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .6:

	; MOD

	stdcall Mod_Dest_Src

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .7:

	; AND

	stdcall And_Dest_Src

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .8:

	; OR

	stdcall Or_Dest_Src

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .9:

	; XOR

	stdcall Xor_Dest_Src

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .10:

	; XCHG

	stdcall Xchg_Dest_Src

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .32_63:

	cmp al,64

	jge .64_95

	; get the immediate value

	lea edi,[Immediate]

	mov edx,[esi+4]
	mov [edi],edx
	mov edx,[esi+8]
	mov [edi+4],edx
	mov edx,[esi+12]
	mov [edi+8],edx
	mov edx,[esi+16]
	mov [edi+12],edx

	cmp al,32
	je .32
	cmp al,33
	je .33
	cmp al,34
	je .34
	cmp al,35
	je .35
	cmp al,36
	je .36
	cmp al,37
	je .37
	cmp al,38
	je .38
	cmp al,39
	je .39
	cmp al,40
	je .40
	cmp al,41
	je .41
	cmp al,42
	je .42
	cmp al,43
	je .43
	cmp al,44
	je .44
	cmp al,45
	je .45
	cmp al,46
	je .46

	jmp .UI_ERROR

  .32:

	; MOVI

	stdcall Mov_Dest_Immed

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .33:

	; ADDI

	stdcall Add_Dest_Immed

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .34:

	; SUBI

	stdcall Sub_Dest_Immed

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .35:

	; ANDI

	stdcall And_Dest_Immed

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .36:

	; ORI

	stdcall Or_Dest_Immed

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .37:

	; XORI

	stdcall Xor_Dest_Immed

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .38:

	; SHL

	stdcall Shl_Dest_Immed

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .39:

	; SHR

	stdcall Shr_Dest_Immed

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .40:

	; ROTL

	stdcall Rotl_Dest_Immed

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .41:

	; ROTR

	stdcall Rotr_Dest_Immed

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .42:

	; TRUNC

	stdcall Trunc_Dest_Immed

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .43:

	; BSET

	stdcall Bset_Dest_Immed

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .44:

	; MULI

	stdcall Mul_Dest_Immed

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .45:

	; DIVI

	stdcall Div_Dest_Immed

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .46:

	; MODI

	stdcall Mod_Dest_Immed

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .64_95:

	cmp al,96

	jge .96_127

	cmp al,64
	je .64
	cmp al,65
	je .65
	cmp al,66
	je .66

	jmp .UI_ERROR

  .64:

	; NOT

	stdcall Not_Dest

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .65:

	; NEG

	stdcall Neg_Dest

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .66:

	; CLR

	stdcall Clr_Dest

	cmp dl,0xff

	je .UI_ERROR

	jmp .NEXT

  .96_127:

	; get the immediate value

	mov edx,[esi+4]

	cmp al,96
	je .96
	cmp al,97
	je .97

	jmp .UI_ERROR

  .96:

	; NBYTES

	mov [nBytes],dx

	jmp .NEXT

  .97:

	; REPEAT

	; immediate value is in EDX

	; put iteration number into EAX

	mov eax,[esi+8]

	cmp eax,edx

	jae .END_REPEAT

	inc eax

	mov [esi+8],eax

	lea esi,[Calculations]

	mov cx,0

	jmp .INSTR

  .END_REPEAT:

	mov [esi+8],dword 0

  .NEXT:

	cmp [nExeErrorCode],byte 0

	jne .EXE_ERROR

	add esi,20

	inc cx

	cmp cx,N_CALCS

	jl .INSTR

	jmp .DONE

  .UI_ERROR:

	; error - unknown instruction or argument code

	mov [nExeErrorCode],byte 1

  .EXE_ERROR:

	mov eax,0xffffffff

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Mov_Dest_Src uses esi edi ecx

  ; copy the src value to the dest

  ; MOV dest src

	shr eax,8

	; dest index is in AL and src index is in AH

	; get ESI for the src arg

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	mov esi,edi

	; get EDI for the dest arg

	mov dl,al

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; copy the src value to dest

	mov cx,0

  .COPY:

	mov dl,[esi]

	mov [edi],dl

	inc esi
	inc edi

	inc cx

	cmp cx,HEX_LEN

	jl .COPY

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Add_Dest_Src uses esi edi ecx

  ; ADD the src value to the dest

  ; ADD dest src

	shr eax,8

	; dest index is in AL and src index is in AH

	; get ESI for the src arg

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	mov esi,edi

	; get EDI for the dest arg

	mov dl,al

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; add the src value to dest

	mov cx,0

	; save CF from iteration to iteration

	; init CF = 0

	clc

	; push flags register to the stack

	pushf

  .ADD:

	mov dl,[esi]

	; get the flags register from the stack

	popf

	adc [edi],dl

	; save the flags register to the stack, for the next iteration

	pushf

	inc esi
	inc edi
	inc cx

	cmp cx,HEX_LEN

	jl .ADD

	; clean up the stack

	popf

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Sub_Dest_Src uses esi edi ecx

  ; SUB the src value from the dest

  ; SUB dest src

	shr eax,8

	; dest index is in AL and src index is in AH

	; get ESI for the src arg

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	mov esi,edi

	; get EDI for the dest arg

	mov dl,al

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; sub the src value from dest

	mov cx,0

	; save CF from iteration to iteration

	; init CF = 0

	clc

	; push flags register to the stack

	pushf

  .SUB:

	mov dl,[esi]

	; get the flags register from the stack

	popf

	sbb [edi],dl

	; save the flags register to the stack, for the next iteration

	pushf

	inc esi
	inc edi
	inc cx

	cmp cx,HEX_LEN

	jl .SUB

	; clean up the stack

	popf

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Mul_Dest_Src uses esi edi ecx

  ; Multiply the dest value by the src value

  ; MUL dest src

	shr eax,8

	; dest index is in AL and src index is in AH

	; get ESI for the src arg

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	mov esi,edi

	; get EDI for the dest arg

	mov dl,al

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; also need an intermediate value - the temp buffer

	; clear the intermediate value

	stdcall ClearTempBuffer

	xor ecx,ecx

	xor edx,edx

  .MULTIPLY:

	; get a byte from ESI and multiply the hex value in EDI

	; add the result to the temp buffer

	mov al,[esi]

	mov ebx,edi

	; the byte is passed in AL, the offset in DL, EDI in EBX

	stdcall ByteMultiply

	inc esi

	inc dx

	inc cx

	cmp cx,HEX_LEN

	jl .MULTIPLY

	; copy the result from the temp buffer back to EDI

	stdcall CopyFromTempBuffer

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc ByteMultiply uses ecx edi

  ; multiply EDI by a byte value and add to TempBf

  ; EDI is passed in EBX

  ; byte is passed in AL

  ; offset is passed in DX

	locals

	  mbyte db 0

	endl

	; store the byte

	mov [mbyte],al

	; get EDI

	mov edi,ebx

	mov cx,0

	xor ebx,ebx

	; put the offset into BX (passed to Add_Word)

	mov bx,dx

  .B_MULT:

	; mul byte [EDI] = [EDI] * AL = |AH|AL| = AX

	mul byte [edi]

	; add AX to the temp buffer

	stdcall Add_Word

	; retore AL for the next iteration

	mov al,[mbyte]

	inc edi

	inc bx

	cmp bx,HEX_LEN

	jge .DONE

	inc cx

	cmp cx,HEX_LEN

	jl .B_MULT

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Add_Word uses edi ecx

  ; add the word in AX to the temp buffer

  ; at the offset given by BX

	cmp bx,HEX_LEN

	jge .DONE

	lea edi,[TempBf]

	xor ecx,ecx

	mov cx,bx

	cmp bx,HEX_LEN-1

	jl .ADD_WD

	add [edi+ecx],al

	jmp .DONE

  .ADD_WD:

	add [edi+ecx],al

	inc cx

	adc [edi+ecx],ah

	jnc .DONE

	inc cx

  .ADD_C:

	cmp cx,HEX_LEN

	jge .DONE

	add byte [edi+ecx],1

	jnc .DONE

	inc cx

	jmp .ADD_C

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Div_Dest_Src uses esi edi ecx

  ; Divide the dest value by the src

  ; DIV dest src

	shr eax,8

	; dest index is in AL and src index is in AH

	; get ESI for the src arg

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	mov esi,edi

	; get EDI for the dest arg

	mov dl,al

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; reset the quotient and divisor

	stdcall Zero_Quot_Div

	; copy the source (ESI) to the divisor

	stdcall CopyToDivisor

	; do the division

	stdcall Divide

	; copy the quotient to the destination

	stdcall QuotientToDest

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Mod_Dest_Src uses esi edi ecx

  ; compute dest mod src

  ; MOD dest src

	shr eax,8

	; dest index is in AL and src index is in AH

	; get ESI for the src arg

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	mov esi,edi

	; get EDI for the dest arg

	mov dl,al

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; reset the quotient and divisor

	stdcall Zero_Quot_Div

	; copy the source (ESI) to the divisor

	stdcall CopyToDivisor

	; do the division

	stdcall Divide

	; remainder is in EDI (the destination)

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc And_Dest_Src uses esi edi ecx

  ; AND the src value into the dest

  ; AND dest src

	shr eax,8

	; dest index is in AL and src index is in AH

	; get ESI for the src arg

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	mov esi,edi

	; get EDI for the dest arg

	mov dl,al

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; AND the src value into dest

	mov cx,0

  .AND:

	mov dl,[esi]

	and [edi],dl

	inc esi
	inc edi

	inc cx

	cmp cx,HEX_LEN

	jl .AND

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Or_Dest_Src uses esi edi ecx

  ; OR the src value into the dest

  ; OR dest src

	shr eax,8

	; dest index is in AL and src index is in AH

	; get ESI for the src arg

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	mov esi,edi

	; get EDI for the dest arg

	mov dl,al

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; OR the src value into dest

	mov cx,0

  .OR:

	mov dl,[esi]

	or [edi],dl

	inc esi
	inc edi

	inc cx

	cmp cx,HEX_LEN

	jl .OR

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Xor_Dest_Src uses esi edi ecx

  ; XOR the src value into the dest

  ; XOR dest src

	shr eax,8

	; dest index is in AL and src index is in AH

	; get ESI for the src arg

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	mov esi,edi

	; get EDI for the dest arg

	mov dl,al

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; XOR the src value into dest

	mov cx,0

  .XOR:

	mov dl,[esi]

	xor [edi],dl

	inc esi
	inc edi

	inc cx

	cmp cx,HEX_LEN

	jl .XOR

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Xchg_Dest_Src uses esi edi ecx

  ; exchange the src value and the dest value

  ; XCHG dest src

	shr eax,8

	; dest index is in AL and src index is in AH

	; get ESI for the src arg

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	mov esi,edi

	; get EDI for the dest arg

	mov dl,al

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; copy ESI to the TempBf

	stdcall CopyToTempBuffer

	; copy EDI to ESI

	xor ecx,ecx

  .COPY:

	mov dl,[edi+ecx]

	mov [esi+ecx],dl

	inc cx

	cmp cx,HEX_LEN

	jl .COPY

	; copy from TempBf to EDI

	stdcall CopyFromTempBuffer

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Mov_Dest_Immed uses esi edi ecx

  ; move the immediate value into the destination:

  ; MOVI dest immed

	; get EDI for the arg (dest)

	; arg index is in AH

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; clear the destination

	stdcall ClearParam

	; get EDI again

	mov dl,ah

	stdcall GetDestPtr

	; copy in the 16 byte immediate value

	lea esi,[Immediate]

	mov edx,[esi]
	mov [edi],edx

	mov edx,[esi+4]
	mov [edi+4],edx

	mov edx,[esi+8]
	mov [edi+8],edx

	mov edx,[esi+12]
	mov [edi+12],edx

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Add_Dest_Immed uses esi edi ecx

  ; add the immediate value to the destination:

  ; ADDI dest immed

	; get EDI for the arg (dest)

	; arg index is in AH

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; add in the 16 byte immediate value

	lea esi,[Immediate]

	mov edx,[esi]
	add [edi],edx

	mov edx,[esi+4]
	adc [edi+4],edx

	mov edx,[esi+8]
	adc [edi+8],edx

	mov edx,[esi+12]
	adc [edi+12],edx

	jnc .DL_0

	add edi,16

	mov cx,16

  .ADD_C:

	add [edi],byte 1

	jnc .DL_0

	inc edi

	inc cx

	cmp cx,HEX_LEN

	jl .ADD_C

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Sub_Dest_Immed uses esi edi ecx

  ; subtract the immediate value from the destination:

  ; SUBI dest immed

	; get EDI for the arg (dest)

	; arg index is in AH

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; subtract the 16 byte immediate value

	lea esi,[Immediate]

	mov edx,[esi]
	sub [edi],edx

	mov edx,[esi+4]
	sbb [edi+4],edx

	mov edx,[esi+8]
	sbb [edi+8],edx

	mov edx,[esi+12]
	sbb [edi+12],edx

	jnc .DL_0

	add edi,16

	mov cx,16

  .SUB_B:

	sub [edi],byte 1

	jnc .DL_0

	inc edi

	inc cx

	cmp cx,HEX_LEN

	jl .SUB_B

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc And_Dest_Immed uses esi edi ecx

  ; AND the immediate value into the destination:

  ; ANDI dest immed

	; get EDI for the arg (dest)

	; arg index is in AH

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; AND in the 16 byte immediate value

	lea esi,[Immediate]

	mov edx,[esi]
	and [edi],edx

	mov edx,[esi+4]
	and [edi+4],edx

	mov edx,[esi+8]
	and [edi+8],edx

	mov edx,[esi+12]
	and [edi+12],edx

	; clear the rest of EDI

	xor ecx,ecx

	mov cx,16

  .CLEAR:

	mov [edi+ecx],byte 0

	inc cx

	cmp cx,HEX_LEN

	jl .CLEAR

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Or_Dest_Immed uses esi edi ecx

  ; OR the immediate value into the destination:

  ; ORI dest immed

	; get EDI for the arg (dest)

	; arg index is in AH

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; OR in the 16 byte immediate value

	lea esi,[Immediate]

	mov edx,[esi]
	or [edi],edx

	mov edx,[esi+4]
	or [edi+4],edx

	mov edx,[esi+8]
	or [edi+8],edx

	mov edx,[esi+12]
	or [edi+12],edx

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Xor_Dest_Immed uses esi edi ecx

  ; XOR the immediate value into the destination:

  ; XORI dest immed

	; get EDI for the arg (dest)

	; arg index is in AH

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; XOR in the 16 byte immediate value

	lea esi,[Immediate]

	mov edx,[esi]
	xor [edi],edx

	mov edx,[esi+4]
	xor [edi+4],edx

	mov edx,[esi+8]
	xor [edi+8],edx

	mov edx,[esi+12]
	xor [edi+12],edx

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Shl_Dest_Immed uses esi edi ecx

  ; shift left dest by the number of bits given in immed

  ; SHL dest immed

	; get EDI for the arg (dest)

	; arg index is in AH

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; clear the shift buffer

	stdcall ClearShiftBuffer

	; check that nBytes is in range

	stdcall Check_nBytes

	; the number of bytes to apply the SHL to is now given by nBytes

	; get the number of bits to shift by

	lea esi,[Immediate]

	mov edx,[esi]

	; if the shift is greater or equal to nBytes, the result is zero

	xor eax,eax

	mov ax,[nBytes]

	shl ax,3

	cmp dx,ax

	jge .DL_0

	; copy EDI to the shift buffer

	; proc takes ESI

	mov esi,edi

	stdcall CopyToShiftBuffer

	; do the shift

	stdcall ShiftLeft

  .DL_0:

	stdcall CopyFromShiftBuffer

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc ShiftLeft uses esi edi ecx

  ; for the SHL operation

  ; SHL the shift buffer by shl_bytes + shl_bits

  ; size of shift in bits passed in EDX

  ; ShiftBf is used as the shift buffer

	locals

	  ncopy dw 0

	endl

	lea esi,[ShiftBf]

	lea edi,[ShiftBf]

	mov ebx,edx

	shr ebx,3

	; EBX now contains shl_bytes

	cmp bx,0

	je .BITS

	; ncopy = number of bytes to copy up = nBytes - shl_bytes

	mov ax,[nBytes]

	mov [ncopy],ax

	sub [ncopy],bx

  .BYTES:

	sub esi,ebx

	xor ecx,ecx

	mov cx,[nBytes]

	dec cx

  .COPY:

	; copy up the bytes

	mov bl,[esi+ecx]

	mov [edi+ecx],bl

	dec cx

	dec word [ncopy]

	cmp [ncopy],word 0

	jg .COPY

	cmp cx,0

	jl .BITS

  .ZERO:

	; zero the rest of the buffer

	mov [edi+ecx],byte 0

	dec cx

	cmp cx,0

	jge .ZERO

  .BITS:

	; calculate the size of the remaining bit shift (less than 8 bits)

	; calc EDX mod 8

	and edx,7

	cmp edx,0

	; if bits = 0, nothing more to do

	je .DONE

	stdcall SHL_Bits

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc SHL_Bits uses edi ecx eax

  ; shift size in bits is passed in DL

	lea edi,[ShiftBf]

	mov cl,dl

	; CL contains the shift size, so use AX as the counter

	xor eax,eax

	mov ax,[nBytes]

	dec ax

	add edi,eax

  .SHL:

	mov dh,[edi-1]

	mov bl,[edi]

	shld bx,dx,cl

	mov [edi],bl

	dec edi

	dec ax

	cmp ax,0

	jg .SHL

  .FIRST:

	shl byte [edi],cl

	ret

endp

; -------------------------------------------------------------------------------------

proc Shr_Dest_Immed uses esi edi ecx

  ; shift right dest by the number of bits given in immed

  ; SHR dest immed

	; get EDI for the arg (dest)

	; arg index is in AH

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; clear the shift buffer

	stdcall ClearShiftBuffer

	; check that nBytes is in range

	stdcall Check_nBytes

	; the number of bytes to apply the SHR to is now given by nBytes

	; get the number of bits to shift by

	lea esi,[Immediate]

	mov edx,[esi]

	; if the shift is greater or equal to nBytes, the result is zero

	xor eax,eax

	mov ax,[nBytes]

	shl ax,3

	cmp dx,ax

	jge .DL_0

	; copy EDI to the shift buffer

	; proc takes ESI

	mov esi,edi

	stdcall CopyToShiftBuffer

	; do the shift

	stdcall ShiftRight

  .DL_0:

	stdcall CopyFromShiftBuffer

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc ShiftRight uses esi edi ecx

  ; for the SHR operation

  ; SHR the shift buffer by shr_bytes + shr_bits

  ; size of shift in bits passed in EDX

  ; ShiftBf is used as the shift buffer

	locals

	  ncopy dw 0

	endl

	lea esi,[ShiftBf]

	lea edi,[ShiftBf]

	mov ebx,edx

	shr ebx,3

	; EBX now contains shr_bytes

	cmp bx,0

	je .BITS

	; ncopy = number of bytes to copy down = nBytes - shr_bytes

	mov ax,[nBytes]

	mov [ncopy],ax

	sub [ncopy],bx

  .BYTES:

	xor ecx,ecx

	add esi,ebx

  .COPY:

	; copy down the bytes

	mov bl,[esi+ecx]

	mov [edi+ecx],bl

	inc cx

	dec word [ncopy]

	cmp [ncopy],word 0

	jg .COPY

	cmp cx,[nBytes]

	jge .BITS

  .ZERO:

	; zero the rest of the buffer

	mov [edi+ecx],byte 0

	inc cx

	cmp cx,[nBytes]

	jl .ZERO

  .BITS:

	; calculate the size of the remaining bit shift (less than 8 bits)

	; calc EDX mod 8

	and edx,7

	cmp edx,0

	; if bits = 0, nothing more to do

	je .DONE

	stdcall SHR_Bits

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc SHR_Bits uses edi ecx eax

  ; shift size in bits is passed in DL

	lea edi,[ShiftBf]

	mov cl,dl

	; CL contains the shift size, so use AX as the counter

	xor eax,eax

  .SHR:

	mov dl,[edi+1]

	mov bh,[edi]

	shrd bx,dx,cl

	mov [edi],bh

	inc edi

	inc ax

	inc ax

	cmp ax,[nBytes]

	jge .LAST

	dec ax

	jmp .SHR

  .LAST:

	shr byte [edi],cl

	ret

endp

; -------------------------------------------------------------------------------------

proc Rotl_Dest_Immed uses esi edi ecx

  ; rot left dest by the number of bits given in immed

  ; ROTL dest immed

	locals

	  shift dw 0

	endl

	; get EDI for the arg (dest)

	; arg index is in AH

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; clear the shift buffer

	stdcall ClearShiftBuffer

	; check that nBytes is in range

	stdcall Check_nBytes

	; the number of bytes to apply the ROTL to is now given by nBytes

	; get the number of bits to shift by

	lea esi,[Immediate]

	mov edx,[esi]

	; if the number of bits to shift by is equal to (8 x nBytes) then no rot needed

	xor eax,eax

	mov ax,[nBytes]

	shl eax,3

	cmp eax,edx

	je .NO_ROT

	; if the number of bits to shift by is greater than (8 x nBytes)

	; calc EDX = EDX mod EAX

  .MOD:

	cmp edx,eax

	jl .B_COPY

	sub edx,eax

	cmp edx,eax

	; if (EDX mod EAX) = 0 then no rot needed

	je .NO_ROT

	jmp .MOD

  .B_COPY:

	mov [shift],dx

	; copy EDI to the shift buffer

	; ROTL(x) = SHL(x) or SHR(nBytes-x)

	; do the left shift

	; proc takes ESI

	mov esi,edi

	stdcall CopyToShiftBuffer

	stdcall ShiftLeft

	lea esi,[ShiftBf]

	; copy the result to the temp buffer

	stdcall CopyToTempBuffer

	; ROTL(x) = SHL(x) or SHR(nBytes-x)

	; do the right shift

	; clear the shift buffer

	stdcall ClearShiftBuffer

	; proc takes ESI

	mov esi,edi

	stdcall CopyToShiftBuffer

	; put (nBytes - shift) into DX

	xor eax,eax

	xor edx,edx

	mov ax,[nBytes]

	shl ax,3

	sub ax,[shift]

	mov dx,ax

	stdcall ShiftRight

	lea esi,[ShiftBf]

	stdcall OrIntoTempBuffer

	stdcall CopyFromTempBuffer

	jmp .DL_0

  .NO_ROT:

	stdcall TruncToNbytes

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Rotr_Dest_Immed uses esi edi ecx

  ; rot right dest by the number of bits given in immed

  ; ROTR dest immed

	locals

	  shift dw 0

	endl

	; get EDI for the arg (dest)

	; arg index is in AH

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; clear the shift buffer

	stdcall ClearShiftBuffer

	; check that nBytes is in range

	stdcall Check_nBytes

	; the number of bytes to apply the ROTR to is now given by nBytes

	; get the number of bits to shift by

	lea esi,[Immediate]

	mov edx,[esi]

	; if the number of bits to shift by is equal to (8 x nBytes) then no rot needed

	xor eax,eax

	mov ax,[nBytes]

	shl eax,3

	cmp eax,edx

	je .NO_ROT

	; if the number of bits to shift by is greater than (8 x nBytes)

	; calc EDX = EDX mod EAX

  .MOD:

	cmp edx,eax

	jl .B_COPY

	sub edx,eax

	cmp edx,eax

	; if (EDX mod EAX) = 0 then no rot needed

	je .NO_ROT

	jmp .MOD

  .B_COPY:

	mov [shift],dx

	; copy EDI to the shift buffer

	; ROTL(x) = SHL(x) or SHR(nBytes-x)

	; do the right shift

	; proc takes ESI

	mov esi,edi

	stdcall CopyToShiftBuffer

	stdcall ShiftRight

	lea esi,[ShiftBf]

	; copy the result to the temp buffer

	stdcall CopyToTempBuffer

	; ROTL(x) = SHL(x) or SHR(nBytes-x)

	; do the left shift

	; clear the shift buffer

	stdcall ClearShiftBuffer

	; proc takes ESI

	mov esi,edi

	stdcall CopyToShiftBuffer

	; put (nBytes - shift) into DX

	xor eax,eax

	xor edx,edx

	mov ax,[nBytes]

	shl ax,3

	sub ax,[shift]

	mov dx,ax

	stdcall ShiftLeft

	lea esi,[ShiftBf]

	stdcall OrIntoTempBuffer

	stdcall CopyFromTempBuffer

	jmp .DL_0

  .NO_ROT:

	stdcall TruncToNbytes

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Check_nBytes uses eax

  ; check that nBytes is in range

	mov ax,[nBytes]

	cmp ax,0

	jle .ADJUST

	cmp ax,HEX_LEN

	jg .ADJUST

	jmp .DONE

  .ADJUST:

	mov [nBytes],HEX_LEN

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Trunc_Dest_Immed uses esi edi ecx

  ; truncate dest to the number of bits specified in immed

  ; TRUNC dest immed

	; get EDI for the arg (dest)

	; arg index is in AH

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; get immed

	lea esi,[Immediate]

	; EAX gives the size of the trunc in bits

	mov eax,[esi]

	cmp eax,HEX_LEN * 8

	jge .DL_0

	; convert EAX to a byte index

	shr eax,3

	xor ecx,ecx

	mov cx,HEX_LEN-1

	cmp cx,ax

	je .BITS

  .TRUNC:

	mov [edi+ecx],byte 0

	dec cx

	cmp cx,ax

	jg .TRUNC

  .BITS:

	; deal with the remaining bits (if any)

	mov edx,[esi]

	and edx,7

	add edi,ecx

	mov cl,8
	sub cl,dl

	mov al,0xff

	shr al,cl

	and [edi],al

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Bset_Dest_Immed uses esi edi ecx

  ; in dest - turn on the number of bits specified in immed

  ; starting from bit 0

  ; BSET dest immed

	; get EDI for the arg (dest)

	; arg index is in AH

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; get immed

	lea esi,[Immediate]

	mov eax,[esi]

	; convert to bytes

	shr eax,3

	cmp eax,0

	je .BITS

	; set the bytes

	mov cx,0

  .BSET:

	mov [edi],byte 0xff

	inc edi

	inc cx

	cmp cx,ax

	je .BITS

	cmp cx,HEX_LEN

	jge .DL_0

	jmp .BSET

  .BITS:

	mov eax,[esi]

	and eax,7

	cmp eax,0

	je .DL_0

	mov cl,8

	sub cl,al

	mov al,0xff

	shr al,cl

	or [edi],al

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Mul_Dest_Immed uses esi edi ecx

  ; Multiply the dest by the immediate value:

  ; MULI dest immed

	; get EDI for the arg (dest)

	; arg index is in AH

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; get the 16 byte immediate value

	lea esi,[Immediate]

	; also need an intermediate value - the temp buffer

	; clear the intermediate value

	stdcall ClearTempBuffer

	xor ecx,ecx

	xor edx,edx

  .MULTIPLY:

	; get a byte from ESI and multiply the hex value in EDI

	; add the result to the temp buffer

	mov al,[esi]

	mov ebx,edi

	; the byte is passed in AL, the offset in DL, EDI in EBX

	stdcall ByteMultiply

	inc esi

	inc dx

	inc cx

	cmp cx,16

	jl .MULTIPLY

	; copy the result from the temp buffer back to EDI

	stdcall CopyFromTempBuffer

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Div_Dest_Immed uses esi edi ecx

  ; Divide the dest value by the immediate value

  ; DIVI dest immed

	shr eax,8

	; dest index is in AL

	; get EDI for the dest arg

	mov dl,al

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; reset the quotient and divisor

	stdcall Zero_Quot_Div

	; copy the immediate value to the divisor

	stdcall ImmediateToDivisor

	; do the division

	stdcall Divide

	; copy the quotient to the destination

	stdcall QuotientToDest

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Mod_Dest_Immed uses esi edi ecx

  ; compute dest mod immed

  ; MODI dest immed

	shr eax,8

	; dest index is in AL

	; get EDI for the dest arg

	mov dl,al

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; reset the quotient and divisor

	stdcall Zero_Quot_Div

	; copy the immediate value to the divisor

	stdcall ImmediateToDivisor

	; do the division

	stdcall Divide

	; remainder is in EDI (the destination)

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Not_Dest uses edi ecx

  ; NOT the destination value

  ; NOT dest

	; get EDI for the arg (dest)

	; arg index is in AH

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; NOT the destination

	mov cx,0

  .NOT:

	not byte [edi]

	inc edi

	inc cx

	cmp cx,HEX_LEN

	jl .NOT

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Neg_Dest uses edi ecx

  ; NEG the destination value = NOT(dest) + 1

  ; NEG dest

	; get EDI for the arg (dest)

	; arg index is in AH

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; NOT the destination

	xor ecx,ecx

  .NOT:

	not byte [edi+ecx]

	inc cx

	cmp cx,HEX_LEN

	jl .NOT

	; add 1 to the dest

	xor ecx,ecx

  .ADD_C:

	add [edi+ecx],byte 1

	jnc .DL_0

	inc cx

	cmp cx,HEX_LEN

	jl .ADD_C

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:


	ret

endp

; -------------------------------------------------------------------------------------

proc Clr_Dest uses edi

  ; CLR the destination value

  ; CLR dest

	; get EDI for the arg (dest)

	; arg index is in AH

	mov dl,ah

	stdcall GetDestPtr

	cmp dl,0xff

	je .DONE

	; clear the destination

	stdcall ClearParam

  .DL_0:

	; return DL = 0 (success)

	xor edx,edx

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc GetDestPtr

  ; the index of the arg is passed in DL

  ; set EDI to point to the arg

  ; error code returned in DL

	cmp dl,1
	je .1
	cmp dl,2
	je .2
	cmp dl,3
	je .3
	cmp dl,4
	je .4
	cmp dl,5
	je .5
	cmp dl,6
	je .6
	cmp dl,7
	je .7
	cmp dl,8
	je .8
	cmp dl,9
	je .9
	cmp dl,10
	je .10
	cmp dl,11
	je .11
	cmp dl,12
	je .12
	cmp dl,13
	je .13
	cmp dl,14
	je .14

	jmp .ERROR

  .1:

	lea edi,[Param_1]
	jmp .DONE

  .2:

	lea edi,[Param_2]
	jmp .DONE

  .3:

	lea edi,[Param_3]
	jmp .DONE

  .4:

	lea edi,[Param_4]
	jmp .DONE

  .5:

	lea edi,[Param_5]
	jmp .DONE

  .6:

	lea edi,[Param_6]
	jmp .DONE

  .7:

	lea edi,[Param_7]
	jmp .DONE

  .8:

	lea edi,[Param_8]
	jmp .DONE

  .9:

	lea edi,[Param_9]
	jmp .DONE

  .10:

	lea edi,[Param_10]
	jmp .DONE

  .11:

	lea edi,[Reg_1]
	jmp .DONE

  .12:

	lea edi,[Reg_2]
	jmp .DONE

  .13:

	lea edi,[Reg_3]
	jmp .DONE

  .14:

	lea edi,[Reg_4]
	jmp .DONE

  .ERROR:

	mov dl,0xff

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc ClearShiftBuffer uses edi ecx

  ; clear ShiftBf

	lea edi,[ShiftBf]

	mov cx,0

  .CLEAR:

	mov [edi],byte 0

	inc edi

	inc cx

	cmp cx,HEX_LEN

	jl .CLEAR

	ret

endp

; -------------------------------------------------------------------------------------

proc CopyToShiftBuffer uses edi ecx

  ; copy nBytes from ESI to the shift buffer

  ; called by the SHL, SHR, ROTL, ROTR functions

  ; ESI is not modified

	lea edi,[ShiftBf]

	xor ecx,ecx

  .COPY:

	mov al,[esi+ecx]

	mov [edi+ecx],al

	inc cx

	cmp cx,[nBytes]

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc CopyFromShiftBuffer uses esi ecx

  ; copy to EDI from the shift buffer

  ; EDI is not modified

	lea esi,[ShiftBf]

	xor ecx,ecx

  .COPY:

	mov al,[esi+ecx]

	mov [edi+ecx],al

	inc cx

	cmp cx,HEX_LEN

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc ClearTempBuffer uses edi ecx

  ; clear TempBf

	lea edi,[TempBf]

	xor ecx,ecx

  .CLEAR:

	mov [edi+ecx],byte 0

	inc cx

	cmp cx,HEX_LEN

	jl .CLEAR

	ret

endp

; -------------------------------------------------------------------------------------

proc CopyToTempBuffer uses edi ecx

  ; copy from ESI to the temp buffer

  ; ESI is not modified

	lea edi,[TempBf]

	xor ecx,ecx

  .COPY:

	mov al,[esi+ecx]

	mov [edi+ecx],al

	inc cx

	cmp cx,HEX_LEN

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc CopyFromTempBuffer uses esi ecx

  ; copy to EDI from the temp buffer

  ; EDI is not modified

	lea esi,[TempBf]

	xor ecx,ecx

  .COPY:

	mov al,[esi+ecx]

	mov [edi+ecx],al

	inc cx

	cmp cx,HEX_LEN

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc OrIntoTempBuffer uses edi ecx

  ; OR ESI into the temp buffer

  ; ESI is not modified

	lea edi,[TempBf]

	xor ecx,ecx

  .OR:

	mov al,[esi+ecx]

	or [edi+ecx],al

	inc cx

	cmp cx,HEX_LEN

	jl .OR

	ret

endp

; -------------------------------------------------------------------------------------

proc TruncToNbytes uses ecx

  ; truncate EDI to nBytes

	xor ecx,ecx

	mov cx,[nBytes]

  .TRUNC:

	cmp cx,HEX_LEN

	jge .DONE

	mov [edi+ecx],byte 0

	inc cx

	jmp .TRUNC

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Divide uses ecx

  ; divide EDI by the divisor

  ; the result is stored in Quotient

  ; remainder in EDI

  ; EDI is set outside of this procedure

	locals

	  offset dw 0

	  qdigit db 0

	endl

	stdcall IsDivisorZero

	cmp al,1

	; divide by zero error

	je .DIV_BY_ZERO

	; is the dividend zero (in EDI)

	stdcall IsZero

	cmp al,1

	je .DONE

	stdcall Is_GTE_Divisor

	cmp al,0

	; do nothing

	je .DONE

	; start the division by aligning the digits in the divisor with the dividend

	; put EDI into EAX for the proc

	mov eax,edi

	stdcall AlignDivisor

	; digit counts returned in EAX

	; EAX = |ndigits(src)|ndigits(dest)|

	cmp ax,0

	je .DONE

	mov [offset],ax

	shr eax,16

	cmp ax,0

	je .DONE

	; offset = ndigits(src) - ndigits(dest)

	sub [offset],ax

	cmp [offset],0

	jl .DONE

  .DIVIDE:

	mov [qdigit],byte 0

  .LT_DIV:

	; if EDI is less than the divisor, shift the divisor right by 1 digit

	stdcall Is_GTE_Divisor

	cmp al,1

	je .SUBTRACT

	stdcall ShiftDigitsRight

	dec word [offset]

	cmp [offset],word 0

	jl .DONE

	jmp .LT_DIV

  .SUBTRACT:

	stdcall SubtractDivisor

	inc byte [qdigit]

	stdcall Is_GTE_Divisor

	cmp al,1

	je .SUBTRACT

	mov al,[qdigit]

	shl eax,16

	mov ax,[offset]

	stdcall AddDigitToQuotient

	jmp .DIVIDE

  .DIV_BY_ZERO:

	mov [nExeErrorCode],byte 2

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc IsZero uses ecx

  ; is the hex value in EDI zero ?

  ; EDI set outside of this procedure

  ; result returned in AL

	xor eax,eax

	xor ecx,ecx

  .TEST:

	cmp [edi+ecx],byte 0

	jne .DONE

	inc cx

	cmp cx,HEX_LEN

	jl .TEST

	; number is zero, set AL = 1

	mov al,1

 .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Is_GTE_Divisor uses esi ecx

  ; compare the hex number in EDI to the divisor

  ; is EDI greater than or equal to ESI ?

  ; EDI is set outside of this procedure

  ; result returned in AL

	lea esi,[Divisor]

	xor eax,eax

	xor ecx,ecx

	mov cx,HEX_LEN-1

  .TEST:

	mov dl,[esi+ecx]

	cmp [edi+ecx],dl

	jb .DONE

	ja .GTE

	dec cx

	cmp cx,0

	jge .TEST

  .GTE:

	mov al,1

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Get_nDigits uses esi ecx

  ; get the number of hex digits in ESI

  ; ESI is passed in EAX

  ; result returned in AX

	mov esi,eax

	xor ecx,ecx

	mov cx,HEX_LEN-1

	xor eax,eax

	; AX will be in the range 0 - 800 (800 hex digits in a 400 byte number)

	mov ax,2*HEX_LEN

  .TEST:

	mov dl,[esi+ecx]

	test dl,0xf0

	jnz .DONE

	dec ax

	test dl,0x0f

	jnz .DONE

	dec ax

	dec cx

	cmp cx,0

	jge .TEST

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc Zero_Quot_Div uses edi ecx

  ; zero the quotient and divisor

	; quotient

	xor ecx,ecx

	lea edi,[Quotient]

  .ZQ:

	mov [edi+ecx],byte 0

	inc cx

	cmp cx,HEX_LEN

	jl .ZQ

	; divisor

	xor ecx,ecx

	lea edi,[Divisor]

  .ZD:

	mov [edi+ecx],byte 0

	inc cx

	cmp cx,HEX_LEN

	jl .ZD

	ret

endp

; -------------------------------------------------------------------------------------

proc AddDigitToQuotient uses edi ecx

  ; add a digit to the Quotient

  ; offset and digit passed in EAX

  ; offset is in nibbles (hex digits)

	lea edi,[Quotient]

	xor ecx,ecx

	; put the offset into cx

	mov cx,ax

	shr eax,16

	; the digit is now in AL

	test cx,1

	jnz .UPPER

  .LOWER:

	; SHR(1) to get the offset in bytes

	shr ecx,1

	or [edi+ecx],al

	jmp .DONE

  .UPPER:

	; SHR(1) to get the offset in bytes

	shr ecx,1

	shl al,4

	or [edi+ecx],al

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc IsDivisorZero uses edi

  ; is the divisor zero ?

	lea edi,[Divisor]

	stdcall IsZero

	ret

endp

; -------------------------------------------------------------------------------------

proc CopyToDivisor uses edi ecx

  ; copy the value in ESI to the divisor

  ; ESI is set outside of this procedure

  ; ESI is not modified

	lea edi,[Divisor]

	xor ecx,ecx

  .COPY:

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,HEX_LEN

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc ImmediateToDivisor uses esi edi ecx

  ; copy the immediate value to the divisor

	lea esi,[Immediate]

	lea edi,[Divisor]

	xor ecx,ecx

  .COPY:

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,16

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc QuotientToDest uses esi ecx

  ; copy the quotient to EDI

  ; EDI is set outside of this procedure

	lea esi,[Quotient]

	xor ecx,ecx

  .COPY:

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	inc cx

	cmp cx,HEX_LEN

	jl .COPY

	ret

endp

; -------------------------------------------------------------------------------------

proc SubtractDivisor uses esi ecx

  ; subtract the divisor from EDI

  ; EDI is set outside of this procedure

	lea esi,[Divisor]

	xor ecx,ecx

	clc

	; push flags register to the stack

	pushf

  .SUB:

	mov dl,[esi+ecx]

	; get the flags register from the stack

	popf

	sbb [edi+ecx],dl

	; save the flags register to the stack, for the next iteration

	pushf

	inc cx

	; cmp affects the CF

	cmp cx,HEX_LEN

	jl .SUB

	; clean up the stack

	popf

	ret

endp

; -------------------------------------------------------------------------------------

proc AlignDivisor uses esi edi ecx

  ; align the divisor with the number in EDI

  ; EDI is passed in EAX

	locals

	  nDigits_Src dw 0

	  nDigits_Dest dw 0

	  shift dw 0

	  ncopy dw 0

	endl

	; get EDI

	mov edi,eax

	xor eax,eax

	lea esi,[Divisor]

	mov eax,esi

	stdcall Get_nDigits

	mov [nDigits_Src],ax

	cmp ax,0

	; number in ESI is zero, do nothing

	je .DONE

	mov eax,edi

	stdcall Get_nDigits

	mov [nDigits_Dest],ax

	cmp ax,0

	; number in EDI is zero, do nothing

	je .DONE

	cmp ax,[nDigits_Src]

	; the two numbers are already aligned or the divisor is greater than EDI

	; do nothing

	jle .DONE

	; compute the shift (the number of hex digits to shift the Divisor up by)

	sub ax,[nDigits_Src]

	mov [shift],ax

	; copy up the hex digits

	lea edi,[Divisor]

	; divide AX by 2 to get the number of bytes to shift up by

	shr ax,1

	cmp ax,0

	je .DIGIT

	; put the shift into EDX and sub from ESI

	xor edx,edx

	mov dx,ax

	sub esi,edx

	xor ecx,ecx

	mov cx,HEX_LEN-1

	; number of bytes to copy up

	mov word [ncopy],HEX_LEN

	sub [ncopy],dx

  .BYTES:

	; copy up the bytes

	mov dl,[esi+ecx]

	mov [edi+ecx],dl

	dec cx

	dec word [ncopy]

	cmp [ncopy],word 0

	jg .BYTES

	cmp cx,0

	jl .DIGIT

  .ZERO:

	; zero the rest of the bytes

	mov [edi+ecx],byte 0

	dec cx

	cmp cx,0

	jge .ZERO

  .DIGIT:

	mov ax,[shift]

	test ax,1

	jz .DONE

	; shift up by 1 more digit (4 bits)

	stdcall ShiftDigitsLeft

  .DONE:

	; return the digit counts in EAX

	mov ax,[nDigits_Src]

	shl eax,16

	mov ax,[nDigits_Dest]

	ret

endp

; -------------------------------------------------------------------------------------

proc ShiftDigitsLeft uses esi edi ecx

  ; shift the Divisor one digit to the left

	lea esi,[Divisor]

	lea edi,[Divisor]

	xor ecx,ecx

	mov cx,HEX_LEN-1

	add esi,ecx
	add edi,ecx

  .SHL:

	mov dl,[esi]

	shl dl,4

	mov [edi],dl

	cmp cx,0

	jle .DONE

	dec esi

	mov dl,[esi]

	shr dl,4

	or [edi],dl

	dec edi

	dec cx

	jmp .SHL

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

proc ShiftDigitsRight uses esi edi ecx

  ; shift the Divisor one digit to the right

	lea esi,[Divisor]

	lea edi,[Divisor]

	mov cx,0

  .SHR:

	mov dl,[esi]

	shr dl,4

	mov [edi],dl

	inc esi

	cmp cx,HEX_LEN-1

	jge .DONE

	mov dl,[esi]

	shl dl,4

	or [edi],dl

	inc edi

	inc cx

	jmp .SHR

  .DONE:

	ret

endp

; -------------------------------------------------------------------------------------

section '.idata' import data readable writeable

  library kernel,'KERNEL32.DLL',\
	  user,'USER32.DLL'

  import kernel,\
	 GetModuleHandle,'GetModuleHandleA',\
	 ExitProcess,'ExitProcess'

  import user,\
	 DialogBoxParam,'DialogBoxParamA',\ 
	 SetDlgItemText,'SetDlgItemTextA',\
	 GetDlgItemText,'GetDlgItemTextA',\
	 EndDialog,'EndDialog'

; -------------------------------------------------------------------------------------

section '.data' readable writeable

  bfDisplay rb 12000

  InputBuffer rb MAX_HEX_DIGITS + 8

  Param_1 rb HEX_LEN
  Param_2 rb HEX_LEN
  Param_3 rb HEX_LEN
  Param_4 rb HEX_LEN
  Param_5 rb HEX_LEN
  Param_6 rb HEX_LEN
  Param_7 rb HEX_LEN
  Param_8 rb HEX_LEN
  Param_9 rb HEX_LEN
  Param_10 rb HEX_LEN

  Reg_1 rb HEX_LEN
  Reg_2 rb HEX_LEN
  Reg_3 rb HEX_LEN
  Reg_4 rb HEX_LEN

  TempBf rb HEX_LEN

  ShiftBf rb HEX_LEN

  Quotient rb HEX_LEN
  Divisor rb HEX_LEN

  Symbol_1 rb 10
  Symbol_2 rb 10
  Symbol_3 rb 10
  Symbol_4 rb 10
  Symbol_5 rb 10
  Symbol_6 rb 10
  Symbol_7 rb 10
  Symbol_8 rb 10
  Symbol_9 rb 10
  Symbol_10 rb 10

  Calculations rb CALCS_BUFFER_SZ

  CurrentLine rb 100

  CurrentInstr rb 10

  CurrentArg rb 10

  bfImmediate rb 40

  Immediate rb 16

  nCalculation db 0

  nErrorCode db 0

  nExeErrorCode db 0

  nBytes dw HEX_LEN

  szInputText db "Define up to ten 400 byte input parameters here:",0

  szExprText db "Enter instructions here:",0

  szError_0 db "Unknown error in calculations input buffer.",0
  szError_1 db "Extra chars found at the end of the line:",0
  szError_2 db "Immediate value argument contains non hex digits in the line:",0
  szError_3 db "Missing argument in the line:",0
  szError_4 db "Could not match argument to symbol in the line:",0
  szError_5 db "Unknown instruction in the line:",0
  szError_6 db "Immediate value must be LTE to 3200 (decimal) in the line:",0
  szError_7 db "Invalid chars in calculations input buffer.",0
  szError_8 db "Invalid chars in hex parameters input buffer.",0

  szExeError_0 db "Unknown error - execution failed:",0
  szExeError_1 db "Unknown instruction or argument code:",0
  szExeError_2 db "Instruction failed - divide by zero error.",0

  Symbol_11 db "reg_1",0
  Symbol_12 db "reg_2",0
  Symbol_13 db "reg_3",0
  Symbol_14 db "reg_4",0

; -------------------------------------------------------------------------------------

section '.rc' resource data readable

  directory RT_DIALOG,dialogs

  resource dialogs,IDD_THE_DIALOG,LANG_ENGLISH+SUBLANG_DEFAULT,the_dialog

  dialog the_dialog,\
  'FASM - Hex Calculator',20,50,522,396,\
  DS_MODALFRAME+WS_MINIMIZEBOX+WS_POPUP+WS_VISIBLE+WS_CAPTION+WS_SYSMENU,\
  0,0,"Lucida Console",11

  dialogitem 'EDIT',0,IDC_INPUT,0,0,260,366,ES_MULTILINE+ES_AUTOVSCROLL+ES_WANTRETURN+WS_VSCROLL+WS_BORDER+WS_VISIBLE,0
  dialogitem 'EDIT',0,IDC_CALCS,262,0,260,120,ES_MULTILINE+ES_AUTOVSCROLL+ES_WANTRETURN+WS_VSCROLL+WS_BORDER+WS_VISIBLE,0
  dialogitem 'EDIT',0,IDC_OUTPUT,262,122,260,244,ES_MULTILINE+ES_AUTOVSCROLL+ES_WANTRETURN+WS_VSCROLL+WS_BORDER+WS_VISIBLE,0

  dialogitem 'BUTTON',"Load Parameters",IDC_BTN_LOAD_DATA,7,375,80,14,BS_PUSHBUTTON+WS_VISIBLE,0
  dialogitem 'BUTTON',"Clear Parameters",IDC_BTN_CLR_DATA,89,375,80,14,BS_PUSHBUTTON+WS_VISIBLE,0
  dialogitem 'BUTTON',"Run Instructions",IDC_BTN_CALC,171,375,80,14,BS_PUSHBUTTON+WS_VISIBLE,0
  dialogitem 'BUTTON',"Clear Instructions",IDC_BTN_CLR_INSTR,253,375,80,14,BS_PUSHBUTTON+WS_VISIBLE,0
  dialogitem 'BUTTON',"Reset All",IDC_BTN_RESET_ALL,335,375,80,14,BS_PUSHBUTTON+WS_VISIBLE,0

  enddialog

; -------------------------------------------------------------------------------------
Advertisements

Create a free website or blog at WordPress.com.

%d bloggers like this: