Difference between revisions of "Initmem.asm and initmem.mac"
(→Using INITMEM.2 to initialize some game variables) |
|||
Line 89: | Line 89: | ||
DIFF DECLE EASY, MEDIUM, HARD | DIFF DECLE EASY, MEDIUM, HARD | ||
− | + | </pre> | |
− | + | And then elsewhere ... | |
− | + | <pre> | |
; R1 = 0 for easy, 1 for medium, 2 for hard | ; R1 = 0 for easy, 1 for medium, 2 for hard | ||
ADDI #DIFF, R1 ; index into table to pick which initializer to use | ADDI #DIFF, R1 ; index into table to pick which initializer to use |
Revision as of 05:08, 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.
Contents
Functions Provided
initmem.mac
Macro | Action performed | Notes |
---|---|---|
INIT val, addr | Output an initializer record to set location addr to val | Both val and addr must be computable at assembly time |
INIT_DONE | Terminate an initializer list | Must immediately follow last INIT line in an initializer list. |
EMIT_CST8 | Output 8-bit constant table | Must be used once per program, after all initializer lists. |
EMIT_CST16 | Output 16-bit constant table | Must 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 point | Function provided | Notes |
---|---|---|
INITMEM | Initialize a set of variables in memory | Initializer list immediately follows CALL. |
INITMEM.1 | Pointer to initializer list immediately follows CALL. | |
INITMEM.2 | Register 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.
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 ;; ;; ======================================================================== ;;