• Please review our updated Terms and Rules here

Tektronix 4051 - FAST GRAPHICS TO PLOTTER - with CALL "EXEC" helper assembly code!

nikola-wan

Veteran Member
Joined
Mar 6, 2018
Messages
1,841
Location
Texas, USA
While searching my boxes of 4050 tapes, I found a tape labeled NICE UTILITIES and remembered I had previously captured and posted all the files in this directory in my repository five years ago:
https://github.com/mmcgraw74/Tektronix-4051-4052-4054-Program-Files/NICE_UTILITIES

File 13 was labeled "Fast Graphics to Plotter" written by Carl Hovey in Dec 1982 - and I remembered Carl Hovey was the author of the Tektronix 4051 Assembler document: program 11 in Tekniques V7 N4 Tape1!

I tried to run it and found it required the 4051 FAST GRAPHICs ROM Pack CALL "IMAGES" which I did not have at that time, so I put the program away.

I examined the CALL "EXEC" routine yesterday and looked at Carl's comments in the program and thought - I should be able to modify his program to display some of my R12 picture files.

His routine was called with this statement:
CALL "EXEC",B$,M,X,Y,X$

B$ was dimensioned as 3 characters and his program extracted 3 characters at a time from A$ which had previously been filled with the FAST GRAPHICS picture data from an ASCII file.

I then remembered I had lots of trouble with FAST GRAPHICS when I got a 4052R12 Graphics Enhancement ROM PACK and found the CALL "IMAGES" command would only work on the internal magnetic tape drive tapes - not GPIB devices including my Flash Drive.

Using the BASIC INPUT command to read the FAST GRAPHICS/R12 *.DAT image files did not work since INPUT terminates a string when a CR character is detected.

I worked around that issue by converting the ASCII DAT image files into BINARY files but encountered a different issue - 4050 BASIC limits BINARY strings to 8192 bytes, and most of the FAST GRAPHICS picture files are much larger than 8KB. I solved that limitation by dividing each image string into blocks. My original R12 viewer program READs binary string blocks and appends each string to form the complete image string that can be displayed with CALL "RDRAW".

However - the largest R12 DEMO TAPE file "Solar System" is 27KB which is the largest image that can be loaded on a 40551 with 32KB of RAM when you include the viewer program.

My binary block based technique to load the images in my original FAST GRAPHICS/R12 PICTURE viewer program still had the same size limitation until I developed a technique to allow each individual data block to be displayed immediately. This technique satisfies the Fast Graphics/R12 requirement that the first vector MUST be a MOVE. I address that requirement in my latest files by taking the last vector in a block and converting it to a MOVE and inserting it at the beginning of the next block - regardless of whether the vector was a MOVE or DRAW.

I wanted to try Carl's assembly program with the 4051 Emulator - and since I was unable to use the example file, I decided to use my Snoopy picture file. Since I have experimented with various binary block sizes, I examined my PICTURE viewer and found that it concatenates 257 byte blocks to create a single string which is viewed with a single CALL "RDRAW" command. I don't remember why I chose 257 bytes as the block size - since the GPIB and MAG TAPE buffers only hold 256 bytes - therefore the BASIC ROM has to perform two reads to get the data for the block.

Another Fast Graphics/R12 image requirement is a string to be displayed MUST have a length evenly divisible by three or you get an error. 257 and 256 are NOT evenly divisible by three but 255/3=85. For my latest R12 technique to allow each block to be immediately displayed I need to leave 3 bytes of space for the initial MOVE vector from the previous block.

Carl's program solves another FAST GRAPHICS/R12 limitation of only displaying to the FAST GRAPHICS to the CRT by NOT using a FAST GRAPHICS command to display the vector but using a PRINT statement. The 4050 PRINT statement can use the primary address of the display (32) or a GPIB plotter like the 4662 - or even the Option 01 backpack serial interface (40) to the 4662 plotter. Carl's routine speeds up the conversion of the three ASCII characters in an R12 vector to the 8-byte floating point format for ALL 4050 BASIC program numeric variables.

I have attached a zip of my modified program that currently does not include Carl's code to change the scale of the plot if using a 4662/4663 plotter.

I also attached a zip file containing my disassembly of Carl's 4051 assembly code. I am a little puzzled by the stack references and put question marks on the variable names where the index register seemed to be pointing to the B$ variable. Since Carl's code pulls variable address pointers from the stack - I have included snippets of the CALL "EXEC" document I found and translated from German to English in my disassembly in the next post.
 
Here is that disassembly listing with some of my comments - the 12KB disassembly with all comments is attached in zip file - this forum limits text in posts to 10KB.

I had to change the name of the index register in my comments from X to IX to differentiate the index register from the variable X :)

Code:
f9dasm: M6800/1/2/3/8/9 / H6309 Binary/OS9/FLEX9 Disassembler V1.82
Loaded binary file FG_PREP.BIN

; CODE FROM CARL HOVEY DEC 82 BASIC PGM
; Carl was also co-author of Tek 4051 Assembler program 11 in Tekniques V7 N4 Tape1!

