MV/Wiki

A Generation Ago

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