Note: This is more of a place holder for things I'm writing as I'm often on the road and without an internet connection, and my addled brain will be more than likely to remember this here than on my computer.
So it's been a long long time since I've done any assembly, but I've been attacking it with renewed vigor, and have even found some useful tidbits of information through the AMD documentation (Desktop: Fam: 15, Model: 75, Processor 3800+) and the intel documentation (Laptop: I5 forgot which particular processor it is).
Also, I used yasm instead of nasm, but I don't think there's that much difference.
The main functions I wanted to create were hexadecimal and binary string generation functions, so I could examine the registers easily. Which is the first code. (The latest incarnate). (Also, I know there's millions of these kinds of examples on the internet, again, this is primarily to help my addled brain to remember this).
Note: I'll update this and add more as I move along. So take all this with a grain of salt as I'm still a beginner.
;
; tobdoh functions tohex, tobin, todec, tooct
;
;
;
; Assembly, RDI string
; RAX number
; RCX length
;
;
; C RDI string
; RSI number
; RDX length
;
;
; tohex, tobin restricted to the 64 bits of a register.
;
; todec Restricted to the length passed to the funtion.
;
; tooct Restricted to 7 octal digits.
section .text
global tobin, todec, tohex, tooct
tobin:
%ifdef _WITH_C_
MOV RAX , RSI
MOV RCX , RDX
%endif
AND RCX , 63 ; Make sure we use no more than up to 64 bits. 0-63
ROR RAX , CL
TEST CL , 63
JNZ tobin_loop
MOV CL , 64
tobin_loop:
XOR BL , BL
SHL RAX , 1
ADC BL , BYTE 0x30
MOV [RDI], BL
INC RDI
LOOP tobin_loop
MOV [RDI], BYTE 0
RET
tohex:
%ifdef _WITH_C_
MOV RAX , RSI
MOV RCX , RDX
%endif
AND RCX , 15 ; Make sure we use no more than up to 16 hexadecimal digits. 0-15
SHL CL , 2
ROR RAX , CL
SHR CL , 2
TEST CL , 15
JNZ tohex_loop
MOV CL , 16
tohex_loop:
ROL RAX , 4
MOV BL , AL
AND BL , 0xF
CMP BL , 10
JL tohex_continue
ADD BL , 7
tohex_continue:
ADD BL , 0x30
MOV [RDI], BL
INC RDI
LOOP tohex_loop
MOV [RDI], BYTE 0
RET
tooct:
%ifdef _WITH_C_
MOV RAX , RSI
MOV RCX , RDX
%endif
AND RCX , 7
MOV CH , CL
SHL CL , 1
ADD CL , CH
ROR RAX , CL
MOV CL , CH
XOR CH , CH
TEST CL , 7
JNZ tooct_loop
MOV CL , 8
tooct_loop:
ROL RAX , 3
MOV BL , AL
AND BL , 7
ADD BL , 0x30
MOV [RDI], BL
INC RDI
LOOP tooct_loop
MOV [RDI], BYTE 0
RET
todec:
%ifdef _WITH_C_
MOV RAX , RSI
MOV RCX , RDX
%endif
ADD RDI , RCX ; Move to end of string.
MOV [RDI], BYTE 0
DEC RDI
MOV RBX , 10
todec_loop:
XOR RDX , RDX ; Clear RDX
DIV RBX
ADD DL , 0x30
MOV [RDI], DL
DEC RDI
LOOP todec_loop
MOV [RDI], BYTE 0
RET
The second code is my very first attempt at writing a big number library. For my current purposes I only need addition, multiplication, and modulo routines, so that is my primary goal.
Update big 2/22/17 5:10pm- Cleaned up the code for bigadd.
Update big 2/23/17 3:10pm- Added multiplication function
Update big 2/24/17 1:00am- Added subtraction function that returns a copy of A if A is smaller than B, else A-B
- Rewrote bignum_write_stack into a local CALL.
- Added macros for saving RBX, R10, and R11 as I've read those should be restored.
- Added array2big for converting an array of ints/unsigned longs to a malloced space so free can be called...well...freely.
Function list- genbig (int / unsigned long ) Creates a big number from a 64bit or 32bit number.
- bigi ( big , i ) returns the i'th element in a big number. bigi(array, 0) returns the length of the array.
- bignum_write_stack Useless outside of the assembly file. Just writes a newly generated big number that was written to the stack.
- array2big( array of ints/unsigned longs ) Allocates space and copies the array to the allocated space.
- C = bigadd( A, B ), adds a big number A with a big number B and returns a newly created big number C.
- C = bigmul( A, B ), multiplies a big number A with a big number B and returns a newly created big number C.
- C = bigsub( A, B ), If A is larger than B then returns a new big number C = A-B, else returns A.
Currently on the todo list, studying the XMM# registers and instructions to see if I can simplify some of this, as well as cutting out some of the useless code I think I've found.
Update big 3/4/17 10:40am- Total rewrite and redesign.
- Added str2big for converting a string to a big number. So other functions not necessary, at the moment.
- Functions:
- big * str2big( char *)
- big * bigadd( big A, big B)
- big * bigmul( big A, big B)
- big * bigsub( big A, big B)
- unsigned short bigtest( big A, big B )
Update big 3/5/17 1:00am- Cleaned up the code a bit
- Now bignum_write_stack takes care of the unnecessary 0's.
%ifdef _WITH_C_
%macro Save_C 0
PUSH RBX
PUSH R10
PUSH R11
%endmacro
%macro Restore_C 0
POP RBX
POP R10
POP R11
%endmacro
%endif
section .text
extern malloc
global bigadd, bigmul, bigsub, str2big
;
; bignum_write_stack Only used in this file.
;
; Registers modified:
; RAX, RBX, RCX, RDI, RSP
bignum_write_stack:
POP RBX ; Get call
MOV RCX , [RSP] ; Get counter
XOR RAX , RAX ; Empty RAX for testing all 64bits
; as I'm unsure if XOR r64/m64, imm32 does so.
bignum_write_stack_remove_zeroes:
ADD RSP , 8
CMP [RSP], RAX
JNZ bignum_write_stack_remove_zeroes_finished
LOOP bignum_write_stack_remove_zeroes
INC RCX ; If end of elements, inc RCX to 1
bignum_write_stack_remove_zeroes_finished:
PUSH RCX ; Reset stack
PUSH RBX
MOV RDI , RCX
INC RDI
SHL RDI , 3
CALL malloc
POP RBX ; Save the CALL for RET
POP RCX ; Get the counter
MOV [RAX], RCX ; Write the counter
SHL RCX , 3
ADD RAX , RCX ; Move to end of RAX
SHR RCX , 3
bignum_write_stack_loop:
POP RDX ; Get element.
MOV [RAX], RDX ; Write it to new bignum
SUB RAX , 8 ; Move to next element.
LOOP bignum_write_stack_loop
PUSH RBX ; Reset CALL
RET
;
; short bigtest( big A, big B )
;
; Returns: 00 => A=B
; 10 A>B
; 01 A<B
;
; Registers modified: AX, RCX, RSI, RDI,
bigtest:
MOV RCX , [RDI] ; Set up counters.
MOV R9 , [RSI]
MOV AX , 0x0100 ; Init for A > B
CMP RCX , R9
JG bigtest_ret
JL bigtest_lessthan
SHL RCX , 3 ; Move to the high order of both big numbers.
ADD RDI , RCX
ADD RSI , RCX
SHR RCX , 3
bigtest_loop:
MOV RDX , [RDI] ; Test first elements.
CMP RDX , [RSI]
JG bigtest_ret
JL bigtest_lessthan
SUB RDI , 8
SUB RSI , 8
LOOP bigtest_loop
MOV AX , 0x0000
JMP bigtest_ret
bigtest_lessthan:
MOV AX , 0x0001
bigtest_ret:
RET
;
; big * str2big( char * )
; RAX = str2big( RDI )
;
; Registers modified: RAX, RBX, RCX, RDX, R8, R9
str2big:
Save_C
PUSH RBP ; Save RBP
MOV RBP , RSP ; Save RSP
XOR RAX , RAX ; Empty RAX
MOV RCX , 10 ; Multiplier
XOR RBX , RBX ; Empty char.
PUSH RAX ; Write 0 to the stack.
str2big_loop:
MOV BL , [RDI] ; Char to BL
TEST BL , 0xFF ; EOS?
JZ str2big_ret
SUB BL , 0x30 ; Set digit to decimal value.
INC RDI ; Next byte
MOV R8 , RBP ; Init low order
SUB R8 , 8
str2big_mul:
MOV RAX , [ R8]
MUL RCX
ADD RAX , RBX ; Add low decimal digit to RAX
ADC RDX , 0 ; Carry to RDX
MOV RBX , RDX ; RBX = low decimal digits.
MOV [ R8], RAX ; Write RAX back to stack.
SUB R8 , 8
CMP R8 , RSP ; Is this the end of the stack?
JGE str2big_mul
CMP RDX , 0 ; Is there a high order element?
JNZ str2big_push_high
ADD R8 , 8 ; Reset R8 if no carry.
JMP str2big_loop
str2big_push_high:
PUSH RDX
JMP str2big_loop
str2big_ret:
MOV RCX , RBP ; Get length.
SUB RCX , RSP
SHR RCX , 3
PUSH RCX
CALL bignum_write_stack
MOV RSP , RBP
POP RBP
Restore_C
RET
;
; big * bigadd( big A, big B )
; RAX = bigadd( RDI , RSI )
;
; Registers modified: RAX, RCX, RDX, R9, RDI, RSI
bigadd:
Save_C
PUSH RBP
MOV RBP , RSP ; Save the stack
MOV RCX , [RDI] ; Set up counters.
MOV R9 , [RSI]
CMP RCX , R9 ; Insure RDI > RSI in length.
JGE bigadd_continue
XCHG RDI , RSI
bigadd_continue:
MOV RCX , [RDI]
MOV R9 , [RSI]
ADD RDI , 8
ADD RSI , 8
XOR RAX , RAX
bigadd_loop:
XOR RDX , RDX
ADD RAX , [RDI] ; Add A
ADC RDX , 0
ADD RDI , 8 ; Move to next element.
TEST R9 , 0xFFFFFFFF
JZ bigadd_no_B
ADD RAX , [RSI] ; Add B
ADC RDX , 0
ADD RSI , 8
DEC R9
bigadd_no_B:
PUSH RAX
MOV RAX , RDX ; Move high order to RAX.
LOOP bigadd_loop
PUSH RDX ; Since bignum_write_stack takes care of 0,
; fahget abaht it.
MOV RCX , RBP
SUB RCX , RSP
SHR RCX , 3
PUSH RCX
CALL bignum_write_stack
POP RBP
Restore_C
RET
;
; big * bigmul( big *A, big *B )
; RAX = bigmul( RDI , RSI )
;
; Registers modified: RAX, RBX, RCX, RDX, R8, R9, R10, R11
;
bigmul:
Save_C
PUSH RBP
MOV RBP , RSP ; Backup RSP
MOV R8 , [RDI] ; Setup counters.
MOV R9 , [RSI]
MOV R10 , RDI ; Backup A and B
MOV R11 , RSI
XOR RAX , RAX ; Empty RAX
MOV RCX , R8 ; Setup length of stack.
ADD RCX , R9
INC RCX
bigmul_init_stack: ; Initialize the stack space.
PUSH RAX
LOOP bigmul_init_stack
MOV RBX , RSP ; Backup RSP
ADD RSI , 8 ; First elements of A and B
ADD RDI , 8
bigmul_loop:
MOV RAX , [RDI] ; Move RDI's element
MUL QWORD [RSI]
ADD R8 , R9 ; Move to current element in C.
SHL R8 , 3
ADD RSP , R8
SHR R8 , 3
SUB R8 , R9
ADD [RSP], RAX ; Write low order
ADC QWORD RDX , 0 ; Add any carry
; MOV RAX , 1 ; Setup 1 for adc
SUB RSP , 8 ; Next element
ADD [RSP], RDX ; Add high order
JNC bigmul_no_adc
bigmul_adc: ; Span the list and add all carry.
SUB RSP , 8
ADD QWORD [RSP], 1
JC bigmul_adc
bigmul_no_adc:
MOV RSP , RBX ; Reset RSP
DEC R8 ; Move to next element.
ADD RDI , 8
TEST R8 , 0xFFFFFFFF ; Test for end of A
JNZ bigmul_loop
DEC R9
ADD RSI , 8
TEST R9 , 0xFFFFFFFF ; Test for end of B
JZ bigmul_exit
MOV RDI , R10 ; Restore RDI
MOV R8 , [RDI]
ADD RDI , 8
JMP bigmul_loop
bigmul_exit:
MOV RCX , RBP ; Setup counter
SUB RCX , RSP
SHR RCX , 3
PUSH RCX
CALL bignum_write_stack
MOV RSP , RBP
POP RBP
Restore_C
RET
;
; big * bigsub( big * A, big * B )
; RAX = bigsub( RDI , RSI )
;
; Registers modified: RAX, RCX, R8, R9, R10, R11, R12
bigsub:
Save_C
MOV R10 , RDI ; Test A and B
MOV R11 , RSI
CALL bigtest
XOR RDX , RDX ; Empty high order
XOR RCX , RCX ; Empty counter
TEST AH , 1 ; If A <= B return 0.
JZ bigsub_ret_empty
MOV RDI , R10 ; Restore A B
MOV RSI , R11
MOV R8 , [RDI]
MOV R9 , [RSI]
ADD RDI , 8 ; Begin reading elements.
ADD RSI , 8
bigsub_loop:
MOV RAX , RDX ; Move high order to low order
XOR RDX , RDX
TEST R8 , 0xFFFFFFFF
JZ bigsub_ret
ADD RAX , [RDI]
ADD RDI , 8
TEST R9 , 0xFFFFFFFF
JZ bigsub_push_rax
SUB RAX , [RSI]
JNC bigsub_inc_B
MOV QWORD RAX , -1
bigsub_inc_B:
DEC R9
ADD RSI , 8
bigsub_push_rax:
PUSH RAX
INC RCX
JMP bigsub_loop
bigsub_ret_empty:
MOV RCX , 1
PUSH RDX
bigsub_ret:
PUSH RCX
CALL bignum_write_stack
Restore_C
RET