From 3faad6e17b03f13e91d4b93a525e6608c72c8ace Mon Sep 17 00:00:00 2001 From: Alexander Sisco <36649949+devspeare@users.noreply.github.com> Date: Sun, 2 Feb 2025 21:01:25 -0800 Subject: [PATCH 01/16] Suggesting a third exercise for bit manipulation --- build.zig | 32 ++ exercises/110_bit_manipulation3.zig | 443 ++++++++++++++++++++++++++++ 2 files changed, 475 insertions(+) create mode 100644 exercises/110_bit_manipulation3.zig diff --git a/build.zig b/build.zig index e528552..9774833 100644 --- a/build.zig +++ b/build.zig @@ -1208,6 +1208,38 @@ const exercises = [_]Exercise{ \\Max difference (new fn): 0.014 , }, + .{ .main_file = "110_bit_manipulation3.zig", .output = + \\Set pins with OR on PORTB + \\------------------------- + \\ 1001 // (initial state of PORTB) + \\| 0100 // (bitmask) + \\= 1101 + \\ + \\ 1001 // (reset state) + \\| 0100 // (bitmask) + \\= 1101 + \\ + \\Clear pins with AND and NOT on PORTB + \\------------------------------------ + \\ 1110 // (initial state of PORTB) + \\& 1011 // (bitmask) + \\= 1010 + \\ + \\ 0111 // (reset state) + \\& 1110 // (bitmask) + \\= 0110 + \\ + \\ + \\Toggle pins with XOR on PORTB + \\----------------------------- + \\ 1100 // (initial state of PORTB) + \\^ 0101 // (bitmask) + \\= 1001 + \\ + \\ 1100 // (initial state of PORTB) + \\^ 0011 // (bitmask) + \\= 1111 + }, .{ .main_file = "999_the_end.zig", .output = diff --git a/exercises/110_bit_manipulation3.zig b/exercises/110_bit_manipulation3.zig new file mode 100644 index 0000000..930f78a --- /dev/null +++ b/exercises/110_bit_manipulation3.zig @@ -0,0 +1,443 @@ +// ---------------------------------------------------------------------------- +// Setting, Clearing, and Toggling Bits +// ---------------------------------------------------------------------------- +// +// Another exciting thing about Zig is its suitability for embedded +// programming. Your Zig code doesn't have to remain on your laptop. You can +// also deploy your code to microcontrollers! This means you can write Zig to +// drive your next robot or greenhouse climate control system! Ready to enter +// the exciting world of embedded programming? This exercise is designed to +// give you a taste of what it's like to control registers in a +// microcontroller. Let's get started! +// +// A common activity in microcontroller programming is setting and clearing +// bits on input and output pins. This lets you control LEDs, sensors, motors +// and more! In a previous exercise (097_bit_manipulation.zig) you learned how +// to swap two bytes using the ^ (XOR - exclusive or) operator. In this +// exercise, we'll take a closer look at bit manipulation and how we can write +// code that sets and clears specific bits as we would if we were programming +// the pins on a real microcontroller. Included at the end of this exercise are +// some helper functions that demonstrate how we might make our code a little +// more readable. +// +// Below is a pinout diagram for the famous ATmega328 AVR microcontroller used +// as the primary microchip on popular microcontroller platforms like the +// Arduino UNO. +// +// ============ PINOUT DIAGRAM FOR ATMEGA328 MICROCONTROLLER ============ +// _____ _____ +// | U | +// (RESET) PC6 --| 1 28 |-- PC5 +// PD0 --| 2 27 |-- PC4 +// PD1 --| 3 26 |-- PC3 +// PD2 --| 4 25 |-- PC2 +// PD3 --| 5 24 |-- PC1 +// PD4 --| 6 23 |-- PC0 +// VCC --| 7 22 |-- GND +// GND --| 8 21 |-- AREF +// |-- PB6 --| 9 20 |-- AVCC +// |-- PB7 --| 10 19 |-- PB5 --| +// | PD5 --| 11 18 |-- PB4 --| +// | PD6 --| 12 17 |-- PB3 --| +// | PD7 --| 13 16 |-- PB2 --| +// |-- PB0 --| 14 15 |-- PB1 --| +// | |___________| | +// \_______________________________/ +// | +// PORTB +// +// Drawing inspiration from this diagram, we'll use the pins for PORTB as our +// mental model for this exercise in bit manipulation. It should be noted that +// in the following examples we are using ordinary variables, one of which we +// have named PORTB, to simulate modifying the bits of real hardware registers. +// But in actual microcontroller code, PORTB would be defined something like +// this: +// pub const PORTB = @as(*volatile u8, @ptrFromInt(0x25)); +// +// This lets the compiler know not to make any optimizations to PORTB so that +// the IO pins are properly mapped to our code. +// +// NOTE : To keep things simple, the following examples are given using type +// u4, so applying the output to PORTB would only affect the lower four pins +// PB0..PB3. Of course, there is nothing to prevent you from swapping the u4 +// with a u8 so you can control all 8 of PORTB's IO pins. + +const std = @import("std"); +const print = std.debug.print; +const testing = std.testing; + +pub fn main() !void { + var PORTB: u4 = 0b0000; // only 4 bits wide for simplicity + // + // ------------------------------------------------------------------------ + // Setting bits with OR: + // ------------------------------------------------------------------------ + // We can set bits on PORTB with the | (OR) operator, like so: + // + // var PORTB: u4 = 0b1001; + // PORTB = PORTB | 0b0010; + // print("PORTB: {b:0>4}\n", .{PORTB}); // output: 1011 + // + // -OR op- ---expanded--- + // _ Set only this bit. + // / + // 1001 1 0 0 1 + // | 0010 0 0 1 0 (bit mask) + // ------ - - - - + // = 1011 1 0 1 1 + // \___\_______\ + // \ + // These bits remain untouched because OR-ing with + // a 0 effects no change. + // + // ------------------------------------------------------------------------ + // To create a bit mask like 0b0010 used above: + // + // 1. First, shift the value 1 over one place with the bitwise << (shift + // left) operator as indicated below: + // 1 << 0 -> 0001 + // 1 << 1 -> 0010 <-- Shift 1 one place to the left + // 1 << 2 -> 0100 + // 1 << 3 -> 1000 + // + // This allows us to rewrite the above code like this: + // + // var PORTB: u4 = 0b1001; + // PORTB = PORTB | (1 << 1); + // print("PORTB: {b:0>4}\n", .{PORTB}); // output: 1011 + // + // Finally, as in the C language, Zig allows us to use the |= operator, so + // we can rewrite our code again in an even more compact and idiomatic + // form: PORTB |= (1 << 1) + + print("Set pins with OR on PORTB\n", .{}); + print("-------------------------\n", .{}); + + PORTB = 0b1001; // reset PORTB + print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB}); + print("| {b:0>4} // (bitmask)\n", .{0b0100}); + PORTB = PORTB ??? (1 << 2); // What's missing here? + checkAnswer(0b1101, PORTB); + + newline(); + + PORTB = 0b1001; // reset PORTB + print(" {b:0>4} // (reset state)\n", .{PORTB}); + print("| {b:0>4} // (bitmask)\n", .{0b0100}); + PORTB ??? (1 << 2); // What's missing here? + checkAnswer(0b1101, PORTB); + + newline(); + + // ------------------------------------------------------------------------ + // Clearing bits with AND and NOT: + // ------------------------------------------------------------------------ + // We can clear bits with the & (AND) bitwise operator, like so: + + // PORTB = 0b1110; // reset PORTB + // PORTB = PORTB & 0b1011; + // print("PORTB: {b:0>4}\n", .{PORTB}); // output -> 1010 + // + // - 0s clear bits when used in conjuction with a bitwise AND. + // - 1s do nothing, thus preserving the original bits. + // + // -AND op- ---expanded--- + // __________ Clear only this bit. + // / + // 1110 1 1 1 0 + // & 1011 1 0 1 1 (bit mask) + // ------ - - - - + // = 1010 1 0 1 0 <- This bit was already cleared. + // \_______\ + // \ + // These bits remain untouched because AND-ing with a + // 1 preserves the original bit value whether 0 or 1. + // + // ------------------------------------------------------------------------ + // We can use the ~ (NOT) operator to easily create a bit mask like 1011: + // + // 1. First, shift the value 1 over two places with the bit-wise << (shift + // left) operator as indicated below: + // 1 << 0 -> 0001 + // 1 << 1 -> 0010 + // 1 << 2 -> 0100 <- The 1 has been shifted two places to the left + // 1 << 3 -> 1000 + // + // 2. The second step in creating our bit mask is to invert the bits + // ~0100 -> 1011 + // we can write this as: + // ~(1 << 2) -> 1011 + // + // But if we try to compile ~(1 << 2), we'll get an error: + // unable to perform binary not operation on type 'comptime_int' + // + // Before Zig can invert our bits, it needs to know the number of + // bits it's being asked to invert. + // + // We do this with the @as (cast as) built-in like this: + // @as(u4, 1 << 2) -> 0100 + // + // Finally, we can invert our new mask by placing the NOT ~ operator + // before our expression, like this: + // ~@as(u4, 1 << 2) -> 1011 + // + // If you are offput by the fact that you can't simply invert bits like + // you can in languages such as C without casting to a particular size + // of integer, you're not alone. However, this is actually another + // instance where Zig is really helpful because it protects you from + // difficult to debug integer overflow bugs that can have you tearing + // your hair out. In the interest of keeping things sane, Zig requires + // you simply to tell it the size of number you are inverting. In the + // words of Andrew Kelley, "If you want to invert the bits of an + // integer, zig has to know how many bits there are." + // + // For more insight into the Zig team's position on why the language + // takes the approach it does with the ~ operator, take a look at + // Andrew's comments on the following github issue: + // https://github.com/ziglang/zig/issues/1382#issuecomment-414459529 + // + // Whew, so after all that what we end up with is: + // PORTB = PORTB & ~@as(u4, 1 << 2); + // + // We can shorten this with the &= combined AND and assignment operator, + // which applies the AND operator on PORTB and then reassigns PORTB. Here's + // what that looks like: + // PORTB &= ~@as(u4, 1 << 2); + // + + print("Clear pins with AND and NOT on PORTB\n", .{}); + print("------------------------------------\n", .{}); + + PORTB = 0b1110; // reset PORTB + print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB}); + print("& {b:0>4} // (bitmask)\n", .{0b1011}); + PORTB = PORTB & ???@as(u4, 1 << 2); // What character is missing here? + checkAnswer(0b1010, PORTB); + + newline(); + + PORTB = 0b0111; // reset PORTB + print(" {b:0>4} // (reset state)\n", .{PORTB}); + print("& {b:0>4} // (bitmask)\n", .{0b1110}); + PORTB &= ~(1 << 0); // What's missing here? + checkAnswer(0b0110, PORTB); + + newline(); + newline(); + + + // + // ------------------------------------------------------------------------ + // Toggling bits with XOR: + // ------------------------------------------------------------------------ + // XOR stands for "exclusive or". We can toggle bits with the ^ (XOR) + // bitwise operator, like so: + // + // + // In order to output a 1, the logic of an XOR operation requires that the + // two input bits are of different values. Therefore, 0 ^ 1 and 1 ^ 0 will + // both yield a 1 but 0 ^ 0 and 1 ^ 1 will output 0. XOR's unique behavior + // of outputing a 0 when both inputs are 1s is what makes it different from + // the OR operator; it also gives us the ability to toggle bits by putting + // 1s into our bitmask. + // + // - 1s in our bitmask operand, can be thought of as causing the + // corresponding bits in the other operand to flip to the opposite value. + // - 0s cause no change. + // + // The 0s in our bitmask preserve these values + // -XOR op- ---expanded--- in the output. + // _______________/ + // / / + // 0110 1 1 0 0 + // ^ 1111 0 1 0 1 (bitmask) + // ------ - - - - + // = 1001 1 0 0 1 <- This bit was already cleared. + // \_______\ + // \ + // We can think of these bits having flipped + // because of the presence of 1s in those columns + // of our bitmask. + + print("Toggle pins with XOR on PORTB\n", .{}); + print("-----------------------------\n", .{}); + PORTB = 0b1100; + print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB}); + print("^ {b:0>4} // (bitmask)\n", .{0b0101}); + PORTB ^= (1 << 1) | (1 << 0); // What's wrong here? + checkAnswer(0b1001, PORTB); + + newline(); + + PORTB = 0b1100; + print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB}); + print("^ {b:0>4} // (bitmask)\n", .{0b0011}); + PORTB ^= (1 << 1) & (1 << 0); // What's wrong here? + checkAnswer(0b1111, PORTB); + + // While the examples in this exercise have used only 4-bit wide variables, + // working with 8 bits is no different. Here's a an example where we set + // every other bit beginning with the two's place: + + // var PORTD: u8 = 0b0000_0000; + // print("PORTD: {b:0>8}\n", .{PORTD}); + // PORTD |= (1 << 1); + // PORTD = setBit(u8, PORTD, 3); + // PORTD |= (1 << 5) | (1 << 7); + // print("PORTD: {b:0>8} // set every other bit\n", .{PORTD}); + // PORTD = ~PORTD; + // print("PORTD: {b:0>8} // bits flipped with NOT (~)\n", .{PORTD}); + // newline(); + // + // // Here we clear every other bit beginning with the two's place. + // + // PORTD = 0b1111_1111; + // print("PORTD: {b:0>8}\n", .{PORTD}); + // PORTD &= ~@as(u8, 1 << 1); + // PORTD = clearBit(u8, PORTD, 3); + // PORTD &= ~@as(u8, (1 << 5) | (1 << 7)); + // print("PORTD: {b:0>8} // clear every other bit\n", .{PORTD}); + // PORTD = ~PORTD; + // print("PORTD: {b:0>8} // bits flipped with NOT (~)\n", .{PORTD}); + // newline(); +} + +// ---------------------------------------------------------------------------- +// Here are some helper functions for manipulating bits +// ---------------------------------------------------------------------------- + +// Functions for setting, clearing, and toggling a single bit +fn setBit(comptime T: type, byte: T, comptime bit_pos: T) !T { + return byte | (1 << bit_pos); +} + +test "setBit" { + try testing.expectEqual(setBit(u8, 0b0000_0000, 3), 0b0000_1000); +} + +fn clearBit(comptime T: type, byte: T, comptime bit_pos: T) T { + return byte & ~@as(T, (1 << bit_pos)); +} + +test "clearBit" { + try testing.expectEqual(clearBit(u8, 0b1111_1111, 0), 0b1111_1110); +} + +fn toggleBit(comptime T: type, byte: T, comptime bit_pos: T) T { + return byte ^ (1 << bit_pos); +} + +test "toggleBit" { + var byte = toggleBit(u8, 0b0000_0000, 0); + try testing.expectEqual(byte, 0b0000_0001); + byte = toggleBit(u8, byte, 0); + try testing.expectEqual(byte, 0b0000_0000); +} + +// ---------------------------------------------------------------------------- +// Some additional functions for setting, clearing, and toggling multiple bits +// at once with a tuple because, hey, why not? +// ---------------------------------------------------------------------------- +// + +fn createBitmask(comptime T: type, comptime bits: anytype) !T { + comptime var bitmask: T = 0; + inline for (bits) |bit| { + if (bit >= @bitSizeOf(T)) return error.BitPosTooLarge; + if (bit < 0) return error.BitPosTooSmall; + + bitmask |= (1 << bit); + } + return bitmask; +} + +test "creating bitmasks from a tuple" { + try testing.expectEqual(createBitmask(u8, .{0}), 0b0000_0001); + try testing.expectEqual(createBitmask(u8, .{1}), 0b0000_0010); + try testing.expectEqual(createBitmask(u8, .{2}), 0b0000_0100); + try testing.expectEqual(createBitmask(u8, .{3}), 0b0000_1000); + // + try testing.expectEqual(createBitmask(u8, .{ 0, 4 }), 0b0001_0001); + try testing.expectEqual(createBitmask(u8, .{ 1, 5 }), 0b0010_0010); + try testing.expectEqual(createBitmask(u8, .{ 2, 6 }), 0b0100_0100); + try testing.expectEqual(createBitmask(u8, .{ 3, 7 }), 0b1000_1000); + + try testing.expectError(error.BitPosTooLarge, createBitmask(u4, .{4})); +} + +fn setBits(byte: u8, bits: anytype) !u8 { + const bitmask = try createBitmask(u8, bits); + return byte | bitmask; +} + +test "setBits" { + try testing.expectEqual(setBits(0b0000_0000, .{0}), 0b0000_0001); + try testing.expectEqual(setBits(0b0000_0000, .{7}), 0b1000_0000); + + try testing.expectEqual(setBits(0b0000_0000, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b1111_1111); + try testing.expectEqual(setBits(0b1111_1111, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b1111_1111); + + try testing.expectEqual(setBits(0b0000_0000, .{ 2, 3, 4, 5 }), 0b0011_1100); + + try testing.expectError(error.BitPosTooLarge, setBits(0b1111_1111, .{8})); + try testing.expectError(error.BitPosTooSmall, setBits(0b1111_1111, .{-1})); +} + +fn clearBits(comptime byte: u8, comptime bits: anytype) !u8 { + const bitmask: u8 = try createBitmask(u8, bits); + return byte & ~@as(u8, bitmask); +} + +test "clearBits" { + try testing.expectEqual(clearBits(0b1111_1111, .{0}), 0b1111_1110); + try testing.expectEqual(clearBits(0b1111_1111, .{7}), 0b0111_1111); + + try testing.expectEqual(clearBits(0b1111_1111, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b000_0000); + try testing.expectEqual(clearBits(0b0000_0000, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b000_0000); + + try testing.expectEqual(clearBits(0b1111_1111, .{ 0, 1, 6, 7 }), 0b0011_1100); + + try testing.expectError(error.BitPosTooLarge, clearBits(0b1111_1111, .{8})); + try testing.expectError(error.BitPosTooSmall, clearBits(0b1111_1111, .{-1})); +} + +fn toggleBits(comptime byte: u8, comptime bits: anytype) !u8 { + const bitmask = try createBitmask(u8, bits); + return byte ^ bitmask; +} + +test "toggleBits" { + try testing.expectEqual(toggleBits(0b0000_0000, .{0}), 0b0000_0001); + try testing.expectEqual(toggleBits(0b0000_0000, .{7}), 0b1000_0000); + + try testing.expectEqual(toggleBits(0b1111_1111, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b000_0000); + try testing.expectEqual(toggleBits(0b0000_0000, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b1111_1111); + + try testing.expectEqual(toggleBits(0b0000_1111, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b1111_0000); + try testing.expectEqual(toggleBits(0b0000_1111, .{ 0, 1, 2, 3 }), 0b0000_0000); + + try testing.expectEqual(toggleBits(0b0000_0000, .{ 0, 2, 4, 6 }), 0b0101_0101); + + try testing.expectError(error.BitPosTooLarge, toggleBits(0b1111_1111, .{8})); + try testing.expectError(error.BitPosTooSmall, toggleBits(0b1111_1111, .{-1})); +} + +// ---------------------------------------------------------------------------- +// Utility functions +// ---------------------------------------------------------------------------- + +fn newline() void { + print("\n", .{}); +} + +fn checkAnswer(expected: u4, answer: u4) void { + if (expected != answer) { + print("*************************************************************\n", .{}); + print("= {b:0>4} <- INCORRECT! THE EXPECTED OUTPUT IS {b:0>4}\n", .{ answer, expected }); + print("*************************************************************\n", .{}); + } else { + print("= {b:0>4}", .{answer}); + } + newline(); +} + From 657fd6aff065b900d55c6e6f037fe4dc3dc6e7f6 Mon Sep 17 00:00:00 2001 From: Alexander Sisco <36649949+devspeare@users.noreply.github.com> Date: Tue, 4 Feb 2025 08:57:23 -0800 Subject: [PATCH 02/16] changed the order of the sections to improve flow --- exercises/110_bit_manipulation3.zig | 110 +++++++++++++++------------- 1 file changed, 60 insertions(+), 50 deletions(-) diff --git a/exercises/110_bit_manipulation3.zig b/exercises/110_bit_manipulation3.zig index 930f78a..baf4045 100644 --- a/exercises/110_bit_manipulation3.zig +++ b/exercises/110_bit_manipulation3.zig @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Setting, Clearing, and Toggling Bits +// Toggling, Setting, and Clearing Bits // ---------------------------------------------------------------------------- // // Another exciting thing about Zig is its suitability for embedded @@ -69,6 +69,58 @@ const testing = std.testing; pub fn main() !void { var PORTB: u4 = 0b0000; // only 4 bits wide for simplicity // + // Let's first take a look at toggling bits. + // + // ------------------------------------------------------------------------ + // Toggling bits with XOR: + // ------------------------------------------------------------------------ + // XOR stands for "exclusive or". We can toggle bits with the ^ (XOR) + // bitwise operator, like so: + // + // + // In order to output a 1, the logic of an XOR operation requires that the + // two input bits are of different values. Therefore, 0 ^ 1 and 1 ^ 0 will + // both yield a 1 but 0 ^ 0 and 1 ^ 1 will output 0. XOR's unique behavior + // of outputing a 0 when both inputs are 1s is what makes it different from + // the OR operator; it also gives us the ability to toggle bits by putting + // 1s into our bitmask. + // + // - 1s in our bitmask operand, can be thought of as causing the + // corresponding bits in the other operand to flip to the opposite value. + // - 0s cause no change. + // + // The 0s in our bitmask preserve these values + // -XOR op- ---expanded--- in the output. + // _______________/ + // / / + // 0110 1 1 0 0 + // ^ 1111 0 1 0 1 (bitmask) + // ------ - - - - + // = 1001 1 0 0 1 <- This bit was already cleared. + // \_______\ + // \ + // We can think of these bits having flipped + // because of the presence of 1s in those columns + // of our bitmask. + + print("Toggle pins with XOR on PORTB\n", .{}); + print("-----------------------------\n", .{}); + PORTB = 0b1100; + print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB}); + print("^ {b:0>4} // (bitmask)\n", .{0b0101}); + PORTB ^= (1 << 1) | (1 << 0); // What's wrong here? + checkAnswer(0b1001, PORTB); + + newline(); + + PORTB = 0b1100; + print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB}); + print("^ {b:0>4} // (bitmask)\n", .{0b0011}); + PORTB ^= (1 << 1) & (1 << 0); // What's wrong here? + checkAnswer(0b1111, PORTB); + + // Now let's take a look at setting bits with the | operator. + // // ------------------------------------------------------------------------ // Setting bits with OR: // ------------------------------------------------------------------------ @@ -129,6 +181,10 @@ pub fn main() !void { newline(); + // So now we've covered how to toggle and set bits. What about clearing + // them? Well, this is where Zig throws us a curve ball. Don't worry we'll + // go through it step by step. + // ------------------------------------------------------------------------ // Clearing bits with AND and NOT: // ------------------------------------------------------------------------ @@ -165,10 +221,10 @@ pub fn main() !void { // // 2. The second step in creating our bit mask is to invert the bits // ~0100 -> 1011 - // we can write this as: + // in C we would write this as: // ~(1 << 2) -> 1011 // - // But if we try to compile ~(1 << 2), we'll get an error: + // But if we try to compile ~(1 << 2) in Zig, we'll get an error: // unable to perform binary not operation on type 'comptime_int' // // Before Zig can invert our bits, it needs to know the number of @@ -225,56 +281,10 @@ pub fn main() !void { newline(); newline(); - - // // ------------------------------------------------------------------------ - // Toggling bits with XOR: + // Conclusion // ------------------------------------------------------------------------ - // XOR stands for "exclusive or". We can toggle bits with the ^ (XOR) - // bitwise operator, like so: // - // - // In order to output a 1, the logic of an XOR operation requires that the - // two input bits are of different values. Therefore, 0 ^ 1 and 1 ^ 0 will - // both yield a 1 but 0 ^ 0 and 1 ^ 1 will output 0. XOR's unique behavior - // of outputing a 0 when both inputs are 1s is what makes it different from - // the OR operator; it also gives us the ability to toggle bits by putting - // 1s into our bitmask. - // - // - 1s in our bitmask operand, can be thought of as causing the - // corresponding bits in the other operand to flip to the opposite value. - // - 0s cause no change. - // - // The 0s in our bitmask preserve these values - // -XOR op- ---expanded--- in the output. - // _______________/ - // / / - // 0110 1 1 0 0 - // ^ 1111 0 1 0 1 (bitmask) - // ------ - - - - - // = 1001 1 0 0 1 <- This bit was already cleared. - // \_______\ - // \ - // We can think of these bits having flipped - // because of the presence of 1s in those columns - // of our bitmask. - - print("Toggle pins with XOR on PORTB\n", .{}); - print("-----------------------------\n", .{}); - PORTB = 0b1100; - print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB}); - print("^ {b:0>4} // (bitmask)\n", .{0b0101}); - PORTB ^= (1 << 1) | (1 << 0); // What's wrong here? - checkAnswer(0b1001, PORTB); - - newline(); - - PORTB = 0b1100; - print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB}); - print("^ {b:0>4} // (bitmask)\n", .{0b0011}); - PORTB ^= (1 << 1) & (1 << 0); // What's wrong here? - checkAnswer(0b1111, PORTB); - // While the examples in this exercise have used only 4-bit wide variables, // working with 8 bits is no different. Here's a an example where we set // every other bit beginning with the two's place: From 0fa86eb8c8fc76f8f1bb396b83d1d453edd870d1 Mon Sep 17 00:00:00 2001 From: Alexander Sisco <36649949+devspeare@users.noreply.github.com> Date: Tue, 4 Feb 2025 09:06:23 -0800 Subject: [PATCH 03/16] rearranged order of expected output in build.zig --- build.zig | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/build.zig b/build.zig index 9774833..c8f26fd 100644 --- a/build.zig +++ b/build.zig @@ -1209,6 +1209,16 @@ const exercises = [_]Exercise{ , }, .{ .main_file = "110_bit_manipulation3.zig", .output = + \\Toggle pins with XOR on PORTB + \\----------------------------- + \\ 1100 // (initial state of PORTB) + \\^ 0101 // (bitmask) + \\= 1001 + \\ + \\ 1100 // (initial state of PORTB) + \\^ 0011 // (bitmask) + \\= 1111 + \\ \\Set pins with OR on PORTB \\------------------------- \\ 1001 // (initial state of PORTB) @@ -1228,17 +1238,6 @@ const exercises = [_]Exercise{ \\ 0111 // (reset state) \\& 1110 // (bitmask) \\= 0110 - \\ - \\ - \\Toggle pins with XOR on PORTB - \\----------------------------- - \\ 1100 // (initial state of PORTB) - \\^ 0101 // (bitmask) - \\= 1001 - \\ - \\ 1100 // (initial state of PORTB) - \\^ 0011 // (bitmask) - \\= 1111 }, .{ .main_file = "999_the_end.zig", From 502ac8711e29bae3d0d9e4b1489b6e475369f321 Mon Sep 17 00:00:00 2001 From: Alexander Sisco <36649949+devspeare@users.noreply.github.com> Date: Tue, 4 Feb 2025 09:31:29 -0800 Subject: [PATCH 04/16] added newline between toggle and set sections to make the expected output match --- exercises/110_bit_manipulation3.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/exercises/110_bit_manipulation3.zig b/exercises/110_bit_manipulation3.zig index baf4045..620df3f 100644 --- a/exercises/110_bit_manipulation3.zig +++ b/exercises/110_bit_manipulation3.zig @@ -119,6 +119,8 @@ pub fn main() !void { PORTB ^= (1 << 1) & (1 << 0); // What's wrong here? checkAnswer(0b1111, PORTB); + newline(); + // Now let's take a look at setting bits with the | operator. // // ------------------------------------------------------------------------ From 596d5e50cadac66bff0d635c38f3993643302adc Mon Sep 17 00:00:00 2001 From: Alexander Sisco <36649949+devspeare@users.noreply.github.com> Date: Sat, 8 Feb 2025 10:07:59 -0800 Subject: [PATCH 05/16] format fix --- exercises/110_bit_manipulation3.zig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/exercises/110_bit_manipulation3.zig b/exercises/110_bit_manipulation3.zig index 620df3f..4ac2032 100644 --- a/exercises/110_bit_manipulation3.zig +++ b/exercises/110_bit_manipulation3.zig @@ -89,7 +89,7 @@ pub fn main() !void { // corresponding bits in the other operand to flip to the opposite value. // - 0s cause no change. // - // The 0s in our bitmask preserve these values + // The 0s in our bitmask preserve these values // -XOR op- ---expanded--- in the output. // _______________/ // / / @@ -452,4 +452,3 @@ fn checkAnswer(expected: u4, answer: u4) void { } newline(); } - From 1478d41801e6385efb5bf3a1acb12d71ad38eb8d Mon Sep 17 00:00:00 2001 From: Alexander Sisco <36649949+devspeare@users.noreply.github.com> Date: Sat, 8 Feb 2025 13:15:48 -0800 Subject: [PATCH 06/16] added patch file for exercise 110 --- patches/patches/110_bit_manipulation3.patch | 56 +++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 patches/patches/110_bit_manipulation3.patch diff --git a/patches/patches/110_bit_manipulation3.patch b/patches/patches/110_bit_manipulation3.patch new file mode 100644 index 0000000..9c64336 --- /dev/null +++ b/patches/patches/110_bit_manipulation3.patch @@ -0,0 +1,56 @@ +--- exercises/110_bit_manipulation3.zig 2025-02-08 11:52:35.609300707 -0800 ++++ answers/110_bit_manipulation3.zig 2025-02-08 13:00:15.414038314 -0800 +@@ -108,7 +108,7 @@ + PORTB = 0b1100; + print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB}); + print("^ {b:0>4} // (bitmask)\n", .{0b0101}); +- PORTB ^= (1 << 1) | (1 << 0); // What's wrong here? ++ PORTB ^= (1 << 2) | (1 << 0); + checkAnswer(0b1001, PORTB); + + newline(); +@@ -116,7 +116,7 @@ + PORTB = 0b1100; + print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB}); + print("^ {b:0>4} // (bitmask)\n", .{0b0011}); +- PORTB ^= (1 << 1) & (1 << 0); // What's wrong here? ++ PORTB ^= (1 << 1) | (1 << 0); + checkAnswer(0b1111, PORTB); + + newline(); +@@ -170,7 +170,7 @@ + PORTB = 0b1001; // reset PORTB + print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB}); + print("| {b:0>4} // (bitmask)\n", .{0b0100}); +- PORTB = PORTB ??? (1 << 2); // What's missing here? ++ PORTB = PORTB | (1 << 2); + checkAnswer(0b1101, PORTB); + + newline(); +@@ -178,7 +178,7 @@ + PORTB = 0b1001; // reset PORTB + print(" {b:0>4} // (reset state)\n", .{PORTB}); + print("| {b:0>4} // (bitmask)\n", .{0b0100}); +- PORTB ??? (1 << 2); // What's missing here? ++ PORTB |= (1 << 2); + checkAnswer(0b1101, PORTB); + + newline(); +@@ -269,7 +269,7 @@ + PORTB = 0b1110; // reset PORTB + print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB}); + print("& {b:0>4} // (bitmask)\n", .{0b1011}); +- PORTB = PORTB & ???@as(u4, 1 << 2); // What character is missing here? ++ PORTB = PORTB & ~@as(u4, 1 << 2); + checkAnswer(0b1010, PORTB); + + newline(); +@@ -277,7 +277,7 @@ + PORTB = 0b0111; // reset PORTB + print(" {b:0>4} // (reset state)\n", .{PORTB}); + print("& {b:0>4} // (bitmask)\n", .{0b1110}); +- PORTB &= ~(1 << 0); // What's missing here? ++ PORTB &= ~@as(u4, 1 << 0); + checkAnswer(0b0110, PORTB); + + newline(); From 20596bc290404a56b2f23e2d95aa35137239e06f Mon Sep 17 00:00:00 2001 From: Alexander Sisco <36649949+devspeare@users.noreply.github.com> Date: Mon, 10 Feb 2025 16:59:48 -0800 Subject: [PATCH 07/16] converted 110 to a quiz (quiz 9) --- build.zig | 2 +- exercises/{110_bit_manipulation3.zig => 110_quiz9.zig} | 0 .../patches/{110_bit_manipulation3.patch => 110_quiz9.patch} | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) rename exercises/{110_bit_manipulation3.zig => 110_quiz9.zig} (100%) rename patches/patches/{110_bit_manipulation3.patch => 110_quiz9.patch} (92%) diff --git a/build.zig b/build.zig index 3b753ee..25d831c 100644 --- a/build.zig +++ b/build.zig @@ -1269,7 +1269,7 @@ const exercises = [_]Exercise{ \\Max difference (new fn): 0.014 , }, - .{ .main_file = "110_bit_manipulation3.zig", .output = + .{ .main_file = "110_quiz9.zig", .output = \\Toggle pins with XOR on PORTB \\----------------------------- \\ 1100 // (initial state of PORTB) diff --git a/exercises/110_bit_manipulation3.zig b/exercises/110_quiz9.zig similarity index 100% rename from exercises/110_bit_manipulation3.zig rename to exercises/110_quiz9.zig diff --git a/patches/patches/110_bit_manipulation3.patch b/patches/patches/110_quiz9.patch similarity index 92% rename from patches/patches/110_bit_manipulation3.patch rename to patches/patches/110_quiz9.patch index 9c64336..9d9b864 100644 --- a/patches/patches/110_bit_manipulation3.patch +++ b/patches/patches/110_quiz9.patch @@ -1,5 +1,5 @@ ---- exercises/110_bit_manipulation3.zig 2025-02-08 11:52:35.609300707 -0800 -+++ answers/110_bit_manipulation3.zig 2025-02-08 13:00:15.414038314 -0800 +--- exercises/110_quiz9.zig 2025-02-08 13:19:48.522641785 -0800 ++++ answers/110_quiz9.zig 2025-02-10 17:42:04.525004335 -0800 @@ -108,7 +108,7 @@ PORTB = 0b1100; print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB}); From a7cd808bb8bdcb4e191c7bcbb427953a0461eac4 Mon Sep 17 00:00:00 2001 From: Alexander Sisco <36649949+devspeare@users.noreply.github.com> Date: Tue, 11 Feb 2025 11:56:57 -0800 Subject: [PATCH 08/16] moved explanatory content below the broken code in main so that the exercise functions more like a quiz --- exercises/110_quiz9.zig | 419 ++++++++++++++++++++++------------------ 1 file changed, 236 insertions(+), 183 deletions(-) diff --git a/exercises/110_quiz9.zig b/exercises/110_quiz9.zig index 4ac2032..171a509 100644 --- a/exercises/110_quiz9.zig +++ b/exercises/110_quiz9.zig @@ -68,40 +68,31 @@ const testing = std.testing; pub fn main() !void { var PORTB: u4 = 0b0000; // only 4 bits wide for simplicity + + // The LCD display on our robot is not behaving as expected. In order to + // get it functioning properly, we must initialize it by sending the + // correct sequence of half-bytes to PORTB's lower four pins. // - // Let's first take a look at toggling bits. + // See if you can solve the following problems to get the lcd working and + // reveal the message our robot has stored in his EEPROM. // - // ------------------------------------------------------------------------ - // Toggling bits with XOR: - // ------------------------------------------------------------------------ - // XOR stands for "exclusive or". We can toggle bits with the ^ (XOR) - // bitwise operator, like so: + // .--. .--. + // | | | | + // +--------------------------+ + // | +----------------------+ | + // | | | | + // | | XXXXXXXX XXXXXXXX | | <-- LCD + // | | | | + // | +----------------------+ | + // | _________ | + // | |_|_|_|_|_| | + // | | + // +--------------------------+ + // | | // - // - // In order to output a 1, the logic of an XOR operation requires that the - // two input bits are of different values. Therefore, 0 ^ 1 and 1 ^ 0 will - // both yield a 1 but 0 ^ 0 and 1 ^ 1 will output 0. XOR's unique behavior - // of outputing a 0 when both inputs are 1s is what makes it different from - // the OR operator; it also gives us the ability to toggle bits by putting - // 1s into our bitmask. - // - // - 1s in our bitmask operand, can be thought of as causing the - // corresponding bits in the other operand to flip to the opposite value. - // - 0s cause no change. - // - // The 0s in our bitmask preserve these values - // -XOR op- ---expanded--- in the output. - // _______________/ - // / / - // 0110 1 1 0 0 - // ^ 1111 0 1 0 1 (bitmask) - // ------ - - - - - // = 1001 1 0 0 1 <- This bit was already cleared. - // \_______\ - // \ - // We can think of these bits having flipped - // because of the presence of 1s in those columns - // of our bitmask. + // The last two problems throw you a bit of a curve ball. Try solving them + // on your own. If you need help, scroll to the bottom to see some in depth + // explanations on toggling, setting, and clearing bits in Zig. print("Toggle pins with XOR on PORTB\n", .{}); print("-----------------------------\n", .{}); @@ -121,49 +112,6 @@ pub fn main() !void { newline(); - // Now let's take a look at setting bits with the | operator. - // - // ------------------------------------------------------------------------ - // Setting bits with OR: - // ------------------------------------------------------------------------ - // We can set bits on PORTB with the | (OR) operator, like so: - // - // var PORTB: u4 = 0b1001; - // PORTB = PORTB | 0b0010; - // print("PORTB: {b:0>4}\n", .{PORTB}); // output: 1011 - // - // -OR op- ---expanded--- - // _ Set only this bit. - // / - // 1001 1 0 0 1 - // | 0010 0 0 1 0 (bit mask) - // ------ - - - - - // = 1011 1 0 1 1 - // \___\_______\ - // \ - // These bits remain untouched because OR-ing with - // a 0 effects no change. - // - // ------------------------------------------------------------------------ - // To create a bit mask like 0b0010 used above: - // - // 1. First, shift the value 1 over one place with the bitwise << (shift - // left) operator as indicated below: - // 1 << 0 -> 0001 - // 1 << 1 -> 0010 <-- Shift 1 one place to the left - // 1 << 2 -> 0100 - // 1 << 3 -> 1000 - // - // This allows us to rewrite the above code like this: - // - // var PORTB: u4 = 0b1001; - // PORTB = PORTB | (1 << 1); - // print("PORTB: {b:0>4}\n", .{PORTB}); // output: 1011 - // - // Finally, as in the C language, Zig allows us to use the |= operator, so - // we can rewrite our code again in an even more compact and idiomatic - // form: PORTB |= (1 << 1) - print("Set pins with OR on PORTB\n", .{}); print("-------------------------\n", .{}); @@ -183,86 +131,6 @@ pub fn main() !void { newline(); - // So now we've covered how to toggle and set bits. What about clearing - // them? Well, this is where Zig throws us a curve ball. Don't worry we'll - // go through it step by step. - - // ------------------------------------------------------------------------ - // Clearing bits with AND and NOT: - // ------------------------------------------------------------------------ - // We can clear bits with the & (AND) bitwise operator, like so: - - // PORTB = 0b1110; // reset PORTB - // PORTB = PORTB & 0b1011; - // print("PORTB: {b:0>4}\n", .{PORTB}); // output -> 1010 - // - // - 0s clear bits when used in conjuction with a bitwise AND. - // - 1s do nothing, thus preserving the original bits. - // - // -AND op- ---expanded--- - // __________ Clear only this bit. - // / - // 1110 1 1 1 0 - // & 1011 1 0 1 1 (bit mask) - // ------ - - - - - // = 1010 1 0 1 0 <- This bit was already cleared. - // \_______\ - // \ - // These bits remain untouched because AND-ing with a - // 1 preserves the original bit value whether 0 or 1. - // - // ------------------------------------------------------------------------ - // We can use the ~ (NOT) operator to easily create a bit mask like 1011: - // - // 1. First, shift the value 1 over two places with the bit-wise << (shift - // left) operator as indicated below: - // 1 << 0 -> 0001 - // 1 << 1 -> 0010 - // 1 << 2 -> 0100 <- The 1 has been shifted two places to the left - // 1 << 3 -> 1000 - // - // 2. The second step in creating our bit mask is to invert the bits - // ~0100 -> 1011 - // in C we would write this as: - // ~(1 << 2) -> 1011 - // - // But if we try to compile ~(1 << 2) in Zig, we'll get an error: - // unable to perform binary not operation on type 'comptime_int' - // - // Before Zig can invert our bits, it needs to know the number of - // bits it's being asked to invert. - // - // We do this with the @as (cast as) built-in like this: - // @as(u4, 1 << 2) -> 0100 - // - // Finally, we can invert our new mask by placing the NOT ~ operator - // before our expression, like this: - // ~@as(u4, 1 << 2) -> 1011 - // - // If you are offput by the fact that you can't simply invert bits like - // you can in languages such as C without casting to a particular size - // of integer, you're not alone. However, this is actually another - // instance where Zig is really helpful because it protects you from - // difficult to debug integer overflow bugs that can have you tearing - // your hair out. In the interest of keeping things sane, Zig requires - // you simply to tell it the size of number you are inverting. In the - // words of Andrew Kelley, "If you want to invert the bits of an - // integer, zig has to know how many bits there are." - // - // For more insight into the Zig team's position on why the language - // takes the approach it does with the ~ operator, take a look at - // Andrew's comments on the following github issue: - // https://github.com/ziglang/zig/issues/1382#issuecomment-414459529 - // - // Whew, so after all that what we end up with is: - // PORTB = PORTB & ~@as(u4, 1 << 2); - // - // We can shorten this with the &= combined AND and assignment operator, - // which applies the AND operator on PORTB and then reassigns PORTB. Here's - // what that looks like: - // PORTB &= ~@as(u4, 1 << 2); - // - print("Clear pins with AND and NOT on PORTB\n", .{}); print("------------------------------------\n", .{}); @@ -283,37 +151,222 @@ pub fn main() !void { newline(); newline(); - // ------------------------------------------------------------------------ - // Conclusion - // ------------------------------------------------------------------------ - // - // While the examples in this exercise have used only 4-bit wide variables, - // working with 8 bits is no different. Here's a an example where we set - // every other bit beginning with the two's place: - - // var PORTD: u8 = 0b0000_0000; - // print("PORTD: {b:0>8}\n", .{PORTD}); - // PORTD |= (1 << 1); - // PORTD = setBit(u8, PORTD, 3); - // PORTD |= (1 << 5) | (1 << 7); - // print("PORTD: {b:0>8} // set every other bit\n", .{PORTD}); - // PORTD = ~PORTD; - // print("PORTD: {b:0>8} // bits flipped with NOT (~)\n", .{PORTD}); - // newline(); - // - // // Here we clear every other bit beginning with the two's place. - // - // PORTD = 0b1111_1111; - // print("PORTD: {b:0>8}\n", .{PORTD}); - // PORTD &= ~@as(u8, 1 << 1); - // PORTD = clearBit(u8, PORTD, 3); - // PORTD &= ~@as(u8, (1 << 5) | (1 << 7)); - // print("PORTD: {b:0>8} // clear every other bit\n", .{PORTD}); - // PORTD = ~PORTD; - // print("PORTD: {b:0>8} // bits flipped with NOT (~)\n", .{PORTD}); - // newline(); } + +// ************************************************************************ +// IN-DEPTH EXPLANATIONS BELOW +// ************************************************************************ + + + + + + + + + + + + +// ------------------------------------------------------------------------ +// Toggling bits with XOR: +// ------------------------------------------------------------------------ +// XOR stands for "exclusive or". We can toggle bits with the ^ (XOR) +// bitwise operator, like so: +// +// +// In order to output a 1, the logic of an XOR operation requires that the +// two input bits are of different values. Therefore, 0 ^ 1 and 1 ^ 0 will +// both yield a 1 but 0 ^ 0 and 1 ^ 1 will output 0. XOR's unique behavior +// of outputing a 0 when both inputs are 1s is what makes it different from +// the OR operator; it also gives us the ability to toggle bits by putting +// 1s into our bitmask. +// +// - 1s in our bitmask operand, can be thought of as causing the +// corresponding bits in the other operand to flip to the opposite value. +// - 0s cause no change. +// +// The 0s in our bitmask preserve these values +// -XOR op- ---expanded--- in the output. +// _______________/ +// / / +// 0110 1 1 0 0 +// ^ 1111 0 1 0 1 (bitmask) +// ------ - - - - +// = 1001 1 0 0 1 <- This bit was already cleared. +// \_______\ +// \ +// We can think of these bits having flipped +// because of the presence of 1s in those columns +// of our bitmask. +// +// Now let's take a look at setting bits with the | operator. +// + + + + + + +// ------------------------------------------------------------------------ +// Setting bits with OR: +// ------------------------------------------------------------------------ +// We can set bits on PORTB with the | (OR) operator, like so: +// +// var PORTB: u4 = 0b1001; +// PORTB = PORTB | 0b0010; +// print("PORTB: {b:0>4}\n", .{PORTB}); // output: 1011 +// +// -OR op- ---expanded--- +// _ Set only this bit. +// / +// 1001 1 0 0 1 +// | 0010 0 0 1 0 (bit mask) +// ------ - - - - +// = 1011 1 0 1 1 +// \___\_______\ +// \ +// These bits remain untouched because OR-ing with +// a 0 effects no change. +// +// ------------------------------------------------------------------------ +// To create a bit mask like 0b0010 used above: +// +// 1. First, shift the value 1 over one place with the bitwise << (shift +// left) operator as indicated below: +// 1 << 0 -> 0001 +// 1 << 1 -> 0010 <-- Shift 1 one place to the left +// 1 << 2 -> 0100 +// 1 << 3 -> 1000 +// +// This allows us to rewrite the above code like this: +// +// var PORTB: u4 = 0b1001; +// PORTB = PORTB | (1 << 1); +// print("PORTB: {b:0>4}\n", .{PORTB}); // output: 1011 +// +// Finally, as in the C language, Zig allows us to use the |= operator, so +// we can rewrite our code again in an even more compact and idiomatic +// form: PORTB |= (1 << 1) + +// So now we've covered how to toggle and set bits. What about clearing +// them? Well, this is where Zig throws us a curve ball. Don't worry we'll +// go through it step by step. + + + + + + +// ------------------------------------------------------------------------ +// Clearing bits with AND and NOT: +// ------------------------------------------------------------------------ +// We can clear bits with the & (AND) bitwise operator, like so: + +// PORTB = 0b1110; // reset PORTB +// PORTB = PORTB & 0b1011; +// print("PORTB: {b:0>4}\n", .{PORTB}); // output -> 1010 +// +// - 0s clear bits when used in conjuction with a bitwise AND. +// - 1s do nothing, thus preserving the original bits. +// +// -AND op- ---expanded--- +// __________ Clear only this bit. +// / +// 1110 1 1 1 0 +// & 1011 1 0 1 1 (bit mask) +// ------ - - - - +// = 1010 1 0 1 0 <- This bit was already cleared. +// \_______\ +// \ +// These bits remain untouched because AND-ing with a +// 1 preserves the original bit value whether 0 or 1. +// +// ------------------------------------------------------------------------ +// We can use the ~ (NOT) operator to easily create a bit mask like 1011: +// +// 1. First, shift the value 1 over two places with the bit-wise << (shift +// left) operator as indicated below: +// 1 << 0 -> 0001 +// 1 << 1 -> 0010 +// 1 << 2 -> 0100 <- The 1 has been shifted two places to the left +// 1 << 3 -> 1000 +// +// 2. The second step in creating our bit mask is to invert the bits +// ~0100 -> 1011 +// in C we would write this as: +// ~(1 << 2) -> 1011 +// +// But if we try to compile ~(1 << 2) in Zig, we'll get an error: +// unable to perform binary not operation on type 'comptime_int' +// +// Before Zig can invert our bits, it needs to know the number of +// bits it's being asked to invert. +// +// We do this with the @as (cast as) built-in like this: +// @as(u4, 1 << 2) -> 0100 +// +// Finally, we can invert our new mask by placing the NOT ~ operator +// before our expression, like this: +// ~@as(u4, 1 << 2) -> 1011 +// +// If you are offput by the fact that you can't simply invert bits like +// you can in languages such as C without casting to a particular size +// of integer, you're not alone. However, this is actually another +// instance where Zig is really helpful because it protects you from +// difficult to debug integer overflow bugs that can have you tearing +// your hair out. In the interest of keeping things sane, Zig requires +// you simply to tell it the size of number you are inverting. In the +// words of Andrew Kelley, "If you want to invert the bits of an +// integer, zig has to know how many bits there are." +// +// For more insight into the Zig team's position on why the language +// takes the approach it does with the ~ operator, take a look at +// Andrew's comments on the following github issue: +// https://github.com/ziglang/zig/issues/1382#issuecomment-414459529 +// +// Whew, so after all that what we end up with is: +// PORTB = PORTB & ~@as(u4, 1 << 2); +// +// We can shorten this with the &= combined AND and assignment operator, +// which applies the AND operator on PORTB and then reassigns PORTB. Here's +// what that looks like: +// PORTB &= ~@as(u4, 1 << 2); +// + +// ------------------------------------------------------------------------ +// Conclusion +// ------------------------------------------------------------------------ +// +// While the examples in this quiz have used only 4-bit wide variables, +// working with 8 bits is no different. Here's a an example where we set +// every other bit beginning with the two's place: + +// var PORTD: u8 = 0b0000_0000; +// print("PORTD: {b:0>8}\n", .{PORTD}); +// PORTD |= (1 << 1); +// PORTD = setBit(u8, PORTD, 3); +// PORTD |= (1 << 5) | (1 << 7); +// print("PORTD: {b:0>8} // set every other bit\n", .{PORTD}); +// PORTD = ~PORTD; +// print("PORTD: {b:0>8} // bits flipped with NOT (~)\n", .{PORTD}); +// newline(); +// +// // Here we clear every other bit beginning with the two's place. +// +// PORTD = 0b1111_1111; +// print("PORTD: {b:0>8}\n", .{PORTD}); +// PORTD &= ~@as(u8, 1 << 1); +// PORTD = clearBit(u8, PORTD, 3); +// PORTD &= ~@as(u8, (1 << 5) | (1 << 7)); +// print("PORTD: {b:0>8} // clear every other bit\n", .{PORTD}); +// PORTD = ~PORTD; +// print("PORTD: {b:0>8} // bits flipped with NOT (~)\n", .{PORTD}); +// newline(); + + + // ---------------------------------------------------------------------------- // Here are some helper functions for manipulating bits // ---------------------------------------------------------------------------- From 465536baf00d07cf9ec5b6232dc3f48eea164cb0 Mon Sep 17 00:00:00 2001 From: Alexander Sisco <36649949+devspeare@users.noreply.github.com> Date: Tue, 11 Feb 2025 13:45:51 -0800 Subject: [PATCH 09/16] made some simple changes to the wording to reflect the fact that this is a quiz --- exercises/110_quiz9.zig | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/exercises/110_quiz9.zig b/exercises/110_quiz9.zig index 171a509..734b9dc 100644 --- a/exercises/110_quiz9.zig +++ b/exercises/110_quiz9.zig @@ -1,20 +1,20 @@ // ---------------------------------------------------------------------------- -// Toggling, Setting, and Clearing Bits +// Quiz Time: Toggling, Setting, and Clearing Bits // ---------------------------------------------------------------------------- // // Another exciting thing about Zig is its suitability for embedded // programming. Your Zig code doesn't have to remain on your laptop. You can // also deploy your code to microcontrollers! This means you can write Zig to // drive your next robot or greenhouse climate control system! Ready to enter -// the exciting world of embedded programming? This exercise is designed to -// give you a taste of what it's like to control registers in a -// microcontroller. Let's get started! +// the exciting world of embedded programming? This quiz is designed to test +// your knowledge of bit manipulationh in Zig while also giving you a taste of +// what it's like to control registers in a microcontroller. Let's get started! // // A common activity in microcontroller programming is setting and clearing // bits on input and output pins. This lets you control LEDs, sensors, motors // and more! In a previous exercise (097_bit_manipulation.zig) you learned how // to swap two bytes using the ^ (XOR - exclusive or) operator. In this -// exercise, we'll take a closer look at bit manipulation and how we can write +// quiz, we'll take a closer look at bit manipulation and how we can write // code that sets and clears specific bits as we would if we were programming // the pins on a real microcontroller. Included at the end of this exercise are // some helper functions that demonstrate how we might make our code a little @@ -47,8 +47,8 @@ // PORTB // // Drawing inspiration from this diagram, we'll use the pins for PORTB as our -// mental model for this exercise in bit manipulation. It should be noted that -// in the following examples we are using ordinary variables, one of which we +// mental model for this quiz on bit manipulation. It should be noted that +// in the following problems we are using ordinary variables, one of which we // have named PORTB, to simulate modifying the bits of real hardware registers. // But in actual microcontroller code, PORTB would be defined something like // this: @@ -57,7 +57,7 @@ // This lets the compiler know not to make any optimizations to PORTB so that // the IO pins are properly mapped to our code. // -// NOTE : To keep things simple, the following examples are given using type +// NOTE : To keep things simple, the following problems are given using type // u4, so applying the output to PORTB would only affect the lower four pins // PB0..PB3. Of course, there is nothing to prevent you from swapping the u4 // with a u8 so you can control all 8 of PORTB's IO pins. From 6fbf81d929273b16136afdfccb6ddbe592eb29ac Mon Sep 17 00:00:00 2001 From: Alexander Sisco <36649949+devspeare@users.noreply.github.com> Date: Tue, 11 Feb 2025 15:07:51 -0800 Subject: [PATCH 10/16] edited the first two paragraphs --- exercises/110_quiz9.zig | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/exercises/110_quiz9.zig b/exercises/110_quiz9.zig index 734b9dc..2db3230 100644 --- a/exercises/110_quiz9.zig +++ b/exercises/110_quiz9.zig @@ -3,22 +3,23 @@ // ---------------------------------------------------------------------------- // // Another exciting thing about Zig is its suitability for embedded -// programming. Your Zig code doesn't have to remain on your laptop. You can +// programming. Your Zig code doesn't have to remain on your laptop; you can // also deploy your code to microcontrollers! This means you can write Zig to // drive your next robot or greenhouse climate control system! Ready to enter -// the exciting world of embedded programming? This quiz is designed to test -// your knowledge of bit manipulationh in Zig while also giving you a taste of -// what it's like to control registers in a microcontroller. Let's get started! +// the exciting world of embedded programming? Let's get started! +// +// ---------------------------------------------------------------------------- +// Some Background +// ---------------------------------------------------------------------------- // // A common activity in microcontroller programming is setting and clearing // bits on input and output pins. This lets you control LEDs, sensors, motors // and more! In a previous exercise (097_bit_manipulation.zig) you learned how -// to swap two bytes using the ^ (XOR - exclusive or) operator. In this -// quiz, we'll take a closer look at bit manipulation and how we can write -// code that sets and clears specific bits as we would if we were programming -// the pins on a real microcontroller. Included at the end of this exercise are -// some helper functions that demonstrate how we might make our code a little -// more readable. +// to swap two bytes using the ^ (XOR - exclusive or) operator. This quiz will +// test your knowledge of bit manipulationh in Zig while giving you a taste of +// what it's like to control registers in a real microcontroller. Included at +// the end are some helper functions that demonstrate how we might make our +// code a little more readable. // // Below is a pinout diagram for the famous ATmega328 AVR microcontroller used // as the primary microchip on popular microcontroller platforms like the From b7b3297d067416f0e44b285fdd8d7347165bf6c4 Mon Sep 17 00:00:00 2001 From: Alexander Sisco <36649949+devspeare@users.noreply.github.com> Date: Tue, 11 Feb 2025 15:11:55 -0800 Subject: [PATCH 11/16] added blank lines between sections to make them easier to find --- exercises/110_quiz9.zig | 44 +++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/exercises/110_quiz9.zig b/exercises/110_quiz9.zig index 2db3230..4412150 100644 --- a/exercises/110_quiz9.zig +++ b/exercises/110_quiz9.zig @@ -158,18 +158,17 @@ pub fn main() !void { // ************************************************************************ // IN-DEPTH EXPLANATIONS BELOW // ************************************************************************ - - - - - - - - - - - - +// +// +// +// +// +// +// +// +// +// +// // ------------------------------------------------------------------------ // Toggling bits with XOR: // ------------------------------------------------------------------------ @@ -204,12 +203,10 @@ pub fn main() !void { // // Now let's take a look at setting bits with the | operator. // - - - - - - +// +// +// +// // ------------------------------------------------------------------------ // Setting bits with OR: // ------------------------------------------------------------------------ @@ -254,12 +251,11 @@ pub fn main() !void { // So now we've covered how to toggle and set bits. What about clearing // them? Well, this is where Zig throws us a curve ball. Don't worry we'll // go through it step by step. - - - - - - +// +// +// +// +// // ------------------------------------------------------------------------ // Clearing bits with AND and NOT: // ------------------------------------------------------------------------ From f7e8d4c44435251aaef9f409d5492895a955ce67 Mon Sep 17 00:00:00 2001 From: Alexander Sisco <36649949+devspeare@users.noreply.github.com> Date: Tue, 11 Feb 2025 15:13:25 -0800 Subject: [PATCH 12/16] added header for quiz problems --- exercises/110_quiz9.zig | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/exercises/110_quiz9.zig b/exercises/110_quiz9.zig index 4412150..9036c04 100644 --- a/exercises/110_quiz9.zig +++ b/exercises/110_quiz9.zig @@ -70,30 +70,14 @@ const testing = std.testing; pub fn main() !void { var PORTB: u4 = 0b0000; // only 4 bits wide for simplicity - // The LCD display on our robot is not behaving as expected. In order to - // get it functioning properly, we must initialize it by sending the - // correct sequence of half-bytes to PORTB's lower four pins. - // - // See if you can solve the following problems to get the lcd working and - // reveal the message our robot has stored in his EEPROM. - // - // .--. .--. - // | | | | - // +--------------------------+ - // | +----------------------+ | - // | | | | - // | | XXXXXXXX XXXXXXXX | | <-- LCD - // | | | | - // | +----------------------+ | - // | _________ | - // | |_|_|_|_|_| | - // | | - // +--------------------------+ - // | | - // - // The last two problems throw you a bit of a curve ball. Try solving them - // on your own. If you need help, scroll to the bottom to see some in depth - // explanations on toggling, setting, and clearing bits in Zig. + // ------------------------------------------------------------------------ + // Quiz + // ------------------------------------------------------------------------ + + // See if you can solve the following problems. The last two problems throw + // you a bit of a curve ball. Try solving them on your own. If you need + // help, scroll to the bottom of main to see some in depth explanations on + // toggling, setting, and clearing bits in Zig. print("Toggle pins with XOR on PORTB\n", .{}); print("-----------------------------\n", .{}); From 2437edd51f58dcf6d5e8d4bdeb4e04be9a74c0b3 Mon Sep 17 00:00:00 2001 From: Alexander Sisco <36649949+devspeare@users.noreply.github.com> Date: Tue, 11 Feb 2025 15:21:26 -0800 Subject: [PATCH 13/16] fixed incorrect bitmask in xor example --- exercises/110_quiz9.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/110_quiz9.zig b/exercises/110_quiz9.zig index 9036c04..d4f0478 100644 --- a/exercises/110_quiz9.zig +++ b/exercises/110_quiz9.zig @@ -175,8 +175,8 @@ pub fn main() !void { // -XOR op- ---expanded--- in the output. // _______________/ // / / -// 0110 1 1 0 0 -// ^ 1111 0 1 0 1 (bitmask) +// 1100 1 1 0 0 +// ^ 0101 0 1 0 1 (bitmask) // ------ - - - - // = 1001 1 0 0 1 <- This bit was already cleared. // \_______\ From 8384d4d9bf8be2d4a5ba55d90e5e633356134fa0 Mon Sep 17 00:00:00 2001 From: Alexander Sisco <36649949+devspeare@users.noreply.github.com> Date: Wed, 12 Feb 2025 12:59:29 -0800 Subject: [PATCH 14/16] fixed minor spelling and grammar typos --- exercises/110_quiz9.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/110_quiz9.zig b/exercises/110_quiz9.zig index d4f0478..89881b1 100644 --- a/exercises/110_quiz9.zig +++ b/exercises/110_quiz9.zig @@ -16,7 +16,7 @@ // bits on input and output pins. This lets you control LEDs, sensors, motors // and more! In a previous exercise (097_bit_manipulation.zig) you learned how // to swap two bytes using the ^ (XOR - exclusive or) operator. This quiz will -// test your knowledge of bit manipulationh in Zig while giving you a taste of +// test your knowledge of bit manipulation in Zig while giving you a taste of // what it's like to control registers in a real microcontroller. Included at // the end are some helper functions that demonstrate how we might make our // code a little more readable. @@ -321,7 +321,7 @@ pub fn main() !void { // ------------------------------------------------------------------------ // // While the examples in this quiz have used only 4-bit wide variables, -// working with 8 bits is no different. Here's a an example where we set +// working with 8 bits is no different. Here's an example where we set // every other bit beginning with the two's place: // var PORTD: u8 = 0b0000_0000; From 90e90c997e912ca4e5c50806dd5ab259e727ed40 Mon Sep 17 00:00:00 2001 From: Alexander Sisco <36649949+devspeare@users.noreply.github.com> Date: Thu, 13 Feb 2025 12:45:46 -0800 Subject: [PATCH 15/16] fixed formatting --- exercises/110_quiz9.zig | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/exercises/110_quiz9.zig b/exercises/110_quiz9.zig index 89881b1..540c6e4 100644 --- a/exercises/110_quiz9.zig +++ b/exercises/110_quiz9.zig @@ -7,7 +7,7 @@ // also deploy your code to microcontrollers! This means you can write Zig to // drive your next robot or greenhouse climate control system! Ready to enter // the exciting world of embedded programming? Let's get started! -// +// // ---------------------------------------------------------------------------- // Some Background // ---------------------------------------------------------------------------- @@ -135,10 +135,8 @@ pub fn main() !void { newline(); newline(); - } - // ************************************************************************ // IN-DEPTH EXPLANATIONS BELOW // ************************************************************************ @@ -346,8 +344,6 @@ pub fn main() !void { // print("PORTD: {b:0>8} // bits flipped with NOT (~)\n", .{PORTD}); // newline(); - - // ---------------------------------------------------------------------------- // Here are some helper functions for manipulating bits // ---------------------------------------------------------------------------- From cd94f6023cd93d448f0b6eea0eea7f0a61980147 Mon Sep 17 00:00:00 2001 From: Alexander Sisco Date: Thu, 13 Feb 2025 13:14:29 -0800 Subject: [PATCH 16/16] fixed spelling of 'bitmask' --- exercises/110_quiz9.zig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/exercises/110_quiz9.zig b/exercises/110_quiz9.zig index 540c6e4..cd0048b 100644 --- a/exercises/110_quiz9.zig +++ b/exercises/110_quiz9.zig @@ -202,7 +202,7 @@ pub fn main() !void { // _ Set only this bit. // / // 1001 1 0 0 1 -// | 0010 0 0 1 0 (bit mask) +// | 0010 0 0 1 0 (bitmask) // ------ - - - - // = 1011 1 0 1 1 // \___\_______\ @@ -211,7 +211,7 @@ pub fn main() !void { // a 0 effects no change. // // ------------------------------------------------------------------------ -// To create a bit mask like 0b0010 used above: +// To create a bitmask like 0b0010 used above: // // 1. First, shift the value 1 over one place with the bitwise << (shift // left) operator as indicated below: @@ -254,7 +254,7 @@ pub fn main() !void { // __________ Clear only this bit. // / // 1110 1 1 1 0 -// & 1011 1 0 1 1 (bit mask) +// & 1011 1 0 1 1 (bitmask) // ------ - - - - // = 1010 1 0 1 0 <- This bit was already cleared. // \_______\ @@ -263,7 +263,7 @@ pub fn main() !void { // 1 preserves the original bit value whether 0 or 1. // // ------------------------------------------------------------------------ -// We can use the ~ (NOT) operator to easily create a bit mask like 1011: +// We can use the ~ (NOT) operator to easily create a bitmask like 1011: // // 1. First, shift the value 1 over two places with the bit-wise << (shift // left) operator as indicated below: @@ -272,7 +272,7 @@ pub fn main() !void { // 1 << 2 -> 0100 <- The 1 has been shifted two places to the left // 1 << 3 -> 1000 // -// 2. The second step in creating our bit mask is to invert the bits +// 2. The second step in creating our bitmask is to invert the bits // ~0100 -> 1011 // in C we would write this as: // ~(1 << 2) -> 1011