* 
* Custom kernal routines, from Blahtune.  (Ignore the ENTs)
*


*-------------------------------
*
* My own little kernal routines -- useful utility
* routines.
*
* INPUT -- Read string
* KERPLOP
* PLOPXY
* PLOP -- Fast screen (text) plotting routines
* CHAROUT -- PLOPs a character to the screen
* PRINT -- Prints a string using CHROUT
* STROUT -- Prints a string immediately following call
* PRINPLOP -- Like PRINT but uses PLOP
* PLOPSTR -- PLOPs a string immediately following call
* MULT16 -- 16-bit integer multiply routine
* DIV16 -- 16-bit divide
* ASCTONUM -- Convert string to 16-bit number
* PLOPNUM -- PLOPs 16-bit number to screen
* PHBYTE -- PLOP two digit hex byte
* BLMOVE -- Block memory move
*

SCREENP  = $FB

READST   = $FFB7
SETLFS   = $FFBA
SETNAM   = $FFBD
OPEN     = $FFC0
CLOSE    = $FFC3
CHKIN    = $FFC6
CHKOUT   = $FFC9
CLRCHN   = $FFCC
CHRIN    = $FFCF
CHROUT   = $FFD2
LOAD     = $FFD5
SAVE     = $FFD8
STOP     = $FFE1
GETIN    = $FFE4
CLALL    = $FFE7

FAQ2     = $69
ACC      = FAQ2
AUX      = ACC+2
EXT      = AUX+2
BLNSW    = $CC
BLNON    = $CF
BLNCT    = $CD

*
* INPUT inputs a string of data into STRBUF.  The maximum
* number of chars to be read are passed in X.  On output,
* zero set means the input was aborted with R/S or is
* empty.  .A (and STRLEN) contains the string length.
*
STRBUF   EQU $0200
STRLEN   ENT
         DFB 00
MAXLEN   DFB 00

INPUT    ENT
         STX MAXLEN
         LDA #00
         STA STRLEN
         STA STRBUF
         LDA $0286        ;Set blink color to
         STA $0287        ;current color.

:LOOP    LDA #00
         STA BLNSW        ;Blink
:WAIT    JSR STOP
         BEQ :RTS
         JSR GETIN
         BEQ :WAIT
         LDY #1
         STY BLNCT
:WAIT2   LDY BLNON
         BNE :WAIT2
         INC BLNSW

         CMP #160         ;Alphanumeric characters
         BCS :NORMAL
         CMP #128
         BCS :LOOP
         CMP #13
         BEQ :DONE
         CMP #20
         BEQ :DEL
         CMP #32
         BCC :LOOP
:NORMAL  
         LDY STRLEN
         CPY MAXLEN
         BCS :LOOP
         STA STRBUF,Y
         JSR CHROUT
         INC STRLEN
         BNE :LOOP

:DEL     LDY STRLEN
         BEQ :LOOP
         JSR CHROUT
         DEC STRLEN
         BPL :LOOP

:DONE    LDY STRLEN
         LDA #00
         STA STRBUF,Y
         TYA              ;Z set if string empty
:RTS     RTS


*
* PLOP
*   Stores the value in A to the screen at the point
*   (ROW,COLUMN) and the color in COLOR to the
*   colormap.  If entered at PLOPXY, ROW is set to X and
*   column is set to Y.  If entered at KERPLOP, char
*   is stored to the kerrent screen pointer.
*   On exit, A X and Y are preserved; carry set indicates
*   an error, i.e. row/col out of range.
*
ROW      ENT
         DFB 0            ;Current row,column
COLUMN   ENT
         DFB 0
COLOR    ENT
         DFB 0

PLOPXY   ENT              ;Set Y=column, X=row
         CPX #25
         BCS PLOPRTS
         CPY #40
         BCS PLOPRTS
         STY COLUMN
         STX ROW
PLOP     ENT
         PHA
         LDA ROW          ;We need 40*ROW
         ASL
         ASL
         ADC ROW          ;5*ROW
         STA SCREENP
         LDA #00          ;A=high byte
         ASL SCREENP      ;Mult by 8
         ROL
         ASL SCREENP
         ROL
         ASL SCREENP
         ROL
         ADC #$04         ;Add 1024
         STA SCREENP+1    ;SCREENP now points to screen
         PLA
PLOPRTS  RTS
KTEMP    DS 1

STAXY    ENT
         JSR PLOPXY       ;plop to X,Y
KERPLOP  ENT              ;PLOP to kerrent screen loc.
         STY KTEMP
         LDY COLUMN
         STA (SCREENP),Y
         LDA SCREENP+1
         CLC
         ADC #$D4         ;Color mem at $D800
         STA SCREENP+1
         LDA COLOR
         STA (SCREENP),Y
         LDA SCREENP+1    ;Restore SCREENP to point to
         SEC              ;screen
         SBC #$D4
         STA SCREENP+1
         CLC              ;Signal all is well.
         LDA (SCREENP),Y  ;restore A
         LDY KTEMP
         RTS