; THIS 4051 CALL EXEC 6800 ASSEMBLY CODE CONVERTS 3 Fast Graphics (R12) CHARS IN B$ INTO M,X,Y
;
; CONVERT B$ FAST GRAPHICS VECTOR TO M,X,Y IN ASSEMBLY: THREE 7-bit ASCII CHARACTERS PER VECTOR
; FIRST CHAR.  HAS MOVE FLAG (M), 3 BIT HI X, 3 BIT HI Y
; SECOND CHAR. HAS 7 BIT LO X, THIRD HAS 7 BIT LO Y

... lots of my comments deleted but are in the disassembly listing zip is attached

;****************************************************
;* Used Labels                                      *
;****************************************************

ZSCRTCH EQU     $02B5   ; SCRTCH - SYSTEM WORK AREA IN PAGE N (260 BYTES)
ZAA3E   EQU     $AA3E   ; A5X    - ADD 5 TO IX REGISTER
ZB1FB   EQU     $B1FB   ; FLOAT1 - CONVERT INTEGER TO FLOATING POINT
ZB70F   EQU     $B70F   ; PULFPN - ROUTINE TO POP FP NUMBER FROM STACK

;****************************************************
;* Program Code / Data Areas                        *
;****************************************************

; since our CALL "EXEC" has B$,M,X,Y,X$ - is pulling data from the stack,
; it doesn't include the CALL "EXEC" bytes of the BASIC program in memory
; but does include other bytes.
;
; I have renumbered the bytes below so B$ starts at stack offset 23h and there are five variables
;
;
;   | 16h |17h|18h| 19h |1Ah|1Bh| 1Ch |1Dh|1Eh| 1Fh |20h|21h| 22h |23h|24h|
;   +-----+---+---+-----+---+---+-----+---+---+-----+---+---+-----+---+---+
;   |[B1h]| H | L |[B0h]| H | L |[B0h]| H | L |[B0h]| H | L |[B1h]| H | L |
;            X$             Y             X             M            B$
;          ---------------------------------------------------------------
;            Pointer to FIVE CALL "EXEC" Variable Items
;            (note their reverse order)
;            (fewer variables will start at offset 17 for CALL "EXEC")

        ORG     $02B5

