Cart.mac
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 ;;
;; ======================================================================== ;;