MODULE; RAMXL.ACT
;-------------------------------------
; Copyright 1985 by Daniel L. Moore.
; RAMXL may not be sold, but may be
; freely copied and distributed.
;-------------------------------------
; Last modified on 03/30/85
;-------------------------------------

;   Support routines for the "extra" 
; 14K of RAM in XLs that is located
; under the OS ROM.
;   When an interrupt occurs and the OS
; is banked out, the RAMXL will bank
; the OS in, and then call the ROM OX
; interrupt handler.  When control
; returns from the ROM OS, the OS is 
; banked out, and control is returned
; to the original program.

;   Only the NMI and IRQ vectors are  
; supported, since the XL hardware banks
; the OS ROM in automatically when a 
; chip reset occurs (the RESET button).

DEFINE  INT_VECTOR = "$FFF0"

CARD    NMI_Vector = $FFFA,
        RES_Vector = $FFFC,
        IRQ_Vector = $FFFE,
        Return_Addr 

BYTE    PortB      = $D301,  
        NMIEN      = $D40E,
        X_Storage

PROC OS_In=*() ; ROM OS resident
 ~[$AD PortB    ; LDA PortB
  $09 $01      ; ORA #$01  toggle OS bit to ON
  $8D PortB    ; STA PortB
  $60]         ; RTS

PROC OS_Out=*(); ROM OS not resident
 ~[$AD PortB    ; LDA PortB
  $29 $FE      ; AND #$FE  toggle OS bit to OFF
  $8D PortB    ; STA PortB
  $60]         ; RTS

PROC JMP_Vector=*()
 ~[$4C $FFFF]   ; JMP $FFFF  

PROC Handle_Interrupt=*()             
; Handle the interrupt that just occured.

 ~[$8E X_Storage    ; STX X_Storage
  $AA              ; TAX        A=the interrupt number
  $20 OS_In        ; JSR OS_In 

; Get the address of the desired interrupt routine
  $BD INT_VECTOR   ; LDA INT_VECTOR,X 
  $8D JMP_Vector+1 ; STA JMP_VECTOR
  $BD INT_VECTOR+1 ; LDA INT_VECTOR,X
  $8D JMP_Vector+2 ; STA JMP_VECTOR+1

; Setup the stack to fake an interrupt and call
; the OS ROM interrupt code.

; First the return address
  $AD Return_Addr+1; LDA Return_Addr+1
  $48              ; PHA
  $AD Return_Addr  ; LDA Return_Addr
  $48              ; PHA
; Then the proccessor status register
  $58              ; CLI      enable IRQs, for Stage 2 VBLANK
  $08              ; PHP 
  
  $4C JMP_Vector]  ; JMP JMP_Vector

PROC Return_Here=*()
; Return here after the ROM OS interrupt code runs
; Bank the OS out, the return to the
; original program.
 ~[$20 OS_Out       ; JSR OS_Out
  $AE X_Storage    ; LDX X_Storage
  $68              ; PLA    from NMI.Handler or IRQ.Handler
  $40]             ; RTI 

PROC NMI_Handler=*()
; Handle NMIs that occur while the OS is
; banked out. Save the A reg, then get 
; the vector number and call Handle_Interrupt.
 ~[$48              ; PHA
  $A9 $0A          ; LDA #$0A
  $4C Handle_Interrupt]; JMP Handle_Interrrupt

PROC IRQ_Handler=*()
 ~[$48              ; PHA
  $A9 $0E          ; LDA #$0A
  $4C Handle_Interrupt]; JMP Handle_Interrrupt

;-------------------------------------
; End of actual interrupt code.
; All that is left is installing 
; the vectors to the routines.
;------------------------------------- 

PROC Install_CharSet()
; Copy the ROM char set at $E000 to $E3FF
; to the RAM bank, so that characters do
; not flicker when the RAM is accessed.
; If this is done, do not use the RAM
; from $E000 to $E3FF (57344 to 58367).
 BYTE POINTER where
 BYTE temp
 
 FOR where=$E000 TO $E3FF
 DO
  OS_In()
  temp=where^
  OS_Out()
  where^=temp
 OD
 OS_In()
RETURN

PROC Install_Interrupts()

 Return_Addr=Return_Here; Set the return address pointer

 NMIEN=0  ; Turn all NMI interrupts off.
 ~[$78]    ; SEI  Turn all IRQ interrupts off.

 OS_Out()

; Install the new interrupt routines
; vectors at $FFFA to $FFFF under the
; OS ROM.
 NMI_Vector = NMI_Handler
 IRQ_Vector = IRQ_Handler

 OS_In()

 ~[$58]    ; CLI  Turn IRQs back on.
 NMIEN=$40; Turn NMIs back on.

 Install_CharSet()
RETURN

;-------------------------------------
; Now the routine that lets you get to
; the RAM that is under the OS.
; There are actually 2 memory areas
; present: 
;    4K at $C000 to $CFFF, 49152 to 53247
;   10K at $D800 to $FFFF, 55296 to 65535
;
; The last 6 bytes of the 10K area are not
; usable, since that is where the interrupt
; routines are located.  Therefore do not
; use any RAM above $FFF9 (65529) or you
; will crash the system.
;------------------------------------- 

PROC MoveBlockXL(BYTE POINTER dest,source, CARD size)
; This is a version of MoveBlock that lets
; you use the extra RAM in XLs.

 OS_Out()
 FOR dest=dest TO dest+size
 DO
   dest^=source^ 
   source==+1
 OD
 OS_In()
RETURN

MODULE; For user.