;;==========================================================================;;
;; GMS Loader Routine.                                                      ;;
;; Copyright 2001, Joe Zbiciak, intvnut AT gmail.com.                       ;;
;; http://spatula-city.org/~im14u2c/intv/                                   ;;
;;==========================================================================;;

;* ======================================================================== *;
;*  This program is free software; you can redistribute it and/or modify    *;
;*  it under the terms of the GNU General Public License as published by    *;
;*  the Free Software Foundation; either version 2 of the License, or       *;
;*  (at your option) any later version.                                     *;
;*                                                                          *;
;*  This program is distributed in the hope that it will be useful,         *;
;*  but WITHOUT ANY WARRANTY; without even the implied warranty of          *;
;*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       *;
;*  General Public License for more details.                                *;
;*                                                                          *;
;*  You should have received a copy of the GNU General Public License       *;
;*  along with this program; if not, write to the Free Software             *;
;*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               *;
;* ======================================================================== *;
;*                   Copyright (c) 2000, Joseph Zbiciak                     *;
;* ======================================================================== *;

            ROMW    16

CPUREGS     EQU $0040
DISPMODE    EQU $004E
PROGRAM     EQU $C800
SCRATCH     EQU $0500
WINDOW      EQU $0800

WBANK       EQU WINDOW SHR 8
CBANK       EQU CPUREGS SHR 8

STACK       EQU SCRATCH + $100

REGS        STRUCT  CPUREGS + WINDOW
@@0         EQU     $ + 0
@@1         EQU     $ + 1
@@2         EQU     $ + 2
@@3         EQU     $ + 3
@@4         EQU     $ + 4
@@5         EQU     $ + 5
@@6         EQU     $ + 6
@@7         EQU     $ + 7
@@stat      EQU     $ + 8
@@intr      EQU     $ + 9
@@b0        EQU     $ + 10
@@b1        EQU     $ + 11
@@b2        EQU     $ + 12

@@isrv      EQU     $ + 13
@@mode      EQU     $ + 14
@@visb      EQU     $ + 15
            ENDS

            ORG     PROGRAM

;; ======================================================================== ;;
;;  GMSLOADER -- This is where we start.                                    ;;
;; ======================================================================== ;;
GMSLOADER   PROC

            MVII    #GMSMAIN,   R0  ; \
            MVO     R0,     $100    ;  |__ Initialize via an interrupt handler
            SWAP    R0              ;  |
            MVO     R0,     $101    ; /
            EIS                     ;
            DECR    PC              ; Spin till interrupt happens.

            ENDP

STUB        JR      R5

;; ======================================================================== ;;
;;  GMSMAIN   -- This is where the actual action begins.                    ;;
;; ======================================================================== ;;
GMSMAIN     PROC

            DIS

            MVII    #STACK, R6      ; Put our stack in scratch memory.

            MVII    #STUB,  R0      ; \
            MVO     R0,     $100    ;  |   Stub out interrupt handler for now.
            SWAP    R0              ;  |-- We need to allow interrupts to 
            MVO     R0,     $101    ;  |   happen otherwise STIC gets unhappy.
            EIS                     ; /

            ;; Copy over most of the important ranges:
            CALL    COPYRAM
            DECLE   $0800,  $4000   ; $4000 - $47FF
            DECLE   $0020,  $0000   ; $0000 - $001F
            DECLE   $000B,  $0028   ; $0028 - $0032
            DECLE   $0010,  $00F0   ; $00F0 - $00FF
            DECLE   $025E,  $0102   ; $0102 - $035F
            DECLE   $0200,  $3800   ; $3800 - $39FF
            DECLE   0

            ;; Copy back the 3 words that go at $7000:
            MVII    #WBANK, R1      ; \
            MVII    #CBANK, R2      ;  |-- Point window at save area
            CALL    IC_SETBANK      ; /

            MVI     WINDOW+0, R0    ; \
            MVI     WINDOW+1, R3    ;  |-- Read three bytes in.
            MVI     WINDOW+2, R4    ; / 

            MVII    #WBANK, R1      ; \
            MVII    #$70,   R2      ;  |-- Point window at $7000
            CALL    IC_SETBANK      ; /

            MVO     R0, WINDOW+0    ; \
            MVO     R3, WINDOW+1    ;  |-- Write three bytes out.
            MVO     R4, WINDOW+2    ; /

            ;; Point the window at our register save area.
            MVII    #WBANK, R1      ; \
            MVII    #CBANK, R2      ;  |-- Point window at CPUREGS
            CALL    IC_SETBANK      ; /

            ;; Prepare for final launch.  We do that from an ISR.
            MVII    #GMSLAUNCH, R1
            MVO     R1,     $100
            SWAP    R1
            MVO     R1,     $101
            DECR    PC              ; spin

            ENDP

;; ======================================================================== ;;
;;  GMSLAUNCH -- Actually launch the game.                                  ;;
;; ======================================================================== ;;
GMSLAUNCH   PROC
            DIS

            ;; Is display visible?
            MVI     REGS.visb,  R0
            TSTR    R0
            BEQ     @@notvis
            MVO     R0,     $20
@@notvis:
            
            ;; Color stack or FG/BG
            MVI     $21,        R0  ; Assume color-stack
            MVI     REGS.mode,  R0
            TSTR    R0
            BEQ     @@colstk
            MVO     R0,         $21 ; FG/BG
