;
; Extended memory driver for the Atari 130XE memory standard
; Shawn Jefferson, 2012-08-11
;
; Banking Register $D301:
; Bit 7: Self-Test on/off
; Bit 6: Unused
; Bit 5: Antic sees bank
; Bit 4: CPU sees bank
; Bit 3: bank control
; Bit 2: bank control
; Bit 1: BASIC on/off
; Bit 0: OS RAM on/off
; 
; Masks: %11100011  $E3     Bank 0
;        %11100111  $E7     Bank 1
;        %11101011  $EB     Bank 2
;        %11101111  $EF     Bank 3
;        %11111111  $FF     Off
;
; Based on the Extended memory driver for the Apple II auxiliary memory
;  by
; Stefan Haubenthal, 2003-12-12
; Ullrich von Bassewitz, 2002-12-02
;

        .include        "zeropage.inc"

        .include        "em-kernel.inc"
        .include        "em-error.inc"


        .macpack        generic


; ------------------------------------------------------------------------
; Header. Includes jump table

.segment        "JUMPTABLE"

; Driver signature

        .byte   $65, $6d, $64           ; "emd"
        .byte   EMD_API_VERSION         ; EM API version number

; Library reference

        .addr   $0000

; Jump table

        .addr   INSTALL
        .addr   UNINSTALL
        .addr   PAGECOUNT
        .addr   MAP
        .addr   USE
        .addr   COMMIT
        .addr   COPYFROM
        .addr   COPYTO

; ------------------------------------------------------------------------
; Constants

BANK    = $4000                         ; bank window
STACK   = $0100                         ; stack location 
PAGES   = 256                           ; 4 x 16k banks


; ------------------------------------------------------------------------
; Data.

.rodata
banks:          .byte $E3,$E7,$EB,$EF   ; 130XE banks for cpu banking

stacktest:      sei
                ldy banks
                sty $D301
                tax                     ; A is set by caller
                inx
                stx $4000               ; change $4000
                ldy #$FF                ; STACK+13
                sty $D301
                cmp $4000               ; changed?
                beq @1
                sec                     ; error
                bcs @2
@1:             clc
@2:             sta $4000               ; restore
                cli
                rts
stacktest_end: 

stackcopy:      sei                     ; disable interrupts
@1:             dex                     ; pre-decrement (full page x=0)
                ldy #$FF                ; this will be replaced  STACK+3
                sty $D301               ; set bank 
                lda $FF00,x             ; address to copy from   STACK+8,+9
                ldy #$FF                ; this will be replaced  STACK+11
                sty $D301 
                sta $FF00,x             ; address to copy to     STACK+16,+17
                cpx #0 
                bne @1
                ldy #$FF                ; portb_save             STACK+23
                sty $D301
                cli                     ; enable interrupts
                rts
stackcopy_end:

stackcopy_byte: sei
                ldy #$FF                ; STACK+2
                sty $D301
                lda $FFFF               ; STACK+7 +8
                ldy #$FF                ; STACK+10
                sty $D301
                sta $FFFF               ; STACK+15 +16
                ldy #$FF                ; STACK+18
                sty $D301
                cli
                rts
stackcopy_byte_end: 


.data
curpage:        .byte   $FF             ; Current page number in bank (invalid)
curbank:        .byte   $FF             ; Current bank number

.bss
window:         .res    256             ; Memory "window"
portb_save:     .res    1               ; portb state

.code

install_transfer:
                ldx #stackcopy_end - stackcopy - 1
@1:             lda stackcopy,x
                sta STACK,x
                dex
                bpl @1
                rts

install_byte_transfer:
                ldx #stackcopy_byte_end - stackcopy_byte - 1
@2:             lda stackcopy_byte,x
                sta STACK,x
                dex
                bpl @2
                rts

install_test:
                ldx #stacktest_end - stacktest - 1
@3:             lda stacktest,x
                sta STACK,x
                dex
                bpl @3
                rts

setpage:
                tax                             ; save page
                and     #$C0                    ; mask out bank
                clc
                ror
                ror                             ; divide by 64
                ror                             ; 64 pages in each bank
                ror
                ror
                ror
                sta     curbank                 ; Remember the new bank
                txa                             ; bring back page
                and     #$3F                    ; mask out page
                sta     curpage                 ; curpage in bank
                rts


