I wrote this code to demonstrate how to convert a binary number into ASCII digits for display on the screen.  This is a complete program that has been tested on a real TI-99/4A computer.  The code assumes VDP graphics mode I (the default) and includes the VDP routines I wrote as replacements to the ones built into the console ROM.  Also shown are two replacement routines for the I2A routine in the main code.  The first one deals with unsigned values only, and the second deals with unsigned values only and does not track the length of the converted number, i.e. assumes you have a known fixed length to display the number.  Which one you should use totally depend on your specific needs.

Subroutine for unsigned numbers only, minimal version:

**
* Integer to ASCII - minimal version
*
* R1       - *END* of buffer to store ASCII digits
* R3       - Destroyed
* R4       - Value to convert, destroyed

TEN    DATA 10
R4LB   EQU  WRKSP0+9      R4 low byte

I2A    INC  R1
I2A1   DEC  R1           Account for the stored digit
       CLR  R3           Clear the high word of the dividend
       DIV  @TEN,R3      Divide R3+R4 by 10
       AI   R4,48        ASCII adjust the value
       MOVB @R4LB,*R1    Move remainder to buffer
       MOV  R3,R4        Move integer result into R4 for the next digit
       JNE  I2A1
       RT

Subroutine for unsigned numbers only:

**
* Integer to ASCII Conversion
*
* Entry Points:
*
*      Unsigned: BL @I2A
*
* Converts a 16-bit number to ASCII characters.  The characters
* are written to a buffer provided, and the length of the number
* is tracked.  Only unsigned numbers are supported.  The original
* number in R4 is destroyed.
*
* The output sets up R1 and R2 for an immediate call to VMBW.
*
* IN:  R1  *END* of the buffer to store the digits
* IN:  R4  16-bit number to convert
*
* OUT: R1  Points to the first character of the number
* OUT: R2  Length of the number
*
* Uses R1 - R5

I2A    LI   R5,10        Divide by 10 to isolate last digit
       CLR  R2           Digit counter

I2A1   CLR  R3           Dividend can only be a 16-bit number
       DIV  R5,R3        Divide R3:R4 by 10 (R5) to isolate last digit
       INC  R2           Account for the digit
       AI   R4,48        Adjust for ASCII display
       SWPB R4           Move to high-byte for writing to buffer
       MOVB R4,*R1       Write the digit to the buffer
       DEC  R1           Move to next digit location in the buffer
       MOV  R3,R4        Set up for the next digit
       JNE  I2A1         Check if all digits have been converted

       INC  R1           Adjust buffer location to start of number
       RT

Full code, signed and unsigned:

       DEF  START

* VDP Memory Map
*
VDPRD  EQU  >8800        VDP read data
VDPSTA EQU  >8802        VDP status
VDPWD  EQU  >8C00        VDP write data
VDPWA  EQU  >8C02        VDP set read/write address
VR1CPY EQU  >83D4        Copy of VDP register 1 - see E/A manual pg. 248
VSYNC  EQU  >83D7        Vertical Sync

* Workspaces
*
WRKSP0 EQU  >8300        Workspace 0
WRKSP1 EQU  >8320        Workspace 1
R0LB   EQU  WRKSP0+1     R0 low byte for VDP routines

* Data
*
NUMBUF BES  6            Storage for the ASCII number
       EVEN

* Set up workspace.
*
START  LIMI 0            Disable interrupts
       LWPI WRKSP0       Set workspace pointer

* Start by clearing the screen.
*
LOOP1  LI   R0,0         Upper left corner of screen
       LI   R1,>2000     Character >20 (32) = space
       LI   R2,>300      Number to write (768)
       BL   @VSMW        Write to VDP

       LI   R0,300       Screen location to display
       LI   R7,0         Initial value

LOOP2  LI   R1,NUMBUF    End of character digit buffer
       MOV  R7,R4
       BL   @I2A
       BL   @VMBW

       LIMI 2
       LIMI 0
       INC  R7
       JEQ  LOOP1
       JMP  LOOP2

**
* Integer to ASCII Conversion
*
* Entry Points:
*
*      Unsigned: BL @I2A
*      Signed:   BL @I2AS
*
* Converts a 16-bit number to ASCII characters.  The
* characters are written to a buffer provided, and the length
* of the number is tracked.  Signed and unsigned numbers are
* supported.  The original number in R4 is destroyed.
*
* The output sets up R1 and R2 for an immediate call to VMBW.
*
* IN:  R1  *END* of the buffer to store the digits
* IN:  R4  16-bit number to convert
*
* OUT: R1  Points to the first character of the number
* OUT: R2  Length of the number
*
* Uses R1 - R6

