TITLE NUM FL=0 A=1 B=2 C=3 D=4 P=17 CHTTYI==1 CHTTYO==2 ;FLAG NAMES NEGF==1 DIGF==2 PDLLEN==100 PDL: BLOCK PDLLEN OP1: 0 X1: 0 LINBUF: BLOCK 30 LINBFE:: LINPTR: 0 START: MOVE P,[-PDLLEN,,PDL-1] ;Open TTY channels. .CALL [SETZ ? SIXBIT/OPEN/ [.UAI,,CHTTYI] ? [SIXBIT/TTY/] ((SETZ))] .LOSE %LSFIL .CALL [SETZ ? SIXBIT/OPEN/ [.UAO,,CHTTYO] ? [SIXBIT/TTY/] ((SETZ))] .LOSE %LSFIL START1: PUSHJ P,GETLIN ;Read in a line of input. MOVE A,[440700,,LINBUF] MOVEM A,LINPTR ;Set up to fetch chars from the line. PUSHJ P,EVAL ;Parse and evaluate expression. PUSHJ P,DECOUT ;Print the answer. MOVEI A,[ASCIZ/ /] PUSHJ P,OUTSTR JRST START1 ;Read and evaluate an expression. Value returned in A. ;Clobbers B. EVAL: PUSHJ P,DECIN ;Read one number. MOVEM B,OP1 ;Save the number. EVAL1: MOVEI B,0 CAIN A,"+ ;Consider the operation character: MOVE B,[ADD B,OP1] ;B gets an instruction to do that operation. CAIN A,"- MOVE B,[SUB B,OP1] CAIN A,"* MOVE B,[IMUL B,OP1] CAIN A,"/ MOVE B,[IDIV B,OP1] JUMPE B,EVALX ;If B is still 0, the terminator ;was not an arith op, so it ends ;the expression or is illegal. MOVEM B,X1 ;It is an arith op, so save the instruction. PUSHJ P,DECIN ;Read the second operand. EXCH B,OP1 ;B gets first op, OP1 gets second operand. XCT X1 ;Compute result of operation, in B. MOVEM B,OP1 ;Save it as first operand of next operation. JRST EVAL1 ;A has terminator of second operand, ;which is the next operation. ;Come here on number terminated by char not an arith op. EVALX: JUMPN A,ERR ;Should be end of line, or it's an error. MOVE A,OP1 ;Otherwise, last saved value is value of exp. POPJ P, ;Print an error message if we see something we don't recognize. ERR: MOVEI A,[ASCIZ/Unrecognized character in expression: /] PUSHJ P,OUTSTR LDB A,LINPTR ;Print the offending character .IOT CHTTYO,A ;as part of the error message. MOVEI A,[ASCIZ / /] PUSHJ P,OUTSTR JRST START1 ;Read a signed decimal number out of the line, returning number in B ;and terminating character in A. DECIN: TRZ FL,NEGF!DIGF ;No minus, no digit seen yet. MOVEI B,0 DECIN1: ILDB A,LINPTR ;Fetch next character of line. CAIL A,"0 CAILE A,"9 JRST DECIN2 ;Jump if character not a digit. IMULI B,10. ;Else accumulate this digit into the number. ADDI B,-"0(A) ;Note that we convert the digit into its value. ;("0 into the value 0, "1 into 1). TRO FL,DIGF ;Set flag saying non-null number seen. JRST DECIN1 DECIN2: CAIN A,"- JRST DECIN3 ;Jump on minus sign. TRNN FL,DIGF ;Anything else: if we saw a number, POPJ P, ;negate it if it began with a minus sign. DECIN4: TRZE FL,NEGF MOVN B,B POPJ P, ;Come here after reading a minus sign. DECIN3: TRNE FL,DIGF ;Does it follow a number? JRST DECIN4 ;Yes. This must be a binary minus. TRC FL,NEGF ;This must be unary minus. ;Complement flag that number is negative. JRST DECIN1 ;(So that two minus signs cancel out). ;Print number in A, positive or negative, in decimal. ;Clobbers A and B. DECOUT: JUMPGE A,DECOT1 .IOT CHTTYO,["-] ;If number is negative, print sign. CAMN A,[400000,,] ;Smallest negative number is a pain: JRST DECOT2 ;its absolute value cannot fit in one word! MOVM A,A ;Else get abs val of negative number and print. DECOT1: IDIVI A,10. HRLM B,(P) ;Save remainder in LH of stack word ;whose RH contains our return address. SKIPE A ;If quotient is nonzero, PUSHJ P,DECOT1 ;print higher-order digits of number. HLRZ A,(P) ;Get back this remainder (this digit). ADDI A,"0 .IOT CHTTYO,A POPJ P, ;Print the abs value of the largest negative number. DECOT2: MOVEI A,[ASCIZ /34359738368/] JRST OUTSTR ;Copy the GETLIN and OUTSTR subroutines here. END START

- 10.: This is a decimal number. You can tell, because it ends with a decimal point.
- LINPTR: this location holds the byte pointer used for fetching characters out of the line. It is usually not worth while to keep this pointer in an accumulator if the parsing is being done over more than a very small piece of code.
- XCT: Note how EVAL chooses an arithmetic instruction based on the arithmetic operator character, then reads the following argument, and then executes the instruction chosen earlier, performing the operation. This is also the first use you have seen of literals containing instructions.
- ERR: this is an example of printing an error message. Error messages should always show the offending data, not just say "something was wrong".
- DECIN: Note how flags in accumulator 0 (FL) are used to keep track of whether any digits have been seen, and whether a minus sign came before them. Accumulator 0 is most often used for such flags because it is the least useful accumulator for anything else (since it cannot be used as an index register).
- DECOUT: This is a very famous program for printing a number. It works recursively because the first digits extracted as the remainders in successive divisions by the radix are the last digits to be printed. So the digits are produced and saved on the way down the recursion, and printed on the way up.
- HRLM: We could save the remainder with PUSH P,B and restore it with POP P,A, but since the left half of each word saved by a PUSHJ is not really going to be used, we can save stack space by using those left halves to store the remainder. It is also faster.