; ------------------------------------------------------------------------
; INSTALL routine. Is called after the driver is loaded into memory. If
; possible, check if the hardware is present and determine the amount of
; memory available.
; Must return an EM_ERR_xx code in a/x.
;

INSTALL:
        lda     $D301                   ; save state of portb
        sta     portb_save
        tay 

        jsr     install_test            ; doesn't touch Y
        sty     STACK+13

        lda     $4000                   ; test for extended memory
        jsr     STACK
        bcs     @1 
        lda     #EM_ERR_OK
        rts
@1:     lda     #EM_ERR_NO_DEVICE
        rts

; ------------------------------------------------------------------------
; UNINSTALL routine. Is called before the driver is removed from memory.
; Can do cleanup or whatever. Must not return anything.
;

UNINSTALL:
        rts


; ------------------------------------------------------------------------
; PAGECOUNT: Return the total number of available pages in a/x.
;

PAGECOUNT:
        lda     #<PAGES
        ldx     #>PAGES
        rts

; ------------------------------------------------------------------------
; MAP: Map the page in a/x into memory and return a pointer to the page in
; a/x. The contents of the currently mapped page (if any) may be discarded
; by the driver.
;

MAP:    jsr setpage                     ; extract the bank/page
        add     #>BANK                  ; $4000 + page  (carry is cleared)
        sta     ptr1+1
        ;ldy     #$00
        ;sty     ptr1

        lda     #<window
        sta     ptr2
        lda     #>window
        sta     ptr2+1

; Transfer one page

        jsr     install_transfer        ; Transfer one page

        ldx     curbank
        lda     banks,x
        sta     STACK+3                 ; set bank to copy from
;        lda     ptr1
;        sta     STACK+8 
        lda     ptr1+1
        sta     STACK+9                 ; set copy from address
        lda     portb_save
        sta     STACK+11                ; set portb restore
        sta     STACK+23                ; set final portb restore
        lda     ptr2
        sta     STACK+16
        lda     ptr2+1
        sta     STACK+17                ; set copy to address 

        ldx     #0                      ; full page copy
        jsr     STACK                   ; do the copy! 

; Return the memory window

        lda     #<window
        ldx     #>window                ; Return the window address

        rts

; ------------------------------------------------------------------------
; USE: Tell the driver that the window is now associated with a given page.

USE:    ;sta     curpage                 ; Remember the page
        jsr setpage                     ; extract bank/page
        lda     #<window
        ldx     #>window                ; Return the window
        rts

; ------------------------------------------------------------------------
; COMMIT: Commit changes in the memory window to extended storage.

COMMIT: lda     curpage                 ; Get the current page
        cmp     #$FF
        beq     commit_done             ; Jump if no page mapped

        clc
        add     #>BANK
        sta     ptr2+1
        ;ldy     #$00
        ;sty     ptr2

        lda     #<window
        sta     ptr1
        lda     #>window
        sta     ptr1+1

; Transfer one page/all bytes

        jsr     install_transfer        ; Transfer one page

        lda     portb_save
        sta     STACK+3                 ; set bank to copy from
        sta     STACK+23                ; set final portb restore
        lda     ptr1
        sta     STACK+8 
        lda     ptr1+1
        sta     STACK+9                 ; set copy from address
        ldx     curbank
        lda     banks,x
        sta     STACK+11                ; set bank to copy to
        ;lda     ptr2
        ;sta     STACK+16
        lda     ptr2+1
        sta     STACK+17                ; set copy to address 

        ldx     #0                      ; full page copy
        jsr     STACK                   ; do the copy! 

commit_done:
        rts


