Difference between revisions of "Initmem.asm and initmem.mac"

From Intellivision Wiki
Jump to: navigation, search
(Notes)
m (Notes)
Line 104: Line 104:
 
Include initmem.mac near the top of your program.  Include initmem.asm where convenient.
 
Include initmem.mac near the top of your program.  Include initmem.asm where convenient.
  
All initialized variables must reside in memory between $0F0 and $35F.  This covers the [[Scratchpad RAM]], [[System RAM]] (including [[BACKTAB]]), both [[PSG]]s.  It does not cover the [[Graphics RAM]], nor does it cover the RAM in the [[Entertainment Computer System|ECS]] unit.
+
All initialized variables must reside in memory between $0F0 and $35F.  This covers the [[Scratchpad RAM]], [[System RAM]] (including [[BACKTAB]]), and both [[PSG]]s.  It does not cover the [[Graphics RAM]], nor does it cover the RAM in the [[Entertainment Computer System|ECS]] unit.
  
 
= Source Code =
 
= Source Code =

Revision as of 05:20, 7 September 2008


These are two files that work together to provide a convenient way of initializing several variables at once. This is useful, for example, when starting a game, or when switching between phases of a game. initmem.mac provides the macros that do all the work of recording initializers and encoding them. initmem.asm provides the actual function that unpacks the initialization data when the time comes.

Functions Provided

initmem.mac

MacroAction performedNotes
INIT val, addrOutput an initializer record to set location addr to valBoth val and addr must be computable at assembly time
INIT_DONETerminate an initializer listMust immediately follow last INIT line in an initializer list.
EMIT_CST8Output 8-bit constant tableMust be used once per program, after all initializer lists.
EMIT_CST16Output 16-bit constant tableMust be used once per program, after all initializer lists.


The INIT and INIT_DONE macros allow you to build up an initializer list. You can put labels ahead of or within an initializer list to indicate alternate starting points within the list. An initializer list is simply a series of INIT lines followed by a single line with INIT_DONE.

   INIT val, addr
   INIT val, addr
   ;...
   INIT val, addr
   INIT val, addr
   INIT_DONE

The EMIT_CST8 and EMIT_CST16 output lookup tables that the INITMEM function uses to decode the initializer lists. These two macros must each be called precisely once somewhere in your program, after all initializer lists have been seen by the assembler. They take no arguments.


initmem.asm

Entry pointFunction providedNotes
INITMEMInitialize a set of variables in memoryInitializer list immediately follows CALL.
INITMEM.1Pointer to initializer list immediately follows CALL.
INITMEM.2Register R4 contains pointer to initializer list.



See examples and code below for usage.

Examples

Using INITMEM to initialize some game variables

The following example assumes that the game has 5 variables named SCORE, LIVES, XPOS, YPOS and TIME. These variables may have been declared with the SCRATCH or SYSTEM macros, but don't need to have been.

   ; Set things up for the start of the game
   CALL    INITMEM
   INIT    0,      SCORE    ; Reset score
   INIT    4,      LIVES    ; Reset lives
   INIT    10,     XPOS     ; \_ put player in the middle of the screen
   INIT    6,      YPOS     ; /
   INIT    60,     TIME     ; Put 60 seconds remaining on the clock
   INIT_DONE

Using INITMEM.1 to initialize some game variables

Building off the example above, suppose your game has an initializer list that you might want to use some subset of, or use from multiple places. Consider the previous example: It sets up variables for the start of a game. The last three entries might be useful for re-initializing some variables if the player loses a life. You could then write things like so:

   ; Set things up for the start of the game
   CALL    INITMEM
   INIT    0,      SCORE    ; Reset score
   INIT    4,      LIVES    ; Reset lives
restart_level:
   INIT    10,     XPOS     ; \_ put player in the middle of the screen
   INIT    6,      YPOS     ; /
   INIT    60,     TIME     ; Put 60 seconds remaining on the clock
   INIT_DONE

and then elsewhere:

   ; After player died, reinitialize some of the state but not all
   CALL    INITMEM.1
   DECLE   restart_level

Using INITMEM.2 to initialize some game variables

