Still no luck finding a way to run the Intel PL/M 8080 compiler, but one of the manuals had some sample code in it.
Consider this PL/M procedure:
PRINT$STRING: PROCEDURE(NAME,LENGTH);
DECLARE NAME ADDRESS,
(LENGTH,I,CHAR BASED NAME) BYTE;
DO I = 0 to LENGTH-1;
CALL PRINT$CHAR(CHAR(I));
END;
END PRINT$STRING;
Before we get to the code generated by the compiler, a few words about the compiler conventions.
Parameters to a procedure are allocated statically in memory unless the procedure is declared to be reentrant. Local variables are allocated next, contiguously.
If there are two or fewer parameters, they are passed in registers.
The first in register pair BC if ADDRESS and in register C if BYTE.
The second in register pair DE if ADDRESS and in register E if BYTE.
00C8 21 02E2 [10] 00078 lxi H,2E2h ; Point to NAME
00CB 71 [7] 00079 mov M,C ; Store NAME
00CC 23 [5] 00080 inx H
00CD 70 [7] 00081 mov M,B
00CE 2C [5] 00082 inr L ; Store LENGTH
00CF 73 [7] 00083 mov M,E
00D0 2C [5] 00084 inr L ; Clear I
00D1 36 00 [10] 00085 mvi M,0
00D3 21 02E4 [10] 00086 lxi H,2E4h ; Point to LENGTH
00D6 4E [7] 00087 mov C,M
00D7 0D [5] 00088 dcr C ; LENGTH-1
00D8 79 [5] 00089 mov A,C
00D9 2C [5] 00090 inr L
00DA 96 [7] 00091 sub M ; Compare with I
00DB DA 00F1 [10] 00092 jc 0F1h ; End loop
00DE 4E [7] 00093 mov C,M ; Make index into NAME
00DF 06 00 [7] 00094 mvi B,0
00E1 2A 02E2 [16] 00095 lhld 2E2h ; Add base of NAME
00E4 09 [10] 00096 dad B
00E5 7E [7] 00097 mov A,M ; Get next CHAR
00E6 4F [5] 00098 mov C,A
00E7 CD 00C0 [17] 00099 call 0C0h ; call PRINT$CHAR
00EA 21 02E5 [10] 00100 lxi H,2E5h ; Point to I
00ED 34 [10] 00101 inr M ; Increment I
00EE C3 00D3 [10] 00102 jmp 0D3h ; Back to top of the loop
00F1 C9 [10] 00103 ret
This is not great code, but it is not bad. The compiler definitely knows about the strengths and weaknesses of the 8080.
Loading a byte from or storing a byte to an arbitrary location in memory takes three bytes of machine code and 13 clock cycles. And it has to go through the accumulator.
Contrast that with indirect access using an address in the HL register pair. The “memory” register M is an equal to the other single-byte registers except for slightly slower access.
This is what an assembly language programmer might write:
00C8 21 02E2 [10] 00107 lxi H,2E2h ; Point to NAME
00CB 71 [7] 00108 mov M,C ; Store NAME
00CC 23 [5] 00109 inx H
00CD 70 [7] 00110 mov M,B
00CE 23 [5] 00111 inx H ; Store LENGTH
00CF 73 [7] 00112 mov M,E
00D0 AF [4] 00113 xra A ; Check for LENGTH = 0
00D1 BB [4] 00114 cmp E
00D2 C8 [5/11] 00115 rz
00D3 2A 02E2 [16] 00116 Loop: lhld 2E2h ; Get next character
00D6 4E [7] 00117 mov C,M
00D7 23 [5] 00118 inx H ; Point to next character
00D8 22 02E2 [16] 00119 shld 2E2h ; Save for next time
00DB CD 00C0 [17] 00120 call 0C0h ; call PRINT$CHAR
00DE 21 02E4 [10] 00121 lxi H,2E4h ; Point to LENGTH
00E1 35 [10] 00122 dcr M ; Decrement LENGTH
00E2 C2 00D3 [10] 00123 jnz Loop
00E5 C9 [10] 00124 ret
Now I am curious what the compiler would generate if I had written PL/M code to do it this way…