ZSCRTCH TSX                              ;02B5: 30             '0'   ; TRANSFER STACK PTR TO IX REG and increment SP
        LDX     $23,X                    ;02B6: EE 23          '.#'  ; LOAD REG IX WITH ADDRESS OF B$ in NAMESPACE table
        LDX     $0B,X                    ;02B8: EE 0B          '..'  ; LOAD REG IX WITH ADDRESS OF B$ VARIABLE (11 BYTE OFFSET)
        LDAA    $05,X                    ;02BA: A6 05          '..'  ; LOAD REG A WITH 1st CHARACTER OF B$ (MOVE flag,HI X,HI Y)
        CLRB                             ;02BC: 5F             '_'   ; CLEAR B
        ROLA                             ;02BD: 49             'I'   ; ROTATE A LEFT, BIT 7 INTO CARRY
        ROLA                             ;02BE: 49             'I'   ; ROTATE A LEFT, BIT 6 INTO CARRY (MOVE flag=1,DRAW=0)
        ADCB    #$14                     ;02BF: C9 14          '..'  ; ADD 14h (20 DECIMAL) PLUS CARRY TO B TO GET BASIC MOVE/DRAW INT value
        PSHB                             ;02C1: 37             '7'   ; SAVE B ON STACK   - Least significant BYTE OF 16-BIT UNSIGNED INTEGER
        CLRB                             ;02C2: 5F             '_'   ; CLEAR B
        PSHB                             ;02C3: 37             '7'   ; SAVE 00h ON STACK - MOST SIGNIFICANT BYTE OF 16-BIT INTEGER
        PSHB                             ;02C4: 37             '7'   ; SAVE 00h ON STACK - NULL TAG FOR THE INTEGER (IGNORED)
        JSR     ZB1FB                    ;02C5: BD B1 FB       '...' ; FLOAT1 - CONVERTS (and REPLACES) MOVE flag INTEGER +tag WITH FP "M" +tag ON THE STACK
        TSX                              ;02C8: 30             '0'   ; TRANSFER STACK PTR TO IX REG and increments SP
        LDX     $23,X                    ;02C9: EE 23          '.#'  ; LOAD REG IX WITH ADDRESS OF "M"?? in NAMESPACE table
        JSR     ZAA3E                    ;02CB: BD AA 3E       '..>' ; ADD 5 TO IX REGISTER TO POINT TO FP "M" 8 data bytes IN NAMESPACE TABLE
        JSR     ZB70F                    ;02CE: BD B7 0F       '...' ; POP "M" BYTES FROM STACK - PUSH TO M BYTES IN NAMESPACE IX+0 TO IX+7
        TSX                              ;02D1: 30             '0'   ; TRANSFER STACK PTR TO IX REG and increment SP
        LDX     $23,X                    ;02D2: EE 23          '.#'  ; LOAD REG IX WITH ADDRESS OF B$?? in NAMESPACE table
        LDX     $0B,X                    ;02D4: EE 0B          '..'  ; LOAD REG IX WITH ADDRESS OF B$ VARIABLE (11 BYTE OFFSET)
        LDAA    $05,X                    ;02D6: A6 05          '..'  ; LOAD REG A WITH 1st CHARACTER OF B$ (MOVE flag,HI X,HI Y)
        LDAB    $06,X                    ;02D8: E6 06          '..'  ; LOAD REG B WITH 2nd CHARACTER OF B$ (7-bit LO X)
        ROLB                             ;02DA: 59             'Y'   ; ROTATE B LEFT, MSB (=0) INTO CARRY
        RORA                             ;02DB: 46             'F'   ; ROTATE A RIGHT 4 times so HI X lsb is in CARRY
        RORA                             ;02DC: 46             'F'   ;
        RORA                             ;02DD: 46             'F'   ;
        RORA                             ;02DE: 46             'F'   ;
        RORB                             ;02DF: 56             'V'   ; ROTATE B RIGHT so LO X 8-bits 7-0 ARE IN REG B
        ANDA    #$03                     ;02E0: 84 03          '..'  ; AND REG A WITH 03h TO mask out all but HI X bits 9-8 IN A bits 1-0
        PSHB                             ;02E2: 37             '7'   ; SAVE B ON STACK - least significant byte of 16-bit INTEGER X
        PSHA                             ;02E3: 36             '6'   ; SAVE A ON STACK - most significant byte of  16-bit INTEGER X
        PSHA                             ;02E4: 36             '6'   ; SAVE A ON STACK as ignored tag for X
        JSR     ZB1FB                    ;02E5: BD B1 FB       '...' ; FLOAT1 - CONVERTS (and REPLACES) X INTEGER +tag WITH FP "X" +tag ON THE STACK
        TSX                              ;02E8: 30             '0'   ; TRANSFER STACK PTR TO IX REG and increments SP
        LDX     $1A,X                    ;02E9: EE 1A          '..'  ; LOAD REG IX WITH ADDRESS OF "X" in NAMESPACE table
        JSR     ZAA3E                    ;02EB: BD AA 3E       '..>' ; ADD 5 TO IX REGISTER TO POINT TO FP "X" 8 data bytes IN NAMESPACE TABLE
        JSR     ZB70F                    ;02EE: BD B7 0F       '...' ; POP "X" BYTES FROM STACK - PUSH TO X 8-BYTES IN NAMESPACE IX+0 TO IX+7
        TSX                              ;02F1: 30             '0'   ; TRANSFER STACK PTR TO IX REG and increments SP
        LDX     $23,X                    ;02F2: EE 23          '.#'  ; LOAD REG IX WITH ADDRESS OF "B$" in NAMESPACE table
        LDX     $0B,X                    ;02F4: EE 0B          '..'  ; LOAD REG IX WITH ADDRESS OF B$ VARIABLE (11 BYTE OFFSET)
        LDAA    $05,X                    ;02F6: A6 05          '..'  ; LOAD REG A WITH 1st CHARACTER OF B$ (MOVE flag,HI X,HI Y)
        LDAB    $07,X                    ;02F8: E6 07          '..'  ; LOAD REG B WITH 3rd CHARACTER OF B$ (7-bit LO Y)
        ROLB                             ;02FA: 59             'Y'   ; ROTATE B LEFT, MSB (=0) INTO CARRY
        RORA                             ;02FB: 46             'F'   ; ROTATE A RIGHT so HI Y lsb is in CARRY
        RORB                             ;02FC: 56             'V'   ; ROTATE B RIGHT so LO Y 8-bits 7-0 ARE IN REG B
        ANDA    #$03                     ;02FD: 84 03          '..'  ; AND REG A WITH 03h TO mask out all but HI Y bits 9-8 IN A bits 1-0
        PSHB                             ;02FF: 37             '7'   ; SAVE B ON STACK - least significant byte of 16-bit INTEGER Y
        PSHA                             ;0300: 36             '6'   ; SAVE A ON STACK - most significant byte of  16-bit INTEGER Y
        PSHA                             ;0301: 36             '6'   ; SAVE A ON STACK as ignored tag for Y
        JSR     ZB1FB                    ;0302: BD B1 FB       '...' ; FLOAT1 - CONVERTS (and REPLACES) Y INTEGER +tag WITH FP "Y" +tag ON THE STACK
        TSX                              ;0305: 30             '0'   ; TRANSFER STACK PTR TO IX REG and increments SP
        LDX     $11,X                    ;0306: EE 11          '..'  ; LOAD REG IX WITH ADDRESS OF "Y" in NAMESPACE table
        JSR     ZAA3E                    ;0308: BD AA 3E       '..>' ; ADD 5 TO IX REGISTER TO POINT TO FP "Y" 8 data bytes IN NAMESPACE TABLE
        JSR     ZB70F                    ;030B: BD B7 0F       '...' ; POP "Y" BYTES FROM STACK - PUSH TO Y 8-BYTES IN NAMESPACE IX+0 TO IX+7
        RTS                              ;030E: 39             '9'   ; RETURN TO BASIC

        END

My confusion on the stack is LDX $23,X after TSX to access B$ (makes sense) but also M. Later LDX $1A,X to access X (my table indicates Y), and LDX $11,X is used to access Y.
I can only imagine the Stack seen by CALL "EXEC" is NOT the same as the CALL "EXEC" document example.

