Some Tips for Using MITS Programming System II (w/ code)

General discussions related to the Altair 8800 Clone

Some Tips for Using MITS Programming System II (w/ code)

Postby pngwen » March 22nd, 2017, 9:48 pm

This week is spring break at Maryville College, which means I have been without students all week long! I have spent a great deal of the past 3 days working with the MITS Programming System II as I am trying to hack together a fairly large system for the Altair 8800. In the past few days I have learned a lot about how the package works, and how to have an effective workflow with it. I thought I would share that with all of you fellow hobbyists! I have also written a couple of utilities which I have been using to help explore the system and hack some code. I hope you find my notes helpful, and I hope you enjoy my programs!

Notes for Using MON, EDT, and AM2
  • The program table appears to be able to hold 13 programs including the built-in commands.
  • Built-in commands are normal entries, so you can clear built-ins you don't use (like CNS and NUL) to make more room.
  • When you load files in EDT using the L command, they are appended to the end of the buffer. The manual doesn't tell you this, but they are. This means that you can save individual subroutines in different files on a tape and then load them into a buffer for use in a larger program. Just open your code using EDT(R) or do an L in the middle of an EDT session to insert a file at the end of your buffer.
  • With EDT and AM2 in memory, you have almost no edit buffer before you clobber your assembler. You should put your edit buffer somewhere else by changing the bytes at 5124 and 5530. 5124 contains the start address of the buffer and 5530 contains the end address of the buffer. Remember that the 8080 uses 16-bit addressing, and it is a little endian machine. For instance, I put my buffer at 110000Q through 147777Q. The sequence of commands to do this are "DEP 5124 <CR> 000 <CR> 220 <CR> <Ctrl+Z> DEP 5530 <CR> 377 <CR> 317 <CR> <Ctrl+Z>".
  • AM2 contains a bug when dealing with high addresses. Mike has provided us with a patch for that, all you have to do is "DEP 13655 <CR> 332 <CR> <Ctrl+Z>" and you will be able to assemble anywhere in memory, except for within the monitor (of course).
  • Get good at moving around in EDT. Pay special attention to the use of the "." and "*" line indicators and arithmetic!

General Workflow Tips
  • You are going to be working with tapes a lot! Make sure you have tape recorder with a good counter.
  • Come up with a system for writing down tape indexes. What I do is write the filename and the start position of it right next to that. Then, after I record my file, I record the leader tone for a couple extra seconds. I write the index that I stop at on the next blank line. That way I always know where the blank part of my tape begins.
  • I keep my binaries on one side of my tape, and the source on the other.
  • Establish a "work tape" where you store both source and binary dumps of programs you are working on. (Binary on one side and source on the other is what I have been doing with my finished programs.)
  • I wrote down the indices of my working tape at 10 minute intervals. These are my working slots, so I can have 4 slots per side of a 90 minute tape. Moving quickly through this tape has been very handy for trying out dumps of programs or multiple approaches to a program!
  • Use 2 audio cables! I have plugged in to both ports on my Altair, and I swap them out at the tape recorder side. I can tell mine apart because my recorder's headphone port uses a 1/4 inch plug, but you may have to put taped labels on yours. I switch back and forth between playing and recording a great deal during a debug session, and so I have to have the trade readily available. On my recorder, sadly, when it plays it also plays the mic port so I can't keep both plugged in at the same time. Still, it's not that big of a deal to swap them if you have 2 cables.
  • Have a subroutine tape! When you write some code that could be generally useful, after you record your program, delete everything but that one routine and save it to a tape. You can patch it in to other programs as needed!

Those are the big notes I have made. I hope this helps clarify some of what is in the manual, especially that bit about EDT loading! Now for my programs.

I have written two programs which interface with the monitor. The first, CPY, allows you to copy blocks of memory. The way you invoke it is CPY(start, end, dest). This copies all the memory from start to end inclusive to the memory beginning at destination. It also throws out errors if you give it an invalid command line or an end that comes before the start. (This is the "A#" error). All of the addresses are specified in octal. I have been using this for a couple of purposes. First, before Mike patched AM2, I was using this to move ORR'd code into position to execute. The second thing I do with it is save my environment to another location in memory. In my operating system experiments, I can sometimes put EDT into invalid states. If I back up the monitor, edt, and am2 before I enter my program I can get things back into a valid state without resorting to using tape. A third use that i have been tinkering with is having multiple edit buffers open. You can copy the contents of the edit buffer around in memory, which can be handy.

