8-bit Multiply: Difference between revisions

From NESdev Wiki
Jump to navigationJump to search
(both of these methods are technically "russian peasant", trying to rewrite their descriptions)
(fixing comment alignment (tabs should only be used to align the left column, not mid-line, because variable tab widths breaks this))
Line 9: Line 9:
;Enter with A,Y, numbers to multiply
;Enter with A,Y, numbers to multiply
;Output with YA = 16-bit result (X is unchanged)
;Output with YA = 16-bit result (X is unchanged)
Multiply
Multiply:
sty Factor ;Store input factor
sty Factor ;Store input factor
ldy #$00
ldy #$00
sty Res
sty Res
sty Res2 ;Clear result
sty Res2   ;Clear result
ldy #$08 ;Number of shifts needed
ldy #$08   ;Number of shifts needed


- lsr A ;Shift right input number
- lsr A       ;Shift right input number
bcc + ;Check if bit is set
bcc +       ;Check if bit is set
pha
pha
lda Res2
lda Res2
clc
clc
adc Factor
adc Factor
sta Res2 ;If so add number to result
sta Res2   ;If so add number to result
pla
pla
+ lsr Res2 ;Shift result right
+ lsr Res2   ;Shift result right
ror Res
ror Res
dey
dey
Line 42: Line 42:
; Multiply two bytes in memory using Russian peasant algorithm
; Multiply two bytes in memory using Russian peasant algorithm
; by frantik
; by frantik


; Accepts: value1ptr and value2ptr, pointers to bytes in memory
; Accepts: value1ptr and value2ptr, pointers to bytes in memory
Line 52: Line 51:
.macro multiply value1ptr, value2ptr
.macro multiply value1ptr, value2ptr


ret  = $00 ; return value
ret  = $00         ; return value
temp = $02 ; temp storage
temp = $02         ; temp storage


lda #$00 ; clear temporary variables
lda #$00       ; clear temporary variables
sta ret
sta ret
sta ret+1
sta ret+1
Line 62: Line 61:


-loop:
-loop:
asl value1ptr ; double first value
asl value1ptr   ; double first value
rol temp ; using 16bit precision
rol temp       ; using 16bit precision
lsr value2ptr ; halve second vale
lsr value2ptr   ; halve second vale
start:
start:
lda value2ptr ;
lda value2ptr   ;
and #01 ; is new 2nd value an odd number?
and #01         ; is new 2nd value an odd number?
beq -loop: ;  
beq -loop:     ;  
clc ; if so, add new 1st value to running total
clc             ; if so, add new 1st value to running total
lda ret ;
lda ret         ;
adc value1ptr ;
adc value1ptr   ;
sta ret ;
sta ret         ;
lda ret+1 ;
lda ret+1       ;
adc temp ;
adc temp       ;
sta ret+1 ;
sta ret+1       ;
lda value2ptr ;
lda value2ptr   ;
cmp #01 ; is 2nd value 1?  if so, we're done
cmp #01         ; is 2nd value 1?  if so, we're done
bne -loop: ; otherwise, loop
bne -loop:     ; otherwise, loop
.endm
.endm
</pre>
</pre>


[[Category:Arithmetic]]
[[Category:Arithmetic]]

Revision as of 06:07, 12 October 2017

The following code multiplies two 8-bit integers (range 0...255) and outputs a 16-bit result using only real calculation, no lockup table so the size of the code is very small.

Bregalad

This routine by Bregalad does binary long multiplication (a.k.a. "the Russian Peasant method").

;8-bit multiply
;by Bregalad
;Enter with A,Y, numbers to multiply
;Output with YA = 16-bit result (X is unchanged)
Multiply:
	sty Factor  ;Store input factor
	ldy #$00
	sty Res
	sty Res2    ;Clear result
	ldy #$08    ;Number of shifts needed

-	lsr A       ;Shift right input number
	bcc +       ;Check if bit is set
	pha
	lda Res2
	clc
	adc Factor
	sta Res2    ;If so add number to result
	pla
+	lsr Res2    ;Shift result right
	ror Res
	dey
	bne -
	lda Res
	ldy Res2
	rts

An optimization for efficiency is made here; binary long multiplication requires adding one multiplicand to the result at various bit-shifts (i.e. multiply by each power of 2). The naive approach might maintain the value to add as a 16-bit value, left shifting it once each iteration to reach the next power of 2. This one, however, takes advantage of the input being only 8-bits wide, and instead pre-multiplies the result by 256 (8 bits), and each iteration instead right-shifts the result. After 8 iterations the pre-multiply is undone, and the advantage gained is that only the shift is 16-bit; adding the multiplicand remains an efficient 8-bit add.

Bob Rost / frantik

This routine by Bob Rost is another binary long multiplication, taken from his NES Development Class PDFs.

; Multiply two bytes in memory using Russian peasant algorithm
; by frantik

; Accepts: value1ptr and value2ptr, pointers to bytes in memory
;   value2ptr should point to the lesser of the two values 
;   for increased efficiency
; Uses: $00, $01, $02 for temporary variables
; Returns: 16 bit value in $00 and $01

.macro multiply value1ptr, value2ptr

ret  = $00          ; return value
temp = $02          ; temp storage

	lda #$00        ; clear temporary variables
	sta ret
	sta ret+1
	sta temp
	jmp start:

-loop:
	asl value1ptr   ; double first value
	rol temp        ; using 16bit precision
	lsr value2ptr   ; halve second vale
start:
	lda value2ptr   ;
	and #01         ; is new 2nd value an odd number?
	beq -loop:      ; 
	clc             ; if so, add new 1st value to running total
	lda ret         ;
	adc value1ptr   ;
	sta ret         ;
	lda ret+1       ;
	adc temp        ;
	sta ret+1       ;
	lda value2ptr   ;
	cmp #01         ; is 2nd value 1?  if so, we're done
	bne -loop:      ; otherwise, loop
.endm