Skip to content

Test 4a: the decimal counter

A game shows numbers: the score, the fuel, the level. The 8086 adds in binary, but a person reads decimal digits. This test keeps the counter in packed decimal: one digit in each half of a byte. Two bytes hold 0000 to 9999. After each ADD, the word DAA makes the byte decimal again. The digits are then ready to show, with no division.

A cassette tape with a handwritten label: "A Side. Assembly - the first language of the machine."

A What the machine does

The routine bcd_inc for the step to the value on the screen now.

    Waiting for the copy of the PC to start…

    Speed:

    Under each digit are the 4 bits of its nibble: yellow is 1, gray is 0. The cell C on the left is the carry. It is red after 9999 becomes 0000. Press Jump to 9990 to see that step soon.

    A cassette tape with a handwritten label: "B Side. Fortran, the language that speaks maths."

    B What the check expects

    The same value, made from the number of steps with MOD and ISHFT, as lab/check_bcd.f90 does.

      What is new in test 4a

      1. A Packed decimal. The number 1234 is the two bytes 12h and 34h: each hexadecimal digit of the byte is one decimal digit. A nibble (4 bits) holds 0 to 15, so the values 10 to 15 are never used. That is the cost of the method: two bytes hold 0 to 9999, not 0 to 65535.
      2. A ADD, then DAA. The 8086 has no decimal flag. It always adds in binary: 09h + 1 = 0Ah. The next word, DAA (decimal adjust after addition), corrects AL: 0Ah becomes 10h, and 9Ah becomes 00h with a carry. DAA works only on AL, so the program does one byte at a time.
      3. A ADD, not INC. INC does not change the carry flag (CF). The program needs the carry from the low byte, so it uses add al, 1. Thenadc al, 0 adds that carry to the high byte. The SNES developer met the same trap: on the 65C816, INC ignores the decimal flag.
      4. A A digit is a table lookup. bcd_digits moves each nibble into AL and uses XLAT on the table "0123456789ABCDEF". There is no division by 10.
      5. A B The check runs every value.The counter has only 10,000 values, so the check tests all of them. The test (test/checkbcd.asm) runs bcd_inc 10,000 times and writes each value to BCD.BIN, then the value after 9999 and the carry. The checker (lab/check_bcd.f90) makes each value again with MOD andISHFT and compares them.
      The two sides agree
      10000 / 10000
      every value from 0000 to 9999
      After 9999
      0000, C = 1
      right: 0000 with a carry
      Size of the game
      623 bytes
      COUNTER.COM, 8086 instructions only

      The check report

      File: BCD.BIN
      A side and B side agree on 10000 of 10000 values
      After 9999: 0000, carry 1 (must be 0000, carry 1): right

      Where this test comes from

      Test 4 is the first test that comes from the review of the SNES video guides. Each step below leads to the next.

      1. The video. In "It Took Every SNES Hardware Trick To Make My Game" at 32:05, Inkbox keeps three game counters in packed decimal. The 65C816 has a decimal flag that does the carry. He also warns that INC and DEC ignore that flag, so he adds with ADC. A 16-bit counter "fits exactly the 10,000" values that he needs.
      2. The review findings.D13-M11: the research report called the counter an unverified detail, but the video shows it in full.D15-M7: the interactive page "simulates" the counter with a decimal string, and it stops at 9999. A real packed decimal counter goes to 0000 with a carry. Thus test 4 checks the step after 9999.D14-M1 and D15-M1: the number 9,999 is the largest value of the counter, not the number of floors.
      3. The concept review. The 8086 has no decimal flag. The idea moves to a different word:DAA after ADD or ADC. The same trap moves too: INC does not change CF. This is learning unit U3, digits on the screen, in thesummary of the SNES video guides.
      4. The segment pages (decision D26). Showing text and reading keyshad the limit "no number formatting". It now says that a packed decimal counter is the smallest answer.Doing arithmetic says: "The 8086 has no decimal flag. After ADD or ADC on packed decimal bytes, write DAA". The B side shows the formats I4.4 and I0.
      5. The plan. PLAN.md, Phase 4d gave the design of test 4: count 0000 to 9999 with ADD, ADC and DAA, check all 10,000 values with MOD and ISHFT, and build a wrong twin without DAA that must fail on 9,990 values. This page is that design, built.
      6. The dictionary. The review added the words that this test uses:DAA, ADC,XLAT and AAM on the A side, and MOD andISHFT on the B side.

      The words that each program uses

      The pills show the words that the dictionary already has. "New words" are real words of the language that the dictionary does not have yet.

      A src/counter.asm· 240 lines · The game (test 4a): shows the counter as four large digits and its 16 bits

      52 dictionary words: MOVPUSHPOPAXBXCXDXSIDIBPDSESCS[ ]BYTEWORDDBDWEQUADDINCDECSHLORXORADCDAACMPTESTJMPJE / JZJNE / JNZJB / JALOOPCALLRETlabel:REPSTOSBLODSBCLDORGBITS%define%includeTIMES;,hINTINT 10h, AX=0013hA000h

      New words (10): AHALBHBLCLCPUDHDLJNCLDS

      A src/bcd.inc· 55 lines · The shared routines: add 1 with ADD, ADC and DAA, and change the nibbles into digits

      19 dictionary words: MOVBXDI[ ]DBDWXLATADDINCSHRANDADCDAACALLRETlabel:;,h

      New words (3): AHALCL

      A test/checkbcd.asm· 57 lines · The test: runs bcd_inc 10,000 times and writes each value to a file

      24 dictionary words: MOVAXBXCXDXDI[ ]DBDWXORADCLOOPCALLlabel:STOSWCLDORGBITS%includeTIMES;,hINT

      New words (4): AHALCPUJC

      B lab/check_bcd.f90· 78 lines · The checker: makes each value again with MOD and ISHFT, and compares all 10,000

      42 dictionary words: INTEGERLOGICALCHARACTER::=PARAMETERINT8 / INT16+ -* /( )MODINT( )IANDISHFT / SHIFTAIF ... THEN ... END IFDOSTOPERROR STOPMERGE== /= < > <= >=.and. .or. .not.x(2:5)COUNTANY / ALLPROGRAMENDUSECONTAINSFUNCTIONIMPLICIT NONECALLINTENT(IN)!&PRINTWRITEREADOPENCLOSEGET_COMMAND_ARGUMENT'(F8.2)'ISO_FORTRAN_ENV

      New words (7): COMMAND_ARGUMENT_COUNTDIMFINDLOCINTRINSICLENONLYTRIM

      Next: test 4b runs this program without DAA, and shows how the check finds it.