This bit of code is useful, but it could be optimized a little bit. For instance I have a repeated section of code in there which was repeated because I thought it made it more readable. Optimizing it may be a fun exercise for you! At any rate, it does show an example of how to process command line arguments and interact with the monitor's error routines.

Here is the code for cpy:
Code: Select all
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CPY(START,END,DEST)
;;   THIS PROGRAM COPIES THE MEMORY FROM START TO END TO THE
;;   MEMORY BEGINNING AT DEST.
;;   
;; WRITTEN BY ROBERT LOWE
;; THIS PROGRAM IS RELEASED INTO THE PUBLIC DOMAIN
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        ORG     30000Q
START:  LDAX    B               ;GET THE LENGTH OF OPTIONS
        SUI     003Q            ;SUBTRACT OUT PROGRAM NAME
        JZ      ADDRF           ;THERE MUST BE A COMMAND LINE!
        JM      ADDRF           ;SAME DEAL, THERE MUST BE A COMMAND LINE.
        STA     COUNT           ;SAVE IT FOR LATER
        CALL    FNUM            ;GET TO THE FIRST NUMBER
        CALL    COCT            ;CONVERT IT
        PUSH    B               ;PUSH THE START
        CALL    FNUM            ;GET THE END
        CALL    COCT            ;
        PUSH    B               ;PUSH THE END
        CALL    FNUM            ;GET THE DESTINATION
        CALL    COCT            ;
        PUSH    B               ;PUSH THE DESTINATION
       
        POP     D               ;GET THE DESTINATION IN D
        POP     B               ;GET THE END IN B
        POP     H               ;GET THE START IN H

        MOV     A,C             ;SUBTRACT BC-HL
        SUB     L               ;
        MOV     C,A             ;
        MOV     A,B             ;
        SBB     H               ;
        MOV     B,A             ;
        JM      ADDRF           ;NEGATIVE? NOT ON MY WATCH!
        INX     B               ;REMBMER THE FIRST ONE

CLOOP:  MOV     A,M             ;GET THE BYTE TO COPY
        STAX    D               ;STORE THE BYTE
        INX     H               ;NEXT BYTE!
        INX     D               ;
        DCX     B               ;COUNT DOWN
        MOV     A,B             ;
        ORA     C               ;
        JNZ     CLOOP           ;CONTINUE UNTIL ZERO

        JMP     MON             ;DONE!
     
     
;--------------------------------------------------------------------   
;COCT - CONVERTS THE STRING POINTED TO BY DE INTO 16-BIT INT
;         STORED IN BC
;
;         CONVERSION STOPS WHEN EITHER THE FIRST NON-NUMERIC OR
;         END OF STRING IS REACHED
;--------------------------------------------------------------------
COCT:   LXI     B,0             ;START WITH 0
COCTL:  LDAX    D               ;GET THE CHARACTER
        CPI     060Q            ;CHECK 0 SIDE
        RC                      ;RETURN IF LESS THAN ASCII ZERO
        CPI     067Q            ;CHECK 7
        RNC                     ;RETURN IF GREATER THAN ASCII SEVEN
        MVI     H,3             ;WE'LL DO THIS 3 TIMES
COCTS:  MOV     A,C             ;SHIFT LEAST SIGNIFICANT
        RAL
        MOV     C,A
        MOV     A,B             ;SHIFT MOST SIGNIFICANT
        RAL
        MOV     B,A
        DCR     H
        JNZ     COCTS           ;SHIFT 3 TIMES
        MOV     A,C             ;OPERATE ON LSB
        ANI     370Q            ;MASK LAST 3 BITS
        MOV     C,A             ;GET READY TO PUT THIS TOGETHER
        LDAX    D               ;GET THE CHARACTER
        SUI     060Q            ;STRIP ASCII
        ADD     C               ;ADD THE ACCUMULATED VALUE
        MOV     C,A             ;ALL DONE WITH THIS ITERATION!
        INX     D               ;NEXT CHARACTER
        LDA     COUNT           ;DECREMENT COUNT
        DCR     A               ;
        STA     COUNT           ;
        JNZ     COCTL           ;CONTINUE IF THE STRING IS STILL THERE.
        RET
       