Fast_Graphics_EXEC.zip contains these files:
  • File 2 - my current R12 PICTURE MENU program
  • File 24 - my current R12 BINARY Snoopy data file
  • File 62 - my mods to Carl's program for Flash Drive
  • File 66 - my converted 255 byte block size Snoopy data file - can be displayed every block
FG_PREP.zip contains the 12.5KB disassembly of Carl's EXEC program with my comments
 

Attachments

Last edited:
I left out the instructions in case someone wants to try running the programs in Fast_Graphics_EXEC.zip

If you load and run file 2 - the picture viewer menu with SOFTWARE ONLY R12 will show the Snoopy file as #16. Enter 16 with CR and I clocked the program taking 18 seconds to display on the 4051 Emulator in speed=1x 4051. I know it will be slower on a real 4051 - but I don't have one to test.

Now load and run file 62 and it will immediately start to draw Snoopy from my file 66 with the PRINT statement drawing each vector separately - clocked at about 7 seconds - so Carl's helper EXEC code does speed it up over my picture viewer software code in BASIC.

While file 62 is loaded - type run 4000 to display Snoopy from file 66 with R12 CALL "RDRAW" drawing each block of 85 vectors - less than 1 second on the 4051 Emulator.

If I can modify Carl's code to display the vectors for an entire 255 byte block, I think we will approach the speed of the R12 ROM Pack CALL "RDRAW".

Here is what the Snoopy vector image should look like:

screen - 2025-08-05T160146.824.png
 
Monty:

The program manipulated the stack after pulling the pointer to the B$ variable. Left to itself, the first four instructions would account for the changes to the stack to add three bytes to the bottom of the stack.

PSHB ;02C1: 37 '7' ; SAVE B ON STACK - Least significant BYTE OF 16-BIT UNSIGNED INTEGER
CLRB ;02C2: 5F '_' ; CLEAR B
PSHB ;02C3: 37 '7' ; SAVE 00h ON STACK - MOST SIGNIFICANT BYTE OF 16-BIT INTEGER
PSHB ;02C4: 37 '7' ; SAVE 00h ON STACK - NULL TAG FOR THE INTEGER (IGNORED)
JSR ZB1FB ;02C5: BD B1 FB '...' ; FLOAT1 - CONVERTS (and REPLACES) MOVE flag INTEGER +tag WITH FP "M" +tag ON THE STACK
TSX ;02C8: 30 '0' ; TRANSFER STACK PTR TO IX REG and increments SP
LDX $23,X ;02C9: EE 23 '.#' ; LOAD REG IX WITH ADDRESS OF "M"?? in NAMESPACE table

Not sure how the call to FLOAT1 leaves the stack - if it leaves the stack pointer in the same relative position on return, then the second LDX $23,X makes sense.
 
Here is the FLOAT1 source code from 4051 DIMSUB-GRAF.pdf microfiche page 146:

1754479378081.png

This code does JMP NORM in line 23 and here is the NORM routine on pdf page 150:

1754479450667.png

1754479555637.png

The FPRET: JMP RTRN in line 79 to end the routine does this in pdf page 145 of the 4051 Assembler doc. RTRN is at global address A9FD (assembler page 73)

1754480791480.png
 
My second attempt at disassembling Carl Hovey's 4051 CALL "EXEC" assembly program makes more sense.

I spent time creating a COMPLETE list of 4051 STACK Tags from the 4051 Assembler doc and the 4051 BASIC ROM source listing microfiche DIMSUB-GRAF pdf page 65:

Code:
STACK TAG CONSTANTs from 4051 ASM Symbol Definitions and EVLSUB pg 65
NAME    VALUE SIZE DESCRIPTION
------  ----- ---- -----------
NULLTG = H00   1   NULL TAG for one NULL byte                - STACK TAG
PLOSTG = H01   5   LITERAL String  - Pointer                 - STACK TAG
ESTG   = H02   9   EVALUATOR STATUS TAG                      - STACK TAG
GOSTG  = H03   3   GOSUB TAG                                 - STACK TAG
FORTG  = H04   23  FOR TAG                                   - STACK TAG
LISTTG = H05   3   LIST TAG        - LIST command entry      - STACK TAG
PGMTG  = H06   3   PROGRAM TAG     - Pointer to PROGRAM LINE - STACK TAG
IMXTG  = H07   3   IMMEDIATE EXECUTE TAG for LINE address    - STACK TAG
PNTSTG = H08   5   STRING          - Pointer to Name Table   - STACK TAG
PSCTG  = H09   5   STRING Count    - Pointer                 - STACK TAG
PNTNTG = H0A   9   NUMERIC         - Pointer to Name Table   - STACK TAG
PAETG  = H0B   9   ARRAY Element   - Pointer to Name Table   - STACK TAG
VALTG  = H0C   9   VALUE TAG for Floating Point Number       - STACK TAG
LNNOTG = H0D   3   LINE NUMBER TAG - for saved LINE NUMBER   - STACK TAG
ITM1TG = H0E   3   ITEM ONE TAG    - for an INTEGER          - STACK TAG
ITM2TG = H0F   3   ITEM TWO TAG    - for an INTEGER          - STACK TAG
LBRKTG = H10   1   LEFT BRACKET TAG in object string         - STACK TAG
SEMITG = H11   1   SEMICOLON TAG                             - STACK TAG
ATSNTG = H12   1   @ or % TAG                                - STACK TAG
BAKSTG = H13   4   BACKWARDS pointers into I/O list          - STACK TAG
ALLTG  = H14   1   DELETE ALL TAG                            - STACK TAG
RTRNTG = H15   3   RETURN TAG      -INTERNAL RETURN address  - STACK TAG
PRTTG  = H16   128 PRINT TAG       - PAGE FULL saved info    - STACK TAG
CALLTG = H17   5   CALL TAG                                  - STACK TAG
EOLTG  = H18   3   END OF LINE TAG                           - STACK TAG
EOSTG  = H19   1   END OF STACK TAG                          - STACK TAG

