JMP COMMONX * *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=* * * * COMMON.LIB * * * * AUTHOR: NATHAN RIGGS * * CONTACT: NATHAN.RIGGS@ * * OUTLOOK.COM * * * * VERSION: 0.2.0 * * DATE: 12-DEC-2018 * * ASSEMBLER: MERLIN 8 PRO * * LICENSE: APACHE 2.0 * * OS: DOS 3.3 * * * * COMMON ASM ROUTINES * * LIBRARY FOR FUNCTIONS SUCH * * AS MEMORY SWAPPING. * * * *------------------------------* * * * LIST OF ROUTINES * * * * MEMFILL : FILL LOCATION WITH * * A SINGLE VALUE. * * MEMMOVE : MOVE A BLOCK OF * * MEMORY FROM ONE * * LOC TO ANOTHER. * * DELAYMS : DELAY FOR A NUMBER * * OF MILLISECONDS. * * THIS DEPENDS ON * * THE ACTUAL SYSTEM * * BEING USED, SO * * ISN'T AS EASILY * * PORTED TO OTHER * * 6502 SIBLINGS. * * ZMSAVE : SAVE STATE OF FREE * * ZERO PAGE LOCATIONS * * AT MEM LOC SPECIFIED * * ZMLOAD : LOAD PREVIOUS SAVED * * ZERO PAGE VALUES * * BACK INTO PROPER * * LOCATION. * * * *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=* * *``````````````````````````````* * MEMFILL :: FILL MEMORY LOC * *- -* * FILLS A BLOCK OF MEMORY WITH * * THE SPECIFIED VALUE; USED * * OFTEN TO CLEAR LARGE BLOCKS. * *- -* * CLOBBERS: * * * * FLAGS: ????---- REG: AXYM * *- -* * CYCLES: ??? * * SIZE: * *- -* * USAGE: * * * * LDA #>$6A00 * * PHA * * LDA #<$6A00 * * PHA * * LDA #>1024 * * PHA * * LDA #<1024 * * PHA * * LDA #0 * * PHA * * JSR MEMFILL * *- -* * ENTRY * * * * TOP OF STACK * * * * LOW BYTE OF RETURN ADDRESS * * HI BYTE OF RETURN ADDRESS * * FILL VALUE FOR MEMORY * * ARRAY SIZE * * LOW BYTE OF STARTING POINT * * HIGH BYTE OF STARTING POINT * *- -* * 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/. * *,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* * MEMFILL * ** SAVE RETURN ADDRESS * PLA STA RETADR PLA STA RETADR+1 * ** GET PARAMETERS * PLA STA :VALUE PLA STA :ARYSZ PLA STA :ARYSZ+1 PLA STA ADDR1 ; ZERO PAGE POINTER PLA ; DEFINED IN STA ADDR1+1 ; DECS * ** FILL WHOLE PAGES FIRST * LDA :VALUE ; GET VAL FOR FILL LDX :ARYSZ+1 ; X=# OF PAGES TO DO BEQ :PARTPG ; BRANCH IF HIGHBYTE OF SZ = 0 LDY #0 :FULLPG STA (ADDR1),Y INY ; INC TO NEXT BYTE BNE :FULLPG ; BRANCH IF NOT DONE W/ PAGE INC ADDR1+1 ; ADVANCE TO NEXT PAGE DEX BNE :FULLPG ; BRANCH IF NOT DONE W/ PAGES * ** DO THE REMAINING PARTIAL PAGE ** REGISTER A STILL CONTAINS VALUE * :PARTPG LDX :ARYSZ ;GET # OF BYTES IN FINAL PAGE BEQ :EXIT ; BRANCH IF LOW BYTE = 0 LDY #0 :PARTLP STA (ADDR1),Y ; STORE VAL INY ; INCREMENT INDEX DEX ; DECREMENT COUNTER BNE :PARTLP ; BRANCH IF NOT DONE :EXIT * ** RESTORE RETURN ADDRESS * LDA RETADR+1 PHA LDA RETADR PHA RTS * ** DATA * :VALUE DS 1 ; FILL VALUE :ARYSZ DS 2 ; ARRAY SIZE * *``````````````````````````````* * MEMMOVE :: MOVE MEM BLOCK * *- -* * MOVES A SPECIFIED BLOCK OF * * MEMORY FROM A SOURCE ADDRESS * * TO A DESTINATION ADDRESS * * RANGE. * *- -* * CLOBBERS: * * * * FLAGS: ????---- REG: AXYM * *- -* * CYCLES: ??? * * SIZE: * *- -* * USAGE: * * * * LDA #>$6A00 * * PHA * * LDA #<$6A00 * * PHA * * LDA #>$7000 ; DEST * * PHA * * LDA #<$7000 * * PHA * * LDA #>1024 * * PHA * * LDA #<1024 * * PHA * * JSR MEMMOVE * *- -* * ENTRY * * * * TOP OF STACK * * * * LOW BYTE OF RETURN ADDRESS * * HI BYTE OF RETURN ADDRESS * * NUMBER OF BYTES TO MOVE * * LOW BYTE OF DESTINATION * * HIGH BYTE OF DESTINATION * * LOW BYTE OF SOURCE ADDRESS * * HIGH BYTE OF SOURCE ADDRESS * *- -* * 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/. * *,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* * MEMMOVE * ** SAVE RETURN ADDRESS * PLA STA RETADR PLA STA RETADR+1 * ** GET PARAMETERS * PLA STA :MVELEN PLA STA :MVELEN+1 PLA STA ADDR2 ; ZERO PAGE POINTER PLA STA ADDR2+1 PLA STA ADDR1 PLA STA ADDR1+1 * ** DETERMINE IF DEST AREA IS ** ABOVE SRC AREA BUT OVERLAPS ** IT. REMEMBER, OVERLAP CAN BE ** MOD 64K. OVERLAP OCCURS IF ** STARTING DEST ADDRESS MINUS ** STARTING SRC ADDRESS (MOD ** 64K) IS LESS THAN NUMBER ** OF BYTES TO MOVE. * LDA ADDR2 ;CALC DEST-SRC SEC SBC ADDR1 TAX LDA ADDR2+1 SBC ADDR1+1 ; MOD 64K AUTOMATIC ; -- DISCARD CARRY TAY TXA ; CMP WITH # OF BYTES TO MOVE CMP :MVELEN TYA SBC :MVELEN+1 BCS :DOLEFT ; BRANCH IF NO OVERLAP * ** DEST AREA IS ABOVE SRC AREA ** BUT OVERLAPS IT. ** MOVE FROM HIGHEST ADDR TO ** AVOID DESTROYING DATA * JSR :MVERHT JMP :EXIT * ** NO PROB DOING ORDINARY MOVE ** STARTING AT LOWEST ADDR * :DOLEFT JSR :MVELEFT :EXIT JMP :MREXIT * ******************************** * SUBROUTINE: MVELEFT * ******************************** * :MVELEFT LDY #0 ; ZERO INDEX LDX :MVELEN+1 ; X=# OF FULL PP TO MOVE BEQ :MLPART ; IF X=0, DO PARTIAL PAGE :MLPAGE LDA (ADDR1),Y STA (ADDR2),Y ;MOVE ONE BYTE INY ; NEXT BYTE BNE :MLPAGE ; CONT UNTIL 256B MOVED INC ADDR1+1 ; ADV TO NEXT SRC PAGE INC ADDR2+1 ; ADV NEXT DEST PAGE DEX ; DEC PAGE COUNT BNE :MLPAGE ; CONT UNTIL ALL FULL ; PAGES ARE MOVED :MLPART LDX :MVELEN ; GET LENGTH OF LAST PAGE BEQ :MLEXIT ; BR IF LENGTH OF LAST ; PAGE = 0 ; REG Y IS 0 :MLLAST LDA (ADDR1),Y STA (ADDR2),Y ; MOVE BYTE INY ; NEXT BYTE DEX ; DEC COUNTER BNE :MLLAST ; CONT UNTIL LAST P DONE :MLEXIT JMP :MREXIT * ******************************** * SUBROUTINE: MVERHT * ******************************** * :MVERHT * ** MOVE THE PARTIAL PAGE FIRST * LDA :MVELEN+1 CLC ADC ADDR1+1 STA ADDR1+1 ;POINT TO LAST P OF SRC LDA :MVELEN+1 CLC ADC ADDR2+1 STA ADDR2+1 ; POINT TO LAST P OF DEST * ** MOVE THE LAST PARTIAL PAGE FIRST * LDY :MVELEN ;GET LENGTH OF LAST PAGE BEQ :MRPAGE ; IF Y=0 DO THE FULL PAGES :MR0 DEY ; BACK UP Y TO NEXT BYTE LDA (ADDR1),Y STA (ADDR2),Y ; MOVE BYTE CPY #0 BNE :MR0 ; BR IF NOT DONE W LAST P :MRPAGE LDX :MVELEN+1 ; GET BYTE OF COUNT AS P CT BEQ :MREXIT ; BR IF HYBYTE = 0 (NO FULL P) :MR1 DEC ADDR1+1 ; BACK UP TO PREV SRC PAGE DEC ADDR2+1 ; AND DEST :MR2 DEY ; BACK UP Y TO NEXT BYTE LDA (ADDR1),Y STA (ADDR2),Y ; MOVE BYTE CPY #0 BNE :MR2 ; BR IF NOT DONE W THIS PAGE DEX BNE :MR1 BR IF NOT ALL PAGES MOVED :MREXIT * ** RESTORE RETURN ADDRESS * LDA RETADR+1 PHA LDA RETADR PHA RTS * ** DATA * :MVELEN DS 2 * *``````````````````````````````* * DELAYMS :: DELAY X MILLISECS * *- -* * DELAYS FOR X NUMBER OF * * MILLISECONDS BY LOOPING * * THROUGH PRECISELY CALCULATED * * NUMBER OF CYCLES. * *- -* * CLOBBERS: * * * * FLAGS: ????---- REG: -XY- * *- -* * CYCLES: ??? * * SIZE: * *- -* * USAGE: * * * * LDY #250 ;1/4 SEC * * JSR DELAYMS * *- -* * ENTRY * * * * Y = NUMBER OF MILLISECONDS * *- -* * EXIT * * * * Y = COUNTER; TRASH * * X = COUNTER; TRASH * * A = UNCHANGED * *- -* * ADAPTED FROM LEVANTHAL AND * * WINTHROP'S /6502 ASSEMBLY * * LANGUAGE ROUTINES/. * *,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* * DELAYMS * MSCNT EQU $0CA ; 202 TIMES THROUGH DELAY1 ; SPECIFIC TO 1.23 MHZ ; SPEED OF APPLE II :DELAY CPY #0 ; 2 CYCLES BEQ :EXIT ; 2C (EXIT IF DEL=0) NOP ; 2 CYCLES (MAKE OVERHEAD=25C) * ** IF DELAY IS 1MS THEN GOTO LAST1 ** THIS LOGIC IS DESIGNED TO BE ** 5 CYCLES THROUGH EITHER ATH * CPY #1 ; 2 CYCLES BNE :DELAYA ; 3C IF TAKEN, ELSE 2C JMP :LAST1 ; 3C * ** DELAY 1 MILLISENCOND TIMES (Y-1) * :DELAYA DEY ; 2C (PREDEC Y) :DELAY0 LDX #MSCNT ; 2C :DELAY1 DEX ; 2C BNE :DELAY1 ; 3C NOP ; 2C NOP ; 2C DEY ; 2C BNE :DELAY0 ; 3C :LAST1 * ** DELAY THE LAST TIME 25 CYCLES ** LESS TO TAKE THE CALL, RETURN, ** AND ROUTINE OVERHEAD INTO ** ACCOUNT. * LDX #MSCNT-3 ; 2C :DELAY2 DEX ; 2C BNE :DELAY2 ; 3C :EXIT RTS ; 6C * * *``````````````````````````````* * ZMSAVE :: SAVE 0-PAGE FREE * *- -* * STORES THE VALUES IN THE * * LITTLE FREE SPACE ON THE * * ZERO PAGE AT AN ALTERNATE * * MEMORY LOCATION TO BE * * RETRIEVED AND RESTORED AT A * * LATER POINT. * *- -* * CLOBBERS: * * * * FLAGS: ????---- REG: AXYM * *- -* * CYCLES: ??? * * SIZE: * *- -* * USAGE: * * * * LDA #>$6A00 * * PHA * * LDA #<$6A00 * * PHA * * JSR ZMSAVE * *- -* * ENTRY * * * * TOP OF STACK * * * * LOW BYTE OF RETURN ADDRESS * * HI BYTE OF RETURN ADDRESS * * LOW BYTE OF DESTINATION * * HIGH BYTE OF DESTINATION * *- -* * EXIT * * * * TOP OF STACK * * * * LOW BYTE OF RETURN ADDRESS * * HI BYTE OF RETURN ADDRESS * * * * Y = TRASH * * X = TRASH * * A = LOW BYTE OF RET ADDR * *,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* * ZMSAVE * ** LOAD ADDR1 ZERO PAGE AREA FIRST, ** SINCE WE'LL BE USING THAT * ** NOTE THAT THIS SHOULD ALWAYS BE ** $06 AND $07, OR ELSE CODE WILL ** HAVE TO CHANGE. * LDX ADDR1 LDY ADDR1+1 * ** SAVE RETURN ADDRESS * PLA STA :RETADR ; LOCAL BECAUSE RETADDR PLA ; IS STORED ON ZERO PAGE STA :RETADR+1 * ** GET PARAMETERS * PLA STA ADDR1 PLA STA ADDR1+1 * ** COPY ZERO PAGE TO SPECIFIED ** NON-ZERO LOCATION (ONLY THE ** FREE ONES..) * TYA ; STORE ADDR1 VALS LDY #1 ; FIRST STA (ADDR1),Y LDY #0 TXA STA (ADDR1),Y LDY #2 LDA $08 STA (ADDR1),Y INY LDA $09 STA (ADDR1),Y INY LDA $19 STA (ADDR1),Y INY LDA $1E STA (ADDR1),Y INY LDA $E3 STA (ADDR1),Y INY LDA $EB STA (ADDR1),Y INY LDA $EC STA (ADDR1),Y INY LDA $ED STA (ADDR1),Y INY LDA $EE STA (ADDR1),Y INY LDA $EF STA (ADDR1),Y INY LDA $FA STA (ADDR1),Y INY LDA $FB STA (ADDR1),Y INY LDA $FC STA (ADDR1),Y INY LDA $FD STA (ADDR1),Y INY LDA $FE STA (ADDR1),Y INY LDA $FF STA (ADDR1),Y * ** RESTORE RETURN ADDRESS * LDA :RETADR+1 PHA LDA :RETADR PHA * RTS * ** DATA * :RETADR DS 2 * *``````````````````````````````* * ZMLOAD :: RESTORE 0-PAGE MEM * *- -* * RESTORES PREVIOUSLY SAVED * * ZERO PAGE VALUES FROM * * HIGHER MEMORY LOCATION. * * CLOBBERS: * * * * FLAGS: ????---- REG: AXYM * *- -* * CYCLES: ??? * * SIZE: * *- -* * USAGE: * * * * LDA #>$6A00 * * PHA * * LDA #<$6A00 * * PHA * * JSR ZMLOAD * *- -* * ENTRY * * * * TOP OF STACK * * * * LOW BYTE OF RETURN ADDRESS * * HI BYTE OF RETURN ADDRESS * * LOW BYTE OF SOURCE * * HIGH BYTE OF SOURCE * *- -* * EXIT * * * * TOP OF STACK * * * * LOW BYTE OF RETURN ADDRESS * * HI BYTE OF RETURN ADDRESS * * * *,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* * ZMLOAD * ** SAVE RETURN ADDRESS * PLA STA :RETADR PLA STA :RETADR+1 * ** GET PARAMETERS * PLA STA ADDR1 PLA STA ADDR1+1 * ** COPY ZERO PAGE TO SPECIFIED ** NON-ZERO LOCATION (ONLY THE ** FREE ONES..) * LDY #2 ; START 2 AHEAD LDA (ADDR1),Y ; AND PUT ADDR1 STA $08 ; AREA LAST INY LDA (ADDR1),Y STA $09 INY LDA (ADDR1),Y STA $19 INY LDA (ADDR1),Y STA $1E INY LDA (ADDR1),Y STA $E3 INY LDA (ADDR1),Y STA $EB INY LDA (ADDR1),Y STA $EC INY LDA (ADDR1),Y STA $ED INY LDA (ADDR1),Y STA $EE INY LDA (ADDR1),Y STA $EF INY LDA (ADDR1),Y STA $FA INY LDA (ADDR1),Y STA $FB INY LDA (ADDR1),Y STA $FC INY LDA (ADDR1),Y STA $FD INY LDA (ADDR1),Y STA $FE INY LDA (ADDR1),Y STA $FF * ** NOW GET ZERO PAGE VALUES ** FOR SPACE OCCUPIED BY ADDR; RETURN IN X,Y * LDY #0 LDA (ADDR1),Y TAX LDA (ADDR1+1),Y TAY STX ADDR1 STY ADDR1+1 * ** RESTORE RETURN ADDRESS * LDA :RETADR+1 PHA LDA :RETADR PHA * RTS * ** DATA * :RETADR DS 2 * *``````````````````````````````* * MEMSWAP :: MEMORY SWAP * *- -* * SWAPS THE VALUES STORED IN * * TWO BLOCKS OF SEQUENTIAL * * MEMORY. * *- -* * CLOBBERS: * * * * FLAGS: ????---- REG: AXYM * *- -* * CYCLES: ??? * * SIZE: * *- -* * USAGE: * * * * LDA #>$6A00 * * PHA * * LDA #<$6A00 * * PHA * * LDA #>$300 * * PHA * * LDA #<$300 * * PHA * * LDA #$100 * * PHA * * JSR MEMSWAP * *- -* * ENTRY * * * * TOP OF STACK * * * * LOW BYTE OF RETURN ADDRESS * * HI BYTE OF RETURN ADDRESS * * LOW BYTE OF FIRST ADDRESS * * HIGH BYTE OF FIRST ADDRESS * * LOBYTE OF SECOND ADDRESS * * HIBYTE OF SECOND ADDRESS * *- -* * EXIT * * * * TOP OF STACK * * * * LOW BYTE OF RETURN ADDRESS * * HI BYTE OF RETURN ADDRESS * * * *,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,* * MEMSWAP * ** GET RETURN ADDRESS * PLA STA RETADR PLA STA RETADR+1 * ** GET VARIABLES * PLA STA :LENGTH PLA STA ADDR1 PLA STA ADDR1+1 PLA STA ADDR2 PLA STA ADDR2+1 * LDY #255 ; COUNTER :LP INY LDA (ADDR1),Y TAX ; X CONTAINS ADDR1 VAL LDA (ADDR2),Y ; A CONTAINS ADDR2 VAL STA (ADDR1),Y TXA STA (ADDR2),Y CPY :LENGTH BNE :LP * ** RESTORE RETURN ADDRESS * LDA RETADR+1 PHA LDA RETADR PHA * RTS * ** VARIABLES * :LENGTH DS 1 * COMMONX RTS