;-----------------------------------------------------------------------
;FNUM - ADVANCES TO THE NEXT NUMBER IN THE STRING.  STOPS IF IT
;         REACHES THE END.
;-----------------------------------------------------------------------
FNUM:   LDA     COUNT           ;GET THE REMAINING COUNT
        ORA     A               ;CHECK FOR ZERO
        JZ      ADDRF           ;ADDRESS FAULT IF NOTHING TO READ
        LDAX    D               ;GET THE CHARACTER
        CPI     060Q            ;COMPARE TO ZERO
        JC      FNUMA           ;ADVANCE TO THE NEXT CHAR
        CPI     067Q            ;COMPARE TO ASCII 7
        JNC     FNUMA           ;ADVANCE TO THE NEXT CHAR
        RET                     ;ALL DONE!

FNUMA:  INX     D               ;INCREMENT DE
        LDA     COUNT           ;DECREMENT COUNT
        DCR     A               ;
        STA     COUNT           ;
        JNZ     FNUM            ;GO TO THE NEXT


;-----------------------------------------------------------------
;ADDRF - ADDRESS FAULT
;        THIS FUNCTION RETURNS CONTROL BACK TO THE MONITOR
;        WITH THE ERROR CODE "A"  iIT DOES NOT RETURN
;        THIS FUNCTION CAN BE CALLED DIRECTLY, OR IT IS REACHED
;        WHEN FNUM FALLS OUT
;-----------------------------------------------------------------
ADDRF:  LXI     B,ADDERR        ;ADDRESS FAULT
        CALL    IO              ;CALL IO
        JMP     MON             ;GET OUT OF HERE!


COUNT:  DB 0
ADDERR: DB      60Q             ;OPERATION CODE FOR ERROR
        DB      "A"             ;ADDRESS ERROR

        BEG     START
        END     CPY
        RUN     CPY
        EOA


My second useful program is called DMT (short for "DuMp Text"). This program is invoked as DMT(start, end). What it does is dump memory to the TTY device. If the memory is a printable non-control character, it is simply printed. If, on the other hand, it is outside of the 7-bit ascii range or it is a control character, it prints the sequence \### where ### is the octal representation of the byte. This has proven useful for dissecting parts of programming system. Most notably, I have been using this to explore the structure of the program and symbol tables. The later, AM2's symbol table, was my real aim. I am currently writing a linker loader which will allow for relocatable code for my Unix system. The way I am doing this is I am using AM2's symbol table output to build a relocatable container for assembled code. I'll post that when I get it working!

Again, this has code has some inefficiencies. It uses more of the monitor though as I used the monitor's write commands. This means this will fit into the device table scheme used by the MITS system.

Here is the code for DMT
Code: Select all
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;DMT(START,END) - DUMPS TEXT FROM START TO END (OCT ADDR)
;;                 WHERE POSSIBLE, THIS PROGRAM ECHOS PRINTABLE
;;                 IT ECHOS \### WHERE ### IS THE OCT OF THE BYTE.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        ORG     30300Q
START:  LDAX    B               ;GET THE LENGTH OF OPTIONS
        SUI     003Q            ;SUBTRACT OUT PROGRAM NAME
        JZ      ADDRF           ;THERE MUST BE A COMMAND LINE!
        JM      ADDRF           ;SAME DEAL, THERE MUST BE A COMMAND LINE.
        STA     COUNT           ;SAVE IT FOR LATER
        CALL    FNUM            ;GET TO THE FIRST NUMBER
        CALL    COCT            ;CONVERT IT
        PUSH    B               ;PUSH THE START
        CALL    FNUM            ;GET THE END
        CALL    COCT            ;
        PUSH    B               ;PUSH THE END

        POP     B               ;GET THE END IN B
        POP     H               ;GET THE START IN H

        MOV     A,C             ;SUBTRACT BC-HL
        SUB     L               ;
        MOV     C,A             ;
        MOV     A,B             ;
        SBB     H               ;
        MOV     B,A             ;
        JM      ADDRF           ;NEGATIVE? NOT ON MY WATCH!
        INX     B               ;REMEMBER TO INCLUDE THE START!

