Assembler Directives
•Instructions for the compiler, not assembly
•Control the assembler, don't create any code
•The leading dot must be in column 1 of the line
•Segment:
➢Everywhere
➢Header
➢Code
➢EEPROM
➢SRAM
Nguyễn Trung Hiếu 4
Assembler Directives: Everywhere
Nguyễn Trung Hiếu 5
.ORG
Defines the address within the respective segment, where
the assembler assembles to
syntax: .ORG 0x0000
.INCLUDE
Includes a file and assembles its content, just like its content
would be part of the calling file
syntax: .INCLUDE <M324PDEF.inc>
.EXIT
End of the assembler-source code
(stops the assembling process)
.LIST
Switches the listing to the .LST-file on (the assembled code
will be listed in a readable text file .LST)
.NOLISTSwitches the output to the .LST-file off, suppresses listing
Assembler Directives: Header
Nguyễn Trung Hiếu 6
.DEF
Defines a synonym for a register
syntax: .DEF MyReg= R16
.SET
Defines a symbol and sets its value (later changes of this
value remain possible)
syntax: .SET test = 1234567
.EQU
Fixes the value of a symbol (later redefinition is not possible)
syntax: .EQU test = 1234567
Assembler Directives: Code
Nguyễn Trung Hiếu 8
.CSEG
Start of the code segment (all that follows is assembled to
the code segment and will go to the program space)
.DB
Inserts one or more constant bytes in the code segment
The number of inserted bytes must be even, otherwise an
additional zero byte will be inserted by the assembler
syntax: .DB 1,’a’,”bc”
.DW
Insert binary words in the code segment, not used string
syntax: .DW 5,’b’
.LISTMAC
Macros will be listed in the .LST-file.
(Default is that macros are not listed)
.MACRO
Beginning of a macro (no code will be produced, call of the
macro later produces code)
syntax: .MACRO macronameparameters
.ENDMACROEnd of the macro
Assembler Directives: Code
Nguyễn Trung Hiếu 9
;Define Macro LOADIO
.MACRO LOADIO
LDI R20,@1
OUT@0,R20
.ENDMACRO
;Call Macro LOADIO
LOADIO PORTA, $20 ;PORTA=$20
.EQU VAL_1 = $FF
LOADIO DDRC, VAL_1 ;DDRC=$FF
LOADIO SPL, $55 ;SPL=$55
@0 and @1 is input parameters
@ can up to 9
Assembler Directives: EEPROM
Nguyễn Trung Hiếu 10
.ESEG
Assemble to the EEPROM-segment (the code produced will go
to the EEPROM section, the code produces an .EEP-file)
.DB
Inserts one or more constant bytes in the EEPROM segment
(could be numbers from 0..255, an ASCII-character like 'c', a
string like “abcde” or a combination like 1,2,3,'abc’)
syntax: .DB 1,’a’,”abcde”
.DW
Inserts a binary word to the EEPROM segment (the lower byte
goes to the next address, the higher byte follows on the
incremented address)
syntax: .DW 15,’d’
Assembler Directives: SRAM
Nguyễn Trung Hiếu 11
.DSEG
Assemble to the data segment (here only BYTE directives and
labels are valid, during assembly only the labels are used)
.BYTE
Reserves one or more bytes space in the data segment (only
used to produce correct labels, does not insert any values!)
Assembler Directives: SRAM
Nguyễn Trung Hiếu 12
.DSEG ; declare DATA segment
.ORG 0x120 ; SRAM, begin address at $120
VAR1: .BYTE 1 ; use 1 byte in SRAM forVAR1 at $120
.CSEG ; declare CODE segment
.ORG 0x10 ; FLASH, begin address = PC = $10
MOV R0,R1 ;
….. ;
….. ;
.ESEG ; declare EEPROM segment
VAR2: .BYTE 5 ; use 5 byte in EEPROM forVAR2
Instruction Types
•Data transfer
•Arithmetic
•Logical
•Boolean variable
•Program branching
•MCU control
Nguyễn Trung Hiếu 13
Used for creating loops,
branching and call subroutine
Program Branching Types
Nguyễn Trung Hiếu 14
•Call, Return
-Call subroutine: save information about where it currently is
executing and then jumps to the subroutine
-Return: when the subroutine is finished, it returns to where the
call was made
•Conditional Jump (Branch)
-If the Branch test is successful, the program jumps to the location
specified (no save information)
-If the Branch test is unsuccessful, the next line of code is executed
•Unconditional Jump (Jump)
-Always jump to the location specified (no save information)
Program Branching Types
Nguyễn Trung Hiếu 15
Call, return
Conditional and
Unconditional Jump
Subroutine Loops, branching
Call and Return
Nguyễn Trung Hiếu 16
MAIN: ... (1)
(RCALL,ICALL)CALL SUBLABEL (2)
... (3)
...
SUBLABEL: ... (4)
...
RET (5)
Structure:
Subroutine
Nguyễn Trung Hiếu 17
▪Divide and Conquer –Allow you to focus on one small “chunk”
of the problem at a time
▪Code Organization –Gives the code organization and structure.
A small step into the world of object-oriented programming
▪Hierarchical Design –Moves information about the program at
the appropriate level of detail
▪Code Readability –Allows others to read and understand the
program in digestible “bites” instead of all at once
▪Encapsulation–Insulates the rest of the program from changes
made within a procedure
▪Team Development –Helps multiple programmers to work on
the program in parallel; a first step to configuration control
ChomộtsốBCDnénnằmởđịachỉ$220củabộnhớSRAM.Viết
chươngtrìnhđọcsốBCDđóvàxuấtgiátrịra2LED7đoạnkếtnối
vớiPORTA(nibblecao)vàPORTC(nibblethấp)củavixửlý.
Nguyễn Trung Hiếu 18
Your Turn!
LDIR16,ENTRY_NUMBER
(LDIR17,$0)
LDIZH,HIGH(TABLE<<1)
LDI ZL,LOW(TABLE<<1)
ADDZL,R16
(ADCZH,R17)
LPM R17,Z
TABLE:.DBdata1, data2, data3, …
Nguyễn Trung Hiếu 19
(Remember) Look-up TableDATA: 8 bits (byte)
Program Memory
AddressHigh ByteLow Byte
data2 data1
data4 data3
… …
… …
TABLE’S ADDRESS
If ENTRY_NUMBER = 0,
Z POINT TO
Nguyễn Trung Hiếu 20
Create the Table
D7D6
g
D5
f
D4
e
D3
d
D2
c
D1
b
D0
a
00100000040H
10111100179H
20010010024H
30011000030H
40001100119H
50001001012H
60000001002H
70111100078H
80000000000H
90001000010H
TABLE:.DB$40,$79,$24,$30,$19,$12,$02,$78,$00,$10
22
Analyze
LOOKUP:
LDIZL,LOW(TABLE<<1)
LDIZH,HIGH(TABLE<<1)
ADDZL,R16
LPMR17,Z
RET
▪Subroutine named LOOKUP
▪The input parameter is stored in R16
▪The return parameter is stored in R17
▪The subroutine can change value in R16 –to keep it safe
LOOKUP:
PUSH R16
….
POP R16
RET
Nguyễn Trung Hiếu
Unconditional Jump
Nguyễn Trung Hiếu 23
JMP:(Direct Program Addressing)
IJMP:(Indirect Program Addressing)
RJMP:(Relative Program Addressing)
Conditional Jumps
Nguyễn Trung Hiếu 24
Instruction Abbreviation of Comment
BREQlabel Branch if Equal Jump to location label,if Z = 1
BRNE label Branch if Not Equal Jump to location label, if Z = 0
BRCSlabel
BRLOlabel
Branch if Carry Set
Branch if Lower
Jump to location label, if C = 1
BRCC label
BRSH label
Branch if Carry Cleared
Branch if Same or Higher
Jump to location label, if C = 0
BRMI label Branch if Minus Jump to location label, if N = 1
BRPL label Branch if Plus Jump to location label,if N = 0
BRGE label Branch if Greater or EqualJump to location label,if S= 0
BRLT label Branch if Less Than Jump to location label,if S= 1
BRHS label Branch if Half Carry SetJump to location label, if H = 1
BRHC label Branch if Half Carry ClearedJump to location label, if H = 0
BRTS label Branch if T flag Set Jump to location label, if T = 1
BRTC label Branch if T flag ClearedJump to location label, if T = 0
BRIS label Branch if I flag set Jump to location label, if I = 1
BRIC label Branch if I flag clearedJump to location label, if I = 0
HSVN CZTISREG
Conditional Jumps
Nguyễn Trung Hiếu 25
Instruction Abbreviation of Comment
SBRS Rd,b Skip if Bit in Register is SetSkip next instruction if Rd(b) = 1
SBRC Rd,b Skip if Bit in Register is ClearedSkip next instruction if Rd(b) = 0
SBIS IO_Addr,bSkip if Bit in I/O Register is SetSkip next instruction if IO_Addr(b) = 1
SBIC IO_Addr,bSkip if Bit in I/O Register is ClearedSkip next instruction if IO_Addr(b) = 0
Branch with conditions of bits
Creating a Loop (1)
Viếtchươngtrìnhthựchiệnghigiá trị$20 vàobộnhớSRAM từô
nhớ$200 đến$210
.ORG $0
LDIR16,$20
STS$200,R16
STS$201,R16
STS$202,R16
STS$203,R16
…
Nguyễn Trung Hiếu 26
17 lần
Viếtchươngtrìnhthựchiệnghigiá trị$20
vàobộnhớSRAM từô nhớ$200 đến
$210
.ORG $0
LDIR16,$20
LDI R17,17
LDI ZH,HIGH($200)
LDI ZL,LOW($200)
Again: ST Z+,R16
DEC R17
BRNE Again
Nguyễn Trung Hiếu 27
Creating a Loop (2)
Ptr$200
(Ptr) R16
PtrPtr+1
Loop=0?
N
Y
Loop 17
LoopLoop-1
R16 $20
Z = 0
Z = 1
SIMILAR TO FOR LOOPS
Viếtchươngtrìnhthựchiệnghigiá trị$20
vàobộnhớSRAM từô nhớ$200 đến
$210
.ORG $0
LDIR16,$20
LDI ZH,HIGH($200)
LDI ZL,LOW($200)
Again: ST Z+,R16
CPI ZL,$11
BRCS Again
Nguyễn Trung Hiếu 28
Creating a Loop (3)
Ptr$200
(Ptr) R16
PtrPtr+1
Ptr<=$210?
Y
N
R16 $20
C = 1
C = 0
ZL -$11
SIMILAR TO DO … WHILE …
Viếtchươngtrìnhthựchiệnghigiá trị$20
vàobộnhớSRAM từô nhớ$200 đến
$210
.ORG $0
LDIR16,$20
LDI ZH,HIGH($200)
LDI ZL,LOW($200)
Again: ST Z+,R16
CPIZL,$11
BRNE Again
Nguyễn Trung Hiếu 29
Creating a Loop (4)
Ptr$200
(Ptr) R16
PtrPtr+1
Ptr=$211?
N
Y
R16 $20
Z = 0
Z = 1
ZL -$11
SIMILAR TO DO … WHILE …
Your Turn!
Cho mộtchuỗisố(cógiá trịtừ0 đến9) đượclưutrong ô nhớ
$200 đến$210 ở bộnhớSRAM (mỗiô lưu1 số).
Viếtchươngtrìnhtiếnhànhđọcgiá trịtừcácô nhớra, sauđó
cộnggiá trịđọcđượcthêm$30 vàlưuvàovịtrícũ
Nguyễn Trung Hiếu 30
Solutions (1)
Nguyễn Trung Hiếu 31
(Ptr) R16
.ORG $0
LDI R17,$30
LDI ZH,HIGH($200)
LDI ZL,LOW($200)
LDI R18,17
Again: LD R16,Z
ADD R16,R17
ST Z+,R16
DEC R18
BRNE Again
Ptr$200
R16 (Ptr)
PtrPtr+1
Loop=0?
N
Y
R16 R16+R17
R17 $30
Z = 0
Z = 1
Loop 17
Loop Loop -1
Solutions (2)
Nguyễn Trung Hiếu 32
(Ptr) R16
.ORG $0
LDI R17,$30
LDI ZH,HIGH($200)
LDI ZL,LOW($200)
Again: LD R16,Z
ADD R16,R17
ST Z+,R16
CPIZL,$11
BRNE Again
Ptr$200
R16 (Ptr)
PtrPtr+1
Ptr=$211?
N
Y
R16 R16+R17
R17 $30
Z = 0
Z = 1
For loop –Application
Nguyễn Trung Hiếu 33
To create a for loop control:
can use BRNE, BRCS (with CPI), …
Example: a 10-time loop
LDI R16,10
LOOP: (begin loop)
…
…
(end loop)
DEC R16
BRNE LOOP
(continue)
Nguyễn Trung Hiếu 34
Use BRNE to create a 1000-time loop?
Ex: 1000 = 250.4 = 200.5 = 100.10 = 10.10.10
LDI R17,4
LOOP1: LDI R16,250
LOOP: (begin loop)
…
…
(end loop)
DEC R16
BRNE LOOP
DEC R17
BRNE LOOP1
(continue)
For loop –Application
ViếtchươngtrìnhthựchiệnxóathanhghiR1, sauđóthực
hiệncộngchoR1 số10 nămlầnliêntiếp
Solution:
CLRR1
LDIR16,10
LDIR17,5
AGAIN: ADDR1,R16
DECR17
BRNEAGAIN
Nguyễn Trung Hiếu 35
For loop –Application
CLRR1 ; 1 MC
LDIR16,10 ; 1 MC
LDIR17,5 ; 1 MC
AGAIN: ADDR1,R16 ; 1 MC
DECR17 ; 1 MC
BRNEAGAIN ; True –2 MCs
; False –1 MC
Nguyễn Trung Hiếu 36
Timing Program
CLRR1 ; 1 MC
LDIR16,10 ; 1 MC
LDIR17,5 ; 1 MC
AGAIN: ADDR1,R16 ; 1 MC
DECR17 ; 1 MC
BRNEAGAIN ; True –2 MCs
; False –1 MC
Nguyễn Trung Hiếu 37
Timing Program
1 time
5 times
4 times T
1 time F
Total time = (1 + 1 + 1).1 + (1 + 1).5 + 2.4 + 1 = 22 MCs
Or = (1 + 1 + 1).1 + (1 + 1 + 2).5 -1 = 22 MCs
If f
MC= 1 MHz →T
MC= 1 µs →Total time = 22 µs
Nguyễn Trung Hiếu 40
BlinkyProgram
250x(1+1+2) –1 = 999 MCs
.ORG 0
SBI DDRC,0
LOOP:SBIPORTC,0
CALL DELAY
CBI PORTC,0
CALL DELAY
RJMP LOOP
DELAY:
LDIR21,100
L1:LDIR20,250
L2:NOP ; 1 MC
DECR20 ; 1 MC
BRNEL2 ; 2/1 MC
DECR21
BRNEL1
RET
Nguyễn Trung Hiếu 41
BlinkyProgram
100x(999+1+1+2) –1
= 100299 MCs
.ORG 0
SBI DDRC,0
LOOP:SBIPORTC,0
CALL DELAY
CBI PORTC,0
CALL DELAY
RJMP LOOP
DELAY:
LDIR21,100
L1:LDIR20,250 ; 1 MC
L2:NOP ; 1 MC
DECR20 ; 1 MC
BRNEL2 ; 2/1 MC
DECR21 ; 1 MC
BRNEL1 ; 2/1 MC
RET
Nguyễn Trung Hiếu 42
BlinkyProgram
100299 + 1 + 4
= 100304 MCs
.ORG 0
SBI DDRC,0
LOOP:SBIPORTC,0
CALL DELAY
CBI PORTC,0
CALL DELAY
RJMP LOOP
DELAY:
LDIR21,100 ; 1 MC
L1:LDIR20,250 ; 1 MC
L2:NOP ; 1 MC
DECR20 ; 1 MC
BRNEL2 ; 2/1 MC
DECR21 ; 1 MC
BRNEL1 ; 2/1 MC
RET ; 4 MC
Nguyễn Trung Hiếu 43
BlinkyProgram
100299 + 1 + 4
= 100304 MCs
.ORG 0
SBI DDRC,0
LOOP:SBIPORTC,0 ; 1 MC
CALL DELAY ; 4 + 100304 MCs
CBI PORTC,0 ; 1 MC
CALL DELAY ; 4 + 100304 MCs
RJMP LOOP ; 2 MC
DELAY:
LDIR21,100 ; 1 MC
L1:LDIR20,250 ; 1 MC
L2:NOP ; 1 MC
DECR20 ; 1 MC
BRNEL2 ; 2/1 MC
DECR21 ; 1 MC
BRNEL1 ; 2/1 MC
RET ; 4 MC
Nguyễn Trung Hiếu 44
BlinkyProgram
.ORG 0
SBI DDRC,0
LOOP:SBIPORTC,0 ; 1 MC
CALL DELAY ; 4 + 100304 MCs
CBI PORTC,0 ; 1 MC
CALL DELAY ; 4 + 100304 MCs
RJMP LOOP ; 2 MC
T
H= 4 + 100304 + 1 = 100309 MCs
T
L= 4 + 100304 + 2 + 1 = 100311 MCs
If f
MC= 1MHz, 1 MC = 1 μs
→t
H= 100,309 MC = 100,309 μs
→t
L= 100,311 MC = 100,311 μs
→T = t
H+ t
L= 200,620 μs
→f = 1/T = 4.98 Hz
Nguyễn Trung Hiếu 45
BlinkyProgram
t
Ht
L
T
PC0 1
Delay t
H
PC0 0
Delay t
L
→t
H= t
L≈ t
DELAY≈ 100 x 250 x 4 MC = 100,000 MC = 100,000 μs
→T ≈ 200,000 μs
→f ≈ 5 Hz
Nguyễn Trung Hiếu 46
BlinkyProgram -Estimating
PC0 1
Delay t
H
PC0 0
Delay t
L
DELAY:
LDIR21,100
L1:LDIR20,250 ; 1 MC
L2:NOP ; 1 MC
DECR20 ; 1 MC
BRNEL2 ; 2/1 MC
DECR21 ; 1 MC
BRNEL1 ; 2/1 MC
RET
4 MCs
Viếtchươngtrìnhtạo1 xungvuôngcótầnsố10 kHz ở
chânPC0. Giảsửrằngtầnsốf
MC= 4MHz
Nguyễn Trung Hiếu 47
10-kHz square wave
Nguyễn Trung Hiếu 48
10-kHz square wave
t
Ht
L
T
PC0 1
Delay t
H
PC0 0
Delay t
L
t
H= t
L
Viếtchươngtrìnhtạo1 xungvuôngcótầnsố10 kHz ở
chânPC0. Giảsửrằngtầnsốf
MC= 4MHz
Nguyễn Trung Hiếu 49
10-kHz square wave
Viếtchươngtrìnhtạo1 xungvuôngcótầnsố10 kHz ở
chânPC0. Giảsửrằngtầnsốf
MC= 4MHz
f
MC= 4MHz →1 MC = 0,25 µs
f = 10 kHz →T
H= T
L= 100 µs = 4.100.0,25 µs = 4.100 MCs
DELAY:
LDIR20,100
L1:NOP ; 1 MC
DECR20 ; 1 MC
BRNEL1 ; 2/1 MC
RET
Nguyễn Trung Hiếu 50
10-kHz square wave, duty cycle 30%
t
Ht
L
T
PC0 1
Delay t
H
PC0 0
Delay t
L
Viếtchươngtrìnhtạo1 xungvuôngcótầnsố10 kHz với
chu kỳnhiệmvụ30% ở chânPC0.
Giảsửrằngtầnsốf
MC= 4MHz
Chu kỳnhiệmvụ= D = T
H/T
Nguyễn Trung Hiếu 51
10-kHz square wave, duty cycle 30%
PC0 1
Delay t
H
PC0 0
Delay t
L
Viếtchươngtrìnhtạo1 xungvuôngcótầnsố10 kHz với
chu kỳnhiệmvụ30% ở chânPC0.
Giảsửrằngtầnsốf
MC= 4MHz
f
MC= 4MHz →1 MC = 0,25 µs
f = 10 kHz →T = 200 µs
T
H= 0,3T = 60 µs = 4.60.0,25 µs = 4.60 MCs
T
L=0,7T = 140 µs
= 4.140.0,25 µs = 4.140 MCs
Nguyễn Trung Hiếu 52
400-kHz square wave
Viếtchươngtrìnhtạo1 xungvuôngcótầnsố400 kHz ở
chânPC0. Giảsửrằngtầnsốf
MC= 4MHz
f
MC= 4MHz →1 MC = 0,25 µs
f = 400 kHz →T
H= T
L= 2.5 µs = 10 MCs
Nguyễn Trung Hiếu 53
400-kHz square wave, duty cycle 40%
Viết chương trình tạo 1 xung vuông có tần số 400 kHzvà
chu kỳnhiệmvụ40%ở chân PC0.
Giả sử rằng tần số f
MC= 4MHz
CPIR16,$05
BRNE Skip
(Statement 1)
Skip:(Continue)
NguyễnTrung Hiếu 54
If … else … –Equal/Not Equal (1)
R16 = $05?
N
Y
Statement 1
Z = 0
Z = 1
CPR16,R17
BRNE Skip
(Statement 1)
Skip:(Continue)
R16 = R17?
N
Y
Statement 1
Z = 0
Z = 1
CPIR16,$05
BRNE Not_Eq
(Statement 1)
RJMP Next
Not_Eq:(Statement 2)
Next:(Continue)
NguyễnTrung Hiếu 55
If … else … –Equal/Not Equal (2)
R16 = $05?
N
Y
Statement 1
Z = 0
Z = 1
Statement 2
CPI R16,$05
BRCC LessThan
(Statement 1)
LessThan:(Continue)
Nguyễn Trung Hiếu 56
If … else … –
Greater Than or Equal/Less Than (1)
R16 ≥ $05?
N
Y
Statement 1
C = 1
C = 0
CPI R16,$05
BRCS GrEqThan
(Statement 1)
GrEqThan:(Continue)
Nguyễn Trung Hiếu 57
If … else … –
Greater Than or Equal/Less Than (2)
R16 < $05?
N
Y
Statement 1
C = 0
C = 1
Viếtchươngtrìnhkiểmtranộidung trong thanhghiR16.
Nếugiá trị5 ≤ R16 ≤ 10, xuấtgiá trịbù1 củaR16 raPORTA.
Ngượclại, xuấtgiá trịbù2 củaR16 raPORTA.
58Nguyễn Trung Hiếu
Vídụ
.ORG $0
LDI R16,$FF
OUT DDRA,R16
CPI R16,5
BRCC LessThan
CPI R16,11
BRCS GrEqThan
LessThan:
NEQ R16
RJMP NEXT
GrEgThan:
COMP R16
NEXT: OUT PORTA,R16
NOP
CPI R16,$05
BRCC LessThan
(Statement 1)
RJMP Next
LessThan:(Statement 2)
Next: (Continue)
Nguyễn Trung Hiếu 59
If … else … –
Greater Than or Equal/Less Than (3)
R16 ≥ $05?
N
Y
Statement 1
C = 1
C = 0
Statement 2
CPI R16,$05
BRCS GrEqThan
(Statement 1)
RJMP Next
GrEqThan:(Statement 2)
Next: (Continue)
Nguyễn Trung Hiếu 60
If … else … –
Greater Than or Equal/Less Than (4)
R16 < $05?
N
Y
Statement 1
C = 0
C = 1
Statement 2
SBRS R16,0
(Statement 1)
(Continue)
NguyễnTrung Hiếu 61
If … else … –Bit in Register is set/cleared (1)
R16(0)=1?
Y
N
Statement 1
SBRC R16,0
(Statement 1)
(Continue)
R16(0)=0?
Y
N
Statement 1
Note: Statement 1
-1 instruction
-If > 1 instruction, call subroutine
SBRS R16,0
RJMP BitSet
(Statement 2)
RJMP Next
BitSet:(Statement 1)
Next: (Continue)
NguyễnTrung Hiếu 62
If … else … –Bit in Register is set/cleared (2)
R16(0)=1?
Y
N
Statement 1Statement 2
Cho 20 bytes nằmtrong bộnhớSRAM bắtđầutạiđịachỉ$250.
Viếtchươngtrìnhxuấtcácbyte chẵnraPORTA
63Nguyễn Trung Hiếu
Bit Testing (1)
64Nguyễn Trung Hiếu
Your Turn!
Cho 20 bytes nằmtrong bộnhớSRAM bắtđầutạiđịachỉ$300.
Viếtchươngtrìnhxuấtcácbyte chẵnraPORTA vàcácbyte lẽra
PORTB
Cho 100 bytes sốcódấunằm trong bộ nhớ SRAM bắt đầu tại địa chỉ
$400. Viết chương trình xuất các sốâmra PORTA và các sốdươngra
PORTB
Nguyễn Trung Hiếu
Bit Testing (2)
65