DOTG   = H22   3   DO TAG (4052A/4054A only)                 - STACK TAG

The TAGTBL is used by TYPARG and BACKUP to find each STACK TAG - since they have fixed SIZE (length) on the stack.

TAGTBL Notes:
  • the SIZE column contains decimal counts - not HEX (FORTG is only 23 bytes and PRTTG is only 128 bytes not 296 bytes)
  • the TAGTBL shows the END OF STACK tag EOSTG as size 0 - but I figure this is the last of the TAGTBL values and the EOSTG tag is really 1 byte.

I added the "DO TAG" since it is in my modification of the 4052 assembler STACK DUMPER assembly program to add my HEXOUT routine since the address of HEXOUT in the example is NOT correct for the v1.5 4054A firmware. Likely there are other 4052A Stack Tags for all the new structured BASIC instructions added to A-Series.

I don't have a 4051 to try porting this 4052A Stack Dumper code to the 4051.

The 4051 Assembler is DDT in the MAXIROM Pack DEBUG ROM, which has commands to write and read ASCII files to/from internal tape and change from display/keyboard to SERIAL comm with a "K" host command.

The 4051 EMULATOR does not support SERIAL port, nor internal TAPE drive - just @1 GPIB files (@daver2 original 405X Emulator code) and @5 GPIB Flash Drive files (@WaveyDipole 4051 Emulator code) - and DDT does not support GPIB devices for files - without me figuring out how to locate and patch 4051 DDT tape commands to use the Flash Drive.
 
Last edited:
I was not satisfied with my disassembly of Carl Hovey's 4051 CALL "EXEC" assembly program to convert an R12 three character vector into M, X, and Y floating point variables - in my earlier post in this thread.

Particularly since the 4052A Assembler example STACK DUMPER program made more sense when I found the TAGTBL used by TYPARG and BACKUP that indicated a FIXED length for each TAG.

So I decided to port the 4052A STACK DUMPER assembly program listed in my post in my 4052A Assembler thread:
4052A STACK DUMPER assembly program
to the 4051 - which I hoped to run on the Tektronix 4051 EMULATOR since I don't have a 4051.

There were two 'new' opcodes in that 4052A listing: PSHRET and RTRN. I assumed that these opcodes were only needed on the 4052A/4054A due to the separate 64KB address spaces for the BASIC ROM and RAM, so I deleted them from my 4051 Assembler program - and when I ran it, the 4051 crashed with SYSTEM ERROR.

Remembering what Benny Hill (UK TV comedian) said about the word 'assume' - I decided to do more research in the 4051 Assembler document and found 4051 PSHRET and RTRN ROM entry points and descriptions for the two routines:

4051 PSHRET and RTRN descriptions.jpg

during my search for PSHRET I found it was used in many of the other BASIC ROM routines in the document - likely explaining why it was added to 4052/4054 and 4052A/4054A 'extended' opcodes along with RTRN.

However, the scan of the Tektronix 4051 Assembler document listing of the address for PSHRET and RTRN was not clear - so I then searched the 4051 firmware microfiche set and found the ROM source code for them in the UTILS - SYSTEM UTILITY ROUTINEs:
4051 PSHRET and RTRN subroutines.jpg
Of course the microfiche shows each of the assembly source modules assembled at relative address 0000, so I used the MAXIROM "DEBUG" call to disassemble the 4051 EMULATOR memory near the possible entry point and found both the entry addresses:

Using 4051 Emulator DEBUG to disassemble PSHRET.png

After adding the 4051 addresses for PSHRET and RTRN into my 4051 Stack Dump assembly program I used JMP for both routines but that caused a crash, so I changed to JSR PSHRET and it worked!


4051 Stack Dump CALL EXEC test.png

I have attached a zip with my 4051 Stack Dump assembly program and the screenshots:
 

Attachments

Here is the 4051 Stack Dump assembly listing from using the A09 assembler.

The only changes to the 4052A Stack Dump code were:
  • SWI were replaced by JSR since the 4051 only has one space for ROM and RAM
  • The 4052A ROM addresses for CTLCHR and BACKUP were replaced by 4051 addresses
  • 4052A PSHRET instruction was replaced by JSR PSHRET
  • 4052A RTRN instruction was replaced by JMP RTRN

Code:
; Tektronix 4051 assembly code - port from 4052A
; A09 assembler options:
; /a09.exe -OM00 -Bstackdump4051.bin -Lstackdump4051.lst "37     ASCII   DATA Stack Dumper4051 294.ASM"