@@colstk:

            ;; Restore ISR vector
            MVI     REGS.isrv,  R0
            MVO     R0,         $100
            SWAP    R0
            MVO     R0,         $101

            ;; Restore registers R1 - R6
            MVII    #REGS.1,    R5
            MVI@    R5,         R1
            MVI@    R5,         R2
            MVI@    R5,         R3
            MVI@    R5,         R4
            INCR    R5
            MVI@    R5,         R6
            MVI     REGS.5,     R5

            ;; Separate return paths based on EIS or DIS:
            MVI     REGS.intr,  R0
            ANDI    #1,         R0
            BEQ     @@do_eis

            ;; Launch w/out EIS.
            MVI     REGS.stat,  R0
            RSWD    R0
            MVI     REGS.0,     R0
            MVI     REGS.7,     R7

            ;; Launch with EIS.
@@do_eis:   MVI     REGS.stat,  R0
            RSWD    R0
            MVI     REGS.0,     R0
            EIS
            MVI     REGS.7,     R7

            ENDP


;;==========================================================================;;
;;  COPYRAM  -- Copies data from Intellicart space to CPU space.            ;;
;;                                                                          ;;
;;  This performs a copy from the Intellicart's private memory space to     ;;
;;  the Intellivision's memory space.  The copy occurs via the bankswitch   ;;
;;  window in chunks no larger than 2048 words.                             ;;
;;                                                                          ;;
;;  Inputs:                                                                 ;;
;;      R5 -- List of ranges to copy, stored as "length, addr" pairs.       ;;
;;            Length == 0 terminates.  Function will return immediately     ;;
;;            after the 0 word.                                             ;;
;;                                                                          ;;
;;  Outputs:                                                                ;;
;;      R0 -- Untouched.                                                    ;;
;;      R1, R2, R3, R4, R5 -- Trashed.                                      ;;
;;==========================================================================;;
COPYRAM     PROC

            B       @@first 

@@o_loop:   MVI@    R5,     R4      ; Get address.
            PSHR    R5              ; Save our table ptr.

@@m_loop:
            MVII    #WBANK, R1      ; \
            MOVR    R4,     R2      ;  |   Point window to start of range
            SWAP    R2              ;  |-- (within 256 word granularity).
            ANDI    #$FF,   R2      ;  |
            CALL    IC_SETBANK      ; /

            MOVR    R4,     R5      ; \
            ANDI    #$FF,   R5      ;  |-- Point source pointer into window.
            ADDI    #WINDOW,R5      ; /

            MOVR    R5,     R2          ; \
            ADDR    R3,     R2          ;  |   Make sure copy loop stays 
            CMPI    #WINDOW + 2048, R2  ;  |__ inside the window.  If 
            BNC     @@cnt_ok            ;  |   "start+count > end_of_window",
            MVII    #WINDOW+2048-1, R2  ;  |   count = end_of_window - start.
@@cnt_ok:   SUBR    R5,     R2          ; /

            SUBR    R2,     R3      ; Subtract this pass' count from total

@@i_loop:   MVI@    R5,     R1      ; \
            MVO@    R1,     R4      ;  |__ Copy as much data as we can.
            DECR    R2              ;  |
            BNEQ    @@i_loop        ; /

            TSTR    R3              ;
            BNEQ    @@m_loop        ; Keep going until it's all copied.
            
            PULR    R5              ; Restore table ptr.
@@first:    MVI@    R5,     R3      ; Get length byte
            TSTR    R3              ; Is it 0?
            BNEQ    @@o_loop        ; No:   Lets do this range.
            JR      R5              ; Yes:  Return

            ENDP


;;==========================================================================;;
;; IC_SETBANK    -- Sets a bank for a given 2K page of memory.              ;;
;;                                                                          ;;
;;  Inputs:                                                                 ;;
;;    R1 -- 5 MSBs of bank address in bits 7..3                             ;;
;;    R2 -- New bank address to point to in 8 LSBs.                         ;;
;;    R5 -- Return address                                                  ;;
;;                                                                          ;;
;;  Outputs:                                                                ;;
;;    R1 -- Address of Intellicart base control register.                   ;;
;;    R2 -- Trashed.                                                        ;;
;;    R5 -- Trashed.                                                        ;;
;;                                                                          ;;
;;  Example:                                                                ;;
;;    To remap 0x7800-0x7FFF in the Inty's address space to point to        ;;
;;    0x4000-0x47FF in the cart's address space, pass in the following      ;;
;;    parameters:  R1 = 0x0078, R2 = 0x0040.                                ;;
;;                                                                          ;;
;;==========================================================================;;
IC_SETBANK  PROC
            PSHR    R5                  ; Save return address.
            MOVR    R1,     R5          ; Get Hi/Lo 2K bit from address in R5
            ADDR    R5,     R5
            ANDI    #$10,   R5

            SLR     R1,     2           ; Put 4 MSBs of page in 4 LSBs of R0
            SLR     R1,     2
            ANDI    #$0F,   R1          ; Keep only the four LSBs.
            ADDR    R5,     R1          ; Merge bits: 76543210 -> xxxx37654
            ADDI    #$40,   R1          ; Point it at Intellicart ctrl regs.
            MVO@    R2,     R1          ; Write to control register.
            PULR    PC                  ; Return
            ENDP

