Useful MASM Snippets
These snippets are mostly culled from the Principles of Operation.
Arithmetic
Get a -1 Efficiently
wadc 0,0 ; Wide Add Complement: Get a -1 in AC0
Use Variables Directly
Values in storage can be used directly as operands…
lwlda 0,FIRST ; get a value (32-bit)
lwsbi 0,SECOND ; substract value in SECOND from it
lwsta 0,RESULT ; store the 32-bit result
See also xwadd and xwmul.
Loops
XWISZ
Wide Increment and Skip if Zero…
nldai -5,0 ; get a constant -5 into AC0
xwsta 0,COUNTER ; put it in COUNTER
LOOP:
.
.
xwisz COUNTER ; Increment counter and skip if zero
wbr LOOP
.
.
COUNTER: .dword 0 ; Counter variable
See also xwdsz.
DO-Loops
wsub 0,0 ; Get an 0
xnsta 0,INDEX ; Init the counter in memory
LOOP:
nldai 5,0 ; Max index value
xndo 0,END-.,INDEX ; Start of Do-loop
.
.
wbr loop
END:
.
INDEX: .word 0 ; Storage for index value
or
wsub 0,0 ; Get an 0
xnsta 0,INDEX ; Init the counter in memory
LOOP:
nldai 5,0 ; Max index value
xwdo 0,END,INDEX ; Start of Do-loop
.
.
wbr loop
END:
.
INDEX: .dword 0 ; Storage for index value
Subroutines
Push-Jump Boilerplate
xpshj SUBROUT ; Call SUBROUT pushing PC onto the stack 1st
.
.
SUBROUT:
.
.
wpopj ; Pop return address and return (ACs not restored)
Call with Arguments
xpefb ARG1*2 ; Push byte addre of ARG1 onto stack
xpef ARG0 ; Push addr of ARG0 onto stack
lcall SUBR,0,2 ; Call SUBR with 2 args
XJSR Boilerplate
XJSR puts PC+3 into AC3 and jumps to a subroutine. The subroutine normally starts with one of WSAVS, WSAVR, WSSVS, or WSSVR and ends with WRTN.
SUB: wssvs 0 ; Save a wide return block
.
.
wrtn ; Return from subroutine call (ACs restored)
Arrays/Data Blocks
Access Word At Array[n]
llef 2,WORD_ARRAY ; Get base address of array of words
wadd 1,2 ; Add the word index from AC1
xnlda 0,0,2 ; Get the indexed word into AC0
.
.
WORD_ARRAY: .blk 16. ; Array of 16 words
Move Block of Characters/Bytes
llefb 2,DEST*2 ; Get the destination byte addr into AC2
llefb 3,SOURCE*2 ; Get the source byte addr into AC3
nldai 32.,0 ; Setup to move 32 bytes to dest
wmov 0,1 ; ...and also 32 bytes from source
wcmv ; Move them all
.
.
DEST: .blk 16. ; 32 bytes
SOURCE: .blk 16. ; 32 bytes
Move Block of Words
xlef 2,DEST ; Get the destination addr
xlef 3,SOURCE ; Get the source addr
nldai 7.,1 ; Setup to move 7 words
wblm ; Move them all
Conversion
Convert Binary to ASCII (1)
; CONVERT subroutine converts binary to ASCII decimal
; AC1 contains binary value
; AC2 contains byte pointer to text message
CONVERT:
wssvs 0
wmov 2,3 ; Use AC3 for byte pointer shifting
wadi 3,3 ; Add 3 to byte pointer
nldai 10.,2 ; AC2 = 10.
DLOOP: wsub 0,0
wdivs
iori 60,0 ; OR 60 for ASCII number
wstb 3,0 ; Store AC0 byte (bits 24-31)
; in byte addr in AC3
wsbi 1,3 ; Decrement the byte addr
mov 1,1,snr ; Did quotient get to 0?
wrtn ; Yes: return
wbr DLOOP ; No: loop back for another digit
Convert Signed 32-Bit to ASCII
; BIN2ASC subroutine converts 32-bit (signed) binary to ASCII decimal
; -------
; On entry AC1 contains the value to be converted
; AC2 contains a byte pointer for the result
;
; The conversion is primarily achieved by putting the integer into
; a floating-point accumulator, then reading it out as a signed unpacked
; decimal - which is then lightly post-processed to remove leading + and 0s.
BIN2ASC:
wssvs 0
wpsh 1,1
wmov 2,3
wflad 1,0 ; FPAC0 = AC1
wldai 3S26.+10.,1 ; Datatype 3 into AC1
; (signed unpacked dec. 10 places)
wsti 0 ; Store FPAC0 as decimal ASCII
wsub 0,0 ; AC0 = NULL
wstb 3,0 ; Store NULL at end of string
; now post-process the string:
wpop 1,1
wmov 2,0
B2ANEG: ; special case if value is negative
wslei -1,1
wbr B2A0
winc 0,0 ; move past -
B2A0:
; special case if value is zero
wseqi 0,1
wbr B2AR0
xjsr LCHOP ; remove sign
wadi 1,0 ; move past initial 0
B2AR0: xjsr LCHOP
wldb 0,2
wsnei 48.,2
wbr B2AR0
B2AEND: wrtn
String Handling
LCHOP (In-Place)
; Chop the 1st character off a null-terminated string (in-place)
; AC0 is a byte pointer to the string
LCHOP:
wssvs 0
wmov 0,1 ; copy string pointer to AC1
LCHOPL: winc 1,1 ; increment copied pointer
wldb 1,2 ; load the byte it points to into AC2
wstb 0,2 ; store that byte in AC0 (i.e. one byte behind)
winc 0,0 ; increment initial pointer
wseqi 0,2 ; was the character a NULL?
wbr LCHOPL ; no: so loop round
LCHOPEND: ; yes: all done
wrtn
RCHOP (In-Place)
; Chop the last character off a null-terminated string (in-place)
; AC0 is a byte pointer to the string
RCHOP:
wssvs 0
RCHOPL:
wldb 0,1 ; load the byte AC0 points to into AC1
wsnei 0,1 ; is the char a null?
wbr RCHOPR ; yes: exit loop
winc 0,0 ; no: increment byte pointer
wbr RCHOPL ; loop around
RCHOPR: wsbi 1,0 ; AC0--
wstb 0,1 ; store a NULL byte
RCHOPEND:
wrtn