A2 03 B5 FB 95 26 CA 10 F9 88 D0 01 60 A5 26 85 61 A5 27 85 62 A5 FB 85 63 A5
FC 85 64 A9 20 85 6F D0 36 18 A5 6D 65 65 85 28 A5 6E 65 66 85 29 A5 4B 85 26
A5 4C 85 27 50 CF 38 A5 6D E5 65 85 4B A5 6E E5 66 85 4C A5 28 85 61 A5 29 85
62 A5 FB 85 63 A5 FC 85 64 06 6F A9 00 85 65 85 66 A2 10 46 62 66 61 90 0D A5
63 18 65 65 85 65 A5 64 65 66 85 66 06 63 26 64 CA 10 E6 A9 FF 24 6F 70 B9 30
02 F0 9E A5 65 85 6D A5 66 85 6E 24 6F 30 14 A5 28 85 61 A5 29 85 62 A5 FD 85
63 A5 FE 85 64 06 6F D0 B4 A5 26 85 61 A5 27 85 62 A5 FD 85 63 A5 FE 85 64 06
6F B0 A0
- -12 byte với cấu trúc "spaghetti" được cải thiện
- -2 byte thay đổi thanh ghi để vượt qua số mũ, vì vậy chúng ta có thể sử dụng chế độ địa chỉ zeropage trong vòng lặp sao chép ban đầu
Đây là mã độc lập với vị trí, chỉ cần đặt nó ở đâu đó trong RAM và gọi nó bằng một jsr
lệnh.
Thường trình lấy cơ sở (phức tạp) làm hai số nguyên có ký hiệu 16 bit (phần bù 2, phần cuối nhỏ) trong $fb/$fc
(thực) và $fd/$fe
(tưởng tượng) và số mũ dưới dạng số nguyên 8 bit không dấu trongY
ghi.
Kết quả được trả về trong $26/$27
(thực) và $28/$29
(tưởng tượng).
Giải trình
Đây vẫn là một thách thức thú vị trên CPU 6502 vì không có hướng dẫn nào để nhân lên. Cách tiếp cận là thẳng tiến, thực hiện một phép nhân phức tạp và thực hiện nó thường xuyên theo yêu cầu của số mũ. Chơi golf được thực hiện bằng cách tránh các chương trình con, thay vào đó tạo ra một loại "spaghetti chi nhánh", do đó, mã để thực hiện phép nhân 16 bit đơn giản cần nhiều lần được sử dụng lại với chi phí thấp nhất có thể. Đây là phần tháo gỡ nhận xét:
.cexp:
A2 03 LDX #$03 ; copy argument ...
.copyloop:
B5 FB LDA $FB,X
95 26 STA $26,X
CA DEX
10 F9 BPL .copyloop ; ... to result
.exploop:
88 DEY ; decrement exponent
D0 01 BNE .mult ; zero reached -> done
60 RTS
.mult: ; multiply (complex) result by argument
A5 26 LDA $26 ; prepare to multiply real components
85 61 STA $61 ; (a*c)
A5 27 LDA $27
85 62 STA $62
A5 FB LDA $FB
85 63 STA $63
A5 FC LDA $FC
85 64 STA $64
A9 20 LDA #$20 ; marker for where to continue
85 6F STA $6F
D0 36 BNE .mult16 ; branch to 16bit multiplication
.mult5:
18 CLC ; calculate sum (a*d) + (b*c)
A5 6D LDA $6D
65 65 ADC $65
85 28 STA $28 ; and store to imaginary component of result
A5 6E LDA $6E
65 66 ADC $66
85 29 STA $29
A5 4B LDA $4B ; load temporary result (a*c) - (b*d)
85 26 STA $26 ; and store to real component of result
A5 4C LDA $4C
85 27 STA $27
50 CF BVC .exploop ; next exponentiation step
.mult3:
38 SEC ; calculate difference (a*c) - (b*d)
A5 6D LDA $6D
E5 65 SBC $65
85 4B STA $4B ; and store to temporary location
A5 6E LDA $6E
E5 66 SBC $66
85 4C STA $4C
A5 28 LDA $28 ; prepare to multiply real component of result
85 61 STA $61 ; with imaginary component of argument
A5 29 LDA $29 ; (a*d)
85 62 STA $62
A5 FB LDA $FB
85 63 STA $63
A5 FC LDA $FC
85 64 STA $64
06 6F ASL $6F ; advance "continue marker"
.mult16:
A9 00 LDA #$00 ; initialize 16bit multiplication
85 65 STA $65 ; result with 0
85 66 STA $66
A2 10 LDX #$10 ; bit counter (16)
.m16_loop:
46 62 LSR $62 ; shift arg1 right
66 61 ROR $61
90 0D BCC .m16_noadd ; no carry -> nothing to add
A5 63 LDA $63 ; add arg2 ...
18 CLC
65 65 ADC $65
85 65 STA $65
A5 64 LDA $64
65 66 ADC $66
85 66 STA $66 ; ... to result
.m16_noadd:
06 63 ASL $63 ; shift arg2 left
26 64 ROL $64
CA DEX ; decrement number of bits to go
10 E6 BPL .m16_loop
A9 FF LDA #$FF ; check marker for where to continue
24 6F BIT $6F
70 B9 BVS .mult3
30 02 BMI .saveres ; have to save result to temp in 2 cases
F0 9E BEQ .mult5
.saveres:
A5 65 LDA $65 ; save result to temporary
85 6D STA $6D
A5 66 LDA $66
85 6E STA $6E
24 6F BIT $6F ; check "continue marker" again
30 14 BMI .mult4
.mult2:
A5 28 LDA $28 ; prepare to multiply imaginary components
85 61 STA $61 ; (b*d)
A5 29 LDA $29
85 62 STA $62
A5 FD LDA $FD
85 63 STA $63
A5 FE LDA $FE
85 64 STA $64
06 6F ASL $6F ; advance "continue marker"
D0 B4 BNE .mult16 ; branch to 16bit multiplication
.mult4:
A5 26 LDA $26 ; prepare to multiply imaginary component of
85 61 STA $61 ; result with real component of argument
A5 27 LDA $27 ; (b*c)
85 62 STA $62
A5 FD LDA $FD
85 63 STA $63
A5 FE LDA $FE
85 64 STA $64
06 6F ASL $6F ; advance "continue marker"
B0 A0 BCS .mult16 ; branch to 16bit multiplication
Ví dụ chương trình sử dụng nó (C64, nguồn lắp ráp trong ca65 -syntax):
.import cexp
CEXP_A = $fb
CEXP_AL = $fb
CEXP_AH = $fc
CEXP_B = $fd
CEXP_BL = $fd
CEXP_BH = $fe
CEXP_RA = $26
CEXP_RAL = $26
CEXP_RAH = $27
CEXP_RB = $28
CEXP_RBL = $28
CEXP_RBH = $29
.segment "LDADDR"
.word $c000
.segment "MAIN"
jsr $aefd ; consume comma
jsr $ad8a ; evaluate number
jsr $b1aa ; convert to 16bit int
sty CEXP_AL ; store as first argument
sta CEXP_AH
jsr $aefd ; ...
jsr $ad8a
jsr $b1aa
sty CEXP_BL ; store as second argument
sta CEXP_BH
jsr $b79b ; read 8bit unsigned into X
txa ; and transfer
tay ; to Y
jsr cexp ; call our function
lda CEXP_RAH ; read result (real part)
ldy CEXP_RAL
jsr numout ; output
ldx CEXP_RBH ; read result (imaginary part)
bmi noplus
lda #'+' ; output a `+` if it's not negative
jsr $ffd2
noplus: txa
ldy CEXP_RBL
jsr numout ; output (imaginary part)
lda #'i'
jsr $ffd2 ; output `i`
lda #$0d ; and newline
jmp $ffd2
numout:
jsr $b391 ; convert to floating point
jsr $bddd ; format floating point as string
ldy #$01
numout_loop: lda $ff,y ; output loop
bne numout_print ; until 0 terminator found
rts
numout_print: cmp #' ' ; skip space characters in output
beq numout_next
jsr $ffd2
numout_next: iny
bne numout_loop
Cách sử dụng : sys49152,[a],[b],[c]
, ví dụ sys49152,5,2,2
(Đầu ra 21+20i
:)