; Description:  this program lists the STACK items in HEX
;               top to bottom of stack, terminating at EOSTG

 0000                 R0         EQU $0000           ; SAVE SP BACKUP
 0001                 R0plus1    EQU $0001           ;  LSB OF SP
 0014                 R10        EQU $0014           ; SAVE X REG
 F22E                 CTLCHR     EQU $F22E           ; PRI CTL CHAR
 D11D                 BACKUP     EQU $D11D           ; SCAN DOWN STACK
 A9E7                 PSHRET     EQU $A9E7           ; 4051 PSEUDO STACK Routine
 A9FD                 RTRN       EQU $A9FD           ; 4051 Return to saved return point

                                 ORG $02B5           ; Scratch buffer used by CALL "EXEC"

 02B5 BDA9E7                     JSR PSHRET          ; STORE RETURN ADDR
 02B8 9F00                       STS R0              ; SAVE STACK PTR
                      ;                                FOR BACKUP ROUTINE
                      ;                                SP+1-->X

 02BA 9600            LOOP      LDA A R0
 02BC BD0313                    JSR HEXOUT
 02BF 9601                      LDA A R0plus1
 02C1 BD0313                    JSR HEXOUT
 02C4 863A                      LDA A #$3A           ; COLON CHARACTER
 02C6 BDF22E                    JSR CTLCHR
 02C9 8620                      LDA A #$20           ; SPACE CHARACTER
 02CB BDF22E                    JSR CTLCHR

 02CE DE00                      LDX R0
 02D0 A601                      LDA A $1,X
 02D2 8119                      CMP A #$19           ; Compare with EOSTG
 02D4 271F                      BEQ DONE
 02D6 DF14                      STX R10
 02D8 BDD11D                    JSR BACKUP

 02DB DE14            L2        LDX R10
 02DD A601                      LDA A $1,X
 02DF BD0313                    JSR HEXOUT
 02E2 8620                      LDA A #$20           ; SPACE CHARACTER
 02E4 BDF22E                    JSR CTLCHR
 02E7 DE14                      LDX R10
 02E9 08                        INX
 02EA DF14                      STX R10
 02EC 9C00                      CPX R0
 02EE 26EB                      BNE L2

 02F0 BD030D                    JSR CRLF
 02F3 20C5                      BRA LOOP

 02F5 BD0313          DONE      JSR HEXOUT
 02F8 BD030D                    JSR CRLF
 02FB 8645                      LDA A #$45          ; E
 02FD BDF22E                    JSR CTLCHR
 0300 864E                      LDA A #$4E          ; N
 0302 BDF22E                    JSR CTLCHR
 0305 8644                      LDA A #$44          ; D
 0307 BDF22E                    JSR CTLCHR
 030A 7EA9FD                    JMP RTRN


 030D 861F            CRLF      LDA A #$1F
 030F BDF22E                    JSR CTLCHR
 0312 39                        RTS

 0313 B70340          HEXOUT    STA A TEMP1        ;SAVE A IN TEMP1
 0316 44                        LSR A              ;MASK LOW NIBBLE
 0317 44                        LSR A              ; BY SHIFTING A
 0318 44                        LSR A              ; 4-BITS RIGHT
 0319 44                        LSR A       
 031A 8109                      CMP A #$9          ;COMPARE A WITH 9
 031C 2F07                      BLE NUMHI          ;NIBBLE IS NUMBER
 031E 8009            LTRHI     SUB A #$9          ;SUBTRACT 9 FROM A
 0320 8B40                      ADD A #$40         ;ADD 40 FOR LETTER
 0322 7E0327                    JMP PRTHI   
 0325 8B30            NUMHI     ADD A #$30         ;ADD 30 FOR NUMBER
 0327 BDF22E          PRTHI     JSR CTLCHR         ;PRINT HI HEX
 032A B60340                    LDA A TEMP1        ;RESTORE BYTE TO A
 032D 840F                      AND A #$F          ;MASK HIGH NIBBLE
 032F 8109                      CMP A #$9          ;COMPARE A WITH 9
 0331 2F07                      BLE NUMLO          ;NIBBLE IS NUMBER
 0333 8009            LTRLO     SUB A #$9          ;SUBTRACT 9 FROM A
 0335 8B40                      ADD A #$40         ;ADD 40 FOR LETTER
 0337 7E033C                    JMP PRTLO   
 033A 8B30            NUMLO     ADD A #$30         ;ADD 30 FOR NUMBER
 033C BDF22E          PRTLO     JSR CTLCHR         ;PRINT LO HEX
 033F 39                        RTS

 0340 00              TEMP1     FCB $0             ;TEMP1 STORAGE


SYMBOL TABLE
    BACKUP 00 D11D      CRLF 02 030D    CTLCHR 00 F22E      DONE 02 02F5
    HEXOUT 02 0313        L2 02 02DB      LOOP 02 02BA     LTRHI 02 031E
     LTRLO 02 0333     NUMHI 02 0325     NUMLO 02 033A     PRTHI 02 0327
     PRTLO 02 033C    PSHRET 00 A9E7        R0 00 0000   R0PLUS1 00 0001
       R10 00 0014      RTRN 00 A9FD     TEMP1 02 0340
