11 印出 hex

寫一個可以印出 hex 內容的 function。

shr 是 bitwise 的 right shift instruction。右移的最後一個 bit 會放到 carry flag 裡。可以用 jcjnc 來做 branch,jc 是如果 carry flag 有被 set 則 jump,後者是沒 set 的情況會 jump。

第一版:處理最後一個字

這個版本可以印出 0x0006。基本概念是用 right shift 得知 dx 最右邊的 bit 是 0 還是 1,以 4 個 bit 為一單位處理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
[org 0x7c00]

mov dx, 0x1fb6
call print_hex
jmp $

; prints the value of DX as hex.
print_hex:
pusha

; only bx can be used as an index register
mov bx, HEX_OUT
add bx, 5 ; bx = HEX_OUT + 5

shr dx, 1 ; dx >> 1
jnc end_if_1
mov cx, 1 ; if cf == 1
add byte [bx], cl ; [bx] += 1
end_if_1:

shr dx, 1 ; dx >> 1
jnc end_if_2
mov cx, 2
add byte [bx], cl ; [bx] += 2
end_if_2:

shr dx, 1 ; dx >> 1
jnc end_if_3
mov cx, 4
add byte [bx], cl
end_if_3:

shr dx, 1 ; dx >> 1
jnc end_if_4
mov cx, 8
add byte [bx], cl
end_if_4:


mov bx, HEX_OUT
call print_string

popa
ret

%include "print_string.asm"

; global variables
HEX_OUT: db '0x0000', 0

; padding and magic number
times 510-($-$$) db 0
dw 0xaa55

然後遇到超過 10 就會錯…因為 ASCII code 在 '9' 後面不是 'a'

第二版

修正 10 以上的錯誤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
[org 0x7c00]

mov dx, 0x1fb6
call print_hex
jmp $

; prints the value of DX as hex.
print_hex:
pusha

mov ax, 5
; only bx can be used as an index register
mov bx, HEX_OUT
add bx, ax ; bx = HEX_OUT + ax

mov cx, 0
shr dx, 1 ; dx >> 1
jnc end_if_1
add cx, 1 ; if cf == 1
end_if_1:

shr dx, 1
jnc end_if_2
add cx, 2
end_if_2:

shr dx, 1
jnc end_if_3
add cx, 4
end_if_3:

shr dx, 1
jnc end_if_4
add cx, 8
end_if_4:

cmp cx, 10
jl less_ten
mov byte [bx], 'a'
sub cx, 10
less_ten:
add [bx], cx

mov bx, HEX_OUT
call print_string

popa
ret

%include "print_string.asm"

; global variables
HEX_OUT: db '0x0000', 0

; padding and magic number
times 510-($-$$) db 0
dw 0xaa55

第三版:加上外層 loop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
[org 0x7c00]

mov dx, 0x1fb6
call print_hex
jmp $

; prints the value of DX as hex.
print_hex:
pusha

mov ax, 5
loop: ; for (ax = 5; ax >= 2; ax--)
cmp ax, 2
jl end_loop
mov cx, 0
shr dx, 1 ; dx >> 1
jnc end_if_1
add cx, 1 ; if cf == 1, cx += 1
end_if_1:

shr dx, 1 ; dx >> 1
jnc end_if_2
add cx, 2 ; if cf == 1, cx += 2
end_if_2:

shr dx, 1 ; dx >> 1
jnc end_if_3
add cx, 4 ; if cf == 1, cx += 4
end_if_3:

shr dx, 1 ; dx >> 1
jnc end_if_4
add cx, 8 ; if cf == 1, cx += 8
end_if_4:

; only bx can be used as an index register
mov bx, HEX_OUT
add bx, ax ; bx = HEX_OUT + ax

cmp cx, 10
jl less_ten
mov byte [bx], 'a' ; [bx] = 'a'
sub cx, 10 ; cx -= 10
less_ten:
add [bx], cx ; [bx] += cx

sub ax, 1 ; ax--
jmp loop
end_loop:

mov bx, HEX_OUT
call print_string

popa
ret

%include "print_string.asm"

; global variables
HEX_OUT: db '0x0000', 0

; padding and magic number
times 510-($-$$) db 0
dw 0xaa55

第四版:拆成多個檔案

print_string.asm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
print_string:           ; function name
pusha ; push all register to stack to preserve them

mov ah, 0x0e ; tty mode

loop_start:
cmp byte [bx], 0 ; compare [bx] which is one byte to zero, for null terminating char
je loop_end ; if [bx] == 0, end loop
mov al, [bx] ; move char printed to al
int 0x10 ; call print interrupt
add bx, 1 ; bx + 1, move to next char
jmp loop_start ; run loop body again

loop_end:
popa ; restore all register
ret ; return to callee

print_hex.asm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
; prints the value of DX as hex.
print_hex:
pusha

mov ax, 5
loop: ; for (ax = 5; ax >= 2; ax--)
cmp ax, 2
jl end_loop
mov cx, 0
shr dx, 1 ; dx >> 1
jnc end_if_1
add cx, 1 ; if cf == 1, cx += 1
end_if_1:

shr dx, 1 ; dx >> 1
jnc end_if_2
add cx, 2 ; if cf == 1, cx += 2
end_if_2:

shr dx, 1 ; dx >> 1
jnc end_if_3
add cx, 4 ; if cf == 1, cx += 4
end_if_3:

shr dx, 1 ; dx >> 1
jnc end_if_4
add cx, 8 ; if cf == 1, cx += 8
end_if_4:

; only bx can be used as an index register
mov bx, HEX_OUT
add bx, ax ; bx = HEX_OUT + ax

cmp cx, 10
jl less_ten
mov byte [bx], 'a' ; [bx] = 'a'
sub cx, 10 ; cx -= 10
less_ten:
add [bx], cx ; [bx] += cx

sub ax, 1 ; ax--
jmp loop
end_loop:

mov bx, HEX_OUT
call print_string

popa
ret

%include "print_string.asm"

boot_sect.asm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[org 0x7c00]

mov dx, 0x1fb6
call print_hex
jmp $

%include "print_hex.asm"

; global variables
HEX_OUT: db '0x0000', 0

; padding and magic number
times 510-($-$$) db 0
dw 0xaa55