From b3f1a63e5139acffd9ab84b4aa3c034b08d904f6 Mon Sep 17 00:00:00 2001 From: Perry Kivolowitz Date: Sat, 18 Jun 2022 09:53:54 -0500 Subject: [PATCH] making progress --- section_2/bitfields/README.md | 145 +++++++++++++++++++++++++--- section_2/bitfields/sophisticated.s | 58 +++++++++++ section_2/bitfields/temp.txt | 10 ++ section_2/bitfields/test.c | 2 +- section_2/bitfields/test.s | 129 +++++++++++++++++++++++++ 5 files changed, 332 insertions(+), 12 deletions(-) create mode 100644 section_2/bitfields/sophisticated.s create mode 100644 section_2/bitfields/temp.txt create mode 100644 section_2/bitfields/test.s diff --git a/section_2/bitfields/README.md b/section_2/bitfields/README.md index 98b081f..031dd41 100644 --- a/section_2/bitfields/README.md +++ b/section_2/bitfields/README.md @@ -67,30 +67,153 @@ five bit field inside one byte. Without bit fields, we would have to write this code: ```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) { *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) { - *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) { *byte &= ~6; } +``` -void SetB(unsigned char * byte, unsigned char value) { - 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 -} +This could *naively* be written as: +```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) { *byte &= 7; // squashes bits 3 to 7 to 0 } diff --git a/section_2/bitfields/sophisticated.s b/section_2/bitfields/sophisticated.s new file mode 100644 index 0000000..b7b00e2 --- /dev/null +++ b/section_2/bitfields/sophisticated.s @@ -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 + + \ No newline at end of file diff --git a/section_2/bitfields/temp.txt b/section_2/bitfields/temp.txt new file mode 100644 index 0000000..3bbc24e --- /dev/null +++ b/section_2/bitfields/temp.txt @@ -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 + \ No newline at end of file diff --git a/section_2/bitfields/test.c b/section_2/bitfields/test.c index 7d74546..fcdad5d 100644 --- a/section_2/bitfields/test.c +++ b/section_2/bitfields/test.c @@ -8,7 +8,7 @@ struct BF { unsigned char noBF = 0; -#undef C +#define C #ifdef C /* Note the absence of defensive programming such as checking to ensure that byte is not null and that bit_number is not diff --git a/section_2/bitfields/test.s b/section_2/bitfields/test.s new file mode 100644 index 0000000..3aa4c91 --- /dev/null +++ b/section_2/bitfields/test.s @@ -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