Sometimes you want to be able to pick from several different sets of initializers, such as when picking difficulty levels. One way to do this is to pick from multiple tables and then tell INITMEM which one to use.

EASY    INIT   1,      SPEED
        INIT   120,    TIME
        INIT   3,      CREEPS
        INIT_DONE

MEDIUM  INIT   3,      SPEED
        INIT   90,     TIME
        INIT   5,      CREEPS
        INIT_DONE

HARD    INIT   5,      SPEED
        INIT   60,     TIME
        INIT   7,      CREEPS
        INIT_DONE

DIFF    DECLE  EASY, MEDIUM, HARD

And then elsewhere ...

        
        ; R1 = 0 for easy, 1 for medium, 2 for hard   
        ADDI   #DIFF,  R1    ; index into table to pick which initializer to use
        MVI@   R1,     R4    ; read pointer to initializer
        CALL   INITMEM.2     ; set up the game variables

Notes

Be sure to call EMIT_CST8 and EMIT_CST16 precisely once in your program, after all INIT blocks. Otherwise your program will not assemble correctly.

Include initmem.mac near the top of your program. Include initmem.asm where convenient.

All initialized variables must reside in memory between $0F0 and $35F. This covers the Scratchpad RAM, System RAM (including BACKTAB), and both PSGs. It does not cover the Graphics RAM, nor does it cover the RAM in the ECS unit.

Source Code

initmem.mac

;* ======================================================================== *;
;*     This file is hereby placed into the public domain by its author,     *;
;*     Joseph Zbiciak.  It may therefore be incorporated into programs      *;
;*     with any license with no restrictions on the resulting program.      *;
;* ======================================================================== *;
                IF (DEFINED _INITMEM_MAC) = 0
_INITMEM_MAC    EQU     1

;; ======================================================================== ;;
;;  INITMEM                                                                 ;;
;;                                                                          ;;
;;  These are the macros that work with the INITMEM function.               ;;
;;                                                                          ;;
;;  MACROS:                                                                 ;;
;;                                                                          ;;
;;      INIT val, addr      Set 'addr' to 'value' as part of init list.     ;;
;;      INIT_DONE           Terminates an init list                         ;;
;;      EMIT_CST8           Outputs the accumulated CST8 table.             ;;
;;      EMIT_CST16          Outputs the accumulated CST16 table.            ;;
;;                                                                          ;;
;;  DETAILS                                                                 ;;
;;                                                                          ;;
;;  Initialize a set of variables to initial values.  Initialization        ;;
;;  records are 1 word each typically.  All variables are in the range      ;;
;;  $0F0 - $35F.  So, each record is formatted as follows.                  ;;
;;                                                                          ;;
;;        15                 10  9                                0         ;;
;;       +---------------------+-----------------------------------+        ;;
;;       |       Value         |             Address               |        ;;
;;       +---------------------+-----------------------------------+        ;;
;;                                                                          ;;
;;  A record of 0 terminates the initializer list.                          ;;
;;                                                                          ;;
;;  The "value" field is actually encoded.  $20 - $3F directly map to       ;;
;;  the constants $00 - $1F.  $00 - $1E map to constants in a separate      ;;
;;  constants table.  $1F indicates the constant appears in the following   ;;
;;  word.  Furthermore, constant table entries $00 - $0F are packed 8-bit   ;;
;;  contants, whereas $10 - $1E are unpacked 16-bit constants.              ;;
;;                                                                          ;;
;; ======================================================================== ;;

_init_const     SET 0
_init_escape    SET 0

;; ======================================================================== ;;
;;  Helper macros:  (Programs should not call these.)                       ;;
;;                                                                          ;;
;;      _init_try       See if constant is already allocated in CST tables. ;;
;;      _init_alloc8    Allocate an 8-bit constant.                         ;;
;;      _init_alloc16   Allocate a 16-bit constant.                         ;;
;;      _init_encode_constant   Given a cst, return bits 15:10 of init rec. ;;
;;      _emit_cst8      Output two entries of CST8 table if defined.        ;;
;;      _emit_cst16     Output an entry of CST16 table if defined.          ;;
;;                                                                          ;;
;; ======================================================================== ;;
MACRO           _init_try(v,c)
                IF      (%v% = _init_cst_%c%)
