字节序
读成(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–