--- /dev/null
+;\r
+; Extended memory driver for 65816 based extra RAM. Driver works without\r
+; problems when statically linked.\r
+;\r
+; Marco van den Heuvel, 2015-12-01\r
+;\r
+\r
+ .include "zeropage.inc"\r
+\r
+ .include "em-kernel.inc"\r
+ .include "em-error.inc"\r
+\r
+\r
+ .macpack generic\r
+ .macpack module\r
+\r
+\r
+; ------------------------------------------------------------------------\r
+; Header. Includes jump table\r
+\r
+ module_header _c64_65816_emd\r
+\r
+; Driver signature\r
+\r
+ .byte $65, $6d, $64 ; "emd"\r
+ .byte EMD_API_VERSION ; EM API version number\r
+\r
+; Library reference\r
+\r
+ .addr $0000\r
+\r
+; Jump table\r
+\r
+ .addr INSTALL\r
+ .addr UNINSTALL\r
+ .addr PAGECOUNT\r
+ .addr MAP\r
+ .addr USE\r
+ .addr COMMIT\r
+ .addr COPYFROM\r
+ .addr COPYTO\r
+\r
+; ------------------------------------------------------------------------\r
+; Data.\r
+\r
+.bss\r
+isnotscpu: .res 1 ; SuperCPU not present\r
+curpage: .res 1 ; Current page number\r
+curbank: .res 1 ; Current bank number (+1)\r
+bankcount: .res 1 ; Number of available banks (pages = banks * 256)\r
+window: .res 256 ; Memory "window"\r
+\r
+.code\r
+\r
+; ------------------------------------------------------------------------\r
+; INSTALL routine. Is called after the driver is loaded into memory. If\r
+; possible, check if the hardware is present and determine the amount of\r
+; memory available.\r
+; Must return an EM_ERR_xx code in a/x.\r
+;\r
+\r
+INSTALL:\r
+ sei\r
+ clc\r
+ sed\r
+ lda #$99\r
+ adc #$01 ; on 65C02, 65SC02, 65CE02, 65802 and 65816 sets the zero flag correctly\r
+ cld\r
+ bne @not_present\r
+ clc\r
+.P816\r
+ sep #$01 ; nop #$01 on 65C02/65SC02 and lda ($01,s),y on 65CE02\r
+.P02\r
+ bcc @not_present\r
+ lda $d0bc\r
+ and #$80\r
+ sta isnotscpu\r
+ lda $07e8\r
+ pha ; save value incase it was used somewhere else\r
+ ldx #$ff\r
+@fillloop: ; fill from top (bank 255) to bottom\r
+ txa\r
+ pha\r
+.P816\r
+ plb ; pull dbr\r
+.P02\r
+ stx $07e8\r
+ dex\r
+ cpx #$ff\r
+ bne @fillloop\r
+ inx\r
+@compareloop: ; check from bottom to top\r
+ txa\r
+ pha\r
+.P816\r
+ plb\r
+.P02\r
+ cmp $07e8\r
+ bne @found_pages\r
+.P816\r
+ inc\r
+.P02\r
+ sta $07e8\r
+ cmp $07e8\r
+ bne @found_pages\r
+ inx\r
+ bne @compareloop\r
+@found_pages:\r
+ dex\r
+ lda #$00\r
+ pha\r
+.P816\r
+ plb\r
+.P02\r
+ pla\r
+ sta $07e8\r
+ cli\r
+ lda isnotscpu\r
+ bne @noextradex\r
+ dex\r
+@noextradex:\r
+ stx bankcount\r
+ lda #<EM_ERR_OK\r
+ ldx #>EM_ERR_OK\r
+ rts\r
+@not_present:\r
+ cli\r
+ lda #<EM_ERR_NO_DEVICE\r
+ ldx #>EM_ERR_NO_DEVICE\r
+; rts ; Run into UNINSTALL instead\r
+\r
+\r
+; ------------------------------------------------------------------------\r
+; UNINSTALL routine. Is called before the driver is removed from memory.\r
+; Can do cleanup or whatever. Must not return anything.\r
+;\r
+\r
+UNINSTALL:\r
+ rts\r
+\r
+\r
+; ------------------------------------------------------------------------\r
+; PAGECOUNT: Return the total number of available pages in a/x.\r
+;\r
+\r
+PAGECOUNT:\r
+ lda #$00 ; a whole bank is either usable or not\r
+ ldx bankcount\r
+ rts\r
+\r
+; ------------------------------------------------------------------------\r
+; MAP: Map the page in a/x into memory and return a pointer to the page in\r
+; a/x. The contents of the currently mapped page (if any) may be discarded\r
+; by the driver.\r
+;\r
+\r
+MAP: sta curpage ; Remember the new page\r
+ stx curbank ; Remember the new bank\r
+\r
+ sta ptr2+1 ; src address low\r
+ lda #$00\r
+ sta ptr2 ; src address high\r
+ inx\r
+ ldy isnotscpu ; check if not scpu\r
+ bne @notscpu\r
+ inx\r
+@notscpu:\r
+ stx tmp2 ; src bank\r
+\r
+ sta tmp1 ; dst bank\r
+\r
+ sta ptr3+1 ; length high\r
+ lda #$ff\r
+ sta ptr3 ; length low\r
+\r
+ lda #<window\r
+ sta ptr1 ; dst address low\r
+ ldx #>window\r
+ stx ptr1+1 ; dst address high\r
+\r
+ jsr transfer\r
+\r
+ rts\r
+\r
+; ------------------------------------------------------------------------\r
+; USE: Tell the driver that the window is now associated with a given page.\r
+\r
+USE: sta curpage ; Remember the page\r
+ stx curbank ; Remember the bank\r
+ lda #<window\r
+ ldx #>window ; Return the window\r
+ rts\r
+\r
+; ------------------------------------------------------------------------\r
+; COMMIT: Commit changes in the memory window to extended storage.\r
+\r
+COMMIT: lda curpage ; Get the current page\r
+ sta ptr1+1 ; dst high\r
+ ldx #$00\r
+ stx ptr1 ; dst low\r
+\r
+ lda #<window\r
+ sta ptr2 ; src low\r
+ lda #>window\r
+ sta ptr2+1 ; src high\r
+\r
+ stx ptr3+1 ; length high\r
+ lda #$ff\r
+ sta ptr3 ; length low\r
+\r
+ stx tmp2 ; src bank\r
+ ldy curbank ; Get the current bank\r
+ iny\r
+ ldx isnotscpu\r
+ bne @notascpu\r
+ iny\r
+@notascpu:\r
+ sty tmp1 ; dst bank\r
+\r
+ jsr transfer\r
+\r
+ rts\r
+\r
+; ------------------------------------------------------------------------\r
+; COPYFROM: Copy from extended into linear memory. A pointer to a structure\r
+; describing the request is passed in a/x.\r
+; The function must not return anything.\r
+;\r
+\r
+COPYFROM:\r
+ sta ptr4\r
+ stx ptr4+1 ; Save the passed em_copy pointer\r
+\r
+ ldy #EM_COPY::COUNT+1 ; start at the end of the struct\r
+ lda (ptr4),y ; get high byte of count\r
+ tax\r
+ dey\r
+ lda (ptr4),y ; get low byte of count\r
+ bne @nodex\r
+ dex\r
+@nodex:\r
+.P816\r
+ dec\r
+.P02\r
+ sta ptr3 ; length low\r
+ stx ptr3+1 ; length high\r
+ dey\r
+ lda (ptr4),y ; get bank\r
+.P816\r
+ inc\r
+.P02\r
+ ldx isnotscpu\r
+ bne @notscpu64\r
+.P816\r
+ inc\r
+.P02\r
+@notscpu64:\r
+ sta tmp2 ; src bank\r
+ dey\r
+ lda (ptr4),y ; get page\r
+ sta ptr2+1 ; src high\r
+ dey\r
+ lda (ptr4),y ; get offset in page\r
+ sta ptr2 ; src low\r
+ dey\r
+ lda (ptr4),y ; get memory buffer high\r
+ sta ptr1+1 ; dst high\r
+ dey\r
+ lda (ptr4),y ; get memory buffer low\r
+ sta ptr1 ; dst low\r
+ lda #$00\r
+ sta tmp1 ; dst bank\r
+\r
+ jsr transfer\r
+\r
+ rts\r
+\r
+; ------------------------------------------------------------------------\r
+; COPYTO: Copy from linear into extended memory. A pointer to a structure\r
+; describing the request is passed in a/x.\r
+; The function must not return anything.\r
+;\r
+\r
+COPYTO: sta ptr4\r
+ stx ptr4+1 ; Save the passed em_copy pointer\r
+\r
+ ldy #EM_COPY::COUNT+1 ; start at the end of the struct\r
+ lda (ptr4),y ; get high byte of count\r
+ tax\r
+ dey\r
+ lda (ptr4),y ; get low byte of count\r
+ bne @nodex2\r
+ dex\r
+@nodex2:\r
+.P816\r
+ dec\r
+.P02\r
+ sta ptr3 ; length low\r
+ txa\r
+ sta ptr3+1 ; length high\r
+ dey\r
+ lda (ptr4),y ; get bank\r
+.P816\r
+ inc\r
+.P02\r
+ ldx isnotscpu\r
+ bne @notascpu64\r
+.P816\r
+ inc\r
+.P02\r
+@notascpu64:\r
+ sta tmp1 ; dst bank\r
+ dey\r
+ lda (ptr4),y ; get page\r
+ sta ptr1+1 ; dst high\r
+ dey\r
+ lda (ptr4),y ; get page offset\r
+ sta ptr1 ; dst low\r
+ dey\r
+ lda (ptr4),y ; get memory buffer high\r
+ sta ptr2+1 ; src low\r
+ dey\r
+ lda (ptr4),y ; get memory buffer low\r
+ sta ptr2 ; src high\r
+ lda #$00\r
+ sta tmp2 ; src bank\r
+\r
+ jsr transfer\r
+\r
+ rts\r
+\r
+; ------------------------------------------------------------------------\r
+; Helper function for moving a block, the following is used:\r
+; ptr1: dst\r
+; ptr2: src\r
+; ptr3: length\r
+; tmp1: dst bank\r
+; tmp2: src bank\r
+\r
+transfer:\r
+.P816\r
+.A8\r
+.I8\r
+ sei\r
+ pha\r
+ phx\r
+ phy\r
+ ldx tmp1 ; load srcbank\r
+ stx @move+1 ; store srcbank in move + 1\r
+ ldy tmp2 ; load dstbank\r
+ sty @move+2 ; store dstbank in move + 2\r
+ clc ; switch to native mode\r
+ xce\r
+ php ; save status bits\r
+ rep #%00110000 ; set A and index to 16bit\r
+.A16\r
+.I16\r
+ ldy ptr1\r
+ ldx ptr2\r
+ lda ptr3\r
+@move:\r
+ mvn 0,0\r
+ plp ; restore status bits\r
+.A8\r
+.I8\r
+ lda #$00\r
+ pha\r
+ plb ; restore dbr\r
+ sec\r
+ xce ; switch to emul mode\r
+ ply\r
+ plx\r
+ pla\r
+ cli\r
+ rts\r
+.P02\r