_init_const     SET     ($%c%) SHL 10
                ENDI
ENDM

MACRO           _init_alloc8(v,c)
                IF (_init_const = -1) AND (DEFINED _init_cst_%c%) = 0
_init_cst_%c%   EQU     (%v%)
_init_const     SET     ($%c%) SHL 10
                ENDI
ENDM
MACRO           _init_alloc16(v,c)
                IF (_init_const = -1) AND (DEFINED _init_cst_%c%) = 0
_init_cst_%c%   EQU     (%v%)
_init_const     SET     ($%c%) SHL 10
                ENDI
ENDM


MACRO           _init_encode_constant(v)

_init_escape    SET     0
_init_const     SET     -1 

                IF  (%v% >= $00) AND (%v% <= $1F)
_init_const     SET     (%v% + $20) SHL 10
                ELSE
                _init_try(%v%,00)
                _init_try(%v%,01)
                _init_try(%v%,02)
                _init_try(%v%,03)
                _init_try(%v%,04)
                _init_try(%v%,05)
                _init_try(%v%,06)
                _init_try(%v%,07)
                _init_try(%v%,08)
                _init_try(%v%,09)
                _init_try(%v%,0A)
                _init_try(%v%,0B)
                _init_try(%v%,0C)
                _init_try(%v%,0D)
                _init_try(%v%,0E)
                _init_try(%v%,0F)
                _init_try(%v%,10)
                _init_try(%v%,11)
                _init_try(%v%,12)
                _init_try(%v%,13)
                _init_try(%v%,14)
                _init_try(%v%,15)
                _init_try(%v%,16)
                _init_try(%v%,17)
                _init_try(%v%,18)
                _init_try(%v%,19)
                _init_try(%v%,1A)
                _init_try(%v%,1B)
                _init_try(%v%,1C)
                _init_try(%v%,1D)
                _init_try(%v%,1E)
                ENDI

                IF ((%v% - $20) AND $FF00) = 0
                _init_alloc8(%v%,00)
                _init_alloc8(%v%,01)
                _init_alloc8(%v%,02)
                _init_alloc8(%v%,03)
                _init_alloc8(%v%,04)
                _init_alloc8(%v%,05)
                _init_alloc8(%v%,06)
                _init_alloc8(%v%,07)
                _init_alloc8(%v%,08)
                _init_alloc8(%v%,09)
                _init_alloc8(%v%,0A)
                _init_alloc8(%v%,0B)
                _init_alloc8(%v%,0C)
                _init_alloc8(%v%,0D)
                _init_alloc8(%v%,0E)
                _init_alloc8(%v%,0F)
                ELSE
                _init_alloc16(%v%,10)
                _init_alloc16(%v%,11)
                _init_alloc16(%v%,12)
                _init_alloc16(%v%,13)
                _init_alloc16(%v%,14)
                _init_alloc16(%v%,15)
                _init_alloc16(%v%,16)
                _init_alloc16(%v%,17)
                _init_alloc16(%v%,18)
                _init_alloc16(%v%,19)
                _init_alloc16(%v%,1A)
                _init_alloc16(%v%,1B)
                _init_alloc16(%v%,1C)
                _init_alloc16(%v%,1D)
                _init_alloc16(%v%,1E)
                ENDI

                IF _init_const = -1
_init_const     EQU     $1F SHL 10
_init_escape    EQU     1
                ENDI
ENDM

MACRO           _emit_cst8(a,b)
                IF      (DEFINED _init_cst_%a%) <> 0
_init_tmp       SET     (((_init_cst_%a% - $20) AND $FF) SHL 8)
                ENDI
                IF      (DEFINED _init_cst_%b%) <> 0
_init_tmp       SET     _init_tmp OR ((_init_cst_%b% - $20) AND $FF)
                ENDI
                IF      (DEFINED _init_cst_%a%) OR (DEFINED _init_cst_%b%)
                DECLE   _init_tmp
                ENDI
ENDM

MACRO           _emit_cst16(a)
                IF  (DEFINED _init_cst_%a%) <> 0
                DECLE _init_cst_%a%
                ENDI
ENDM