*
* CHAROUT
*   Sticks a CHR$ character to the screen at the current
*   row,column and advances the column.
*   All registers are preserved.
*
CHAROUT  ENT
         EOR #$E0         ;Convert to screen char
         CLC
         ADC #32
         BPL :CONT
         ADC #64
         BPL :CONT
         EOR #$A0
:CONT    JSR KERPLOP
         INC COLUMN
         RTS

*
* PRINT
*   As the name suggests, this prints a string to the
*   screen, where the address of the string is
*   contained in (.X,.A) = (lo,hi).  The string is
*   assumed to be null-terminated.
*
*   On exit, the location of the last character read
*   (the null-terminator hopefully) is returned in
*   (.X,.A) = (lo,hi).  Y is preserved.
*
PRINT    ENT
         STA :LOOP+2
:LOOP    LDA $A000,X
         BEQ :DONE
         JSR CHROUT
         INX
         BNE :LOOP
         INC :LOOP+2
         BNE :LOOP
:DONE    
         LDA :LOOP+2
         RTS

*
* STROUT
*   Like PRINT, but the string immediately follows the
*   subroutine call; execution resumes right after the
*   null-termination byte.
*
*   On exit, A and X are thoroughly hosed.
*
STROUT   ENT
         PLA
         TAX              ;Lo byte
         PLA
         INX
         BNE :CONT
         CLC
         ADC #01
:CONT    JSR PRINT
         PHA              ;lo byte
         TXA
         PHA              ;hi byte
         RTS

*
* Like PRINT, but uses PLOP.
*
PRINPLOP ENT
         STA :LOOP+2
:LOOP    LDA $A000,X
         BEQ :DONE
         JSR CHAROUT
         INX
         BNE :LOOP
         INC :LOOP+2
         BNE :LOOP
:DONE    
         LDA :LOOP+2
         RTS
*
* PLOPSTR
*   Like STROUT, but uses PLOP
*
TEMPA    DFB 00
TEMPX    DFB 00
PLOPSTR  ENT
         STA TEMPA
         STX TEMPX
         PLA
         TAX              ;Lo byte
         PLA
         INX
         BNE :CONT
         CLC
         ADC #01
:CONT    JSR PRINPLOP
         PHA              ;lo byte
         TXA
         PHA              ;hi byte
         LDX TEMPX
         LDA TEMPA
         RTS

*-------------------------------------
* 16 bit multiply and divide routines.
* Three 16 bit (two-byte) locations
* ACC, AUX and EXT must be set up,
* preferably on zero page.
*-------------------------------------

* MULTIPLY ROUTINE

* ACC*AUX -> [ACC,EXT] (low,hi) 32 bit result

MULT16   ENT
         LDA #0
         STA EXT+1
         LDY #$11
]LOOP    LSR EXT+1
         ROR
         ROR ACC+1
         ROR ACC
         BCC MUL2
         CLC
         ADC AUX
         PHA
         LDA AUX+1
         ADC EXT+1
         STA EXT+1
         PLA
MUL2     DEY
         BNE ]LOOP
         STA EXT
         RTS

* DIVIDE ROUTINE

* ACC/AUX -> ACC, remainder in EXT

DIV16    ENT
         LDA #0
         STA EXT+1
         LDY #$10
]LOOP    ASL ACC
         ROL ACC+1
         ROL
         ROL EXT+1
         PHA
         CMP AUX
         LDA EXT+1
         SBC AUX+1
         BCC DIV2
         STA EXT+1
         PLA
         SBC AUX
         PHA
         INC ACC
DIV2     PLA
         DEY
         BNE ]LOOP
         STA EXT
         RTS

*
* ASCTONUM
*   Converts a string to a 16-bit number.  The address
*   of the string is passed in in (A,X) = (lo,hi), and
*   the base of the number is contained in Y.  Valid
*   bases are 0-16.  The string is assumed to be
*   null-terminated.
*
*   On exit, carry set denotes an error, either an
*   invalid base, an invalid string, or a number
*   overflow.  The number is contained in ACC, as
*   used in MULT16.  The last char read is contained
*   in (A,X)=(lo,hi).  Y is toast.
*
ASCTONUM ENT
         CPY #17
         BCS :EXIT
         STY AUX          ;AUX=number base
         LDY #00
         STY AUX+1
         STY ACC
         STY ACC+1
         STX :LOOP+2
         TAX              ;X=lo byte string

:LOOP    LDA $A000,X      ;Valid chars are $30-$39, $41-$46
         BEQ :EXIT
         EOR #$30
         CMP #10
         BCC :CONT
         EOR #$70         ;Should use SBC #$70-9
         ADC #8           ;Now A-F = 10-16
