Error
2 revisions of this difference (7399 and 14772) were not found.
This is usually caused by following an outdated diff link to a page that has been deleted. Details can be found in the deletion log.
Latest revision as of 08:12, 4 December 2010
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 ;; ;; ======================================================================== ;;