;; ======================================================================== ;;
;;  INIT val, addr -- Output an initialization record                       ;;
;; ======================================================================== ;;
MACRO           INIT    v, a

                LISTING "code"
                _init_encode_constant(%v%)
                IF      _init_escape = 0
                DECLE   _init_const OR %a%
                ELSE
                DECLE   _init_const OR %a%, %v%
                ENDI
                LISTING "prev"
ENDM

;; ======================================================================== ;;
;;  INIT_DONE   Terminate an initialization list.                           ;;
;; ======================================================================== ;;
MACRO           INIT_DONE
                DECLE   0
ENDM


;; ======================================================================== ;;
;;  EMIT_CST8   Output the CST8 table.  This call must appear after all     ;;
;;              INIT lists due to assembler restrictions.                   ;;
;; ======================================================================== ;;
MACRO           EMIT_CST8

CST8            PROC
                LISTING "code"
                IF  (DEFINED _cst8_fix) AND ((CST8 AND $8000) = 0)
_init_tmp       SET     $
                ORG     _cst8_fix
                CLRC                ; patch up INITMEM
                ORG     _init_tmp
                ENDI

                _emit_cst8(00, 01)
                _emit_cst8(02, 03)
                _emit_cst8(04, 05)
                _emit_cst8(06, 07)
                _emit_cst8(08, 09)
                _emit_cst8(0A, 0B)
                _emit_cst8(0C, 0D)
                _emit_cst8(0E, 0F)
                LISTING "prev"
                ENDP
ENDM

;; ======================================================================== ;;
;;  EMIT_CST16  Output the CST16 table.  This call must appear after all    ;;
;;              INIT lists due to assembler restrictions.                   ;;
;; ======================================================================== ;;
MACRO           EMIT_CST16

CST16           PROC
                LISTING "code"
                _emit_cst16(10)
                _emit_cst16(11)
                _emit_cst16(12)
                _emit_cst16(13)
                _emit_cst16(14)
                _emit_cst16(15)
                _emit_cst16(16)
                _emit_cst16(17)
                _emit_cst16(18)
                _emit_cst16(19)
                _emit_cst16(1A)
                _emit_cst16(1B)
                _emit_cst16(1C)
                _emit_cst16(1D)
                _emit_cst16(1E)
                LISTING "prev"
                ENDP
ENDM

                ENDI

;; ======================================================================== ;;
;;  End of File:  initmem.mac                                               ;;
;; ======================================================================== ;;

initmem.asm

;* ======================================================================== *;
;*     This file is hereby placed into the public domain by its author,     *;
;*     Joseph Zbiciak.  It may therefore be incorporated into programs      *;
;*     with any license with no restrictions on the resulting program.      *;
;* ======================================================================== *;

;; ======================================================================== ;;
;;  INITMEM                                                                 ;;
;;                                                                          ;;
;;  Initialize a set of variables to initial values.  Initialization        ;;
;;  records are 1 word each typically.  All variables are in the range      ;;
;;  $0F0 - $35F.  So, each record is formatted as follows.                  ;;
;;                                                                          ;;
;;        15                 10  9                                0         ;;
;;       +---------------------+-----------------------------------+        ;;
;;       |       Value         |             Address               |        ;;
;;       +---------------------+-----------------------------------+        ;;
;;                                                                          ;;
;;  A record of 0 terminates the initializer list.                          ;;
;;                                                                          ;;
;;  The "value" field is actually encoded.  $20 - $3F directly map to       ;;
;;  the constants $00 - $1F.  $00 - $1E map to constants in a separate      ;;
;;  constants table.  $1F indicates the constant appears in the following   ;;
;;  word.  Furthermore, constant table entries $00 - $1F are packed 8-bit   ;;
;;  contants, whereas $20 - $2E are unpacked 16-bit constants.              ;;
;;                                                                          ;;
;;  INPUTS  (INITMEM.0)                                                     ;;
;;      R5  Initializer table.  Returns following table.                    ;;
;;                                                                          ;;
;;  INPUTS  (INITMEM.1)                                                     ;;
;;      R5  Pointer to initializer table.  Returns following pointer.       ;;
;;                                                                          ;;
;;  INPUTS  (INITMEM.2)                                                     ;;
;;      R5  Return address                                                  ;;
;;      R4  Initialization table                                            ;;
;;                                                                          ;;
;; ======================================================================== ;;