:CONT    CMP AUX          ;Compare with number base
         BCS :EXIT
         PHA
         JSR MULT16       ;Multiply number times base
         PLA
         LDY EXT          ;Did multiplication exceed 16 bits?
         BNE :ERROR
         CLC
         ADC ACC
         STA ACC
         LDA ACC+1
         ADC #00
         STA ACC+1
         INX
         BNE :LOOP
         INC :LOOP+2
         BNE :LOOP
:ERROR   SEC
:EXIT    TXA
         LDX :LOOP+2
         RTS

*
* PRINTNUM
*   Prints the 16-bit number in (A,X) = (lo,hi)
*   to the screen, i.e. prints using CHROUT.
*   The number base is contained in Y.
*
PRINTNUM ENT
         STA ACC
         STX ACC+1
         STY AUX          ;Base
         LDX #00
         STX AUX+1
* ACC/AUX -> ACC, remainder in EXT
:LOOP    JSR DIV16
         INX
         LDA EXT
         PHA
         LDA ACC
         ORA ACC+1
         BNE :LOOP        ;Divide until result=0
:POOP    PLA
         ORA #$30         ;Convert to chr$
         CMP #$3A
         BCC :PLOP
         ADC #$06         ;$3A->A $3B->B etc.
:PLOP    JSR CHROUT
         DEX
         BNE :POOP
         RTS

*
* PLOPNUM
*   Plops the 16-bit number in (A,X) = (lo,hi)
*   to the screen, i.e. prints using PLOP routine.
*   The number base is contained in Y.
*
PLOPNUM  ENT
         STA ACC
         STX ACC+1
         STY AUX          ;Base
         LDX #00
         STX AUX+1
* ACC/AUX -> ACC, remainder in EXT
:LOOP    JSR DIV16
         INX
         LDA EXT
         PHA
         LDA ACC
         ORA ACC+1
         BNE :LOOP        ;Divide until result=0
:POOP    PLA
         ORA #$30         ;Convert to chr$
         CMP #$3A
         BCC :PLOP
         ADC #$06         ;$3A->A $3B->B etc.
:PLOP    JSR CHAROUT
         DEX
         BNE :POOP
         RTS

*
* PHBYTE
*   Print a hex byte in .A, including trailing zeros
*
PHBYTE   ENT
         PHA
         LSR
         LSR
         LSR
         LSR
         JSR :PRINT
         PLA
         AND #$0F
:PRINT   ORA #$30
         CMP #$3A
         BCC :PLOP
         ADC #$06
:PLOP    JMP CHAROUT


*
* BLMOVE
*   Moves a block of memory to another location.
*   Set BLSTART to the beginning of the block of
*   memory, BLEND to the last byte of the block,
*   and BLDEST to the destination byte (2-bytes
*   each, in lo,hi format).
*
*   The routine is smart, and can tell the difference
*   between forwards and backwards moves, etc.
*
*   AUX and EXT are used, from mult routine.
*
BLSTART  ENT
         DA 0
BLEND    ENT
         DA 0
BLDEST   ENT
         DA 0

BLMOVE   ENT
         LDA BLEND        ;Number of bytes to move
         SEC
         SBC BLSTART
         TAX
         LDA BLEND+1
         SBC BLSTART+1
         BCC :DONE
         STA AUX          ;(.X,AUX) = (LO,HI)

         LDA BLSTART
         SEC
         SBC BLDEST
         LDA BLSTART+1
         SBC BLDEST+1
         BCC :FORWARD

         LDA BLSTART
         STA :BSRC+1
         LDA BLSTART+1
         STA :BSRC+2
         LDA BLDEST
         STA :BDEST+1
         LDA BLDEST+1
         STA :BDEST+2
         INC AUX
         INX
         LDY #00
:LOOP    
:BSRC    LDA $1000,Y
:BDEST   STA $1000,Y
         INY
         BNE :C1
         INC :BSRC+2
         INC :BDEST+2
:C1      DEX
         BNE :LOOP
         DEC AUX
         BNE :LOOP
:DONE    RTS

:FORWARD                  ;Move block forwards
         TXA
         ADC BLDEST       ;Copy to end of blocks
         STA :FDEST+1
         LDA AUX
         ADC BLDEST+1
         STA :FDEST+2
         LDA BLEND
         STA :FSRC+1
         LDA BLEND+1
         STA :FSRC+2
         INC AUX
         INX
         LDY #00
:FLOOP   
:FSRC    LDA $1000,Y
:FDEST   STA $1000,Y
         DEY
         CPY #$FF
         BNE :C2
         DEC :FSRC+2
         DEC :FDEST+2
:C2      DEX
         BNE :FLOOP
         DEC AUX
         BNE :FLOOP
         RTS
