Understanding Hexadecimal, Nibble by Nibble

Why should we learn hexadecimal notation?

In DVD Spec commands, there are only 8 registers available to the author, if he or she is using an abstraction-layer authoring system such as DVD Studio Pro, or 16 if there is no abstraction layer, such as in Creator or Scenarist, or if you replace the abstraction layer and author from scratch in DVDAfterEdit.

To make better use of these registers, each register can be split up into individual "fields" to be treated individually. Since registers contain 16 bits, there could be 16 individual bit fields that each kept track of a single on/off state. (Binary bits are either on or off). Or it could be split up into 4 fields of 4 bits each. (It is quite often useful to do this).

Bit fields don't translate to decimal values very well. For example, the rightmost (lowest) bit in a register has a value of 1. The highest bit has a value of 32768. The next highest bit has a value of 16384. (Notice these are all powers of 2). Combining or doing arithmetic on groups of these bits in decimal would be very tedious and confusing. But good news! Once you understand hexadecimal, it becomes much easier.

Hexadecimal

Hexadecimal is base 16 arithmetic. This means that each digit in hex has 16 possible values, instead of 10 in decimal or 2 in binary. This is very good for three reasons:

  1. Sixteen is a power of two, 2 ** 4.
  2. Base 16 numbers require four bits to represent them.
  3. Four bits is evenly divisible into the 16 bits in a register.

These facts all combine to make it easy to work in hexadecimal, as you will discover as you continue reading this document. If you plan to do extensive DVD Spec "scripting", it would be a good idea to buy an inexpensive hex calculator.

In hexadecimal notation, each digit is commonly referred to as a "nibble". The possible nibble values are:

hexadecimal nibble values

The DVD Spec uses registers that are 16 bits long. This equates to four nibbles of four bits each. This means a register can hold a value from 0 to FFFF in hex, which is 65,535 in decimal.

Two's complement arithmetic

Typically in the DVD Spec, all values are positive numbers only. But almost all microprocessors, including all of those in DVD players, use "two's complement" arithmetic, which essentially allows you to think of these values as negative values if you wish. In two's complement arithmetic -1 is represented as $FFFF (we will use $ to represent hexadecimal numbers from here on out).

As an exercise let us add one to 9999 (decimal). We add one to the lowest 9, and get 10. We write down the zero and carry the one. We add the carry to the next digit, also 9, and get a similar result. When we finish we have 10000.

Now let us add one to $FFFF. We add one to the lowest F and get $10 (16). We write down the zero and carry the one, etc. When we are finished we have $10000. But only four nibbles fit in our register, so the last carry is thrown away, and the result is zero.

So you can see that in two's complement arithmetic, -1 + 1 still equals zero. For scripting purposes, we don't really need to know much more than that, except that it would is possible to store a negative value in a register, and our good old hex calculator will tell us what that value should be, if we need to know.

Binary bits

The DVD Spec uses the Boolean operators "and", "or", and "exclusive or". These operators only make sense using binary notation. But hex is a good way to organize your binary data into manageable chunks. With a little practice it is very quick to translate a hex number into binary by drawing out the nibbles on a piece of scratch paper, thus:

$06FF:
0000 0110 1111 1111

$1ABC:
0001 1010 1011 1100

(Please refer to the table, above, to get each nibble of the 16-bit hexadecimal number).

