*************************** * SUBROUTINES * * FOR XMODEM AND TXTMODEM * *************************** *==================================* * SUBROUTINE ENQUEUE * Put A in queue and circular- * increment HL, and increment BUFBC * TRASHES A *==================================* ENQUE: MOV M,A ;PUT BYTE IN BLKBF PUSH H ;BUMP BUFFER COUNT LHLD BUFBC INX H SHLD BUFBC POP H * FALL INTO CINCH *======================* * SUBROUTINE CINCH * circular-increment HL *======================* CINCH: INX H ;CIRCULAR-INCREMENT BLKBF POINTER MOV A,H CPI BBMAX/256 ;ASSUMES 256-BYTE ALIGNED BUFFER RNZ MVI H,BLKBF/256 RET *====================================================* * SUBROUTINE PDATA: Send one BLKSIZ-byte XMODEM block * ON ENTRY: * INIFL <> 0 IF WE HAVEN'T SENT ANY BLOCKS YET * CRCFL = 0 FOR CHECKSUM, >0 FOR CRC * BLKBF = CIRCULAR QUEUE WITH DATA * BUFBC = NUMBER OF BYTES IN BLKBF * BUFOP = = ADDRESS OF NEXT BYTE TO SEND * BLKNO = BLOCK NUMBER OF PREVIOUS XMODEM BLOCK * ON RETURN: * INIFL = 0 * CRCFL = 0 FOR CHECKSUM, >0 FOR CRC * BUFBC = BUFBC-BLKSIZ * BUFOP = ADDRESS OF NEXT BYTE TO SEND * A, BC, DE, HL TRASHED *====================================================* *--------------------------------------------- * IF THIS WILL BE THE FIRST XMODEM BLOCK, THEN * WAIT FOR EITHER 'C' OR NAK FROM RECEIVER, TO * SELECT CRC OR CHECKSUM TRANSMISSION. *--------------------------------------------- PDATA: LDA INIFL ;NEED TO NEGOTIATE? ORA A JZ PDAT3 ;NZ: WE ALREADY KNOW CRC OR CKSUM PDAT0: MVI B,0 ;TEST FOR LOCAL KEY CALL TXBYT ;SEND A NULL TO SEE MVI C,NAKTO ;LONG TIMEOUT PDAT1: DCR C ;TIMEOUT? JZ NAKAB ;YES: ABORT CALL RXBY1 ;WILL RETURN 0 IF TIMEOUT JC PDAT1 ;TIMEOUT? ORA A ;0 BUT NO TIMEOUT? CZ LOCAL ;LOOKS LIKE LOCAL KEY ON JZ PDAT0 CPI NAK ;CHECKSUM MODE? JZ PDAT2 ;CHECKSUM MODE ENABLED XRI 'C' ;CRC MODE? JNZ PDAT1 ;UNKNOWN CHR INR A ;A=1 STA CRCFL ;REMEMBER MODE PDAT2: XRA A ;DONE WITH NEGOTIATION STA INIFL IF PACFY LXI D,TRNMS ;TRANSMITTING WITH CALL PMODE ;PRINT CHECKSUMS OR CRC ENDF *---------------- * SEND NEXT BLOCK *---------------- PDAT3: LHLD BLKNO ;inc 16-bit block count INX H SHLD BLKNO XRA A ;NO ERROR IN THIS BLOCK YET STA ERRCT *------------------------------- * SEND BLOCK HEADER: * SOH, BLOCK #, INVERTED BLOCK # *------------------------------- PDAT4: MVI B,SOH ;SOH 1ST CALL TXBYT LXI H,BLKNO ;8-BIT BLOCK NUMBER MOV B,M CALL TXBYT MOV A,M CMA . ;INVERSE BLOCK NUMBER MOV B,A CALL TXBYT *------------------------------------------------ * SEND BLKSZ BYTES FROM BLKBF. CALCULATE CHECKSUM * IN C AND 16-BIT CRC IN CRC16. *------------------------------------------------ LXI H,0 ;CLEAR CRC SHLD CRC16 MOV C,H ;...CLEAR CHECKSUM IN C LHLD BUFOP ;POINT TO NEXT DATA MVI B,BLKSZ ;LOOP COUNTER PDAT5: PUSH B ;LOOP COUNTER, CKSUM MOV B,M ;GET BYTE FROM BLKBF CALL TXBYT ;SEND CHR FROM B MOV A,B ;COMPUTE CRC CALL CACRC ;RETURNS CRC IN DE MOV A,B GET CHR FOR CHECKSUM POP B ;LOOP COUNTER, CKSUM ADD C ;COMPUTE CHECKSUM IN C MOV C,A CALL CINCH ;CIRCULAR INC HL DCR B ;ALL BYTES SENT? JNZ PDAT5 ;LOOP THROUGH ALL BYTES *------------------------------------- * SEND CHECKSUM OR CRC, BASED ON CRCFL ; C= CHECKSUM ; DE= 16-BIT CRC FROM CALCRC *------------------------------------- LDA CRCFL ;CHKSUM OR CRC? ORA A JZ PDAT7 ;Z: SEND CHKSUM MOV B,D ;HIGH CRC BYTE 1ST CALL TXBYT MOV C,E ;NOW 2ND CRC BYTE PDAT7: MOV B,C ;CKSUM OR 2ND CRC BYTE CALL TXBYT *--------------------------------------------- * WAIT FOR ACK. IF NAK OR TIMEOUT, THEN RESEND *--------------------------------------------- CALL GTACK ;WAIT FOR Z SET = ACK JNZ PDAT8 ;NZ: TIMEOUT OR NAK *------------------------------ * ACK RECEIVED. UPDATE POINTERS *------------------------------ SHLD BUFOP ;UPDATED OUT-POINTER LHLD BUFBC ;UPDATE BYTE COUNT MOV A,L SUI BLKSZ MOV L,A MOV A,H SBI BLKSZ/256 ;BORROW IF NEEDED MOV H,A SHLD BUFBC IF PACFY *-------------------------- * PRINT PACIFIER AND RETURN *-------------------------- MVI A,PACACK ;HAPPY PACIFIER CALL CONOT ;RETURN FROM THERE ENDF RET *------------ * BLOCK RETRY *------------ PDAT8: LHLD RTRYS INX H SHLD RTRYS JMP PDAT4 ;RETRY IT NOW *===============================================* * SUBROUTINE GTACK: Get an ACK from the receiver * ON RETURN: * CARRY SET IFF TIMEOUT * Z SET IFF ACK RECEIVED * ERRCT INCREMENTED ON TIMEOUT * IF TOO MANY ERRORS, ABORT * TRASHES A, B *===============================================* *--------------------------------- * DELETE NOISE-INDUCED BOGUS CHRS * LONG BEFORE ACK/NAK WOULD ARRIVE *--------------------------------- GTACK: CALL RXBY0 CALL RXBY0 ;SUCK UART DRY *------------------------------ * GET XMODEM BYTE, TEST FOR ACK *------------------------------ MVI B,ACKTO*2 ;ACK TIMEOUT VAL CALL RXBYT ;GET XMODEM BYTE JC GTAC1 ;CARRY MEANS TIMEOUT CPI ACK RZ . ;ACK: RETURN WITH Z SET *------------------------------ * IF NAK, PRINT NAK PACIFIER, * AND RETURN WITH Z & C CLEARED *------------------------------ CPI NAK ;NAK? JNZ GTAC1 ;NZ: BOGUS CHR IF PACFY MVI A,PACNAK ;NAK PACIFIER CALL CONOT ENDF ORA A ;NAK: CLEAR Z & C RET *----------------------------------------- * TIMEOUT OR BOGUS CHR. BUMP & CHECK ERRCT * ABORT IF TOO MANY, OTHERWISE SET C & RET *----------------------------------------- GTAC1: LDA ERRCT INR A STA ERRCT ;TOO MANY ERRORS? CPI ERLIM RC . ;No: RET w/ C SET, Z CLEAR *----------------------- * ABORT: TOO MANY ERRORS *----------------------- LXI D,AERMS ;TOO MANY ACK ERRORS JMP ABORT *=========================================* * SUBROUTINE GDATA: * Get a buffer-full of data from sender * ON ENTRY: * INIFL>0 NUMBER OF TIMES TO TRY CRC MODE * 0FFH: NOT THE FIRST BLOCK * UNLESS THIS IS THE FIRST BLOCK, THEN * THE PREVIOUS BLOCK NEEDS TO BE ACK'D * ON RETURN: * INIFL = 0FFH UNLESS ERROR IN 1ST BLOCK * BUFIP = BLKBF * BUFBC = NUMBER OF BYTES IN BUFFER * UNLESS WE GOT AN EOF, THE LAST BLOCK * HAS NOT BEEN ACK'D * Z SET IF BUFFER IS STILL EMPTY (EOF) * A,BC,DE,HL TRASHED * ON ENTRY AND RETURN: * CSMFL = 0 FOR CHECKSUM, 1 FOR CRC * EOFFL = 1 IF EOF RECEIVED FROM SENDER * BLKNO = LAST BLOCK NUMBER RECEIVED *=========================================* GDATA: LDA EOFFL ;GOT EOF ALREADY? XRI 1 RZ . ;YES: RETURN WITH Z LXI H,BLKBF ;POINT TO BUFFER BEGINNING SHLD BUFIP *--------------------------------------------- * SEND ACK FOR PREVIOUS BLOCK. UNLESS THIS IS * THE 1ST BLOCK (INIFL<>FF): THEN SEND 'C' TO * REQUEST CRC TRANSMISSION. AFTER CRTRY TRIES, * SEND NAK TO REQUEST CHECKSUM TRANSMISSION *--------------------------------------------- GDA01: XRA A ;CLEAR ERROR COUNT STA ERRCT MVI B,ACK GDA02: LXI H,INIFL ;FIRST BLOCK? MOV A,M INR A ;TEST FOR INFL=FF JZ GDA04 ;NO: SEND ACK (OR NAK ON RETRY) MVI B,NAK DCR A ;CHECKSUM MODE? JZ GDA03 MVI B,'C' ;STILL TRYING CRC DCR M ;COUNT DOWN 'C' RETRIES GDA03: STA CRCFL ;REMEMBER CKSUM OR CRC MODE GDA04: CALL TXBYT ;SEND B AS ACK *--------------------------------------- * WAIT FOR SOH TO START RECEPTION. IF WE * GET AN EOT INSTEAD, THEN WE ARE DONE. *--------------------------------------- MVI B,SOHTO*2 ;TIMEOUT FOR SOH CALL RXBYT JC GDA06 ;CARRY MEANS TIMEOUT CPI SOH ;DID WE GET THE SOH? JZ GDA07 ;YES: GET THE BLOCK XRI EOT ; SET EOFFL & END IF EOT JNZ GDA05 ;OTHERWISE, HEADER ERROR INR A ;A=1 STA EOFFL ;REMEMBER WE GOT EOF MVI B,ACK ;ACK THE EOF CALL TXBYT JMP GDA10 ;DONE RECEIVING *------------------------------------------ * NO SOH OR EOT: BOGUS CHR. IF WE ARE STILL * SENDING 'C' THEN SEE IF WE GOT 'C' BACK. * IF SO, THE LOCAL KEY IS PROBABLY ON. * HL=INIFL HERE *------------------------------------------ GDA05: CPI 47H ;= 'C' XOR EOT JNZ GDA51 MOV A,M ;INIFL CPI CRTRY-1 ;FIRST TIME TRYING 'C'? JNZ GDA51 INR M ;UNDO 'C' TRY CALL LOCAL ;PLEASE TURN OFF LOCAL LEY JMP GDA01 TRY AGAIN *-------------------------------- * SOME SORT OF BOGUS INPUT. PURGE * UNTIL THE SERIAL PORT IS QUIET. *-------------------------------- GDA51: CALL RXBY1 ;CARRY SET ON TIMEOUT JNC GDA51 ;PURGE UNTIL TIMEOUT * FALL INTO GDA06 *--------------------------------------- * BUMP COUNTERS, PRINT PACIFIER, & RETRY *--------------------------------------- GDA06: LXI H,ERRCT INR M ;BUMP ERROR COUNT LHLD RTRYS ;BUMP RETRY COUNTER INX H SHLD RTRYS IF PACFY MVI A,PACRSD ;ERROR PACIFIER CALL CONOT ENDF MVI B,NAK ;RETRY REQUEST MOV A,M CPI ERLIM ;TOO MANY ERRORS? JC GDA02 ;NO: TRY AGAIN WITH B=NAK *----------------------- * ABORT: TOO MANY ERRORS *----------------------- LXI D,ECEMS ;TOO MANY ERRORS JMP ABORT *--------------------------------- * GOT SOH. NOW GET REST OF HEADER: * BLOCK #, INVERTED BLOCK # * HL = INIFL HERE *--------------------------------- GDA07: MVI M,0FFH ;WE GOT AN SOH LXI H,0 ;INIT CRC SHLD CRC16 LHLD BUFIP CALL RXBY1 ;GET BLOCK # JC GDA06 ;TIMEOUT? MOV D,A CALL RXBY1 ;INVERTED BLOCK # JC GDA06 ;TIMEOUT? CMA . ;INVERT TO COMPARE CMP D JNZ GDA51 ;NO MATCH: ERROR STA RXBLK ;SAVE BLOCK # *-------------------------------- * RECEIVE BLKSZ BYTES INTO BLKBF. * COMPUTE CHKSM & CRC AS WE GO. *-------------------------------- LXI B,BLKSZ*256+0 ;B=BYTES, c=0 CKSM LHLD BUFIP ;Point to buffer GDA08: PUSH B ;SAVE BYTE CNT & CKSM CALL RXBY1 ;GET 1 DATA BYTE POP B JC GDA06 ;TIMEOUT? IF TXTMO ;EAT LFS IN TEXT MODE CPI MSEOF ;EAT PAD CHRS AT END JZ GDA18 CPI LF ;CHUCK LINEFEEDS JZ GDA18 ENDF MOV M,A ;STORE DATA IN BLKBF GDA28: INX H ;NEXT BYTE GDA18: MOV D,A ;TEMP SAVE FOR CRC ADD C ;COMPUTE CKSM MOV C,A MOV A,D ;GET DATA BYTE BACK CALL CACRC ;CALC CRC TOO, IN DE DCR B ;DONE YET? JNZ GDA08 *------------------------- * VERIFY CHECKSUM IN C OR * CRC IN DE BASED ON CRCFL *------------------------- LDA CRCFL ;CRC MODE? ORA A ;0 MEANS CKSM JZ GDA09 CALL RXBY1 ;GET 1ST CRC BYTE JC GDA06 ;TIMEOUT? CMP D ;CRC FIRST BYTE MATCH? JNZ GDA51 ;NO: PURGE DATA & TRY AGAIN MOV C,E ;C=2ND CRC BYTE GDA09: CALL RXBY1 ;2ND CRC BYTE OR CKSM JC GDA06 ;TIMEOUT? CMP C ;MATCH? JNZ GDA06 ;NO: ERROR IF PACFY *----------------------------------- * IF WE JUST GOT THE 1ST BLOCK, THEN * WE NOW KNOW THE ERROR-CHECK MODE. * PRINT CONSOLE MESSAGE NOW. *----------------------------------- PUSH H LHLD BLKNO ;STILL AT INIT VAL? MOV A,H ORA L POP H LXI D,RCVMS ;RECEIVING WITH CZ PMODE ;PRINT CHECKSUMS OR CRC ENDF *------------------------------------------- * GOOD BLOCK. DID WE ALREADY GET THIS BLOCK? * (MIGHT BE A RETRANSMISSION.) IF IT'S THE * SAME NUMBER AS THE PREVIOUS BLOCK, IGNORE * IT. IF IT'S THE NEXT BLOCK, KEEP GOING. * ANYTHING ELSE IS A SYNC ERROR. *------------------------------------------- XCHG . ;BLKBF POINTER TO DE LHLD BLKNO ;PREV. 16-BIT BLOCK NUMBER LDA RXBLK ;A=THIS BLOCK # SUB L ;8-BIT BLOCK NUMBER JZ GDA01 ;..SEND ACK & IGNORE DCR A ;SHOULD BE NEXT BLOCK JNZ SYERR INX H ;BUMP 16-BIT BLKNO SHLD BLKNO XCHG . ;BLKBF PINTER TO HL *------------------------------------------- * GOT A GOOD BLOCK. PRINT PACIFIER. IF * THERE'S ROOM IN THE BUFFER, GET MORE DATA. * THIS ASSUMES THE BUFFER IS ON A 256-BYTE * BOUNDARY, AND THAT MAX DATA IN A BLOCK * (INCLUDING INSERTED LF'S) <= 256 BYTES *------------------------------------------- SHLD BUFIP ;SAVE BUFFER ADDRESS IF PACFY MVI A,PACBLK ;GOOD BLOCK PACIFIER CALL CONOT ENDF MOV A,H CPI BBMAX/256-1 ;SIMPLE CHECK JNZ GDA01 *------------------------------------- * BLKBF IS FULL. CALCULATE BYTE COUNT, * RESET BUFFER POINTER & RETURN *------------------------------------- GDA10: LHLD BUFIP MOV A,H ;BUFBC := BUFIP-BLKBF SUI BLKBF/256 MOV H,A SHLD BUFBC ;BUFBC IS CURRENT ORA L ;SET Z IF BUFFER IS EMPTY LXI H,BLKBF ;POINT TO START OF BLKBF SHLD BUFIP RET . ;WITH Z SET IF EOF FOUND *-------------------------------- * SYNC ERROR: BLOCKS OUT OF ORDER *-------------------------------- SYERR: LXI D,SERMS ;SYNC ERROR JMP ABORT *===========================================* * SUBROUTINE CACRC: Update 16-bit CRC with A * ON ENTRY: * A=NEW DATA BYTE * CRC16 IS CURRENT FOR PRIOR BYTES * ON RETURN: * CRC16 UPDATED * DE = CRC SO FAR * A TRASHED *===========================================* CACRC: PUSH H LHLD CRC16 XRA H ;XOR INTO TOP BYTE MOV H,A MVI D,8 ;PREPARE FOR 8 ROTATION CCRC1: DAD H ;16-BIT SHIFT LEFT JNC CCRC2 ;SKIP IF BIT 15 WAS 0 MOV A,H ;CRC = CRC XOR 1021H XRI 10H MOV H,A MOV A,L XRI 21H MOV L,A CCRC2: DCR D JNZ CCRC1 ;ROTATE 8 TIMES SHLD CRC16 ;SAVE CRC SO FAR XCHG ;RESULT IN DE FOR RETURN POP H RET *========================================* * SUBROUTINE RXBYT: Receive a byte from * serial port, abort if user types ESCAPE * ENTRY AT RXBY1: 1-SECOND TIMEOUT * ENTRY AT RXBYT: * B = TIMEOUT IN UNITS OF 1/2 SEC * ON RETURN: * A = RECEIVED CHR * B = REMAINING TIME IN TIMEOUT * A=B=0 & CARRY SET IF TIMEOUT *========================================* RXBY1: MVI B,2 ;1-SECOND TIMEOUT RXBYT: PUSH H PUSH D *----------------------------------------------- * LOOP 'TIL TIMEOUT, CHECK FOR ESC EVERY 1/2 SEC *----------------------------------------------- RXW1: PUSH B ;(11) CALL CONST ;(17+141)USER TYPE ANYTHING? CNZ CKESC ;(11)GO SEE WHAT WAS TYPED POP B ;(10) *---------------------------------- * INNER LOOP: 201 CYCLES = 100.5 uS * 0.5 sec / 100.5 uS = 4975 CYCLES *---------------------------------- LXI D,4975D ;1/2-SEC COUNT DOWN RXW2: CALL RXBY0 ;(17+160)RECEIVE CHR INTO A JNZ RXW3 ;(10)GOT A CHARACTER? DCX D ;(5)timeout timer MOV A,D ;(5) ORA E ;(4) JNZ RXW2 ;(10) DCR B JNZ RXW1 *------------------------- * TIMEOUT: SET CARRY & RET *------------------------- STC POP D POP H RET *------------------------- * RETURN WITH RX BYTE IN A *------------------------- RXW3: ORA A ;CLEAR CARRY POP D POP H RET *=============================================* * SUBROUTINE RXBY0: Receive 1 serial port byte * ON RETURN: * A = CHR AND Z CLEARED IF CHR READY * A = 0 AND Z SET IF NO CHR * (160 US) *=============================================* RXBY0: MVI A,SERPP ;(7)SERIAL PSEUDOPORT JMP SOLOS+AINP ;(17+136)Z SET IF NO CHR IF PACFY *=======================================================* * SUBROUTINE PMODE: Print error-checking mode on console * ON entry: * DE POINTS TO INITIAL STRING * CRCFL = 0 FOR CKSUM, >0 FOR CRC * TRASHES A,DE *=======================================================* PMODE: CALL PRNTF ;SENDING WITH OR RECEIVING WITH LXI D,CKSMS ;CHECKSUMS LDA CRCFL ORA A JZ PRNTF ;RETURN VIA PRNTF LXI D,CRCMS ;CRCS JMP PRNTF ;RETURN VIA PRNTF ENDF *========================================* * SUBROUTINE TXBYT: Send B to serial port * CHR REMAINS IN B *========================================* TXBYT: MVI A,SERPP ;PSEUDOPORT IN A JMP SOLOS+AOUT *=========================================* * SUBROUTINE TO ASK USER TO TURN LOCAL OFF * RETURNS WITH Z SET *=========================================* LOCAL: LXI D,LKYMS ;'PLEASE TURN OFF LOCAL KEY' CALL PRNTF ;...'READY (Y/N)?' CALL CONIN ;WAIT FOR A KEY ANI 05FH ;CONVERT TO UPPERCASE MOV C,A LXI D,CRLFM ;PRINT CR LF CALL PRNTF MOV A,C ;WHAT GOT TYPED? CPI 'Y' RZ . ;Y MEANS READY CPI 'N' JNZ LOCAL CALL SYS DB RESET ;PTDOS WILL NOT RETURN FROM THIS *=======================================* * TERMINATE DUE TO NO INITIAL NAK OR 'C' * FROM SENDER *=======================================* NAKAB: LXI D,NNKMS JMP ABORT *==========================================* * SUBROUTINE CKESC: Check for ESC FROM USER * Abort if ESC typed, ignore all else. *==========================================* CKESC: CALL CONIN ;GET THE CHR ANI 7FH ;(7)STRIP PARITY CPI ESC ;(7)ESCAPE MEANS QUIT RNZ . ;(11)IGNORE ALL ELSE LXI D,UABMS ;USER ABORT * FALL INTO ABORT *======================================* * SUBROUTINE ABORT: Print abort message * and return directly to PTDOS * ON ENTRY: * DE = MESSAGE ADDRESS * SAVSP = RETURN1 ADDRESS TO PTDOS *======================================* ABORT: LHLD SAVSP ;RESTORE PTDOS RET ADDRESS SPHL * FALL INTO PRNTF, RETURN FROM THERE *===========================================* * SUBROUTINE PRNTF: Print message on console * ON ENTRY: * DE = ADDRESS OF 0-TERMINATED STRING * ON RETURN: * A,DE TRASHED, ALL OTHER REGS PRESERVED *===========================================* PRNTF: LDAX D ORA A RZ . ;NULL TERMINATION? CALL CONOT ;A TO CONSOLE INX D JMP PRNTF *=================================================* * SUBROUTINE TO PRINT HL IN DECIMAL ON THE CONSOLE * TRASHES ALL REGISTERS *=================================================* PRNTD: MVI D,0 ;SUPPRESS LEADING ZEROS LXI B,-10000 CALL DIGIT LXI B,-1000 CALL DIGIT LXI B,-100 CALL DIGIT LXI B,-10 CALL DIGIT MOV A,L ;LEAST SIG DIGIT IS SIMPLE JMP DIG0 ;AND IS NEVER SUPRESSED *--------------------------------------------- * LOCAL SUBROUTINE TO DIVIDE HL BY POWER OF 10 * AND PRINT RESULT, UNLESS IT'S A LEADING 0 * RETURNS REMAINDER IN HL * D=0 IF PREVIOUS DIGITS WERE ALL 0 * ON ENTRY: * HL=VALUE TO DIVIDE * BC=NEGATIVE POWER OF 10 * D=0 IFF ALL PRIOR DIGITS WERE 0 * ON RETURN: * INTEGER QUOTENT HAS BEEN PRINTED UNLESS IT * IS ZERO AND D WAS ZERO * HL=REMAINDER * D=0 IFF THIS AND PRIOR DIGITS ARE 0 *--------------------------------------------- DIGIT: MVI A,-1 ;WILL GO ONE TOO MANY TIMES PUSH D ;SAVE LEADING 0 SUPPRESSION DIGLP: MOV D,H ;DE GETS PREVIOUS VALUE MOV E,L INR A DAD B ;SUBTRACT A POWER OF 10 JC DIGLP ;LOOP UNTIL BORROW XCHG HL=REMAINDER POP D ;LEADING 0 SUPPRESSION MOV E,A ;SAVE DIGIT FOR PRINTING ORA D ;STILL LEADING ZEROS? MOV D,A SAVE FOR NEXT DIGIT RZ . ;DONE IF STILL SUPPRESSING MOV A,E DIG0: ADI '0' ;MAKE IT ASCII JMP CONOT ;PRINT ON CONSOLE & RET