; ------------------------------------------------------------------------
; COPYFROM: Copy from extended into linear memory. A pointer to a structure
; describing the request is passed in a/x.
; The function must not return anything.
;
COPYFROM:
        sta     ptr3
        stx     ptr3+1                  ; Save the passed em_copy pointer

        jsr     install_byte_transfer   ; install the stack copy routine

        ldy     #EM_COPY::OFFS
        lda     (ptr3),y
        sta     STACK+7                 ; offset goes into BANK low 

        ldy     #EM_COPY::PAGE
        lda     (ptr3),y
        sta     tmp1                    ; save page for later
        ;add     #>BANK
        ;sta     STACK+8                 ; BANK + page goes into BANK high

        ldy     #EM_COPY::BUF
        lda     (ptr3),y
        sta     STACK+15                ; buffer goes into dest low
        iny
        lda     (ptr3),y
        sta     STACK+16                ; buffer goes into dest high

        ldy     #EM_COPY::COUNT
        lda     (ptr3),y                ; Get bytes in last page
        sta     ptr4
        iny
        lda     (ptr3),y                ; Get number of pages
        sta     ptr4+1

        lda     tmp1                    ; extract bank/page
        jsr     setpage                 ; sets curbank/curpage
        lda     curpage
        add     #>BANK                  ; add to BANK address
        sta     STACK+8                 ; current page in bank
        ldx     curbank
        lda     banks,x 
        sta     STACK+2                 ; set bank in stack 
        lda     portb_save 
        sta     STACK+10                ; set bank restore in stack
        sta     STACK+18                ; set final restore too

copyfrom_copy:
        lda     ptr4                    ; check if count is zero
        bne     @4
        lda     ptr4+1
        beq     done

@4:     jsr     STACK                   ; copy one byte

        sec
        lda     ptr4
        sub     #1
        sta     ptr4
        bcs     @1
        lda     ptr4+1
        beq     @1
        sub     #1
        sta     ptr4+1

@1:     inc     STACK+7                 ; increment address in BANK
        bne     @2
        inc     STACK+8
        lda     STACK+8
        cmp     #$80                    ; we stepped outside bank
        bne     @2

        inc     curbank                 ; get next bank
        ldx     curbank
        lda     banks,x
        sta     STACK+2                 ; set new bank
        lda     #$40                    ; set address back to $4000
        sta     STACK+8

@2:     inc     STACK+15                ; increment buffer address
        bne     @3
        inc     STACK+16

@3:     jmp     copyfrom_copy           ; copy another byte 

done:
        rts

; ------------------------------------------------------------------------
; COPYTO: Copy from linear into extended memory. A pointer to a structure
; describing the request is passed in a/x.
; The function must not return anything.
;

COPYTO:
        sta     ptr3
        stx     ptr3+1                  ; Save the passed em_copy pointer

        jsr     install_byte_transfer   ; install the stack copy routine

        ldy     #EM_COPY::OFFS
        lda     (ptr3),y
        sta     STACK+15                 ; offset goes into BANK low 

        ldy     #EM_COPY::PAGE
        lda     (ptr3),y
        sta     tmp1                    ; save page for later
        ;add     #>BANK
        ;sta     STACK+16                ; BANK + page goes into BANK high

        ldy     #EM_COPY::BUF
        lda     (ptr3),y
        sta     STACK+7                ; buffer goes into dest low
        iny
        lda     (ptr3),y
        sta     STACK+8                ; buffer goes into dest high

        ldy     #EM_COPY::COUNT
        lda     (ptr3),y                ; Get bytes in last page
        sta     ptr4
        iny
        lda     (ptr3),y                ; Get number of pages
        sta     ptr4+1

        lda     tmp1                    ; extract bank/page
        jsr     setpage                 ; sets curbank/curpage
        lda     curpage
        add     #>BANK                  ; add to BANK address
        sta     STACK+16                ; current page in bank
        ldx     curbank
        lda     banks,x 
        sta     STACK+10                ; set bank in stack 
        lda     portb_save 
        sta     STACK+2                 ; set bank restore in stack
        sta     STACK+18                ; set final restore too

copyto_copy:
        lda     ptr4                    ; check if count is zero
        bne     @4
        lda     ptr4+1
        beq     done

@4:     jsr     STACK                   ; copy one byte

        sec
        lda     ptr4
        sub     #1
        sta     ptr4
        bcs     @1
        lda     ptr4+1
        beq     @1
        sub     #1
        sta     ptr4+1

@1:     inc     STACK+15               ; increment address in BANK
        bne     @2
        inc     STACK+16
        lda     STACK+16
        cmp     #$80                    ; we stepped outside bank
        bne     @2

        inc     curbank                 ; get next bank
        ldx     curbank
        lda     banks,x
        sta     STACK+10                ; set new bank
        lda     #$40                    ; set address back to $4000
        sta     STACK+16

@2:     inc     STACK+7                ; increment buffer address
        bne     @3
        inc     STACK+8

@3:     jmp     copyto_copy           ; copy another byte 

