PACKED DECIMAL

Note: This article uses terms like ASCII, binary numbers, etc., with very little in the way of explanation. Full explanation of every term used would make this article way too long; turn it into a text book. Please refer to a book on assembly language, or look up the terms on the Internet when the use of those terms is unclear.

I went on vacation after my last entry into the VIOTW, so this is my first entry in two weeks. Maybe that's a good thing because it breaks up the methodical flow of instructions I was contemplating, and leads to some more interesting aspects of VAX MACRO; e.g., packed decimal numbers. Packed decimal numbers are rarely used in assembly language programs but many higher level languages use them; e.g., COBOL and RPG. Why do some programming languages use packed decimal numbers, but they're rarely used in assembly language? Assembly language is mainly used when program performance is the paramount issue, but packed decimals are slower to use then the primitive numbers normally used in assembly language; e.g., integers and floating point numbers. So why do some languages use packed decimals? Packed decimals make it much easier for the programmer to perform I/O.

So what are packed decimal numbers? Well, what are decimal numbers? We use decimal numbers in our everyday lives; numbers composed of the counting digits 0 through 9. 123 is a decimal number, and so is 9876543210. Computes don't store numbers as decimals, at least not primitive number types. Computers store numbers as binary numbers; numbers composed of the counting digits 0 and 1. So a computer stores a decimal 123 as 01111011. What's with the leading 0? 123 can be stored in a quantity of memory called a byte. Today's computers use 8 bits for a byte; a bit is a 0 or 1. A byte doesn't have to be 8 bits; a bytes is the storage (memory storage) necessary to represent a character. ...and a character is what you see on the screen.

VAX computers represent characters in memory as ASCII characters; the most common way to represent characters on computers today. ASCII characters use 8 bits to represent 1 character (we already said that) but a binary 0 is not represented in ASCII as 00000000. ASCII represents 0 through 9 as a hexadecimal 30 through 39. By definition each character uses one byte of memory, and the VAX and most other computers use 8 bits to represent one byte, and hence one character. Therefore an ASCII 9 in computer memory looks like 00111001.

Before seeing packed (short for packed decimal) we'll take a look at BCD (Binaty Coded Decimal) numbers. Note that in the last paragraph we saw that an ASCII 9 (and this is what the computer determines you should see on the screen as a 9) is represented in memory as 00111001. If we split the high order from low order bits it looks like 0011 1001 . In binary 0011 is 3 in decimal, and 1001 is 9 in decimal. Since we only have the counting numbers 0 through 9 to worry about, we can split any digit into 0011 num, where num is the binary representation of the number we want to represent in ASCII. If we want to change the ASCII number to BCD, all we do is change the 0011 to 0000; i.e., we just clear out the high order byte. So a BCD 9 is 00001001.

What's great about BCD is we can get string input from the user, in an interactive program, and start performing calculations with it after a very simple data transduction; from ASCII to BCD. Of course we don't even have to convert to BCD; we could write a routine that performs arithemtic directly from ASCII. Some assemblers even have instructions for performing arithmetic directly on ASCII number; Intel assembler is one of them. However, BCD is an accepted format for arithemtic, and there have been many BCD arithemtic libraries written.

Now we're ready to discuss packed decimal numbers. BCD numbers are wasteful of space; only half of each byte is used. So why not pack the numbers together? That's what packed decimal does; stores two digits per byte. So 00111001 is a decimal 39. VAX registers are 32 bits, or 4 bytes long; a VAX register can store a decimal 0000 to 9999. BCD would limit us to just 99, so packed decimal offers a tremendous storage advantage over BCD.

Here's an example of a packed decimal operation from the IBM AS/400:

PGM
DCL VAR(&anumber) TYPE(*DEC) LEN(4)
DCL VAR(&astring) TYPE(*CHAR) LEN(4)
CHGVAR VAR(&anumber) VALUE(9999)
CHGVAR VAR(&astring) VALUE(&anumber)
ENDPGM