I2AS   MOV  R4,R6        Sign indicator registor
       ANDI R6,>8000     Mask the sign bit for later check
       ABS  R4           Always work with a positive number
       JMP  I2A0

I2A    CLR  R6           Unsigned entry point

I2A0   LI   R5,10        Divide by 10 to isolate last digit
       CLR  R2           Digit counter

I2A1   CLR  R3           Dividend can only be a 16-bit number
       DIV  R5,R3        Divide R3:R4 by 10 (R5) to isolate last digit
       INC  R2           Account for the digit
       AI   R4,48        Adjust for ASCII display
       SWPB R4           Move to high-byte for writing to buffer
       MOVB R4,*R1       Write the digit to the buffer
       DEC  R1           Move to next digit location in the buffer
       MOV  R3,R4        Set up for the next digit
       JNE  I2A1         Check if all digits have been converted

       CI   R6,0         Check if the number was positive
       JEQ  I2A2
       LI   R4,>2D00     Move the ASCII value of the minus '-' (45)
       MOVB R4,*R1       Write the minus sign
       INC  R2           Account for the sign character
       RT

I2A2   INC  R1           Adjust buffer location to start of number
       RT

**
* VDP Routines
**

* General register use is:
* R0   VDP RAM starting address.
* R1   MSB contains the value to write or to receive a value
*      when reading.  For multiple byte reads/writes, contains
*      the CPU buffer address.
* R2   Counter (counts down) for multiple reads/writes.

**
* VDP Single Byte Write and Single Byte Multiple Write
*
* Writes the value in the most-significant byte of R1 to the
* VDP RAM address indicated in R0.  For VSMW, the value in R2
* determines how many times to write the byte.  This is very
* useful when initializing large amounts of VDP RAM for things
* such as clearing the screen or setting up bitmap mode.
*
VSBW   LI   R2,1         Force the byte count to 1 for single byte write
VSMW   MOVB @R0LB,@VDPWA Send low byte of VDP RAM write address
       ORI  R0,>4000     Set read/write bits 14 and 15 to write (01)
       MOVB R0,@VDPWA    Send high byte of VDP RAM write address
       ANDI R0,>3FFF     Restore R0 to be as nondestructive as possible
VSMWLP MOVB R1,@VDPWD    Write byte to VDP RAM
       DEC  R2           Byte counter
       JNE  VSMWLP       Check if done
       RT

**
* VDP Multiple Byte Write
*
* Writes the number of bytes indicated in R2 from the CPU RAM
* starting at the address indicated by R1 and places them in
* the VDP RAM starting at the address indicated by R0.
*
VMBW   MOVB @R0LB,@VDPWA Send low byte of VDP RAM write address
       ORI  R0,>4000     Set read/write bits 14 and 15 to write (01)
       MOVB R0,@VDPWA    Send high byte of VDP RAM write address
       ANDI R0,>3FFF     Restore R0 to be as nondestructive as possible
VMBWLP MOVB *R1+,@VDPWD  Write byte to VDP RAM
       DEC  R2           Byte counter
       JNE  VMBWLP       Check if done
       RT

**
* VDP Single Byte Read
*
* Reads a byte from VDP RAM address indicated in R0 and places
* it in the most-significant byte of R1.
*
VSBR   MOVB @R0LB,@VDPWA Send low byte of VDP RAM write address
       ANDI R0,>3FFF     Set read/write bits 14 and 15 to read (00)
       MOVB R0,@VDPWA    Send high byte of VDP RAM write address
       MOVB @VDPRD,R1    Read byte from VDP RAM
       RT

**
* VDP Multiple Byte Read
*
* Reads the number of bytes indicated in R2 from the VDP RAM
* starting at the address indicated by R0 and places them in
* the CPU RAM starting at the address indicated by R1.
*
VMBR   MOVB @R0LB,@VDPWA Send low byte of VDP RAM write address
       ANDI R0,>3FFF     Set read/write bits 14 and 15 to read (00)
       MOVB R0,@VDPWA    Send high byte of VDP RAM write address
VMBRLP MOVB @VDPRD,*R1+  Read byte from VDP RAM
       DEC  R2           Byte counter
       JNE  VMBRLP       Check if finished
       RT

**
* VDP Write To Register
*
* Writes the value in the least-significant byte of R0 to the
* VDP register indicated in the most-significant byte of R0.
*
VWTR   MOVB @R0LB,@VDPWA Send low byte (value) to write to VDP register
       ORI  R0,>8000     Set up a VDP register write operation
       MOVB R0,@VDPWA    Send high byte (address) of VDP register
       RT

       END