AppleIIAsm-Collection/source/d1_required/T.LIB.REQUIRED.ASM

504 lines
18 KiB
NASM
Raw Normal View History

*
*``````````````````````````````*
* LIB.REQUIRED *
* *
* LIBRARY OF REQUIRED ROUTINES *
* AS PART OF THE APPLEIIASM *
* MACRO AND SUBROUTINE LIBRARY *
* *
* AUTHOR: NATHAN RIGGS *
* CONTACT: NATHAN.RIGGS@ *
* OUTLOOK.COM *
* *
* DATE: 28-NOV-2019 *
* ASSEMBLER: MERLIN 8 PRO *
* LICENSE: APACHE 2.0 *
* OS: DOS 3.3 *
* *
* SUBROUTINES: *
* *
* __DUMP : DUMP MEMORY *
* __ERRH : HANDLE ERRORS *
* __GETRET : GET RETURN VAL *
* __P : PRINT *
* DELAYMS : DELAY MLSECS *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
** LIBRARY-SPECIFIC VARIABLES
*
]RIGHT DS 1
]LEFT DS 1
]LENGTH DS 1
]A DS 1 ; REGISTER .A BACKUP
]X DS 1 ; REGISTER .X BACKUP
]Y DS 1 ; REGISTER .Y BACKUP
]C DS 1 ; CARRY FLAG BACKUP
]Z DS 1 ; ZERO FLAG BACKUP
]N DS 1 ; NEGATIVE FLAG BACKUP
]O DS 1 ; OVERFLOW FLAG BACKUP
]HEXTAB ASC "0123456789ABCDEF"
*
** LIBRARY-SPECIFIC HOOKS
*
]COUT EQU $FDF0 ; SCREEN OUTPUT ROUTINE
]KYBD EQU $C000 ; KEYBOARD INPUT
]STROBE EQU $C010 ; KEYBOARD STROBE
*
*
*``````````````````````````````*
* __DUMP: (NATHAN RIGGS) *
* *
* INPUT: *
* *
* .A = ADDRESS LOBYTE *
* .X = ADDRESS HIBYTE *
* .Y = NUMBER OF BYTES *
* *
* OUTPUT: *
* *
* OUTPUTS DATA LOCATED AT THE *
* SPECIFIED ADDRESS IN HEX *
* FORMAT FOR SPECIFIED NUMBER *
* OF BYTES. *
* *
* DESTROYS: NZCIDV *
* ^^^ *
* *
* CYCLES: 787+ *
* SIZE: 111 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
__DUMP
STY ]LENGTH ; ++<4C3B> LENGTH PASSED IN .Y
STA ADDR1 ; ++<3C2B> ADDRESS LOBYTE IN .A
STX ADDR1+1 ; ++<3C2B> ADDRESS HIBYTE IN .X
LDA #$8D ; ++<2C2B> LOAD CARRIAGE RETURN
JSR ]COUT ; ++<6C3B[83C]> SEND TO COUT
LDA ADDR1+1 ; ++<2C2B> GET ADDRESS HIBYTE
CLRHI ; ++<6C3B[16C]> CLEAR HIBITS
TAX ; ++<2C1B> TRANSFER TO .X
LDA ]HEXTAB,X ; ++<5C3B> LOAD HEX CHAR FROM TABLE AT .X
JSR ]COUT ; ++<6C3B[83C]> SEND TO COUT
LDA ADDR1+1 ; ++<2C2B> LOAD ADDRESS HIBYTE AGAIN
AND #$0F ; ++<2C2B> CLEAR LOBITS
TAX ; ++<2C1B> TRANSER TO .X
LDA ]HEXTAB,X ; ++<5C3B> LOAD HEX CHAR FROM TABLE AT .X
JSR ]COUT ; ++<6C3B[83C]> SENT TO COUT
LDA ADDR1 ; ++<4C3B> LOAD LOBYTE
CLRHI ; ++<[16C10B]> CLEAR HIBITS
TAX ; ++<2C1B> TRANSFER TO .X
LDA ]HEXTAB,X ; ++<5C3B> LOAD HEXCHAR AT .X
JSR ]COUT ; ++<6C3B[83C]> SEND TO COUT
LDA ADDR1 ; ++<3C2B> LOAD LOBYTE AGAIN
AND #$0F ; ++<2C2B> CLEAR LOBITS
TAX ; ++<2C1B> TRANSFER T .X
LDA ]HEXTAB,X ; ++<[16C]> LOAD HEXCHAR AT .X
JSR ]COUT ; ++<6C3B[83C]> SEND TO COUT
LDA #":" ; ++<2C2B>
JSR ]COUT ; ++<6C3B[83C]>SEND COLON TO COUT
LDA "#" ; ++<2C2B>
JSR ]COUT ; ++<6C3B[83C]> SEND SPACE TO COUT
LDY #0 ; ++<2C2B> RESET COUNTER
:LP
LDA (ADDR1),Y ; ++<6C2B> LOAD BYTE FROM ADDRESS
CLRHI ; ++<[16C10B] AT COUNTER OFFSET; CLEAR HIBITS
STA ]LEFT ; ++<4C3B> SAVE LEFT INDEX
LDA (ADDR1),Y ; ++<6C2B> RELOAD
AND #$0F ; ++<2C2B> CLEAR LOBITS
STA ]RIGHT ; ++<4C3B> SAVE RIGHT INDEX
LDX ]LEFT ; ++<4C3B> LOAD LEFT INDEX
LDA ]HEXTAB,X ; ++<5C3B> GET NIBBLE CHAR
JSR ]COUT ; ++<6C3B[83C]> SEND TO COUT
LDX ]RIGHT ; ++<4C3B> LOAD RIGHT INDEX
LDA ]HEXTAB,X ; ++<5C3B> GET NIBBLE CHAR
JSR ]COUT ; ++<6C3B[83C]> SEND TO COUT
LDA #160 ; ++<4C3B> LOAD SPACE
JSR ]COUT ; ++<6C3B[83C]> SEND TO COUT
INY ; ++<2C1B> INCREASE COUNTER
CPY ]LENGTH ; ++<4C3B> IF COUNTER < LENGTH
BNE :LP ; ++<4C2B> CONTINUE LOOP
RTS ; ++<6C1B> ELSE, EXIT
*
*``````````````````````````````*
* __ERRH (NATHAN RIGGS) *
* *
* INPUT: *
* *
* .A = ADDRESS LOBYTE *
* .X = ADDRESS HIBYTE *
* *
* OUTPUT: *
* *
* SETS NEW ADDRESS FOR THE *
* APPLSOFT ERROR HANDLING *
* ROUTINE. *
* *
* DESTROYS: NZCIDV *
* ^^^ *
* *
* CYCLES: 53 *
* SIZE: 32 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
__ERRH
LDA #1 ; ++<2C2B> TRICK DOS INTO THINKING
STA $AAB6 ; ++<4C3B> IT'S IN APPLESOFT MODE
STA $75+1 ; ++<3C2B> APPLESOFT LINE NUMBER POINTER
STA $33 ; ++<3C2B> APPLESOFT PROMPT CHARACTER
STA ADDR1 ; ++<3C2B> ADDRESS LOBYTE IN .A
STX ADDR1+1 ; ++<3C2B> ADDRESS HIBYTE IN .X
LDA #$FF ; ++<2C2B> TURN ON ERROR HANDLING
STA $D8 ; ++<3C3B> BYTE HERE
LDY #0 ; ++<2C2B> CLEAR OFFSET
LDA (ADDR1),Y ; ++<6C2B> LOAD ADDRESS LOBYTE
STA $9D5A ; ++<4C3B> SET AS ERROR HANDLING LO
INY ; ++<2C1B> INCREASE OFFSET
LDA (ADDR1),Y ; ++<6C2B> LOAD ADDRESS HIBYTE
STA $9D5B ; ++<4C3B> SET AS ERROR HANDLING HI
RTS ; ++<6C1B> EXIT SUBROUTINE
*
*``````````````````````````````*
* __P: (NATHAN RIGGS) *
* *
* INPUT: *
* *
* ASC STRING FOLLOWING CALL *
* TERMINATED WITH A 00 BYTE *
* *
* OUTPUT: *
* *
* CONTENTS OF STRING. *
* *
* DESTROYS: NZCIDV *
* ^^^ ^ *
* *
* CYCLES: 72+(155+ WITH COUT) *
* SIZE: 35 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
__P
PLA ; ++<4C1B> PULL RETURN LOBYTE
STA ADDR1 ; ++<3C2B> STORE TO ZERO PAGE
PLA ; ++<4C1B> PULL RETURN HIBYTE
STA ADDR1+1 ; ++<3C2B> STORE TO ZERO PAGE --3C,2B
LDY #1 ; ++<2C2B> SET OFFSET TO PLUS ONE
:LP LDA (ADDR1),Y ; ++<6C2B> LOAD BYTE AT OFFSET .Y
BEQ :DONE ; ++<4C2B> IF BYTE = 0, QUIT
JSR ]COUT ; ++<6C3B[83C]> OTHERWISE, PRINT BYTE
INY ; ++<2C1B> INCREASE OFFSET
BNE :LP ; ++<4C2B> IF .Y <> 0, CONTINUE LOOP
:DONE CLC ; ++<2C1B> CLEAR CARRY FLAG
TYA ; ++<2C1B> TRANSFER OFFSET TO .A
ADC ADDR1 ; ++<3C2B> ADD OFFSET TO RETURN ADDRESS
STA ADDR1 ; ++<4C2B> STORE TO RETURN ADDRESS LOBYTE
LDA ADDR1+1 ; ++<4C2B> DO THE SAME WITH THE HIBYTE
ADC #0 ; ++<3C2B> CARRY NOT RESET, SO INC HIBYTE
PHA ; ++<3C1B> IF NEEDED; THEN, PUSH HIBYTE
LDA ADDR1 ; ++<4C2B> LOAD LOBYTE
PHA ; ++<3C1B> PUSH LOBYTE
RTS ; ++<6C1B> EXIT
*
*``````````````````````````````*
* DELAYMS (LEVENTHAL/SEVILLE) *
* *
* ADAPTED FROM LEVANTHAL AND *
* SEVILLE'S /6502 ASSEMBLY *
* LANGUAGE ROUTINES/. *
* *
* INPUT: *
* *
* .Y = NUMBER OF MILLISECS *
* *
* OUTPUT: *
* *
* DELAYS FOR X NUMBER OF *
* MILLISECONDS BY LOOPING *
* THROUGH A PRECISE NUMBER *
* OF CYCLES. *
* *
* DESTROYS: AXYNVBDIZCMS *
* ^^^^ ^^^ *
* *
* CYCLES: 39+ *
* SIZE: 29 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
DELAYMS
*
]MSCNT EQU $0CA ; LOOP 202 TIMES THROUGH DELAY1
; SPECIFIC TO 1.23 MHZ
; SPEED OF APPLE II
:DELAY
CPY #0 ; ++<2C2B> IF Y = 0, THEN EXIT
BEQ :EXIT ; ++<2C|3C2B>
NOP ; ++<2C1B> (MAKE OVERHEAD=25C)
*
** IF DELAY IS 1MS THEN GOTO LAST1
** THIS LOGIC IS DESIGNED TO BE
** 5 CYCLES THROUGH EITHER ATH
*
CPY #1 ; ++<2C2B> USE 2 CYCLES
BNE :DELAYA ; ++<2C|3C2B> 3C IF TAKEN, ELSE 2C
JMP :LAST1 ; ++<3C3B>
*
** DELAY 1 MILLISENCOND TIMES (Y-1)
*
:DELAYA
DEY ; ++<2C1B> (PREDEC Y)
:DELAY0
LDX #]MSCNT ; ++<2C2B>
:DELAY1
DEX ; ++<2C1B>
BNE :DELAY1 ; ++<3C2B>
NOP ; ++<2C1B>
NOP ; ++<2C1B>
DEY ; ++<2C1B>
BNE :DELAY0 ; ++<3C2B>
:LAST1
*
** DELAY THE LAST TIME 25 CYCLES
** LESS TO TAKE THE CALL, RETURN,
** AND ROUTINE OVERHEAD INTO
** ACCOUNT.
*
LDX #]MSCNT-3 ; ++<2C2B>
:DELAY2
DEX ; ++<2C1B>
BNE :DELAY2 ; ++<3C2B>
:EXIT
RTS ; ++<6C1B>
*
*``````````````````````````````*
* MEMFILL (LEVENTHAL/SAVILLE) *
* *
* ADAPTED FROM LEVANTHAL AND *
* SAVILLE'S /6502 ASSEMBLY *
* LANGUAGE ROUTINES/. FILLS A *
* SPECIFIED RANGE OF MEMORY *
* WITH A FILL VALUE. *
* *
* INPUT: *
* *
* BPAR1 = FILL VALUE *
* WPAR2 = FILL LENGTH *
* WPAR3 = STARTING ADDRESS *
* *
* DESTROYS: NZCIDV *
* ^^ *
* *
* CYCLES: 59+ *
* SIZE: 31 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]FILL EQU BPAR1 ; FILL VALUE
]SIZE EQU WPAR2 ; RANGE LENGTH IN BYTES
]ADDR EQU WPAR1 ; RANGE STARTING ADDRESS
*
MEMFILL
*
** FILL WHOLE PAGES FIRST
*
LDA ]FILL ; ++<3C2B> GET VAL FOR FILL
LDX ]SIZE+1 ; ++<3C2B> X=# OF PAGES TO DO
BEQ :PARTPG ; ++<3C2B> BRANCH IF HIGHBYTE OF SZ = 0
LDY #0 ; ++<2C2B> RESET INDEX
:FULLPG
STA (]ADDR),Y ; ++<6C2B> FILL CURRENT BYTE
INY ; ++<2C1B> INCREMENT INDEX
BNE :FULLPG ; ++<3C2B> BRANCH IF NOT DONE W/ PAGE
INC ]ADDR+1 ; ++<5C2B> ADVANCE TO NEXT PAGE
DEX ; ++<2C1B> DECREMENT COUNTER
BNE :FULLPG ; ++<3C2B> BRANCH IF NOT DONE W/ PAGES
*
** DO THE REMAINING PARTIAL PAGE
** REGISTER A STILL CONTAINS VALUE
*
:PARTPG
LDX ]SIZE ; ++<3C2B> GET # OF BYTES IN LAST PAGE
BEQ :EXIT ; ++<3C2B> BRANCH IF LOW BYTE = 0
LDY #0 ; ++<2C2B> RESET INDEX
:PARTLP
STA (]ADDR),Y ; ++<6C2B> STORE VAL
INY ; ++<2C1B> INCREMENT INDEX
DEX ; ++<2C1B> DECREMENT COUNTER
BNE :PARTLP ; ++<3C2B> BRANCH IF NOT DONE
:EXIT
RTS ; ++<6C1B>
*``````````````````````````````*
* MEMMOVE (LEVENTHAL/SEVILLE) *
* *
* ADAPTED FROM LEVANTHAL AND *
* SEVILLE'S /6502 ASSEMBLY *
* LANGUAGE ROUTINES/. COPIES *
* SERIES OF BYTES FROM SRCLOC *
* TO DESTLOC. *
* *
* INPUT: *
* *
* WPAR3 = LENGTH IN BYTES *
* WPAR1 = SOURCE ADDRESS *
* WPAR2 = DESTINATION ADDRESS *
* *
* DESTROY: NZCIDV *
* *
* *
* CYCLES: 207+ *
* BYTES: 117 *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]SIZE EQU WPAR3 ; LENGTH TO COPY (BYTES)
]ADDR1 EQU WPAR1 ; SOURCE ADDRESS
]ADDR2 EQU WPAR2 ; DESTINATION ADDRESS
*
MEMMOVE
*
** 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 ; ++<3C2B> CALC DEST-SRC
SEC ; ++<2C1B> SET CARRY
SBC ]ADDR1 ; ++<3C2B> SUBTRACT SOURCE ADDRESS
TAX ; ++<2C1B> HOLD VAL IN .X
LDA ]ADDR2+1 ; ++<3C2B>
SBC ]ADDR1+1 ; ++<3C2B> MOD 64K AUTOMATIC
; -- DISCARD CARRY
TAY ; ++<2C1B> HOLD HIBYTE IN .Y
TXA ; ++<2C1B> CMP LOBYTE WITH # TO MOVE
CMP ]SIZE ; ++<3C2B>
TYA ; ++<2C1B>
SBC ]SIZE+1 ; ++<3C2B> SUBTRACT SIZE+1 FROM HIBYTE
BCS :DOLEFT ; ++<3C2B> BRANCH IF NO OVERLAP
*
** DEST AREA IS ABOVE SRC AREA
** BUT OVERLAPS IT.
** MOVE FROM HIGHEST ADDR TO
** AVOID DESTROYING DATA
*
JSR :MVERHT ; ++<3C6B>
JMP :MREXIT ; ++<3C3B>
*
** NO PROB DOING ORDINARY MOVE
** STARTING AT LOWEST ADDR
*
:DOLEFT
JSR :MVELEFT ; ++<6C3B>
:EXIT
JMP :MREXIT ; ++<3C3B>
:MVELEFT
LDY #0 ; ++<2C2B> ZERO INDEX
LDX ]SIZE+1 ; ++<3C2B> X=# OF FULL PP TO MOVE
BEQ :MLPART ; ++<3C2B> IF X=0, DO PARTIAL PAGE
:MLPAGE
LDA (]ADDR1),Y ; ++<6C2B> LOAD BYTE FROM SOURCE
STA (]ADDR2),Y ; ++<6C2B> MOVE BYTE TO DESTINATION
INY ; ++<2C1B> NEXT BYTE
BNE :MLPAGE ; ++<3C2B> CONT UNTIL 256B MOVED
INC ]ADDR1+1 ; ++<5C2B> ADV TO NEXT SRC PAGE
INC ]ADDR2+1 ; ++<5C2B> ADV NEXT DEST PAGE
DEX ; ++<2C1B> DEC PAGE COUNT
BNE :MLPAGE ; ++<3C2B> CONT UNTIL ALL FULL
; PAGES ARE MOVED
:MLPART
LDX ]SIZE ; ++<3C2B> GET LENGTH OF LAST PAGE
BEQ :MLEXIT ; ++<3C2B> BR IF LENGTH OF LAST
; PAGE = 0
; REG Y IS 0
:MLLAST
LDA (]ADDR1),Y ; ++<6C2B> LOAD BYTE FROM SOURCE
STA (]ADDR2),Y ; ++<6C2B> MOVE BYTE TO DESTINATION
INY ; ++<2C1B> NEXT BYTE
DEX ; ++<2C1B> DEC COUNTER
BNE :MLLAST ; ++<3C2B> CONT UNTIL LAST P DONE
:MLEXIT
JMP :MREXIT ; ++<3C3B>
*
********************************
*
:MVERHT
*
** -- MOVE THE PARTIAL PAGE FIRST
*
LDA ]SIZE+1 ; ++<3C2B> GET SIZE HIBYTE
CLC ; ++<2C1B> CLEAR CARRY
ADC ]ADDR1+1 ; ++<3C2B> ADD SOURCE ADDRESS HIBYTE
STA ]ADDR1+1 ; ++<3C2B> POINT TO LAST PAGE OF SRC
LDA ]SIZE+1 ; ++<3C2B> GET SIZE HIBYTE
CLC ; ++<2C1B> CLEAR CARRY
ADC ]ADDR2+1 ; ++<3C2B> ADD DESTINATION HIBYTE
STA ]ADDR2+1 ; ++<3C2B> POINT TO LAST P OF DEST
*
** MOVE THE LAST PARTIAL PAGE FIRST
*
LDY ]SIZE ; ++<3C2B> GET LENGTH OF LAST PAGE
BEQ :MRPAGE ; ++<3C2B> IF Y=0 DO THE FULL PAGES
:MR0
DEY ; ++<2C1B> BACK UP Y TO NEXT BYTE
LDA (]ADDR1),Y ; ++<6C2B> LOAD CURRENT SOURCE BYTE
STA (]ADDR2),Y ; ++<6C2B> STORE IN CURRENT DESTINATION
CPY #0 ; ++<2C2B> BRANCH IF NOT DONE
BNE :MR0 ; ++<3C2B> WITH THE LAST PAGE
:MRPAGE
LDX ]SIZE+1 ; ++<3C2B> GET SIZE HIBYTE
BEQ :MREXIT ; ++<3C2B> BR IF HYBYTE = 0 (NO FULL P)
:MR1
DEC ]ADDR1+1 ; ++<5C2B> BACK UP TO PREV SRC PAGE
DEC ]ADDR2+1 ; ++<5C2B> TO DEST
:MR2
DEY ; ++<2C1B> BACK UP Y TO NEXT BYTE
LDA (]ADDR1),Y ; ++<6C2B> LOAD SOURCE CURRENT BYTE
STA (]ADDR2),Y ; ++<6C2B> STORE BYTE IN DESTINATION
CPY #0 ; ++<2C2B> IF NOT DONE WITH PAGE
BNE :MR2 ; ++<3C2B> THEN BRANCH OUT
DEX ; ++<2C1B> DECREASE BYTE COUNTER
BNE :MR1 ; ++<3C2B> BR IF NOT ALL PAGES MOVED
:MREXIT
RTS ; ++<6C1B>
*
*``````````````````````````````*
* MEMSWAP (NATHAN RIGGS) *
* *
* INPUT: *
* *
* BPAR1 = SWAP LENGTH *
* WPAR1 = 1ST ADDRESS *
* WPAR2 = 2ND ADDRESS *
* *
* OUTPUT: *
* *
* SWAPS THE VALUES IN THE *
* MEMORY LOCATIONS GIVEN *
* FOR THE SPECIFIED LENGTH. *
* *
* DESTROYS: NZCIDV *
* ^^^ *
* *
* CYCLES: 46+ *
* SIZE: 18 BYTES *
*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,*
*
]SIZE EQU BPAR1 ; SIZE OF RANGE TO SWAP
]ADDR1 EQU WPAR1 ; SOURCE ADDRESS 1
]ADDR2 EQU WPAR2 ; SOURCE ADDRESS 2
*
MEMSWAP
LDY #255 ; ++<2C2B> RESET BYTE INDEX
:LP
INY ; ++<2C1B> INCREASE BYTE INDEX
LDA (]ADDR1),Y ; ++<6C2B> LOAD BYTE FROM FIRST ADDRESS
TAX ; ++<2C1B> TRANSFER TO .X
LDA (]ADDR2),Y ; ++<6C2B> LOAD BYTE FROM SECOND ADDRESS
STA (]ADDR1),Y ; ++<6C2B> STORE IN FIRST ADDRESS
TXA ; ++<2C1B> TRANSFER FIRST BYTE VAL TO .A
STA (]ADDR2),Y ; ++<6C2B> NOW STORE THAT IN SECOND ADDRESS
CPY ]SIZE ; ++<3C2B> IF BYTE INDEX < LENGTH,
BNE :LP ; ++<3C2B> CONTINUE LOOPING
RTS ; ++<6C1B> OTHERWISE, EXIT
*
RTS ; ++<6C1B>