字节序
读成(indian), 和内存中的二进制有关,主要处理二进制data的时候
the most significant bit
在左(big)或者在右(little)
dword 00000004
big_endian_memory = 00 00 00 04 // IBM mainframes, RISC processors and Motorola processors
little_endian_memory = 04 00 00 00 //Intel-based processors
- 当二进制数据在不同的计算机之间传输时(无论是通过文件还是网络),可能会出现字节序(byte order)不同的问题
- 当二进制数据被写入内存作为多字节整数时,可能会出现字节序不同的问题。如果写入数据的计算机和读取数据的计算机的字节序不同,那么读取数据的结果可能会与写入数据的结果不同
整数表示
最高位,作为符号位表示正负数,0为正数,1为负数
符号表示
符号位+数的绝对值表示,这样1个byte可表示的范围[-127, 127]
有一个问题,有两个0,一正一负
反码
就是每个bit翻转一下,也可以看成(1 - 旧数)
+56 = bin(0 0111000)
-56 = bin(1 1000111)
+0 = bin(0 0000000)
-0 = bin(1 1111111)
+56 = hex(38)
-56 = hex(C7) // F - 3 = C, F - 8 = 7
补码
现代计算机都是这么表示的,即反码+1
有个注意是0, 取反+1
之后,高位进位是被忽略的
0 = 00000000
11111111
+ 1
c 00000000
这样表示,就只有一个0了
存储伸缩
CPU和汇编语言都不知道,具体byte到底表示什么, 怎样读取取决于具体指令(数据类型告诉具体使用哪个指令)
缩小
规则是被截断的部分,对于数字的表示不重要
- 对于无符号正数,被截断的部分应该都是0
- 对于符号数,被截断的部分全是1或0,且剩余部分符号相同
mov ax, 0034h ; ax = 52 (stored in 16 bits)
mov cl, al ; cl = lower 8-bits of ax
放大
- 对于无符号数,新增的部分都是0
- 对于符号数,新增的部分都为符号位
无符号
// AX = AH + AL
mov ah, 0 ; zero out upper 8-bits
movzx eax, ax ; extends ax into eax
movzx eax, al ; extends al into eax
movzx ax, al ; extends al into ax
movzx ebx, ax ; extends ax into ebx
有符号
CBW ; (Byte->Word) extends the AL register into AX
CWD ; (Word -> Double Word) extends AX into DX:AX, 8086没有32位寄存器,两个合并来用
// 80386
CWDE ; (Word -> Double Word) extends AX into EAX
CDQ ; (Double World -> Quad Word) extend EAX into EDX:EAX, 64位
MOVSX ; movzx的有符号版本
C代码
类型转换
unsigned char uchar = 0xFF;
signed char schar = 0xFF;
int a = (int) uchar; /∗ a = 255 (0x000000FF) 使用MOVZX ∗/
int b = (int ) schar ; /∗ b = −1 (0xFFFFFFFF) 使用MOVSX ∗/
int fgetc( FILE * );
返回int
, 函数会遇到两种情况,读取字符串转成int(000000xx), EOF=-1(FFFFFFFF)
char ch;
while( (ch = fgetc(fp)) != EOF ) {
/∗ do something with ch ∗/
}
// 首先 000000FF, FFFFFFFF 的结果都是FF
// 若char无符号,EOF=(FFFFFFFF)=CHAR(FF)=INT(000000FF), 死循环
// 若char有符号,EOF=(FFFFFFFF)=CHAR(FF)=INT(FFFFFFFF), 有两种跳出条件000000FF和EOF(FFFFFFFF)
运算指令
加减法
在Add和sub指令中,FLAGS中有两个标志位,overflow
和carry
overflow: 运算结果过大,无法放到目标位置 carry: 加法中的进位,减法中的借位
由于二进制的表示,add和sub可以对有无符号数操作
002C 44
+ FFFF + (-1)
-------------------
002B 43
乘法
mul source
, source 是内存或者寄存器
- source(8-bit) 结果AX = Source * AL
- source(16-bit) 结果DX:AX = Source * AX
- source(32-bit) 结果EDX:EAX = Source * EAX
imul source
imul dest, source1 ; 只有乘法有
imul dest, source1, source2 ; 只有乘法有
除法
除法类似,DIV
和IDIV
div source
- source(8-bit) 结果AL = AX / source, AH = remainder
- source(16-bit) 结果AX = DX:AX / source, DX = remainder
- source(32-bit) 结果EAX = EDX:EAX / source, EDX = remainder
NEG
neg operand
, 计算补码支持(8,16,32-bit register和mem), 看起来就是正负数转换
代码
segment .data
;
; Output strings
;
prompt db "Enter a number: ", 0
square_msg db "Square of input is ", 0
cube_msg db "Cube of input is ", 0
cube25_msg db "Cube of input times 25 is ", 0
quot_msg db "Quotient of cube/100 is ", 0
rem_msg db "Remainder of cube/100 is ", 0
neg_msg db "The negation of the remainder is ", 0
segment .bss
input resd 1
segment .text
global asm_main
asm_main:
enter 0,0 ; setup routine
pusha
mov eax, prompt
call print_string
call read_int
mov [input], eax
imul eax ; edx:eax = eax * eax
mov ebx, eax ; save answer in ebx
mov eax, square_msg
call print_string
mov eax, ebx
call print_int
call print_nl
mov ebx, eax
imul ebx, [input] ; ebx *= [input]
mov eax, cube_msg
call print_string
mov eax, ebx
call print_int
call print_nl
imul ecx, ebx, 25 ; ecx = ebx*25
mov eax, cube25_msg
call print_string
mov eax, ecx
call print_int
call print_nl
mov eax, ebx
cdq ; initialize edx by sign extension
mov ecx, 100 ; can't divide by immediate value
idiv ecx ; edx:eax / ecx
mov ecx, eax ; save quotient into ecx
mov eax, quot_msg
call print_string
mov eax, ecx
call print_int
call print_nl
mov eax, rem_msg
call print_string
mov eax, edx
call print_int
call print_nl
neg edx ; negate the remainder
mov eax, neg_msg
call print_string
mov eax, edx
call print_int
call print_nl
popa
mov eax, 0 ; return back to C
leave
ret
执行结果
root@vultr:~/pcasm/examples/linux# ./math
Enter a number: 4
Square of input is 16
Cube of input is 64
Cube of input times 25 is 1600
Quotient of cube/100 is 0
Remainder of cube/100 is 64
The negation of the remainder is -64
大数加减
大数的加减法,可以将大数分解成小部分,进行运算
ADC ; operand1 = operand1 + carry flag + operand2
SBB ; operand1 = operand1 - carry flag - operand2
CLC ; carry flag = 0
例如64位,EDX:EAX + EBX:ECX
add eax, ecx ; add lower 32-bits
adc edx, ebx ; add upper 32-bits and carry from previous sum
sub eax, ecx ; subtract lower 32-bits
sbb edx, ebx ; subtract upper 32-bits and borrow
更大的数可以使用for循环
–END–