PLOOP:  MOV     A,M             ;GET THE BYTE TO PRINT
        PUSH    B
        PUSH    H
        CALL    PRINT           ;PRINT THE BYTE!
        POP     H
        POP     B
        INX     H               ;NEXT BYTE!
        DCX     B               ;COUNT DOWN
        MOV     A,B             ;
        ORA     C               ;
        JNZ     PLOOP           ;CONTINUE UNTIL WE HIT 0

        JMP     MON             ;DONE!


;--------------------------------------------------------------------   
;COCT - CONVERTS THE STRING POINTED TO BY DE INTO 16-BIT INT
;         STORED IN BC
;
;         CONVERSION STOPS WHEN EITHER THE FIRST NON-NUMERIC OR
;         END OF STRING IS REACHED
;--------------------------------------------------------------------
COCT:   LXI     B,0             ;START WITH 0
COCTL:  LDAX    D               ;GET THE CHARACTER
        CPI     060Q            ;CHECK 0 SIDE
        RC                      ;RETURN IF LESS THAN ASCII ZERO
        CPI     067Q            ;CHECK 7
        RNC                     ;RETURN IF GREATER THAN ASCII SEVEN
        MVI     H,3             ;WE'LL DO THIS 3 TIMES
COCTS:  MOV     A,C             ;SHIFT LEAST SIGNIFICANT
        RAL
        MOV     C,A
        MOV     A,B             ;SHIFT MOST SIGNIFICANT
        RAL
        MOV     B,A
        DCR     H
        JNZ     COCTS           ;SHIFT 3 TIMES
        MOV     A,C             ;OPERATE ON LSB
        ANI     370Q            ;MASK LAST 3 BITS
        MOV     C,A             ;GET READY TO PUT THIS TOGETHER
        LDAX    D               ;GET THE CHARACTER
        SUI     060Q            ;STRIP ASCII
        ADD     C               ;ADD THE ACCUMULATED VALUE
        MOV     C,A             ;ALL DONE WITH THIS ITERATION!
        INX     D               ;NEXT CHARACTER
        LDA     COUNT           ;DECREMENT COUNT
        DCR     A               ;
        STA     COUNT           ;
        JNZ     COCTL           ;CONTINUE IF THE STRING IS STILL THERE.
        RET
;-----------------------------------------------------------------------
;FNUM - ADVANCES TO THE NEXT NUMBER IN THE STRING.  STOPS IF IT
;         REACHES THE END.
;-----------------------------------------------------------------------
FNUM:   LDA     COUNT           ;GET THE REMAINING COUNT
        ORA     A               ;CHECK FOR ZERO
        JZ      ADDRF           ;ADDRESS FAULT IF NOTHING TO READ
        LDAX    D               ;GET THE CHARACTER
        CPI     060Q            ;COMPARE TO ZERO
        JC      FNUMA           ;ADVANCE TO THE NEXT CHAR
        CPI     067Q            ;COMPARE TO ASCII 7
        JNC     FNUMA           ;ADVANCE TO THE NEXT CHAR
        RET                     ;ALL DONE!

FNUMA:  INX     D               ;INCREMENT DE
        LDA     COUNT           ;DECREMENT COUNT
        DCR     A               ;
        STA     COUNT           ;
        JNZ     FNUM            ;GO TO THE NEXT


;-----------------------------------------------------------------
;ADDRF - ADDRESS FAULT
;        THIS FUNCTION RETURNS CONTROL BACK TO THE MONITOR
;        WITH THE ERROR CODE "A"  iIT DOES NOT RETURN
;        THIS FUNCTION CAN BE CALLED DIRECTLY, OR IT IS REACHED
;        WHEN FNUM FALLS OUT
;-----------------------------------------------------------------
ADDRF:  LXI     B,ADDERR        ;ADDRESS FAULT
        CALL    IO              ;CALL IO
        JMP     MON             ;GET OUT OF HERE!


