;; ======================================================================== ;; ;; UTIL.MAC Default Macro Set ;; ;; General utility macros. ;; ;; Joseph Zbiciak ;; ;; These macros are hereby released into the Public Domain. ;; ;; ;; ;; Macros defined in this file: ;; ;; ;; ;; SETISR a, r Sets ISR vector to point to 'a'. 'r' is a temp. ;; ;; MVOD r, a Writes 'r' as double-byte-data to 'a'. ;; ;; MVOD@ r, p Writes 'r' as double-byte-date via 'p'. ;; ;; COPY s, t, d Copies address 's' to address 'd' via 't'. ;; ;; COPY@ s, t, d Copies from ptr 's' to ptr 'd' via 't'. ;; ;; CALLD label Calls 'label' and disables interrupts. ;; ;; LOOP r, l Decrements 'r' and loops to 'l' if non-zero. ;; ;; LOOPPL r, l Decrements 'r' and loops to 'l' if non-negative. ;; ;; SWITCH r, tbl Implements switch-case via branch lookup table. ;; ;; SWITCHB r Implements switch-case via series of branches. ;; ;; SPINEQ a, r Loop while value at address is equal to 'r' ;; ;; SPINEQ@ p, r Loop while value at address is equal to 'r' ;; ;; SPINNE a, r Loop while value at address is not equal to 'r' ;; ;; SPINNE@ p, r Loop while value at address is not equal to 'r' ;; ;; SLRC r, n Shift Logically Right into Carry/Overflow ;; ;; DSLLC h, l, n 32-bit Shift Logically Left into Carry/Overflow ;; ;; DSLRC h, l, n 32-bit Shift Logically Right into Carry/Overflow ;; ;; DSARC h, l, n 32-bit Shift Arithmetically Right into Carry/Ov. ;; ;; SLLn r, n Shift Logically Left ;; ;; SLLCn r, n Shift Logically Left into Carry/Overflow ;; ;; SLRn r, n Shift Logically Right ;; ;; SARn r, n Shift Arithmetically Right ;; ;; SLRCn r, n Shift Logically Right into Carry/Overflow ;; ;; SARCn r, n Shift Arithmetically Right into Carry/Overflow ;; ;; DSLLCn h, l, n 32-bit Shift Logically Left into Carry/Overflow ;; ;; DSLRCn h, l, n 32-bit Shift Logically Right into Carry/Overflow ;; ;; DSARCn h, l, n 32-bit Shift Arithmetically Right into Carry/Ov. ;; ;; DADDR h0,l0,h1,l1 Add 32-bit value in h0:l0 to value in h1:l1 ;; ;; DSUBR h0,l0,h1,l1 Subtract 32-bit value in h0:l0 from h1:l1 ;; ;; XCHG a, b Exchange contents of reg 'a' with reg 'b' ;; ;; ORR a, b Bitwise OR register 'a' into register 'b' ;; ;; ORI a, b Bitwise OR constant 'a' into register 'b' ;; ;; OR@ a, b Bitwise OR value at ptr 'a' into register 'b' ;; ;; ORD a, b Bitwise OR value at addr 'a' into register 'b' ;; ;; XTND8 r Sign-extend 'r' from 8 to 16 bits. ;; ;; UNPKL r, l Unpack 8-bit values to lower byte of two regs. ;; ;; UNPKH r, l Unpack 8-bit values to upper byte of two regs. ;; ;; pack(h,l) Pack two 8-bit constants into a 16-bit constant ;; ;; packn(h,l) Pack two 4-bit constants into an 8-bit constant ;; ;; ;; ;; ======================================================================== ;; IF (DEFINED _UTIL_MAC) = 0 _UTIL_MAC EQU 1 MACRO ICALL mod INCLUDE "engine/%mod%.asm" ENDM MACRO __ LISTING "off" ENDM MACRO _ LISTING "on" ENDM ;; ======================================================================== ;; ;; SETISR a, r ;; ;; Sets ISR vector to point to address 'a'. Trashes 'r'. ;; ;; ;; ;; ARGUMENTS ;; ;; a Address of ISR ;; ;; r Register to use as a temporary (R0 thru R3) ;; ;; ======================================================================== ;; ;MACRO SETISR a, r ; MVII #%a%, %r% ; MVOD %r%, $100 ;ENDM IF 0 MACRO SETISR a LISTING "code" ;; ------------------------------------------------------------------------ ;; ;; ;; ;; 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 ;; ;; W 0: [ Bits 5:0 of ISR addr ] 0 0 0 0 0 0 0 1 0 0 ;; ;; W 1: [ Bits 11:6 of ISR addr ] 0 1 [ bits 15:10 of _SETISR ] 0 0 ;; ;; W 2: [ 15:12 of ISR ] 0 0 [ . . . . Bits 9:0 of _SETISR . . . . ] ;; ;; ;; ;; Due to how the number is reconstructed: ;; ;; ;; ;; ISR 3:0 gets XORd with: Bits 15:12 of _SETISR, bits 3:0 of _SETISR ;; ;; ISR 5:4 gets XORd with: 01, bits 5:4 of _SETISR ;; ;; ISR 11:6 gets XORd with: 0100, Bits 9:6 of _SETISR ;; ;; ISR 15:12 gets by unadulterated. ;; ;; ;; ;; ------------------------------------------------------------------------ ;; DECLE $004 OR ((((%a%) AND $3F) XOR (_SETISR SHR 12) XOR (_SETISR AND $3F) XOR $10) SHL 10) DECLE $100 OR (($FC00 AND _SETISR) SHR 8) OR (((((%a%) SHR 6) AND $3F) XOR ((_SETISR SHR 6) AND $F) XOR 4) SHL 10) DECLE ($3FF AND _SETISR) OR ($F000 AND (%a%)) LISTING "prev" ENDM ELSE MACRO SETISR a CALL _SETISR DECLE %a% ENDM ENDI ;; ======================================================================== ;; ;; MVOD r, a ;; ;; Write a 16-bit value as Double-Byte-Data. Leaves its first operand ;; ;; byte-swapped. ;; ;; ;; ;; ARGUMENTS ;; ;; r Register to write. Must be one of R0, R1, R2, R3 ;; ;; a Address to write to. Macro will write to Addr and Addr + 1. ;; ;; ======================================================================== ;; MACRO MVOD r, a MVO %r%, %a% SWAP %r% MVO %r%, %a% + 1 ENDM ;; ======================================================================== ;; ;; MVOD@ r, p ;; ;; Write a 16-bit value as Double-Byte-Data. Leaves its first operand ;; ;; byte-swapped. If 'p' is R4, R5, or R6, it is left pointing after the ;; ;; written data. If 'p' is R1, R2, or R3, it is left pointing at the ;; ;; same location. ;; ;; ;; ;; ARGUMENTS ;; ;; r Register to write. Must be one of R0, R1, R2, R3 ;; ;; p Register containing address to write to. Must be one of R1 ;; ;; through R6. ;; ;; ======================================================================== ;; MACRO MVOD@ r, p MVO@ %r%, %p% SWAP %r% MVO@ %r%, %p% ENDM ;; ======================================================================== ;; ;; COPY s, t, d ;; ;; Copies a value from the source address 's' to the destination address ;; ;; 'd', holding the copy in temporary register 't'. ;; ;; ;; ;; ARGUMENTS ;; ;; s Source address (R1 thru R6) ;; ;; t Temporary register (R0 thru R6) ;; ;; d Destination address (R1 thru R6) ;; ;; ======================================================================== ;; MACRO COPY s, t, d MVI %s%, %t% MVO %t%, %d% ENDM ;; ======================================================================== ;; ;; COPY@ s, t, d ;; ;; Copies a value from the source pointer 's' to the destination pointer ;; ;; 'd', holding the copy in temporary register 't'. ;; ;; ;; ;; ARGUMENTS ;; ;; s Source pointer (R1 thru R6) ;; ;; t Temporary register (R0 thru R6) ;; ;; d Destination pointer (R1 thru R6) ;; ;; ======================================================================== ;; MACRO COPY@ s, t, d MVI@ %s%, %t% MVO@ %t%, %d% ENDM ;; ======================================================================== ;; ;; CALLD label ;; ;; Call and disable interrupts. Return address goes to R5. ;; ;; ======================================================================== ;; MACRO CALLD label JSRD R5, %label% ENDM ;; ======================================================================== ;; ;; LOOP r, l ;; ;; Implements a simple looping construct. Decrements 'r', and branches ;; ;; to the label 'l' if it's non-zero. ;; ;; ;; ;; ARGUMENTS ;; ;; r Register to decrement. ;; ;; l Label to jump to if register is non-zero. ;; ;; ;; ;; EXAMPLE ;; ;; MVII #10, R0 ;; ;; @@l MVO@ R1, R4 ;\__ This iterates 10 times. ;; ;; LOOP R0, @@l ;/ ;; ;; ======================================================================== ;; MACRO LOOP r, l DECR %r% BNEQ %l% ENDM ;; ======================================================================== ;; ;; LOOPPL r, l ;; ;; Implements a simple looping construct. Decrements 'r', and branches ;; ;; to the label 'l' if it's non-negative. ;; ;; ;; ;; ARGUMENTS ;; ;; r Register to decrement. ;; ;; l Label to jump to if register is non-zero. ;; ;; ;; ;; EXAMPLE ;; ;; MVII #10, R0 ;; ;; @@l MVO@ R1, R4 ;\__ This iterates 11 times. ;; ;; LOOPPL R0, @@l ;/ ;; ;; ======================================================================== ;; MACRO LOOPPL r, l DECR %r% BPL %l% ENDM ;; ======================================================================== ;; ;; SWITCH r, tbl ;; ;; Implements a simple "jump table". The argument 'tbl' points to an ;; ;; array of labels representing the 'cases' of the SWITCH. The register ;; ;; provided by the user will be left pointing into the jump table. If ;; ;; the register is auto-incrementing, it will point *after* the entry ;; ;; used, otherwise it will point at the entry used. ;; ;; ;; ;; The switch table can appear immediately after SWITCH, or it can ;; ;; appear elsewhere in the program. This SWITCH macro only works with ;; ;; 16-bit ROM widths. ;; ;; ;; ;; ARGUMENTS ;; ;; r Value to "switch" on. Must be a register R1 thru R5. Value ;; ;; in the register must be in range 0...n. ;; ;; ;; ;; EXAMPLE ;; ;; SWITCH R0, @@swtbl ;; ;; @@swtbl: ;; ;; DECLE @@case_0, @@case_1, @@case_2, @@case_3 ;; ;; ;; ;; @@case_0: ;; ;; ;.... ;; ;; ;; ;; @@case_1: ;; ;; ;.... ;; ;; ;; ;; @@case_2: ;; ;; ;.... ;; ;; ;; ;; @@case_3: ;; ;; ;.... ;; ;; ;; ;; ======================================================================== ;; MACRO SWITCH r, tbl ADDI #%tbl%, %r% MVI@ %r%, PC ENDM ;; ======================================================================== ;; ;; SWITCHB r ;; ;; Implements a simple "jump table". User must follow "SWITCH" with a ;; ;; series of "B @@label" for each of the cases. The register supplied to ;; ;; the switch will be left doubled. ;; ;; ;; ;; This form of switch statement requires the branch instructions to ;; ;; immediately follow the SWITCHB. This version may be more useful if ;; ;; retaining the value of 'r' is required. ;; ;; ;; ;; ARGUMENTS ;; ;; r Value to "switch" on. Must be in range 0...n. Value will be ;; ;; doubled after this macro. ;; ;; ;; ;; EXAMPLE ;; ;; SWITCHB R0 ;; ;; B @@case_0 ;; ;; B @@case_1 ;; ;; B @@case_2 ;; ;; B @@case_3 ;; ;; ======================================================================== ;; MACRO SWITCHB r ADDR %r%, %r% ADDR %r%, PC ENDM ;; ======================================================================== ;; ;; SPINEQ a, r ;; ;; Loop while value at 'a' equals value held in 'r'. Can be useful for ;; ;; synchronizing with ISRs. ;; ;; ;; ;; ARGUMENTS ;; ;; a Address to spin on. ;; ;; r Value to look for. ;; ;; ;; ;; EXAMPLE ;; ;; CLRR R0 ;; ;; MVO R0, FLAG ; Clear flag ;; ;; SPINEQ FLAG, R0 ; Wait until ISR sets flag. ;; ;; ======================================================================== ;; MACRO SPINEQ a, r CMP %a%, %r% ; 2 words BEQ $ - 2 ; $ avoids label. ;-) ENDM ;; ======================================================================== ;; ;; SPINEQ@ p, r ;; ;; Loop while value pointed to by 'a' equals value held in 'r'. Can be ;; ;; useful for synchronizing with ISRs. ;; ;; ;; ;; NOTE: 'p' should be a non-incrementing pointer! If an incrementing ;; ;; pointer is used, then this becomes a sort of "string search" instead. ;; ;; ;; ;; ARGUMENTS ;; ;; p Address to spin on. ;; ;; r Value to look for. ;; ;; ;; ;; EXAMPLE ;; ;; CLRR R0 ;; ;; MVO@ R0, R3 ; Clear flag ;; ;; SPINEQ@ R3, R0 ; Wait until ISR sets flag. ;; ;; ======================================================================== ;; MACRO SPINEQ@ a, r CMP@ %a%, %r% ; 1 words BEQ $ - 1 ; $ avoids label. ;-) ENDM ;; ======================================================================== ;; ;; SPINNE a, r ;; ;; Loop while value at 'a' does not equal value held in 'r'. ;; ;; Can be useful for synchronizing with ISRs. ;; ;; ;; ;; ARGUMENTS ;; ;; a Address to spin on. ;; ;; r Value to look for. ;; ;; ;; ;; EXAMPLE ;; ;; MVII #3, R0 ;; ;; MVO@ R0, CNT ; Wait 3 frames ;; ;; CLRR R0 ;; ;; SPINNE CNT, R0 ; Wait until ISR's counter expires ;; ;; ======================================================================== ;; MACRO SPINNE a, r CMP %a%, %r% ; 2 words BNEQ $ - 2 ; $ avoids label. ;-) ENDM ;; ======================================================================== ;; ;; SPINEQ@ p, r ;; ;; Loop while value pointed to by 'a' does not equal value held in 'r'. ;; ;; Can be useful for synchronizing with ISRs. ;; ;; ;; ;; NOTE: 'p' should be a non-incrementing pointer! If an incrementing ;; ;; pointer is used, then this becomes a sort of "string search" instead. ;; ;; ;; ;; ARGUMENTS ;; ;; p Address to spin on. ;; ;; r Value to look for. ;; ;; ;; ;; EXAMPLE ;; ;; MVII #3, R0 ;; ;; MVO@ R0, R3 ; Wait 3 frames ;; ;; CLRR R0 ;; ;; SPINNE@ R3, R0 ; Wait until ISR's counter expires ;; ;; ======================================================================== ;; MACRO SPINNE@ a, r CMP@ %a%, %r% ; 1 words BNEQ $ - 1 ; $ avoids label. ;-) ENDM ;; ======================================================================== ;; ;; SHIFT MACROS ;; ;; The following set of macros implement "extended shifts", extending the ;; ;; shift amount of the existing CP-1600 shift instructions. ;; ;; ;; ;; WARNING: These shift sequences are non-interruptible, so be careful. ;; ;; Shift amounts larger than 8, and double-precision shift amounts larger ;; ;; than 4 can potentially affect video display in a negative way. ;; ;; ;; ;; The following shift macros are provided: ;; ;; ;; ;; SLRC h, l, n ; Shift logically right into Carry/Overflow ;; ;; DSLLC h, l, n ; 32-bit Shift Logically Left into Carry/Overflow ;; ;; DSLRC h, l, n ; 32-bit Shift Logically Right into Carry/Overflow ;; ;; DSARC h, l, n ; 32-bit Shift Arithmetically Right into Carry/Ov. ;; ;; SLLn r, n ; Shift Logically Left ;; ;; SLLCn r, n ; Shift Logically Left into Carry/Overflow ;; ;; SLRn r, n ; Shift Logically Right ;; ;; SARn r, n ; Shift Arithmetically Right ;; ;; SLRCn r, n ; Shift Logically Right into Carry/Overflow ;; ;; SARCn r, n ; Shift Arithmetically Right into Carry/Overflow ;; ;; DSLLCn h, l, n ; 32-bit Shift Logically Left into Carry/Overflow ;; ;; DSLRCn h, l, n ; 32-bit Shift Logically Right into Carry/Overflow ;; ;; DSARCn h, l, n ; 32-bit Shift Arithmetically Right into Carry/Ov. ;; ;; ;; ;; The Dxxxx macros implement a double-precision (32-bit) shift across ;; ;; two registers. The variants lacking the 'n' suffix only support ;; ;; shift amounts of 1 or 2. ;; ;; ;; ;; For each of the above, the final shift in the expanded series is ;; ;; always a shift-by-2, except of course in the case where the user ;; ;; specifies a shift of exactly 1. ;; ;; ======================================================================== ;; ;; ======================================================================== ;; ;; SLRC r, n ;; ;; Shift logical right into carry/overflow. ;; ;; ;; ;; ARGUMENTS ;; ;; r Register to shift right. ;; ;; n Shift amount. Must be a constant expression that evaluates to ;; ;; 1 or 2. ;; ;; ======================================================================== ;; MACRO SLRC r, n LISTING "code" IF ((%n%) < 1) OR ((%n%) > 2) ERR "SLRC shift amount out of range!" ENDI IF (%n%) = 1 CLRC RRC %r%, 1 ENDI IF (%n%) = 2 SARC %r%, %n% ANDI #$3FFF, %r% ENDI LISTING "prev" ENDM ;; ======================================================================== ;; ;; DSLLC, DSARC, DSLRC ;; ;; Double-precision shift macros. These macros combine a shift with a ;; ;; rotate in order to shift a 32-bit value across two registers. ;; ;; ;; ;; ARGUMENTS ;; ;; h Upper register of register pair to shift (R0 through R3) ;; ;; l Lower register of register pair to shift (R0 through R3) ;; ;; n Shift amount. Must be a constant expression that evaluates to ;; ;; 1 or 2. ;; ;; ======================================================================== ;; MACRO DSLLC h, l, n ;; double-precision shift logical left into carry/over SLLC %l%, %n% RLC %h%, %n% ENDM MACRO DSARC h, l, n ;; double-precision shift arith right into carry/over SARC %h%, %n% RRC %l%, %n% ENDM MACRO DSLRC h, l, n ;; double-precision shift logical right into carry/over LISTING "code" IF ((%n%) < 1) OR ((%n%) > 2) ERR "DSLRC shift amount out of range!" ENDI IF (%n%) = 1 CLRC RRC %h%, 1 RRC %l%, 1 ENDI IF (%n%) = 2 SARC %h%, %n% RRC %l%, %n% ANDI #$3FFF, %h% ENDI LISTING "prev" ENDM ;; ======================================================================== ;; ;; __shft_n s, r, n ;; ;; Generic "shift expansion" macro set. Expands shift amounts out to a ;; ;; series of shifts by 1 or 2. The macro uses a repeat block to generate ;; ;; the series of shifts. ;; ;; ======================================================================== ;; MACRO __shft_n s, r, n LISTING "code" IF ((%n%) < 1) ERR "Shift amount out of range. Must be >= 1." ELSE IF ((%n%) AND 1) %s% %r%, 1 ENDI RPT ((%n%) SHR 1) %s% %r%, 2 ENDR ENDI LISTING "prev" ENDM ;; ======================================================================== ;; ;; SLLn, SLLCn, SARn, SARCn, SLRn, SLRCn ;; ;; Shift macros that expand the allowed constant range on the CP-1600's ;; ;; shift instructions. These rely on the generic macro above to expand ;; ;; each macro out as a series of shifts. ;; ;; ;; ;; ARGUMENTS ;; ;; r Register to shift (R0 thru R3) ;; ;; n Shift amount. Must be a constant expression >= 1 ;; ;; ======================================================================== ;; MACRO SLLn r, n __shft_n SLL, %r%, %n% ENDM MACRO SLLCn r, n __shft_n SLLC, %r%, %n% ENDM MACRO SARn r, n __shft_n SAR, %r%, %n% ENDM MACRO SARCn r, n __shft_n SARC, %r%, %n% ENDM MACRO SLRn r, n __shft_n SLR, %r%, %n% ENDM MACRO SLRCn r, n LISTING "code" IF ((%n%) < 3) SLRC %r%, %n% ELSE __shft_n SLR, %r%, ((%n%) - 2) SARC %r%, 2 ENDI LISTING "prev" ENDM ;; ======================================================================== ;; ;; DSLLCn, DSARCn, DSLRCn ;; ;; Shift macros that expand the allowed constant range on the double- ;; ;; precision shift macros. These rely on the generic macro above to ;; ;; expand each macro out as a series of shifts, and on the Dxxxx shift ;; ;; macros to provide the double-precision shifts. ;; ;; ;; ;; Notice how the 'h' and 'l' operands are grouped together as a single ;; ;; operand when invoking __shft_n using the [] grouping operators. ;; ;; ;; ;; ARGUMENTS ;; ;; h Upper register of register pair to shift (R0 through R3) ;; ;; l Lower register of register pair to shift (R0 through R3) ;; ;; n Shift amount. Must be a constant expression >= 1 ;; ;; ======================================================================== ;; MACRO DSLLCn h, l, n __shft_n DSLLC, [%h%,%l%], %n% ENDM MACRO DSARCn h, l, n __shft_n DSARC, [%h%,%l%], %n% ENDM MACRO DSLRCn h, l, n __shft_n DSARC, [%h%,%l%], %n% ANDI #($FFFF SHR %n%), %h% ENDM ;; ======================================================================== ;; ;; DADDR h0,l0, h1,l1 ;; ;; Adds register pair h0:l0 to register pair h1:l1 (32-bit ADDR). ;; ;; ======================================================================== ;; MACRO DADDR h0,l0,h1,l1 ADDR %l0%, %l1% ; add lower halves ADCR %h1% ; propogate carry into upper half ADDR %h0%, %h1% ; add upper halves ENDM ;; ======================================================================== ;; ;; DSUBR h0,l0, h1,l1 ;; ;; Subtracts register pair h0:l0 from register pair h1:l1 (32-bit SUBR). ;; ;; ======================================================================== ;; MACRO DSUBR h0,l0,h1,l1 SUBR %l0%, %l1% ; subtract lower halves ADCR %h1% ; propogate borrow to upper half DECR %h1% ; turn borrow into not-borrow SUBR %h0%, %h1% ; subtract upper halves ENDM ;; ======================================================================== ;; ;; XCHG a, b ;; ;; Exchanges two values in registers using three-XOR-swap. ;; ;; ;; ;; ARGUMENTS ;; ;; a Register 1 to swap ;; ;; b Register 2 to swap ;; ;; ======================================================================== ;; MACRO XCHG a, b XORR %a%, %b% XORR %b%, %a% XORR %a%, %b% ENDM ;; ======================================================================== ;; ;; ORR a, b -- Register-mode OR ;; ;; Bitwise-ORs register 'a' into register 'b' ;; ;; ;; ;; ARGUMENTS ;; ;; a Register with value to "OR" ;; ;; b Register holding result ;; ;; ======================================================================== ;; MACRO ORR a, b COMR %a% ANDR %a%, %b% COMR %a% XORR %a%, %b% ENDM ;; ======================================================================== ;; ;; ORI a, b -- Immediate mode OR ;; ;; Bitwise-ORs constant 'a' into register 'b' ;; ;; ;; ;; ARGUMENTS ;; ;; a Value to "OR" ;; ;; b Register holding result ;; ;; ======================================================================== ;; MACRO ORI a, b ANDI #($FFFF AND (NOT (%a%))), %b% XORI #($FFFF AND (%a%) ), %b% ENDM ;; ======================================================================== ;; ;; OR@ a, b -- Indirect mode OR ;; ;; Bitwise-ORs value pointed to by 'a' into register 'b'. This macro ;; ;; requires one word of stack space. ;; ;; ;; ;; ARGUMENTS ;; ;; a Address holding value to OR. ;; ;; b Register holding result. (R0 thru R5) ;; ;; ======================================================================== ;; MACRO OR@ a, b PSHR %b% COMR %b% AND@ %a%, %b% XOR@ SP, %b% ENDM ;; ======================================================================== ;; ;; ORD a, b -- Direct mode OR ;; ;; Bitwise-ORs value at address 'a' into register 'b'. This macro ;; ;; requires one word of stack space. ;; ;; ;; ;; This macro is named "ORD" so as to not conflict with the assembler ;; ;; keyword "OR". ;; ;; ;; ;; ARGUMENTS ;; ;; a Address holding value to OR. ;; ;; b Register holding result. (R0 thru R5) ;; ;; ======================================================================== ;; MACRO ORD a, b PSHR %b% COMR %b% AND %a%, %b% XOR@ SP, %b% ENDM ;; ======================================================================== ;; ;; XTND8 r ;; ;; Sign-extend 8-bit value to 16 bits. Assumes value has 0s in upper ;; ;; half already. ;; ;; ;; ;; ARGUMENTS ;; ;; r Register to extend from 8 to 16 bits ;; ;; ======================================================================== ;; MACRO XTND8 r XORI #$80, %r% SUBI #$80, %r% ENDM ;; ======================================================================== ;; ;; UNPKL r, l ;; ;; Unpack 16-bit value in 'r' to two 8-bit values in 'r' and 'l'. ;; ;; The unpacked values are left in the lower 8 bits of 'r' and 'l'. ;; ;; ;; ;; ARGUMENTS ;; ;; r Register to unpack. When done, this will hold upper 8 bits. ;; ;; Note: 'r' must be R0 through R3. ;; ;; l Register to place lower 8 bits in. ;; ;; ======================================================================== ;; MACRO UNPKL r, l MOVR %r%, %l% ANDI #$FF, %l% XORR %l%, %r% SWAP %r% ENDM ;; ======================================================================== ;; ;; UNPKH r, l ;; ;; Unpack 16-bit value in 'r' to two 8-bit values in 'r' and 'l'. ;; ;; The unpacked values are left in the upper 8 bits of 'r' and 'l'. ;; ;; ;; ;; ARGUMENTS ;; ;; r Register to unpack. When done, this will hold upper 8 bits. ;; ;; Note: 'r' must be R0 through R3. ;; ;; l Register to place lower 8 bits in. ;; ;; ======================================================================== ;; MACRO UNPK r, l MOVR %r%, %l% ANDI #$FF, %l% XORR %l%, %r% SWAP %l% ENDM ;; ======================================================================== ;; ;; pack(h,l) ;; ;; Pack two 8 bit constants into a 16-bit constant. ;; ;; ;; ;; ARGUMENTS ;; ;; h Value to put in upper 8 bits ;; ;; l Value to put in lower 8 bits ;; ;; ;; ;; NOTE: This macro accepts and returns constants. No error checking ;; ;; performed. ;; ;; ======================================================================== ;; MACRO pack(h,l) ((((%h%) AND $FF) SHL 8) OR ((%l%) AND $FF)) ENDM ;; ======================================================================== ;; ;; packn(h,l) ;; ;; Pack two 4 bit constants into an 8-bit constant. ;; ;; ;; ;; ARGUMENTS ;; ;; h Value to put in upper 4 bits ;; ;; l Value to put in lower 4 bits ;; ;; ;; ;; NOTE: This macro accepts and returns constants. No error checking ;; ;; performed. ;; ;; ======================================================================== ;; MACRO packn(h,l) ((((%h%) AND $F) SHL 4) OR ((%l%) AND $F)) ENDM ENDI