mirror of
https://github.com/pkivolowitz/asm_book.git
synced 2026-06-22 21:36:45 +08:00
making progress
This commit is contained in:
parent
6ce1759392
commit
b3f1a63e51
5 changed files with 332 additions and 12 deletions
|
|
@ -67,30 +67,153 @@ five bit field inside one byte.
|
||||||
Without bit fields, we would have to write this code:
|
Without bit fields, we would have to write this code:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
/* Note the absence of defensive programming such as checking
|
|
||||||
to ensure that byte is not null and that bit_number is not
|
|
||||||
too large.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void ClearA(unsigned char * byte) {
|
void ClearA(unsigned char * byte) {
|
||||||
*byte &= ~1;
|
*byte &= ~1;
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This function takes the address of the byte containing the `a`,
|
||||||
|
`b` and `c` portions.
|
||||||
|
|
||||||
|
*Good programming practice would check `byte` against `NULL`
|
||||||
|
or `nullptr`.*
|
||||||
|
|
||||||
|
The `~` operator is a bitwise negation. All the bits in the
|
||||||
|
value are flipped from 0 to 1 or 1 to 0. `~1` in an unsigned
|
||||||
|
char will produce `0xFE`, or all ones except for bit 0. `and`ing
|
||||||
|
this value to `*byte` ensures that its bit 0 is 0 and all other
|
||||||
|
bits are left alone.
|
||||||
|
|
||||||
|
In assembly language, written *naively*, this would look like
|
||||||
|
this:
|
||||||
|
|
||||||
|
```asm
|
||||||
|
ClearA: ldrb w1, [x0] // 1
|
||||||
|
mov w2, 1 // 2
|
||||||
|
mvn w2, w2 // 3
|
||||||
|
and w1, w1, w2 // 4
|
||||||
|
strb w1, [x0] // 5
|
||||||
|
ret // 6
|
||||||
|
```
|
||||||
|
|
||||||
|
`x30` does not have to be backed up or restored as this function is a "leaf."
|
||||||
|
|
||||||
|
`Line 3` uses the instruction `mvn` to flip all the bits in `w2`.
|
||||||
|
|
||||||
|
This code completely tracks the C / C++ code.
|
||||||
|
|
||||||
|
We have no obligation to follow the C / C++ code exactly. Instead we
|
||||||
|
could write:
|
||||||
|
|
||||||
|
```asm
|
||||||
|
ClearA: ldrb w1, [x0] // 1
|
||||||
|
and w1, w1, 0xFE // 2
|
||||||
|
strb w1, [x0] // 3
|
||||||
|
ret // 4
|
||||||
|
```
|
||||||
|
|
||||||
|
Here, the `0xFE` literal takes the place of `lines 2 and 3` in the previous
|
||||||
|
version.
|
||||||
|
|
||||||
|
For setting the `a` bit, we would do this:
|
||||||
|
|
||||||
|
```c
|
||||||
void SetA(unsigned char * byte) {
|
void SetA(unsigned char * byte) {
|
||||||
*byte &= ~1;
|
|
||||||
*byte |= 1;
|
*byte |= 1;
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This is an anomaly for bit bashing. In almost all cases when setting
|
||||||
|
bit values, the bits must be cleared first because an *or* instruction
|
||||||
|
is responsible for setting any 1 bits to 1. In the case, it is a single
|
||||||
|
bit we're setting so we can just or it in.
|
||||||
|
|
||||||
|
In assembly language:
|
||||||
|
|
||||||
|
```asm
|
||||||
|
SetA: ldrb w1, [x0] // 1
|
||||||
|
orr w1, w1, 1 // 2
|
||||||
|
strb w1, [x0] // 3
|
||||||
|
ret // 4
|
||||||
|
```
|
||||||
|
|
||||||
|
`orr` is one of several or instructions in AARCH64. It is the one that maps
|
||||||
|
most closely to `|` in C and C++.
|
||||||
|
|
||||||
|
Moving onto the `b` field, things begin to get a little more interesting.
|
||||||
|
To clear the `b` field we might do this in C | C++.
|
||||||
|
|
||||||
|
```c
|
||||||
void ClearB(unsigned char * byte) {
|
void ClearB(unsigned char * byte) {
|
||||||
*byte &= ~6;
|
*byte &= ~6;
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
void SetB(unsigned char * byte, unsigned char value) {
|
This could *naively* be written as:
|
||||||
value &= 3; // ensures only bits 0 and 1 can be set
|
|
||||||
*byte &= ~6; // clears bits 1 and 2 in byte
|
|
||||||
*byte |= (value << 1); // stores bits 0 and 1 into bits 2 and 3
|
|
||||||
}
|
|
||||||
|
|
||||||
|
```asm
|
||||||
|
ClearB: ldrb w1, [x0] // 1
|
||||||
|
mov w2, 6 // 2
|
||||||
|
mvn w2, w2 // 3
|
||||||
|
and w1, w1, w2 // 4
|
||||||
|
strb w1, [x0] // 5
|
||||||
|
ret // 6
|
||||||
|
```
|
||||||
|
|
||||||
|
This code is essentially the same as the *naive* version of `ClearA` given
|
||||||
|
above. Once again, we can pre-compute the results of `lines 2 and 3` to
|
||||||
|
make:
|
||||||
|
|
||||||
|
```asm
|
||||||
|
ClearB: ldrb w1, [x0] // 1
|
||||||
|
and w1, w1, 0xF9 // 2
|
||||||
|
strb w1, [x0] // 3
|
||||||
|
ret // 4
|
||||||
|
```
|
||||||
|
|
||||||
|
Turning to setting `b`, the code gets a little more complicated as for
|
||||||
|
the first time, we have to accept a parameter for the value to place into
|
||||||
|
`b`.
|
||||||
|
|
||||||
|
```c
|
||||||
|
void SetB(unsigned char * byte, unsigned char value) { // 1
|
||||||
|
value &= 3; // ensures only bits 0 and 1 can be set // 2
|
||||||
|
*byte &= ~6; // clears bits 1 and 2 in byte // 3
|
||||||
|
*byte |= (value << 1); // stores bits 0 and 1 into bits 2 and 3 // 4
|
||||||
|
} // 5
|
||||||
|
```
|
||||||
|
|
||||||
|
`Line 2` is necessary to prevent stray 1's from being or'ed into `*byte`.
|
||||||
|
|
||||||
|
`Line 3` is necessary to squash the existing target bits to zero prior
|
||||||
|
to being or'ed.
|
||||||
|
|
||||||
|
Notice `value` is being shifted left by 1 bit as the `b` field begins at
|
||||||
|
bit index 1.
|
||||||
|
|
||||||
|
In *naive* assembly language we could write this:
|
||||||
|
|
||||||
|
```asm
|
||||||
|
SetB: ldrb w3, [x0] // 1
|
||||||
|
and w1, w1, 3 // value &= 3 // 2
|
||||||
|
lsl w1, w1, 1 // 3
|
||||||
|
mov w2, 6 // 4
|
||||||
|
mvn w2, w2 // 5
|
||||||
|
and w3, w3, w2 // B is cleared // 6
|
||||||
|
orr w3, w3, w1 // 7
|
||||||
|
strb w3, [x0] // 8
|
||||||
|
ret // 9
|
||||||
|
```
|
||||||
|
|
||||||
|
The only interesting thing in this code as that we chose to perform the
|
||||||
|
left shift by one bit early in the code rather than later. There is no
|
||||||
|
side effect to changing this order.
|
||||||
|
|
||||||
|
`lsl` means "left shift logical" which fills the right side recently
|
||||||
|
vacated bits with zero.
|
||||||
|
|
||||||
|
|
||||||
|
```c
|
||||||
void ClearC(unsigned char * byte) {
|
void ClearC(unsigned char * byte) {
|
||||||
*byte &= 7; // squashes bits 3 to 7 to 0
|
*byte &= 7; // squashes bits 3 to 7 to 0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
58
section_2/bitfields/sophisticated.s
Normal file
58
section_2/bitfields/sophisticated.s
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
.global SetA
|
||||||
|
.global SetB
|
||||||
|
.global SetC
|
||||||
|
.global ClearA
|
||||||
|
.global ClearB
|
||||||
|
.global ClearC
|
||||||
|
|
||||||
|
.text
|
||||||
|
.align 2
|
||||||
|
|
||||||
|
ClearA: ldrb w1, [x0]
|
||||||
|
and w1, w1, 0xFE
|
||||||
|
strb w1, [x0]
|
||||||
|
ret
|
||||||
|
|
||||||
|
ClearB: ldrb w1, [x0]
|
||||||
|
and w1, w1, 0xF9
|
||||||
|
strb w1, [x0]
|
||||||
|
ret
|
||||||
|
|
||||||
|
ClearC: ldrb w1, [x0]
|
||||||
|
and w1, w1, 7
|
||||||
|
strb w1, [x0]
|
||||||
|
ret
|
||||||
|
|
||||||
|
SetA: ldrb w1, [x0]
|
||||||
|
orr w1, w1, 1
|
||||||
|
strb w1, [x0]
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
SetB: ldrb w3, [x0]
|
||||||
|
and w1, w1, 3 // value &= 3
|
||||||
|
lsl w1, w1, 1
|
||||||
|
mov w2, 6
|
||||||
|
mvn w2, w2
|
||||||
|
and w3, w3, w2 // B is cleared
|
||||||
|
orr w3, w3, w1
|
||||||
|
strb w3, [x0]
|
||||||
|
ret
|
||||||
|
|
||||||
|
SetC: ldrb w3, [x0]
|
||||||
|
|
||||||
|
mov w2, 0x1F
|
||||||
|
and w1, w1, w2
|
||||||
|
lsl w1, w1, 3
|
||||||
|
|
||||||
|
lsl w2, w2, 3
|
||||||
|
mvn w2, w2
|
||||||
|
and w3, w3, w2
|
||||||
|
|
||||||
|
orr w3, w3, w1
|
||||||
|
strb w3, [x0]
|
||||||
|
ret
|
||||||
|
|
||||||
|
.end
|
||||||
|
|
||||||
|
|
||||||
10
section_2/bitfields/temp.txt
Normal file
10
section_2/bitfields/temp.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
SetB: ldrb w3, [x0]
|
||||||
|
and w1, w1, 3 // value &= 3
|
||||||
|
lsl w1, w1, 1
|
||||||
|
mov w2, 6
|
||||||
|
mvn w2, w2
|
||||||
|
and w3, w3, w2 // B is cleared
|
||||||
|
orr w3, w3, w1
|
||||||
|
strb w3, [x0]
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
@ -8,7 +8,7 @@ struct BF {
|
||||||
|
|
||||||
unsigned char noBF = 0;
|
unsigned char noBF = 0;
|
||||||
|
|
||||||
#undef C
|
#define C
|
||||||
#ifdef C
|
#ifdef C
|
||||||
/* Note the absence of defensive programming such as checking
|
/* Note the absence of defensive programming such as checking
|
||||||
to ensure that byte is not null and that bit_number is not
|
to ensure that byte is not null and that bit_number is not
|
||||||
|
|
|
||||||
129
section_2/bitfields/test.s
Normal file
129
section_2/bitfields/test.s
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
.section __TEXT,__text,regular,pure_instructions
|
||||||
|
.build_version macos, 12, 0 sdk_version 12, 3
|
||||||
|
.globl _ClearA ; -- Begin function ClearA
|
||||||
|
.p2align 2
|
||||||
|
_ClearA: ; @ClearA
|
||||||
|
.cfi_startproc
|
||||||
|
; %bb.0:
|
||||||
|
ldrb w8, [x0]
|
||||||
|
and w8, w8, #0xfe
|
||||||
|
strb w8, [x0]
|
||||||
|
ret
|
||||||
|
.cfi_endproc
|
||||||
|
; -- End function
|
||||||
|
.globl _SetA ; -- Begin function SetA
|
||||||
|
.p2align 2
|
||||||
|
_SetA: ; @SetA
|
||||||
|
.cfi_startproc
|
||||||
|
; %bb.0:
|
||||||
|
ldrb w8, [x0]
|
||||||
|
orr w8, w8, #0x1
|
||||||
|
strb w8, [x0]
|
||||||
|
ret
|
||||||
|
.cfi_endproc
|
||||||
|
; -- End function
|
||||||
|
.globl _ClearB ; -- Begin function ClearB
|
||||||
|
.p2align 2
|
||||||
|
_ClearB: ; @ClearB
|
||||||
|
.cfi_startproc
|
||||||
|
; %bb.0:
|
||||||
|
ldrb w8, [x0]
|
||||||
|
and w8, w8, #0xfffffff9
|
||||||
|
strb w8, [x0]
|
||||||
|
ret
|
||||||
|
.cfi_endproc
|
||||||
|
; -- End function
|
||||||
|
.globl _SetB ; -- Begin function SetB
|
||||||
|
.p2align 2
|
||||||
|
_SetB: ; @SetB
|
||||||
|
.cfi_startproc
|
||||||
|
; %bb.0:
|
||||||
|
ldrb w8, [x0]
|
||||||
|
and w8, w8, #0xfffffff9
|
||||||
|
ubfiz w9, w1, #1, #2
|
||||||
|
orr w8, w8, w9
|
||||||
|
strb w8, [x0]
|
||||||
|
ret
|
||||||
|
.cfi_endproc
|
||||||
|
; -- End function
|
||||||
|
.globl _ClearC ; -- Begin function ClearC
|
||||||
|
.p2align 2
|
||||||
|
_ClearC: ; @ClearC
|
||||||
|
.cfi_startproc
|
||||||
|
; %bb.0:
|
||||||
|
ldrb w8, [x0]
|
||||||
|
and w8, w8, #0x7
|
||||||
|
strb w8, [x0]
|
||||||
|
ret
|
||||||
|
.cfi_endproc
|
||||||
|
; -- End function
|
||||||
|
.globl _SetC ; -- Begin function SetC
|
||||||
|
.p2align 2
|
||||||
|
_SetC: ; @SetC
|
||||||
|
.cfi_startproc
|
||||||
|
; %bb.0:
|
||||||
|
ldrb w8, [x0]
|
||||||
|
bfi w8, w1, #3, #8
|
||||||
|
strb w8, [x0]
|
||||||
|
ret
|
||||||
|
.cfi_endproc
|
||||||
|
; -- End function
|
||||||
|
.globl _main ; -- Begin function main
|
||||||
|
.p2align 2
|
||||||
|
_main: ; @main
|
||||||
|
.cfi_startproc
|
||||||
|
; %bb.0:
|
||||||
|
sub sp, sp, #48
|
||||||
|
stp x20, x19, [sp, #16] ; 16-byte Folded Spill
|
||||||
|
stp x29, x30, [sp, #32] ; 16-byte Folded Spill
|
||||||
|
add x29, sp, #32
|
||||||
|
.cfi_def_cfa w29, 16
|
||||||
|
.cfi_offset w30, -8
|
||||||
|
.cfi_offset w29, -16
|
||||||
|
.cfi_offset w19, -24
|
||||||
|
.cfi_offset w20, -32
|
||||||
|
ldrb w8, [sp, #15]
|
||||||
|
orr w8, w8, #0x1
|
||||||
|
strb w8, [sp, #15]
|
||||||
|
ldrb w8, [sp, #15]
|
||||||
|
and w8, w8, #0xfffffff9
|
||||||
|
orr w8, w8, #0x4
|
||||||
|
strb w8, [sp, #15]
|
||||||
|
ldrb w8, [sp, #15]
|
||||||
|
mov w19, #24
|
||||||
|
bfxil w19, w8, #0, #3
|
||||||
|
strb w19, [sp, #15]
|
||||||
|
mov w8, #58
|
||||||
|
adrp x9, _noBF@PAGE
|
||||||
|
strb w8, [x9, _noBF@PAGEOFF]
|
||||||
|
str x8, [sp]
|
||||||
|
Lloh0:
|
||||||
|
adrp x0, l_.str@PAGE
|
||||||
|
Lloh1:
|
||||||
|
add x0, x0, l_.str@PAGEOFF
|
||||||
|
bl _printf
|
||||||
|
str x19, [sp]
|
||||||
|
Lloh2:
|
||||||
|
adrp x0, l_.str.1@PAGE
|
||||||
|
Lloh3:
|
||||||
|
add x0, x0, l_.str.1@PAGEOFF
|
||||||
|
bl _printf
|
||||||
|
mov w0, #0
|
||||||
|
ldp x29, x30, [sp, #32] ; 16-byte Folded Reload
|
||||||
|
ldp x20, x19, [sp, #16] ; 16-byte Folded Reload
|
||||||
|
add sp, sp, #48
|
||||||
|
ret
|
||||||
|
.loh AdrpAdd Lloh2, Lloh3
|
||||||
|
.loh AdrpAdd Lloh0, Lloh1
|
||||||
|
.cfi_endproc
|
||||||
|
; -- End function
|
||||||
|
.globl _noBF ; @noBF
|
||||||
|
.zerofill __DATA,__common,_noBF,1,0
|
||||||
|
.section __TEXT,__cstring,cstring_literals
|
||||||
|
l_.str: ; @.str
|
||||||
|
.asciz "noBF should be 0x3A - value: 0x%X\n"
|
||||||
|
|
||||||
|
l_.str.1: ; @.str.1
|
||||||
|
.asciz "bf should be 0x1D - value: 0x%X\n"
|
||||||
|
|
||||||
|
.subsections_via_symbols
|
||||||
Loading…
Reference in a new issue