* JMP STRINGSX * *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=* * * * STRINGS ROUTINE LIBRARY * * * * AUTHOR: NATHAN RIGGS * * CONTACT: NATHAN.RIGGS@ * * OUTLOOK.COM * * * * VERSION: 0.1.2 * * DATE: 30-OCT-2018 * * ASSEMBLER: MERLIN 8 PRO * * * * LICENSE: APACHE 2.0, WHERE * * APPLICABLE. CODE INSPIRED * * BY THE WORKS OF OTHERS MAY * * FALL UNDER A DIFFERENT * * LICENSE (NOTED IN ROUTINE). * * * * THIS IS A STANDARD LIBRARY * * FOR STRING MANIPULATION. * * STRINGS ARE MOSTLY JUST 1-D * * ARRAYS PRECEDED BY A LENGTH * * BYTE. THIS DIFFERS FROM THE * * USE OF STRING IN THE PRINT * * ROUTINES OF STDIO (THOUGH * * NOT THE SINPUT ROUTINE) IN * * THAT THOSE "STRINGS" ARE * * TERMINATED WITH A #00 TO * * INDICATE AN END, RATHER THAN * * USING A LENGTH BYTE. * * * *------------------------------* * * * LIST OF ROUTINES * * * * STRCOMP : STRING COMPARE * * STRCAT : CONCATENATE STRING * * ASC2STR : NUL TO INDEXED STR * * STR2ASC : INDEXED TO NUL STR * * PRNSTR : PRINT STRING * * NUM2STR : NUMBER TO STRING * * STR2NUM : STRING TO NUMBER * * * *=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-* * *``````````````````````````````* * STRCMP :: STRING COMPARE * *- -* * COMPARE TWO STRINGS AND * * DETERMINE IF THEY ARE * * IDENTICAL; IF NOT, DETERMINE * * WHICH IS THE SHORTEST AND * * WHICH IS THE LONGEST. * * * * Z FLAG = 1 IF IDENTICAL * * Z FLAG = 0 IF NOT * * CARRY = 1 IF STR2 > STR1 LEN * * CARRY = 0 IF STR1 > STR2 LEN * *- -* * CLOBBERS: * * * * FLAGS: ????---- REG: AXYM * *- -* * CYCLES: ??? * * SIZE: * *- -* * USAGE: * * * * LDA #>STR1 * * PHA * * LDA #STR2 * * PHA * * LDA # STR1 LENGTH ** CARRY = 1 IF STR1 LENGTH >= STR2 LENGTH * :EXIT * ** RESTORE RETURN ADDRESS * LDA RETADR+1 PHA LDA RETADR PHA * LDY #0 LDA (ADDR1),Y ; GET STR1 LENGTH TAX ; RETURN IN X LDA (ADDR2),Y ; STR2 LENGTH TAY ; RETURN IN Y * RTS * *``````````````````````````````* * STRCAT :: STRING CONCATENATE * *- -* * CONCATENATE TWO STRINGS INTO * * A SINGLE STRING STORE WHERE * * THE FIRST STRING IS LOCATED * *- -* * CLOBBERS: * * * * FLAGS: ????---- REG: AXYM * *- -* * CYCLES: ??? * * SIZE: * *- -* * USAGE: * * * * LDA #>CATSTR * * PHA * * LDA #CATSTR2 * * PHA * * LDA # 255 CMP :MLEN ; CHECK AGAINST MAX LENGTH BEQ :LENOK ; BR IF LENGTH < MAX BCC :LENOK * ** RESULTING STRING WILL BE TOO LONG SO ** INDICATE A STRING OVERFLOW, __SOVF = 0FF ** SET NUMBER OF CHARS TO CONCAT = MLEN - S1LEN ** SET LENGTH OF STRING 1 TO MAX LENGTH * :TOOLONG LDA #$0FF STA :SOVF ; INDICATE OVERFLOW LDA :MLEN SEC SBC :S1LEN BCC :EXIT ; EXIT IF MLEN < S1LEN STA :SCNT ; ORIG STR WAS TOO LONG LDA :MLEN STA :S1LEN ; SET STR1 LENGTH TO MAX JMP :DOCAT * ** RESULTING LENGTH DOES NOT EXCEED MAX ** LENGTH OF STRING 1 = S1LEN + S2LEN ** INDICATE NO OVERFLOW, __SOVF = 0 ** SET NUM OF CHARS TO CONCAT TO LENGTH OF STR 2 * :LENOK STA :S1LEN LDA #0 ; SAVE SUM OF 2 LENGTHS STA :SOVF ; INDICATE NO OVERFLOW LDA :S2LEN STA :SCNT ; COUNT = LENGTH OF STRING 2 * ** CONCAT STRINGS * :DOCAT LDA :SCNT BEQ :EXIT ; EXIT IF NO BYTES LEFT * :CATLP LDY :S2IND LDA (ADDR2),Y ; GET NEXT BYTE FROM S2 LDY :S1IND STA (ADDR1),Y ; MOVE IT TO END OF STR 1 INC :S1IND ;INC STRING 1 INDEX INC :S2IND ; INC STRING 2 INDEX DEC :SCNT ; DECREMENT COUNTER BNE :CATLP ; CONT UNTIL __SCNT = 0 * * :EXIT * ** RESTORE RETURN ADDRESS * LDA RETADR+1 PHA LDA RETADR PHA * LDA :S1LEN LDY #0 STA (ADDR1),Y LDA :SOVF ROR A ; CARRY = 1 IF OVERFLOW, 0 IF NOT LDX :S3LEN ; RETURN FINAL LENGTH * RTS * ** DATA * :S3LEN DS 1 :S1LEN DS 1 :S1IND DS 1 :S2LEN DS 1 :S2IND DS 1 :MLEN DS 1 :SCNT DS 1 :SOVF DS 1 * *``````````````````````````````* * PRNSTR :: PRINT INDEXED STR * *- -* * PRINT A STRING WITH LENGTH * * THAT PRECEDES START OF STR * *- -* * CLOBBERS: * * * * FLAGS: ????---- REG: AXYM * *- -* * CYCLES: ??? * * SIZE: * *- -* * USAGE: * * * * LDA #>STR ; STRING ADDRESS * * PHA * * LDA #$300 ; DESTINATION * * PHA * * LDA #<$300 * * PHA * * LDA #>11111 ; VALUE TO * * PHA ; CONVERT * * LDA #<11111 * * PHA * * JSR NUM2STR * *- -* * ENTRY * * * * TOP OF STACK * * * * LOW BYTE OF RETURN ADDRESS * * HI BYTE OF RETURN ADDRESS * * LO BYTE DESTINATION ADDRESS * * HI BYTE DESTINATION ADDRESS * * LO BYTE VALUE TO CONVERT * * HI BYTE VALUE TO CONVERT * *- -* * EXIT * * * * TOP OF STACK * * * * LOW BYTE OF RETURN ADDRESS * * HI BYTE OF RETURN ADDRESS * * * * Y = COUNTER; TRASH * * X = COUNTER; TRASH * * A = LOW BYTE OF RET ADDR * *- -* * ADAPTED FROM LEVANTHAL AND * * WINTHROP'S /6502 ASSEMBLY * * LANGUAGE ROUTINES/. * * AS SUCH, THIS MAY NOT FALL * * UNDER THE APACHE 2.0 LICENSE * * AGREEMENT, SINCE THE BOOK * * WAS WRITTEN BEFORE THE * * LICENSE! * *,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* * NUM2STR * ** SAVE RETURN ADDRESS * PLA STA RETADR PLA STA RETADR+1 * ** GET PARAMETERS * PLA STA :VALSTR PLA STA :VALSTR+1 * STA :NGFLAG BPL :GETBP ; BR IF VAL IS POS LDA #0 SEC SBC :VALSTR STA :VALSTR LDA #0 SBC :VALSTR+1 STA :VALSTR+1 * :GETBP PLA STA ADDR1 ; ADDRESS TO STORE STRING PLA STA ADDR1+1 LDA #0 ; SET BUFFER TO EMPTY LDY #0 STA (ADDR1),Y ; BUFFER(0) = 0 * ** CONVERT VAL TO STRING * :CNVERT * ** VALUE = VALUE DIV 10 ** MOD10 = VALUE MOD 10 * LDA #0 STA :MOD10 STA :MOD10+1 LDX #16 CLC ; CLEAR CARRY * :DVLOOP ROL :VALSTR ; SHIFT CARRY INTO DIVBIT 0 ROL :VALSTR+1 ; WHICH WILL BE THE QUOTIENT ROL :MOD10 ; + SHIFT DIV AT SAME TIME ROL :MOD10+1 * ** A,Y = DIVIDEND - DIVISOR * SEC LDA :MOD10 SBC #10 TAY ; SAVE LOWB IN REG Y LDA :MOD10+1 SBC #0 ; SUBTRACT CARRY BCC :DECCNT ; BR IF DEND < DIVISOR STY :MOD10 ; ELSE STA :MOD10+1 ; NXT BIT OF Q IS A ONE AND SET ; DIVIDEND = DEND - DIVISOR :DECCNT DEX BNE :DVLOOP ROL :VALSTR ; SHIFT IN LAST CARRY FOR Q ROL :VALSTR+1 * ** CONCAT NEXT CHAR * :CONCH LDA :MOD10 CLC ADC #$B0 * ** ADC #'0' ; CONVERT 0..9 TO ASCII 0-9 * JSR :CONCAT * ** IF VALUE <> 0 THEN CONTINUE * LDA :VALSTR ORA :VALSTR+1 BNE :CNVERT ; BR IF VALUE != 0 * :EXIT LDA :NGFLAG BPL :POS ; BR IF ORIG VAL POS LDA #'-' ; ELSE JSR :CONCAT ; PUT A MINUS SIGN IN FRONT * :POS * ** RESTORE RETURN ADDRESS * LDA RETADR+1 PHA LDA RETADR PHA * RTS ; RETURN * ******************************** * CONCAT SUBROUTINE ******************************** * :CONCAT PHA ; SAVE CHAR ON STACK * ** MOVE BUFFER RIGHT ONE CHAR * LDY #0 LDA (ADDR1),Y ; GET CURRENT LENGTH TAY BEQ :EXITMR ; BR IF LENGTH=0 * :MVELP LDA (ADDR1),Y ; GET NEXT CHAR INY STA (ADDR1),Y ; STORE IT DEY DEY BNE :MVELP ; CONT UNTIL DONE * :EXITMR PLA ; GET CHAR BACK FROM STACK LDY #1 STA (ADDR1),Y ; STORE THE CHAR LDY #0 LDA (ADDR1),Y ; GET LENGTH BYTE CLC ADC #1 ; INC LENGTH BY ONE STA (ADDR1),Y ; UPDATE LENGTH * RTS * ** DATA * :NGFLAG DS 1 :VALSTR DS 2 :MOD10 DS 2 * *``````````````````````````````* * STR2NUM :: STRING TO NUMBER * *- -* * CONVERTS A STRING TO THE * * EQUIVALENT 16BIT NUMBER. * *- -* * CLOBBERS: * * * * FLAGS: ????---- REG: AXYM * *- -* * CYCLES: ??? * * SIZE: * *- -* * USAGE: * * * * LDA #>$300 ; STRING ADDR * * PHA * * LDA #<$300 * * PHA * * JSR STR2NUM * *- -* * ENTRY * * * * TOP OF STACK * * * * LOW BYTE OF RETURN ADDRESS * * HI BYTE OF RETURN ADDRESS * * LO BYTE OF STRING ADDRESS * * HI BYTE OF STRING ADDRESS * *- -* * EXIT * * * * TOP OF STACK * * * * LOW BYTE OF RETURN ADDRESS * * HI BYTE OF RETURN ADDRESS * * * * Y = HI BYTE OF NUMBER * * X = LO BYTE OF NUMBER * * A = LOW BYTE OF RET ADDR * *- -* * ADAPTED FROM LEVANTHAL AND * * WINTHROP'S /6502 ASSEMBLY * * LANGUAGE ROUTINES/. * * AS SUCH, THIS MAY NOT FALL * * UNDER THE APACHE 2.0 LICENSE * * AGREEMENT, SINCE THE BOOK * * WAS WRITTEN BEFORE THE * * LICENSE! * *,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* * * STR2NUM * ** SAVE RETURN ADDRESS * PLA STA RETADR PLA STA RETADR+1 * ** GET PARAMETERS * PLA STA ADDR1 ; ADRESS OF STRING PLA ; TO CNVERT STA ADDR1+1 * ** INITIALIZE * LDY #0 LDA (ADDR1),Y TAX ; GET LENGITH; TO REGX LDA #1 STA :NINDEX ; INDEX = 1 LDA #0 STA :NACCUM ; ACCUM = 0 STA :NACCUM+1 STA :SNGFLAG ; SIGN IS POSITIVE * ** CHECK THAT BUFFER IS NOT ZERO * TXA BNE :INIT1 ; EXIT WITH ACCUM = 0 ; IF BUFFER IS EMPTY JMP :EREXIT ; ERROR EXIT IF NOTHING ; IN BUFFER :INIT1 LDY :NINDEX LDA (ADDR1),Y CMP #'-' BNE :PLUS ; BR IF NOT - LDA #$0FF STA :SNGFLAG ; ELSE SIGN IS NEGATIVE INC :NINDEX DEX ; DECREMENT COUNT BEQ :EREXIT ; ERROR EXIT IF ONLY ; - IN BUFFER JMP :CNVERT :PLUS CMP #'+' BNE :CHKDIG ; START CONVERSION IF 1ST ; CHARACTER IS NOT A + INC :NINDEX DEX ; DEC COUNT; IGNORE + SIGN BEQ :EREXIT ; ERROR EXIT IF ONLY ; + IN THE BUFFER :CNVERT LDY :NINDEX LDA (ADDR1),Y ; GET NEXT CHAR :CHKDIG CMP #$B0 ; "0" BMI :EREXIT ; ERROR IF NOT A NUMERAL CMP #$BA ; '9'+1; TECHNICALLY : BPL :EREXIT ; ERR IF > 9 (NOT NUMERAL) PHA ; PUSH DIGIT TO STACK * ** VALID DECIMAL DIGIT SO ** ACCUM = ACCUM * 10 ** = * (8+2) ** = (ACCUM * 8) + (ACCUM * 2) * ASL :NACCUM ROL :NACCUM+1 ; TIMES 2 LDA :NACCUM LDY :NACCUM+1 ; SAVE ACCUM * 2 ASL :NACCUM ROL :NACCUM+1 ASL :NACCUM ROL :NACCUM+1 ; TIMES 8 CLC ADC :NACCUM ; SUM WITH * 2 STA :NACCUM TYA ADC :NACCUM+1 STA :NACCUM+1 ; ACCUM=ACCUM * 10 * ** ADD IN THE NEXT DIGIT ** ACCUM = ACCUM + DIGIT * PLA ; GET THE DIGIT NACK SEC SBC #$B0 CLC ; CONVERT STR TO BIN ADC :NACCUM STA :NACCUM BCC :D2B1 ; BRANCH IF NO CARRY TO HBYTE INC :NACCUM+1 ; ELSE INC HIGH BYTE :D2B1 INC :NINDEX ;INC TO NEXT CHARACTER DEX BNE :CNVERT ; CONTINUE CONVERSION LDA :SNGFLAG BPL :OKEXIT ; BR IF VAL IS POSITIVE LDA #0 ; ELSE REPLACE WITH -RESULT SEC SBC :NACCUM STA :NACCUM LDA #0 SBC :NACCUM+1 STA :NACCUM+1 * ** GET THE BINARY VALUE AND RETURN * :OKEXIT CLC BCC :EXIT :EREXIT SEC :EXIT ; Y IS ALREADY LOW BYTE * ** RESTORE RETURN ADDRESS * LDA RETADR+1 PHA LDA RETADR PHA * LDX :NACCUM+1 LDY :NACCUM LDA :NINDEX * RTS * ** DATA * :NACCUM DS 2 :SNGFLAG DS 1 :NINDEX DS 1 * STRINGSX *