;-----------------------------------------------------------------
;PRINT - PRINTS THE CHARACTER IN A.  THIS PROCEDURE
;        DETERMINES IF A CONTAINS A PRINTABLE CHARACTER, AND
;        IT TAKES APPROPRIATE ACTION TO DISPLAY IT.
;-----------------------------------------------------------------
PRINT:  CPI     040Q            ;SEE IF IT IS A CONTROL CHAR
        JC      POCT            ;PRINT OCTAL IF IT IS
        CPI     177Q            ;SEE IF IT IS AN EXTENDED CHAR
        JNC     POCT            ;PRINT OCTAL IF IT IS
        STA     CHARBUF         ;PUT THE CHARACTER IN THE BUFFER
        LXI     B,CHARPKT               ;PRINT THE CHARACTER
        CALL    IO              ;
        RET
POCT:   LXI     H,OCTBUFEND     ;CONVERT A
        CALL    SOCT
        LXI     B,OCTPKT        ;PRINT THE OCTAL
        CALL    IO
        RET


;-----------------------------------------------------------------
;SOCT - CONVERT THE BYTE IN A TO AN OCTAL STRING
;       HL POINTS TO THE BEGINNING OF THE
;       HL POINTS TO THE END OF THE STRING (RIGHT HAND SIDE)
;       THIS PROCEDURE ALSO USES B AND C
;-----------------------------------------------------------------
SOCT:   MOV     B,A             ;PRESERVE THE BYTE TO WRITE
        MVI     C,3             ;WE EXTRACT 3 DIGITS
SOCTL:  MOV     A,B             ;THE BYTE SO FAR
        ANI     007Q            ;MASK ALL BUT LAST 3 BITS
        ADI     060Q            ;ADD ASCII
        MOV     M,A             ;PUT THE BYTE IN THE STRING
        DCX     H               ;GO TO THE NEXT DIGIT
        MOV     A,B             ;GET THE ORIGINAL BYTE
        ANI     370Q            ;DISPOSE OF THE BITS WE HAVE JUST PRINTED
        RRC                     ;ROTATE 3 TIMES
        RRC                     ;
        RRC                     ;
        MOV     B,A             ;PUT THE BYTE BACK
        DCR     C               ;COUNT
        JNZ     SOCTL           ;CONTINUE WHILE NOT 0
        RET

COUNT:  DB 0
ADDERR: DB      60Q             ;OPERATION CODE FOR ERROR
        DB      "A"             ;ADDRESS ERROR
        CHARPKT:        DB      22Q     ;WRITE OPERATION
                DB      "TTY"   ;DEVICE
                DW      CHARBUF ;ADDRESS OF BUFFER
                DW      1       ;CHARACTERS TO PRINT
                DW      STAT    ;STATUS WORD ADDRESS
CHARBUF:                DB      0       ;CHARACTER BUFFER
OCTPKT:         DB      22Q     ;WRITE OPERATION
                DB      "TTY"   ;DEVICE
                DW      OCTBUF  ;BUFFER
                DW      4       ;CHARACTERS TO PRINT
                DW      STAT    ;STATUS WORD
OCTBUF:         DB      "\  "   ;BACKSLASH AND TWO SPACES
OCTBUFEND:      DB      " "     ;THE LAST CHAR OF THE OCTBUF
STAT:           DW      0       ;STAT WORD


        BEG     START
        END     DMT
        RUN     DMT
        EOA


The past few days have been a lot of fun, and I think I've got a pretty solid handle on using the programming system. It really is a neat development environment. Is anyone else on here using it? If not, give it a go and we can chat about it!
pngwen
 
Posts: 14
Joined: July 7th, 2016, 9:57 am

Re: Some Tips for Using MITS Programming System II (w/ code)

Postby AltairClone » March 23rd, 2017, 4:26 pm

This makes for a great head-start for others that may follow. Thanks for pulling this information together and posting it for us.

Mike
AltairClone
Site Admin
 
Posts: 303
Joined: April 5th, 2013, 10:55 am


Return to General Discussions

Who is online

Users browsing this forum: No registered users and 1 guest