19 SYMBOLS

0 error(s), 0 warning(s)
 
Last edited:
Next I wanted to see the stack with Carl Hovey's R12 to M, X, Y assembly code.

I modified my Stack Dump test program to add his assembly code and put his code and my stack dump code inside a FOR/NEXT loop - hoping to see both in the stack dump, but FOR/NEXT loops are not handled the same as the A-Series "DO" loop, so I settled for changing my CALL "EXEC" stack dump statement to: CALL "EXEC",K$,M,X,Y,L$ where L$ was my stack dump code, and I initialized B$="123", M=0, X=1, Y=2.

I also added my Namespace code and got this result on the 4051 Emulator:

4051 Stack Dump CALL EXEC R12 to M,X,Y test.png

Notice that each of the three PNTNTG Numeric variable stack tag has a length of 9 BYTES even though the pointer only requires 2 bytes.
The reason this tag is 9 bytes is so a program can replace the 8 bytes following the PNNTG tag with a VALTG(0C) and not have to change anything else on the stack!

You can see that the M, X and Y simple numeric SCALAR variables in the Namespace table are stored directly in the namespace entry.
The string variables and numeric arrays are never stored there - there is a pointer to the Header of the string in the last two bytes of the namespace table entry.

In this example K$ Header address is 127A. The string data is always offset 5 bytes from the Header address - and I set K$="123" in my program, you can see the K$ data in memory is 32,32,33!

The Namespace table in this example shows that variables after the CALL "EXEC" are stored on the stack in reverse order: L$,Y,X,M,K$ based on the pointers on the stack and the corresponding variable namespace addresses.

Apparently any number of parameters can be passed to the CALL "EXEC" routine - with the last being the code string - limited to the 256 byte Scratch buffer in the 4051.

That is why larger 4051 assembly code like the tape file version of 4051 DDT that was on the Tekniques tape require a small loader code string in a BASIC program file, which then finds the DDT file and loads that code into a separate string variable.

My 4051 BASIC program is attached in a zip file:
 

Attachments

Last edited:
I wanted to see a SCALAR VALTG(0C) in the stack dump, so I changed my BASIC program to CALL "EXEC", K$, X, 456, L$ which puts 456 into the stack as a SCALAR, while the previous Y variable has been stored in the Namespace table as a numeric 2.

4051 Stack Dump with SCALAR.png


In this screenshot, I highlighted and numbered the following changes from my previous screenshot:

  1. We now see a VALTG (0C) on the second entry of the stack dump with a SCALAR value of 0409E400000000.
  2. The CALL "EXEC" now has replaced Y with the number 456.
  3. The stack dump entry for Y is identical with that entry in my previous post - because Y is still initialized =2.
  4. I had dimensioned I$(5632) in the program, so typing CALL "DEBUG",I$ launched DDT.
  5. Since DDT supports two columns, I pressed RETURN a couple of times and then entered 0409E40000000000 into RAM at 5000h, the same value as in highlight 1 - since DDT has changed the stack that value is gone.
    • The quote character is the DDT command to enter HEX data into RAM, but it only supports up to three bytes, so it takes three quote commands to enter an 8-byte floating point number.
  6. I entered the DDT "U" command on address 5000 to convert the eight byte floating point value to decimal and got 456!
You might also notice that the first two bytes of each Namespace table HEADER are links to the next HEADER and those two bytes for the Y HEADER are 0000, because it is currently the LAST Namespace variable name used in the program!

The lower case "d" after highlight 6 is the blinking DDT input prompt character.

Armed with this Stack Dump information, I will now go back to my disassembly of Carl Hovey's 4051 R12 to M,X,Y program to hopefully see that the stack offsets match my stack dump of those variables.
 
Last edited:
Here is the 4051 Stack Dump assembly listing from using the A09 assembler.

The only changes to the 4052A Stack Dump code were:
  • SWI were replaced by JSR since the 4051 only has one space for ROM and RAM
  • The 4052A ROM addresses for CTLCHR and BACKUP were replaced by 4051 addresses
  • 4052A PSHRET instruction was replaced by JSR PSHRET
  • 4052A RTRN instruction was replaced by JMP RTRN

Code:
; Tektronix 4051 assembly code - port from 4052A
; A09 assembler options:
; /a09.exe -OM00 -Bstackdump4051.bin -Lstackdump4051.lst "37     ASCII   DATA Stack Dumper4051 294.ASM"