INITMEM     PROC
@@0:        MOVR    R5,     R4          ; Read init table from after CALL
            CLRR    R5                  ; Flag:  Return to R4.
            INCR    PC                  ; Skip MVI@
@@1:        MVI@    R5,     R4          ; Read init table from elsewhere
@@2:    


@@next:
            ;; ------------------------------------------------------------ ;;
            ;;  Decode next initializer symbol.  R1 will hold the 10-bit    ;;
            ;;  variable address and R2 holds the encoded constant value.   ;;
            ;; ------------------------------------------------------------ ;;
            MVI@    R4,     R2          ; Get next record
            MOVR    R2,     R1          ; Save for factoring addr from cst

            BEQ     @@done              ; Terminate on zero
            ANDI    #$3FF,  R1          ; Factor address from data
            XORR    R1,     R2          ; Clear address from data
            SWAP    R2                  ; \_ put data in 6 LSBs
            SLR     R2,     2           ; /

            SUBI    #$20,   R2          ; $20-$3F are cst $00-$1F.  
            BMI     @@cst_tbl           ; $00-$1F indicate "constant tbl"

            MVO@    R2,     R1          ; \_ Store decoded constant 
            B       @@next              ; /  and move to next initializer

            ;; ------------------------------------------------------------ ;;
            ;;  If the encoded value was $00 - $1F, then we have one of     ;;
            ;;  the following:                                              ;;
            ;;      $00 - $0F   8 bit constant in packed 8-bit table        ;;
            ;;      $10 - $1E   16 bit constant in 16-bit table             ;;
            ;;      $1F         Escape code for arbitrary constant.         ;;
            ;; ------------------------------------------------------------ ;;
@@cst_tbl   INCR    R2                  ; \_ $1F specifies an escape
            BEQ     @@escape            ; /

            ADDI    #$F,    R2          ; \_ $00-$0F specify 8 bit cst
            BMI     @@8bit              ; /  $10-$1E specify 16 bit cst

            ;; ------------------------------------------------------------ ;;
            ;;  R2 now $00-$0E for $10-$1E input.  Use it to index into     ;;
            ;;  16-bit constants table.                                     ;;
            ;; ------------------------------------------------------------ ;;
            ADDI    #CST16, R2
            MVI@    R2,     R0          ; \_ Copy constant of interest out
            MVO@    R0,     R1          ; /  
            B       @@next

@@escape    MVI@    R4,     R2          ; \_ Escape:  Copy next word to
            MVO@    R2,     R1          ; /  indicated address
            B       @@next

@@8bit:
            ;; ------------------------------------------------------------ ;;
            ;;  R2 is now -$10 to -$01 to specify one of the 16 8-bit csts. ;;
            ;;  We add CST8*2 to the index and then right-shift by 1 to     ;;
            ;;  determine the integer index.  The C bit will tell us hi or  ;;
            ;;  lo byte.  We bias the result up by $20 since $00-$1F don't  ;;
            ;;  require the constant table.                                 ;;
            ;; ------------------------------------------------------------ ;;
            ADDI    #$FFFF AND (CST8*2+16), R2      ; 
    IF (DEFINED CST8) AND ((CST8 AND $8000) = 0)
            CLRC                        ; MSB of CST8 was 0
    ELSE
_cst8_fix   SETC                        ; MSB of CST8 was 1
    ENDI
            RRC     R2                  ; Divide index by 2
            MVI@    R2,     R0          ; Get pair of 8 bit values
            ADCR    PC                  ; If idx%2 == 1, take lo half
            SWAP    R0                  ; else take hi half
            ANDI    #$FF,   R0          ; Clear away upper byte
            ADDI    #$20,   R0          ; 
            MVO@    R0,     R1          ; Store it out
            B       @@next

@@done      TSTR    R5                  ; Return after data?
            BEQ     @@jrr4              ; If R5==0, return after data.
            JR      R5                  ; Else return to R5.
@@jrr4      JR      R4                  
            ENDP

;; ======================================================================== ;;
;;  End of File:  initmem.asm                                               ;;
;; ======================================================================== ;;