Cart.mac

From Intellivision Wiki
Jump to: navigation, search


This file supersedes dseg.mac.

Functions Provided

(Proper documentation to do. For now, I'm including the email I sent that introduces the code. This needs to be Wiki-ized.)

I've decided to tackle the "memory map" problem. The Intellivision provides you a lot of address space to work with, but it isn't contiguous. If you want to write a 16K game using the historical Mattel memory map (the same memory map that Space Patrol and Stonix use), you have to divide your ROM into three segments--one that goes from $5000 - $6FFF, one that goes from $D000 - $DFFF, and one that goes from $F000 - $FFFF.

Historically, I've used the ORG directive to divide my ROM up into these segments. This isn't terribly convenient, though. If you look at the top-level file in Space Patrol, you'll see it's a mess where I include all the pieces between the various ORG directives. The problem only gets worse if you want to go to larger games.

What I've done is write a family of macros that are intended to be INCLUDEd at the very start of your program. In fact, this file should be the very first INCLUDE in your program.

The first macro, ROMSETUP, actually sets everything up. ROMSETUP even sets up the cartridge header for you, as well as your stack. ROMSETUP is called as follows:

     ROMSETUP map, year, "title", start_address, stack_size

The year and title are self -explanatory. The "start_address" is the label that the Intellivision will jump to after performing some basic initialization. The "stack_size" is the number of words of System RAM to allocate to your stack. I use the number 32 as a good starting point personally.

The "map" argument can take on one of two values: 16K or 42K. This selects between two memory maps:

16K:
SEGMENT  RANGE
   0     $5000 - $6FFF
   1     $D000 - $DFFF
   2     $F000 - $FFFF


42K:
SEGMENT  RANGE
   0     $5000 - $6FFF
   1     $A000 - $C020
   2     $C022 - $FFFF
   3     $2000 - $2FFF
   4     $7100 - $7FFF
   5     $4800 - $4FFF

The 42K map also includes some additional RAM from $8040 to $9EFF.

Both maps are compatible with the Intellicart, the CC3, the various emulators and my recently announced cartridge design JLP. The 16K map is also compatible with the older cartridge design we've used for previous home-brews. I expect most new games to consider the 42K memory map, since the new homebrew design supports it.

The ROMSETUP macro configures a cartridge header and inserts some simple setup code to disable the ECS ROMs when using the 42K map. It also configures your stack pointer in bottom of the Intellivision's System RAM.

The cart.mac file provides 5 additional macros: ROMSEG, ROMEND, SCRATCH, SYSTEM, CARTRAM.

ROMSEG selects between the various ROM segments. You insert this into your code to change which segment the assembler will place the following code into. This makes it easy to divide your program among the available ROM segments. The segments are numbered as in the tables above. Example:

        ROMSEG 0  ; assemble the following into the first ROM segment
FUNC    PROC
        ; yadda yadda
        ENDP

        ROMSEG 2  ; assemble the following into the third ROM segment
FUNC2   PROC
        ; yadda yadda
        ENDP

You should only use the ROMSEG macro between PROC directives, not in the middle of a chunk of code. ROMSEG does move the current assembler origin, introducing a discontinuity in the code.

The ROMEND macro should be used at the end of your program. It includes some checks to ensure you have not overflowed any of your ROM segments, and it also will compute how much ROM space you've used and how much is still available. It places this information into several symbols that you can see in the assembler listing file, or in the .sym file if you use as1600's "-s" flag to output that. ROMEND sets the following symbols:

_m.0sz   The length in words of segment 0
_m.1sz   The length in words of segment 1
_m.2sz   The length in words of segment 2
_m.3sz   The length in words of segment 3
_m.4sz   The length in words of segment 4
_m.5sz   The length in words of segment 5

_m.0av   The number of words still available in segment 0
_m.1av   The number of words still available in segment 1
_m.2av   The number of words still available in segment 2
_m.3av   The number of words still available in segment 3
_m.4av   The number of words still available in segment 4
_m.5av   The number of words still available in segment 5

_m.size  The total size of the program
_m.avail The total words still available in the selected map

The SCRATCH and SYSTEM macros provide a handy way to allocate variables in the Intellivision's built-in memory. These macros can be invoked from anywhere to "declare" variables. Indeed, when using cart.mac, they should be the only way you declare variables, since they assume they own the entire Intellivision internal RAMs. They're used as follows:

label SCRATCH len   ;  Allocate 'len' locations of 8-bit memory from the Intellivision's Scratchpad RAM, assigning its address to 'label'
label SYSTEM  len   ;  Allocate 'len' locations of 16-bit memory from the Intellivision's System RAM, assigning its address to 'label'

Currently, an implementation of these appears in "dseg.mac" on IntelliWiki. The implementation in "cart.mac" should supersede it.

In addition to SCRATCH and SYSTEM, "cart.mac" provides a third macro, CARTRAM, that works similarly. It allocates variables in the additional RAM present in the 42K memory map, and thus only works with that memory map. Usage:

label CARTRAM len   ;  Allocate 'len' locations of 16-bit memory from the cartridge's additional RAM at $8040-$9EFF, assigning its address to 'label'

Using these macros should simplify getting started noticeably. For example, "Hello World" shortens down dramatically using "cart.mac" and the various library functions that are on IntelliWiki:

        INCLUDE "cart.mac"
        ROMSETUP 42K, 2008, "Hello World!", MAIN, 32

        INCLUDE "gimini.asm"
        INCLUDE "stic.mac"
        INCLUDE "print.mac"

MAIN:   PROC

        CALL        CLRSCR          ; Clear the screen
        PRINT_CSTK  5, 4, Yellow, "Hello World!"

        EIS
        DECR    PC
        ENDP

        ROMSEG  2

        INCLUDE "print.asm"       ; PRINT.xxx routines
        INCLUDE "fillmem.asm"     ; CLRSCR/FILLZERO/FILLMEM

        ROMEND

The "42K" on the ROMSETUP line can be changed to "16K" without changing anything else about your code. The memory map will switch automagically. The "ROMSEG 2" line before the INCLUDEs for print.asm and fillmem.asm isn't necessary--it's just included for illustrative purposes, showing how you might use ROMSEG.

Source Code

;; ======================================================================== ;;
;;  CART.MAC                                                                ;;
;;                                                                          ;;
;;  Cartridge Support Routines                                              ;;
;;                                                                          ;;
;;  The macros and code in this file are intended to manage the memory map  ;;
;;  of your Intellivision cartridge.  This includes allocating your code    ;;
;;  to its ROM segments, and allocating variables in the Intellivision's    ;;
;;  8-bit scratch and 16-bit system memories.                               ;;
;;                                                                          ;;
;;  This file provides several useful macros.                               ;;
;;                                                                          ;;
;;  ROM functions:                                                          ;;
;;                                                                          ;;
;;      ROMSETUP    Sets memory map & stack for cart; Provides ROM header.  ;;
;;      ROMSEG      Switches between ROM segments when assembling           ;;
;;      ROMEND      Used at end of program to detect any assembly errors    ;;
;;                                                                          ;;
;;  RAM functions:                                                          ;;
;;                                                                          ;;
;;      SCRATCH     Allocates storage in 8-bit RAM in Intellivision         ;;
;;      SYSTEM      Allocates storage in 16-bit RAM in Intellivision        ;;
;;      CARTMEM     Allocates storage in 16-bit RAM provided in cartridge   ;;
;;                                                                          ;;
;; ======================================================================== ;;

        ROMW    16

;; ======================================================================== ;;
;;  ROMSETUP    Sets memory map & stack for cart; Provides ROM header       ;;
;;                                                                          ;;
;;  This macro must be called very early in your program, probably before   ;;
;;  anything else.  It configures your memory map (as seen by the ROMSEG,   ;;
;;  SYSTEM and CARTMEM macros), and gives you a simple and well defined     ;;
;;  cartridge header.                                                       ;;
;;                                                                          ;;
;;  USAGE                                                                   ;;
;;                                                                          ;;
;;      ROMSETUP map, year, "title", startaddr, stacksize                   ;;
;;                                                                          ;;
;;  where:                                                                  ;;
;;                                                                          ;;
;;      "map" is one of "16K" or "42K".  "16K" sets the cartridge up for    ;;
;;      the historic Mattel 16K-word memory map and "42K" sets things up    ;;
;;      for a much more aggressive memory map that offers 2.5x the space.   ;;
;;                                                                          ;;
;;      "year" is the copyright year (e.g. 2008).                           ;;
;;                                                                          ;;
;;      "title" is the title of the game, in quotes.                        ;;
;;                                                                          ;;
;;      "startaddr" is the label of the entry point for the game.           ;;
;;                                                                          ;;
;;      "stacksize" is the number of words to reserve for the stack.        ;;
;;      32 words is usually a reasonable number.                            ;;
;;                                                                          ;;
;;  EXAMPLE                                                                 ;;
;;                                                                          ;;
;;      ROMSETUP 16K, 2008, "Hello World", MAIN, 32                         ;;
;;                                                                          ;;
;;  DETAILS                                                                 ;;
;;                                                                          ;;
;;      ROMSETUP lets you pick between two memory maps.  The 16K memory     ;;
;;      map corresponds to the historic Mattel 16K-word ROM memory map.     ;;
;;      Games such as Stonix and Space Patrol adhere to this memory map.    ;;
;;                                                                          ;;
;;      That memory map has three ROM segments:                             ;;
;;                                                                          ;;
;;      SEGMENT     RANGE                                                   ;;
;;         0        $5000 - $6FFF                                           ;;
;;         1        $D000 - $DFFF                                           ;;
;;         2        $F000 - $FFFF                                           ;;
;;                                                                          ;;
;;      You can switch between segments using the ROMSEG macro.  This       ;;
;;      makes it easy to put portions of code in each of the three ROM      ;;
;;      segments and make the most use of the available ROM space.          ;;
;;                                                                          ;;
;;      The 42K mapping has six ROM segments:                               ;;
;;                                                                          ;;
;;      SEGMENT     RANGE                                                   ;;
;;         0        $5000 - $6FFF                                           ;;
;;         1        $A000 - $C020                                           ;;
;;         2        $C022 - $FFFF                                           ;;
;;         3        $2000 - $2FFF                                           ;;
;;         4        $7100 - $7FFF                                           ;;
;;         5        $4800 - $4FFF                                           ;;
;;                                                                          ;;
;;      It also contains an additional RAM segment from $8040 - $9EFF.      ;;
;;      This memory map is available on the CC3, the Intellicart, and       ;;
;;      the JLP home brew production cart.                                  ;;
;;                                                                          ;;
;;      Both memory maps insert a ROM header that configures the basics,    ;;
;;      bypasses the EXEC, configures a stack and then jumps to the user    ;;
;;      code.  The 42K map also includes a small stub at $4800 that goes    ;;
;;      and switches out the ECS ROMs at $2xxx, $7xxx and $Exxx, so they    ;;
;;      do not interfere with the user's program.                           ;;
;;                                                                          ;;
;; ======================================================================== ;;
MACRO   ROMSETUP map, year, title, startaddr, stacksize

        LISTING "code"

_m.map  SET     _m.%map%

        IF  _m.map = _m.16K
_m.segs SET     3
_m.0s   SET     $5000
_m.0e   SET     $6FFF
_m.1s   SET     $D000
_m.1e   SET     $DFFF
_m.2s   SET     $F000
_m.2e   SET     $FFFF
_m.3s   SET     $0000
_m.3e   SET     $0000
_m.4s   SET     $0000
_m.4e   SET     $0000
_m.5s   SET     $0000
_m.5e   SET     $0000
_m.rams SET     $0000
_m.rame SET     $0000
_m.5pos SET     0
        ENDI

        IF  _m.map = _m.42K
_m.segs SET     6
_m.0s   SET     $5000
_m.0e   SET     $6FFF
_m.1s   SET     $A000
_m.1e   SET     $C020
_m.2s   SET     $C022
_m.2e   SET     $FFFF
_m.3s   SET     $2000
_m.3e   SET     $2FFF
_m.4s   SET     $7100
_m.4e   SET     $7FFF
_m.5s   SET     $4800
_m.5e   SET     $4FFF
_m.rams SET     $8040
_m.rame SET     $9EFF

        ORG     $4800
        ; Disable ECS ROMs so that they don't conflict with us
        MVII    #$2A5F, R0
        MVO     R0,     $2FFF
        MVII    #$7A5F, R0
        MVO     R0,     $7FFF
        MVII    #$EA5F, R0
        MVO     R0,     $EFFF

        B       $1041       ; resume boot
_m.5pos SET     $

        ENDI

_m.1pos     SET     _m.1s
_m.2pos     SET     _m.2s
_m.3pos     SET     _m.3s
_m.4pos     SET     _m.4s
.CARTMEM    SET     _m.rams

        IF _m.rams > 0
            ORG     _m.rams, _m.rams, "=RW"
            RMB     _m.rame - _m.rams + 1
        ENDI

        ORG     $5000
        BIDECLE $500D       ; 5000  MOB picture base   (points to NULL list)
        BIDECLE $500D       ; 5002  Process table      (points to NULL list)
        BIDECLE %startaddr% ; 5004  Program start address
        BIDECLE $500D       ; 5006  Bkgnd picture base (points to NULL list)
        BIDECLE $500F       ; 5008  GRAM pictures      (points to NULL list)
        BIDECLE $5014       ; 500A  Cartridge title/date
        DECLE   $03C0       ; 500C  Skip ECS, run code after title, no clicks
        DECLE   $0000       ; 500D  Screen border control
        DECLE   $0000       ; 500E  0 = color stack, 1 = f/b mode
        DECLE   1,1,1,1,1   ; 500F  Initial color stack 0 and 1: Blue
       
        IF %year% < 2000    ; Y2K hurray!
            IF %year% < 78
                DECLE  %year% + 100 ; $5014
            ELSE
                DECLE  %year%       ; $5014
            ENDI
        ELSE
            DECLE  %year% - 1900    ; $5014
        ENDI
       
        STRING  %title%, 0
_m.stk  SYSTEM  %stacksize%
        MVII    #_m.stk, R6
        B       %startaddr%

_m.cur  SET     0
_m.0pos SET     $

        __m_set_range 0

        LISTING "prev"
ENDM

;; ======================================================================== ;;
;;  ROMSEG  Switch between one of the various ROM segments.                 ;;
;;                                                                          ;;
;;  USAGE                                                                   ;;
;;      ROMSEG s                                                            ;;
;;                                                                          ;;
;;  where s is 0..n-1, and 'n' is the number of segments, determined by a   ;;
;;  previous call to ROMSETUP.                                              ;;
;;                                                                          ;;
;;  When the memory map is 16K, there are 3 ROM segments:                   ;;
;;                                                                          ;;
;;      SEGMENT     RANGE                                                   ;;
;;         0        $5000 - $6FFF                                           ;;
;;         1        $D000 - $DFFF                                           ;;
;;         2        $F000 - $FFFF                                           ;;
;;                                                                          ;;
;;  When the memory map is 42K, there are 6 ROM segments and an add'l RAM   ;;
;;  segment:                                                                ;;
;;                                                                          ;;
;;      SEGMENT     RANGE                                                   ;;
;;         0        $5000 - $6FFF                                           ;;
;;         1        $A000 - $C020                                           ;;
;;         2        $C022 - $FFFF                                           ;;
;;         3        $2000 - $2FFF                                           ;;
;;         4        $7100 - $7FFF                                           ;;
;;         5        $4800 - $4FFF                                           ;;
;;        RAM       $8040 - $9EFF                                           ;;
;;                                                                          ;;
;; ======================================================================== ;;
MACRO   ROMSEG s

    LISTING "code"
    IF %s% < _m.segs
        __m_chk_range
        __m_rec_range
        __m_set_range %s%
    ELSE
        ERR "Invalid segment number for memory map."
    ENDI

    LISTING "prev"
ENDM

;; ======================================================================== ;;
;;  ROMEND  Close any open segment, and set symbols in the assembler's own  ;;
;;          symbol table to indicate how much space was used and how much   ;;
;;          was left.                                                       ;;
;; ======================================================================== ;;
MACRO   ROMEND
    LISTING "code"
        __m_chk_range
        __m_rec_range
_m.0sz  SET _m.0pos - _m.0s 
_m.1sz  SET _m.1pos - _m.1s 
_m.2sz  SET _m.2pos - _m.2s 
_m.3sz  SET _m.3pos - _m.3s 
_m.4sz  SET _m.4pos - _m.4s 
_m.5sz  SET _m.5pos - _m.5s 
_m.size SET _m.0sz + _m.1sz + _m.2sz + _m.3sz + _m.4sz + _m.5sz
_m.0av  SET _m.0e - _m.0pos + 1
_m.1av  SET _m.1e - _m.1pos + 1
_m.2av  SET _m.2e - _m.2pos + 1
_m.3av  SET _m.3e - _m.3pos + 1
_m.4av  SET _m.4e - _m.4pos + 1
_m.5av  SET _m.5e - _m.5pos + 1
_m.avail SET _m.0av + _m.1av + _m.2av + _m.3av + _m.4av + _m.5av
    LISTING "prev"
ENDM


_m      PROC
@@16K   EQU 0 ; default $5000-$6FFF, $D000-$DFFF, $F000-$FFFF
@@42K   EQU 1 ; $4800-$6FFF, $7100-$7FFF, $A000-$C020, $C022-$FFFF,
              ; ... RAM $8040 - $9EFF
        ENDP

MACRO   __m_set_range_ r

        IF  _m.cur = %r%
_m.cs   SET _m.%r%s
_m.ce   SET _m.%r%e
_m.cpos SET _m.%r%pos
        ENDI
        ORG _m.cpos
ENDM

MACRO   __m_rec_range_ r

        IF  _m.cur = %r%
_m.%r%pos SET $
        ENDI
ENDM

MACRO   __m_set_range s
        
_m.cur  SET %s%
        __m_set_range_ 0
        __m_set_range_ 1
        __m_set_range_ 2
        __m_set_range_ 3
        __m_set_range_ 4
        __m_set_range_ 5
ENDM

MACRO   __m_rec_range
        
        __m_rec_range_ 0
        __m_rec_range_ 1
        __m_rec_range_ 2
        __m_rec_range_ 3
        __m_rec_range_ 4
        __m_rec_range_ 5
ENDM

MACRO   __m_overflow n
    IF _m.cur = %n%
        ERR "Segment overflow in segment %n%"
    ENDI
ENDM

MACRO   __m_chk_range

    IF  ($ < _m.cs) OR ($ - 1 > _m.ce)
        __m_overflow 0
        __m_overflow 1
        __m_overflow 2
        __m_overflow 3
        __m_overflow 4
        __m_overflow 5
    ENDI
ENDM

.SCRMEM     SET     $102
.SYSMEM     SET     $2F0

;; ======================================================================== ;;
;;  SCRATCH  -- Allocate variables in 8-bit Intellivision RAM.              ;;
;;  SYSTEM   -- Allocate variables in 16-bit Intellivision RAM.             ;;
;;  CARTRAM  -- Allocate variables in 16-bit Cartridge RAM.                 ;;
;; ======================================================================== ;;
MACRO       SCRATCH l
            EQU     .SCRMEM
            LISTING "off"
.SCRMEM     SET     .SCRMEM + %l%
            IF      .SCRMEM > $1F0
                ERR     "Scratch Memory Overflow"
            ENDI
            LISTING "prev"
ENDM

MACRO       SYSTEM  l
            EQU     .SYSMEM
            LISTING "off"
.SYSMEM     SET     .SYSMEM + %l%
            IF      .SYSMEM > $360
                ERR     "System Memory Overflow"
            ENDI
            LISTING "prev"
ENDM

MACRO       CARTRAM l
            EQU     .CARTMEM
            LISTING "off"
.CARTMEM    SET     .CARTMEM + %l%
            IF      .CARTMEM > _m.rame
                ERR     "Cartridge RAM overflow"
            ENDI
            LISTING "prev"

            ENDI
ENDM

;; ======================================================================== ;;
;;  End of file:  cart.mac                                                  ;;
;; ======================================================================== ;;