; Description:  this program lists the STACK items in HEX
;               top to bottom of stack, terminating at EOSTG

 0000                 R0         EQU $0000           ; SAVE SP BACKUP
 0001                 R0plus1    EQU $0001           ;  LSB OF SP
 0014                 R10        EQU $0014           ; SAVE X REG
 F22E                 CTLCHR     EQU $F22E           ; PRI CTL CHAR
 D11D                 BACKUP     EQU $D11D           ; SCAN DOWN STACK
 A9E7                 PSHRET     EQU $A9E7           ; 4051 PSEUDO STACK Routine
 A9FD                 RTRN       EQU $A9FD           ; 4051 Return to saved return point

                                 ORG $02B5           ; Scratch buffer used by CALL "EXEC"

 02B5 BDA9E7                     JSR PSHRET          ; STORE RETURN ADDR
 02B8 9F00                       STS R0              ; SAVE STACK PTR
                      ;                                FOR BACKUP ROUTINE
                      ;                                SP+1-->X

 02BA 9600            LOOP      LDA A R0
 02BC BD0313                    JSR HEXOUT
 02BF 9601                      LDA A R0plus1
 02C1 BD0313                    JSR HEXOUT
 02C4 863A                      LDA A #$3A           ; COLON CHARACTER
 02C6 BDF22E                    JSR CTLCHR
 02C9 8620                      LDA A #$20           ; SPACE CHARACTER
 02CB BDF22E                    JSR CTLCHR

 02CE DE00                      LDX R0
 02D0 A601                      LDA A $1,X
 02D2 8119                      CMP A #$19           ; Compare with EOSTG
 02D4 271F                      BEQ DONE
 02D6 DF14                      STX R10
 02D8 BDD11D                    JSR BACKUP

 02DB DE14            L2        LDX R10
 02DD A601                      LDA A $1,X
 02DF BD0313                    JSR HEXOUT
 02E2 8620                      LDA A #$20           ; SPACE CHARACTER
 02E4 BDF22E                    JSR CTLCHR
 02E7 DE14                      LDX R10
 02E9 08                        INX
 02EA DF14                      STX R10
 02EC 9C00                      CPX R0
 02EE 26EB                      BNE L2

 02F0 BD030D                    JSR CRLF
 02F3 20C5                      BRA LOOP

 02F5 BD0313          DONE      JSR HEXOUT
 02F8 BD030D                    JSR CRLF
 02FB 8645                      LDA A #$45          ; E
 02FD BDF22E                    JSR CTLCHR
 0300 864E                      LDA A #$4E          ; N
 0302 BDF22E                    JSR CTLCHR
 0305 8644                      LDA A #$44          ; D
 0307 BDF22E                    JSR CTLCHR
 030A 7EA9FD                    JMP RTRN


 030D 861F            CRLF      LDA A #$1F
 030F BDF22E                    JSR CTLCHR
 0312 39                        RTS

 0313 B70340          HEXOUT    STA A TEMP1        ;SAVE A IN TEMP1
 0316 44                        LSR A              ;MASK LOW NIBBLE
 0317 44                        LSR A              ; BY SHIFTING A
 0318 44                        LSR A              ; 4-BITS RIGHT
 0319 44                        LSR A   
 031A 8109                      CMP A #$9          ;COMPARE A WITH 9
 031C 2F07                      BLE NUMHI          ;NIBBLE IS NUMBER
 031E 8009            LTRHI     SUB A #$9          ;SUBTRACT 9 FROM A
 0320 8B40                      ADD A #$40         ;ADD 40 FOR LETTER
 0322 7E0327                    JMP PRTHI
 0325 8B30            NUMHI     ADD A #$30         ;ADD 30 FOR NUMBER
 0327 BDF22E          PRTHI     JSR CTLCHR         ;PRINT HI HEX
 032A B60340                    LDA A TEMP1        ;RESTORE BYTE TO A
 032D 840F                      AND A #$F          ;MASK HIGH NIBBLE
 032F 8109                      CMP A #$9          ;COMPARE A WITH 9
 0331 2F07                      BLE NUMLO          ;NIBBLE IS NUMBER
 0333 8009            LTRLO     SUB A #$9          ;SUBTRACT 9 FROM A
 0335 8B40                      ADD A #$40         ;ADD 40 FOR LETTER
 0337 7E033C                    JMP PRTLO
 033A 8B30            NUMLO     ADD A #$30         ;ADD 30 FOR NUMBER
 033C BDF22E          PRTLO     JSR CTLCHR         ;PRINT LO HEX
 033F 39                        RTS

 0340 00              TEMP1     FCB $0             ;TEMP1 STORAGE


SYMBOL TABLE
    BACKUP 00 D11D      CRLF 02 030D    CTLCHR 00 F22E      DONE 02 02F5
    HEXOUT 02 0313        L2 02 02DB      LOOP 02 02BA     LTRHI 02 031E
     LTRLO 02 0333     NUMHI 02 0325     NUMLO 02 033A     PRTHI 02 0327
     PRTLO 02 033C    PSHRET 00 A9E7        R0 00 0000   R0PLUS1 00 0001
       R10 00 0014      RTRN 00 A9FD     TEMP1 02 0340
19 SYMBOLS

0 error(s), 0 warning(s)

Actually three changes to the 4052A Assembly "Stack Dumper2" code were needed - the third change was to replace the 4052A instruction INXSTX R10 with INX then STX R10 at address 02E9.

I found this while converting my 4052A Stack Dumper2 program to be converted by my new 4052 CALL "XXEC" program that I am working on - so all 4052/4054 can run assembly programs - since CALL "EXEC" and/or CALL "EXEC" are undocumented and I cannot even find either call in the 4052 firmware v5.1 - and trying to run CALL "EXEC" and CALL "EXEC" result in hanging my 4052, requiring a power cycle.
 
Last edited:
Back
Top