Then you can isolate the piece of the GPRM or SPRM you are interested in by drawing a long vertical line between the appropriate bits (counting from the right as bit 0), and apply the Boolean logic to it. (I'll explain Boolean logic in a moment).

For example, button numbers are stored in SPRM's and referenced starting with button number one having a value of 1024, or $0400. Button two is $0800, button 3 $0C00, etc.

So my example of button number one would be drawn thus:

0000 01|00 0000 0000

(On paper you would make the vertical bar taller.) If you draw all of the possible buttons out you will see that the 10 bits to the right of the button number are always zero. This means that scripting can keep track of a button number and still have 10 more bits to play with in the same GPRM!

This concept is extremely powerful, and is what permits complicated navigation with only 16 GPRM's. Essentially, GPRM's are very expensive, and commands are cheap since you can have up to 128 of them in each PGC, and you can have tons of PGC's.

Boolean Logic

The three Boolean operators used in DVD Spec commands are "and", "or", and "exclusive or". These operands are usually represented by the operators "&", "|", and "^".

Boolean operators are applied one bit at a time to two operands, and each bit in an operand is compared to the same bit in the other operand. The result of the compare is stored in the destination operand. (In DVD commands, the destination is always a GPRM, or a "temporary value" (never stored) in an if statement).

"and": If both bits are true (1), the resulting bit is true.

"or": If either bit is true, the resulting bit is true.

"exclusive or": If either bit is true, but not both, the resulting bit is true.

Some examples:

$0010 & $0011 = $0010
$0011 & $1000 = $0000
$0001 | $0010 = $0011
$0000 | $abcd = $abcd
$8000 ^ $8000 = $0000
$e000 ^ $4000 = $a000

etc.

An abstraction layer example

Following is an abstraction layer example from DVDSP 1.5, which was written before DVDSP 2.0 was shipped. Although the abstraction layers for the two products are different, the principles are the same.

This example was also written before DVDAfterEdit supported the hexadecimal preference for commands. Here in this update we will show the hexadecimal. For those who have read the previous version of this document, you will see that this makes the code much easier to follow.

If you look at a typical DVDSP 1.5 project, at the Video Title Set Menus, you will see that the first 20 lines of PGC 2 are always identical.

1: Set r15 = r8
2: Set r15 ^= $E000
3: if r15 & $8000 then GoTo Line 7
4: Set r14 = r8
5: Set r14 &= 7
6: Set Stream Audio r14
7: if r15 & $4000 then GoTo Line 13
8: Set r14 = r8
9: Set r14 /= 8
10: Set r14 &= 63
11: if r14 != $3F then Set r14 |= 64
12: Set Stream Subpic r14
13: if r15 & $2000 then GoTo Line 18
14: Set r14 = r8
15: Set r14 /= 512
16: Set r14 &= 15
17: Set Stream Angle r14
18: Set r8 = 0
19: Set r15 = r13
20: if r15 == 0 then Link Resume

This code is using a single GPRM, r8, to hold three flag bits and three values. Before reading any further, notice that $E000 has three bits on, $8000 has one of those same bits on, $4000 has another, and $2000 has another. Gee, there must be a method to this madness!

Here is a picture of the bits as they are arranged in register 8:

center,bits as they are arranged in register 8

Now to describe each command line and what it does:

  1. This brings register 8 into a "work" register, r15, so that we can see what is in it without destroying register 8.
  2. "Exclusive or" the register with the value $E000. This will invert the top three bits in the register and leave the lower 13 bits unchanged.
  3. This tests the work register against the value $8000. If it is on, we skip over the next few lines. This means that the high-order (leftmost) bit was off originally, since we "flipped the bit" by exclusive or'ing it with a one bit in that position (the $8000 part of $E000).
  4. We move the original register 8, the interesting one, into another work register, r14. We use another one because we are not through with the current value of r15, see below.
  5. We "and" the value of r14 with 7. This eliminates any extra bits to left of the first three (bits 0 thru 2), yielding a result that is guaranteed to be in the range 0-7.
  6. We now set the audio stream to a legal value.
  7. This tests the work register against the value $4000. If it was originally off (now on), we skip to line 13.
  8. We move that same register r8 to the second work register.
  9. We divide the value by 8. This removes the three bits that might have contained an audio stream, and moves the next interesting 6 bits to the rightmost part of the register. Any time we divide by a power of 2 (8 = 2 ** 3), it is the same as shifting the bits in the register to the right by the power number positions.
  10. We "and" the value with $3f. This isolates 6 bits of a sub picture stream. The sub picture SPRM uses values 0-31 for stream numbers, and a value of 63 to indicate a "dummy stream".
  11. If the value is not 63 we "or" in another bit, 64 (bit #6). This bit "on" means display the sub picture stream. This bit off means do not display it.
  12. We deposit the register (r14) into the SPRM (SPRM 2).
  13. We test the work register against $2000, and skip to line 18 if it was originally off.
  14. We get the interesting variable.
  15. We divide it by $200, getting rid of the nine bits we have already used up.
  16. We "and" it with $f, leaving a number between 0 and 15.
  17. We deposit the angle (1 thru 9) into the SPRM. We assume the angle number is correct, since it was generated by the abstraction layer.
  18. We have used up the register 8, which held up to three values that needed to be transferred to their SPRM's, so we clear it to zero so that the next time we won't have to repeat the steps above, since the SPRM's will keep track of it handily.
  19. We get r13 so we can test it.
  20. If r13 was zero, we do a Link Resume.

From here on out, the individual menus "do their thing", usually testing r13 for particular values to go to particular places.

Notice that the abstraction layer coding isn't particularly efficient in the number of instructions used, but is very efficient in its use of the bits in general parameters.

For example, the abstraction layer designer could have reversed the meaning of the three high-order bits in register 8, which would have saved the instructions which had to move the bits to another register and invert them in order to test each bit. But this sort of optimization is unimportant in typical navigation, since commands themselves execute in microseconds and there is room for 128 of them in each PGC. It is only when we actually jump to other domains and areas that navigation slows down.