The above language is called CL. The only number representation it uses is packed decimal. So CL isn't blindingly fast when it comes to arithmetic calculations, but the above example shows how flexible packed decimal manipulations are. We can assign a character string to a number! The IBM AS/400 uses EBCDIC instead of ASCII, so the hexadecimal representation of a character 9 is F9 (instead of 39 like it is in ASCII). A packed decimal 9999 fills up 2 bytes with the bytes

1001 1001 1001 1001

The EBCDIC string (character string) for 9999 is:

1111 1001 1111 1001 1111 1001 1111 1001

So to convert between type *CHAR (printable character string) and *DEC (numbers that can be used in calculations) all CL has to do is insert a hexadecimal F before each packed decimal digit (4 bits each). So an assignment of a character string to a decimal number isn't a big leap fo CL or any othe anguage that uses packed decimal numbers. There are other languages that used packed decimals, but CL uses them exclusively and thus makes CL a shining example of packed decimal use.

Even though packed decimals rarely see use in assembly language, VAX MACRO gives full support of packed decimals. We can see this support in a simple example:

		.TITLE PACKED_DEMO
$DSCDEF			; descriptor definitions
;
INDSC:	.WORD	6		; input descriptor
.BYTE	DSC$K_DTYPE_T,DSC$K_CLASS_S
		.ADDRESS	INPUT
OUTDSC:	.WORD	6		; output descriptor
		.BYTE	DSC$K_DTYPE_T,DSC$K_CLASS_S
		.ADDRESS	OUTADR
INPUT:	.BLKB	6		; ASCII numeric string
PRMPT0:	.ASCID	/Enter a number (snnnnn): /
PRMPT1:	.ASCID	/Enter another number (snnnnn): /
MSG:		.ASCID	/Their sum is: /
;
NUM0:	.BLKB	3		; will hold a packed decimal
NUM1:	.BLKB	3		; another packed decimal
ANS:		.BLKB   3	        ; the sum of NUM0 and NUM1
OUTADR:	.BLKB	6		; output string
;
	.ENTRY	DEMO
	PUSHAQ	PRMPT0		; ask for first number
	PUSHAQ	INDSC
	CALLS	#2, G^LIB$GET_INPUT
	CVTSP	#5, INPUT, #5, NUM0 ; convert string to packed
	PUSHAQ	PRMPT1		; ask for second number
	PUSHAQ	INDSC
	CALLS	#2, G^LIB$GET_INPUT
	CVTSP	#5, INPUT, #5, NUM1 ; convert string to packed
	ADDP6	#5, NUM0, #5, NUM1, #5, ANS  ;add them
	CVTPS	#5, ANS, #5, OUTADR ; convert ANS to string
	PUSHAQ	MSG			; output answer
	#1, G^LIB$PUT_OUTPUT
	PUSHAQ	OUTDSC
	#1, G^LIB$PUT_OUTPUT
	.END	DEMO		

In the short example above we use .BLKB to store packed decimals. Packed decimals are just strings of bits occupying memory, like any other number. What if we need to define a packed decimal number in static memory? .PACKED num_value, num_length is a directive for defining a a packed decimal. num_length is calculated by the assembler, so all we have to do is supply a variable name for it. In our example we supply the length of the packed decimals with the literal #5. We convert the inputed string to packed decimal with CVTSP. We convert the packed decimal answer to string with CVTPS. The actual addition is performed with ADD Packed. There are other packed decimal operators like DIVP and MULP, and in fact VAX MACRO offers enough packed decimal support that a MACRO programmer could use packed decimal exclusively (just like IBM CL). But whereas CL is easy to write in because of it's use of packed decimal numbers (among other features) this isn't the case for VAX MACRO. MACRO-32 requires we use conversion routines between strings and packed decimal numbers; unlike making a direct assignment of a string to a number like CL. Furthermore, CL only supports packed decimals. MACRO-32 supports integers and floating point numbers (they're native to the VAX processor). So using packed decimals doesn't reduce the amount of coding a VAX MACRO programmer has to do. Like we said earlier, packed decimal arithmetic is slower then integer of floating point arithmetic, so packed decimals are actually unattractive to the VAX MACRO programmer.

Return to VAX Instruction of the Week