Tuesday, October 3, 2017

ESP8266 vs ESP32

SpecificationsESP8266ESP32
MCUXtensa Single-core 32-bit L106Xtensa Dual-Core 32-bit LX6 with 600 DMIPS
802.11 b/g/n Wi-FiYes, HT20Yes, HT40
WiFi Security?WEP, WPA/WPA2 PSK/Enterprise
Hardware Accelerated Encryption?AES/SHA2/Elliptical Curve Cryptography/RSA-4096
BluetoothNonedual-mode: Bluetooth 4.2 BLE or classi
Typical Frequency80 MHz240 MHz
SRAM160 KB520 KB
FlashSPI Flash, up to 16 MBSPI Flash, up to 16 MB, memory mapped to CPU code space.
GPIO1736
Hardware/Software PWMNone/8 channels1/16 channels
SPI/I2S/I2C/UART 2/1/2/2 3/2/2/3
ADC10 bit12-bit, 18 channels
DACNone2
CANNone?
Ethernet MAC Interface None 1
Touch Sensor None Yes
Temperature SensorNoneyes
Working Temperature-40 C - 125 C-40 C - 125 C
Operating Voltage?2.3V to 3.6V C
Power Consumption?5 μA power consumption in Deep-sleep

Reference: http://espressif.com/en/products/hardware/esp32/overview

My protoboard


Friday, September 29, 2017

ZigBee for IoT

The ZigBee and Z-Wave short-range wireless technologies are used for remote monitoring and control. However, their specifications and applications are different. Both technologies are ideal for home-area networks (HANs), which is becoming an in.


Differences between ZigBee and Z-Wave:


TechnologyFrequencyModulationData RateRangeApplications
ZigBee902 to 928 MHz (Americas and Australia)2.4 - 2.483 GHz (ISM)BPSK (900 MHz band) or
OQPSK (2.4 GHz band)
250 kbps10 mHome Automation, Smart Grid, Remote control
Z-Wave908.42 MHzGFSK9.6/40 kbps30 mHome Automation, security


ZigBee
It is ratified in the IEEE’s 802.15.4 personal-area network (PAN) radio standard. ZigBee is an open wireless standard from the ZigBee Alliance. The IEEE 802.15.4 standard provides layer 1 (physical layer, or PHY) and layer 2 (media access controller, or MAC) of the network, while the ZigBee stack software provides the network and application layers.

The Zigbee protocol is designed to communicate data through hostile RF environments that are common in commercial and industrial applications.

Zigbee protocol features include:
  • Support for multiple network topologies such as point-to-point,
  • point-to-multipoint and mesh networks
  • Low duty cycle – provides long battery life
  • Low latency
  • Direct Sequence Spread Spectrum (DSSS)
  • Up to 65,000 nodes per network
  • 128-bit AES encryption for secure data connections
  • Collision avoidance, retries, and acknowledgments (CSMA/CA)

ZigBee Physical Layer
ZigBee PHY operates in various bands, but the most common one is in the 2.4 GHz band. It uses offset quadrature phase-shift keying (OQPSK) that transmits two bits per symbol. In 900 MHz band, it uses BPSK for modulation.  The radio uses DSSS for digital streaming.

There are three (3) kind of devices in ZigBee:
  1. ZigBee Coordinator (ZR)
  2. ZigBee Router (ZR)
  3. ZigBee End Device (ZED)

Tuesday, September 26, 2017

Hot skills in Embedded and IoT

Based on my observation in the job market, the following skills are currently in demand in embedded systems surrounding IoT development:

  1. 802.11 (WiFi)
  2. Bluetooth, especially Bluetooth 4.0 + Low Energy (BLE) or newer
  3. Zigbee (IEEE 802.15.4)
  4. Z-Wave: Based on ITU G.9959 (PHY) and Z-Wave Application layer
  5. Bonjour (mDNS/DNS-SD)
  6. SSDP (Simple Service Discovery Protocol)
  7. OCF (Open Connectivity Foundation's uPnP Device Control Protocol)
  8. TCP/UDP
  9. TLS (Transport Layer Security)
  10. CoAP (Constrained Application Protocol; RFC-7252)
  11. HTTP, especially RESt API
  12. MQTT (Mosquitto)
  13. MultiThread
  14. Websockets
  15. RESTful (Representational State Transfer) architecture
  16. Rust (Rust is a programming language that’s focused on safety, speed, and concurrency)
  17. Jenkins
  18. XML
  19. LWM2M
  20. SQS
  21. AMQP
  22. Kafka
  23. AWS (Amazon Web Service)
  24. Microsoft Azure
  25. I2C
  26. SPI
  27. Asynchronous serial/UART programming
  28. Linux Kernel and Driver development



Thursday, September 21, 2017

Pointer to certain address in memory

In embedded system where we are working with microcontroller, many times we find a scenario where we need to access a memory-mapped register in the MCU.  How do we do that in generic way (for example with GCC compiler)?

The answer is to write it like below:

volatile <type> *<varname> = (<type> *)<address>

For example:

#include <stdio.h>

volatile char *var = (char *)0x1000;

int main()
{
    if (*var != 0)
        puts("NOT NULL!");
}

rendered into x86 assembly (64-bit Intel CPU) as:
...
...
.LC0:
        .string "NOT NULL!"

main:
movq var(%rip), %rax      # rax = address of var
movzbl (%rax), %eax         # eax = *var
testb %al, %al
jne .L9
xorl %eax, %eax
ret
.L9:
pushq %rax
movl $.LC0, %edi
call puts
xorl %eax, %eax
popq %rdx
ret

...
.LCOLDE1:
.section .text.startup
.LHOTE1:
.globl var
.data
.align 8
.type var, @object
.size var, 8
var:
.quad 4096           # or 0x1000

Wednesday, September 20, 2017

Deleting duplicate entries and sorting the rest

I learned an interesting (and very easy) way to remove duplicate entries as well as to sort them. Basically, in C++ it is achieved by creating a new set and initialize with values from the vector.  We shift all the hardwork to the std library to do this.

#include <iostream>
#include <vector>
#include <set>


void print(std::vector<int> &v)
{
    std::cout << "[";
    for(std::vector<int>::iterator it = v.begin(); it != v.end(); ++it)
    {
        std::cout << *it << ",";
    }
    std::cout << "]" << std::endl;
}


void print(std::set<int> &v)
{
    std::cout << "{";
    for(std::set<int>::iterator it = v.begin(); it != v.end(); ++it)
    {
        std::cout << *it << ",";
    }
    std::cout << "}" << std::endl;
}



int main()
{
    int inits[] = {1,10,3,2,2,4,4,5,6,7,8,8,9};
    std::vector<int> vec(inits, inits + sizeof(inits)/sizeof(inits[0]));
    print(vec);
    std::set<int> ves(vec.begin(), vec.end()); // both sorted & unique already
    print(ves);
}

Tuesday, September 19, 2017

Finding missing sequential numbers

Say you have an array with integer sequential numbers in it, but there are missing numbers in it.  How to find what are those numbers?  Assume the array is already sorted ascendant.

Solution
On the Internet, I found cases where there is only one number missing and the approach is to do sum of the numbers and verify if the sum S equals to n*(n+1)/2.  S(n) = n + (n-1) + (n-2) + .... + 0.  If not equal, then difference is equal to the missing number, because S + missing number must be = n*(n+1)/2.

def MissingSingleNum(A):
    sum = 0
    n = A[len(A)-1]
    for i in A:
        sum = sum + i
    computed = n*(n+1)/2
    if sum != computed:
        return computed - sum
    else:
        return "None"

A = [1,2,4,6,7,8]
MissingSingleNum(A)

Some weaknesses of the above algorithm it cannot find more than one missing number and numbers in the array should not be negative.  So in case of [-3,-2,-1,0,1,2,3,5], the computed sum is 8*(8+1)/2 = 36 (remember n is = number of elements in the array), where it is supposed to be 5, and the missing number is 4.  The same issue for A=[0,1,2,4,6,7,8] where [3,5] are missing, the algorithm cannot find that either.

To solve multiple missing numbers, we cannot use the above approach.  Basically we need to classify the cases.  Say we have A = [-2, 0, 2, 6, 12]
By looking at it, we know for the range [-2..12], the missing numbers are [-1,1,3,4,5,7,8,9,10,11].

One approach is, we scan from the lowest number to highest number and see if there are missing sequential numbers. Because we already know the array is sorted, we scan a range from A[0] to A[len-1] and check against a dictionary we build (see below).  The dictionary (or hash table) is very fast, it's only O(1) when the numbers are unique.

def IsExist(d,k):
    try:
        d[k]
        return True
    except:
        return False


def MissingNumbers(A):
    n = len(A)
    miss = []
    d = {}
    j = 0
    for e in A:
        d[e] = j
        j = j+1

    for i in range(A[0], A[n-1]):
        try:
            if not IsExist(d,i):
                miss.append(i)
        except:
                pass
                
    return miss


Test:

A[] = [0, 2, 4, 5, 6]
Missing numbers: [1, 3]

A[] = [-2, 0, 5, 16]
Missing numbers: [-1, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]

A slightly better way or an improvement of above code is the factthat we can use <array>.index(value) to check if a value exists in the array (so no need to use dictionary).

def MissingNumbers(A):
    n = len(A)
    miss = []

    for i in range(A[0], A[n-1]):
        try:
            if A.index(i):
                pass
        except:
                miss.append(i)
                
    return miss


Wednesday, September 13, 2017

Finding two items in array that has sum of s

Suppose we have an array A with n items (A[0]...A[n-1]) and we want to find two members in the array (must be unique items, e.g, cannot be A[i] + A[i]) that has the sum of s.

With Brute-Force approach, we actually scan the array from i=0 to n-1 and check if the sum with its next item equals s.

For example, a Python code like below:

#!/usr/bin/python3

A = [-1, 3, 5, 6, -2, 10, 7, 9, 8, 4]

s = int(input("Enter the wanted sum: "))

# brute-force

for k in range(len(A)-1):
    for l in range(k+1,len(A)):
        if A[k] + A[l] == s:
            print("Found it: A[%d]+A[%d] = %d" % (k,l,s))
            print("\t%d+%d = %d" % (A[k],A[l],s))
            break


The problem with this approach is obviously not optimal (the complexity is O(n2)).

A better method is if we don't need to walk through the array second time (the second loop).  We still need to walk through each item in the list, though.

def ArrayToDict(A):
    h = {}
    for i in enumerate(A):
        key = i[1]
        val = i[0]
        h.setdefault(key, []).append(val)
    return h

def IsValExist(H,k):
H = ArrayToDict(A)
for enum in enumerate(A):

    ret = True
    try:
        H[k]
    except:
        ret = False
    return ret


    i = enum[0]
    v = m - A[i]
    if IsValExist(H,v):
        for iv in H[v]:
            if iv != i:
                print("GOT IT: A[%d](%d) + A[%d] (%d) = %d" % (i, A[i], iv, A[iv], m))


With this approach, the complexity is only O(n) (assuming only very few key duplicates, if any, in the hash/dictionary).

Sunday, September 10, 2017

Horse Racing Puzzle

Question: There are 25 horses available.  We want to select the fastest three horses out of this 25 but are allowed to do horse-racing with 5 horses at a time only.  How many minimum numbers of horse races we need to do to get the best 3?

Answer:

If you answer 11, it might not be wrong, but that's not optimal.  The key here is "can we quantify the speed of each horse doing the race?".  Say we do 5-horses racing, can we note the individual speed of each horse?  If we can, then the life is beautiful.

Here is the steps (if can measure the speed of each horse):

  1. Do 5-group races (5-horse for each group).  We record the speed of top-3 (say a,b,c,d,e correspond to race group, and 1, 2, and 3 correspond to the speed ranking, then we get S[a][1], S[a][2], S[a][3], S[b][1], S[b][2], S[b][3], ..., S[e][1], S[e][2], S[e][3]).  (Note: S[group][rank] := speed value for group and rank).
  2. Sort the table (We can flatten the 2-dimension to 1-dimension). For example S[a][1] = T[1], S[a][2]=T[2], ...., so on. 
  3. Pick the top-3.  We get the top-3 fastest horses.
  4. Total race = 5
What about if don't have the ability to measure the speed (we know the top-3 just from seeing the race)?
  1. Do 5-group races (say Group A, ..., E), each with 5 horses (total = 5 races)
  2. For each group, we position the top-3 winners. For example, for group A: A1>A2>A3, B1>B2>B3 so on. (A1, A2, ..., A5 are arbitraries)
  3. As we are interested in the top-3 only, we should remove the Rank 4 - rank 5 horses (e.g, eliminate all horses in rank 4 and 5.  Thus A4,A5, B4, B5, C4,C5,D4,D5,E4,E5 are eliminated)
  4. We don't know whether A3>B3, or A3>C3, or B3>E3.  The same thing for A2 vs B2 so on.
  5. We do another race (6th race): A1, B1, C1, D1, E1.  If we get the winner: E1>C1>D1>B1>A1 (E1 the 1st winner, C1 the 2nd, D1 the 3rd) so we can eliminate B1 and A1 (the 4th and 5th).
  6. Because we eliminate A1 (the 1st winner in Group A), A2 and A3 (2nd and 3rd winners of group A) are automatically eliminated as well.
  7. Because we eliminate the top winner in group B (B1), there is no chance B2 and B3 gonna be the winners so they would be eliminated as well.
  8. 7th race: we do race C1 against D1 against E1.  Say the outcome is E1>C1>D1.  This way, we are sure E1 would be the fastest of all, C1 would be the runner-up and D1 is in the third place.  What about C2, C3, D2, D3, E2, E3? There is no chance they would win (Hey, if the best in each group [rank 1] couldn't win the top, how the 2nd and 3rd place would win?)
The original table (after 5 races), we rank the winners of each group as follows:
Group A Group B Group C Group D Group E
Rank 1 A1 B1 C1 D1 E1
Rank 2 A2 B2 C2 D2 E2
Rank 3 A3 B3 C3 D3 E3
Rank 4 A4 B4 C4 D4 E5
Rank 5 A5 B5 C5 D5 E5

After eliminating 4th and 5th of each group:
Group A Group B Group C Group D Group E
Rank 1 A1 B1 C1 D1 E1
Rank 2 A2 B2 C2 D2 E2
Rank 3 A3 B3 C3 D3 E3
Rank 4




Rank 5





After 6th race (A1,B1,C1,D1,E1), the winners are E1>C1>D1>B1>A1.  We eliminate A1, B1 (4th and 5th winners of 6th race)
Group A Group B Group C Group D Group E
Rank 1
C1 D1 E1
Rank 2 A2 B2 C2 D2 E2
Rank 3 A3 B3 C3 D3 E3
Rank 4




Rank 5





Because the best of group A (A1) and the best of group B (B1) are eliminated (losers), there is no chance for 2nd and 3rd winner of group A (A2, A3)  and 2nd and 3rd winner of B (B2, B3) would win. We eliminate A1, A2, B1, and B2:

Group A Group B Group C Group D Group E
Rank 1
C1 D1 E1
Rank 2
C2 D2 E2
Rank 3

C3 D3 E3
Rank 4




Rank 5





We've got E1>C1>D1.  If the best of group D (D1) only sits in the 3rd place, the worse two in group D (D2 and D3) would definitely never have a chance to win.  We eliminate D2, D3.

C1 could only sit in the 2nd place, C2 is still possible to sit in the 3rd place, but the 3rd place in group C (C3) would not get a chance to sit in the last 3rd place either. Eliminate C3.
Group A Group B Group C Group D Group E
Rank 1
C1 D1 E1
Rank 2
C2
E2
Rank 3


E3
Rank 4




Rank 5





We already know, E1 is the best of all, so there is no point to do a race with the horse again.  Now we have E2, E3, C1, C2, D1 to race against each other.

7th race: Say we get E2>C1>C2.  We could eliminate E3, D1 then.  Now we have E1>E2>C1>C2.  We eliminate the 4th (C2).

We get the top-3 (E1>E2>C1) !!!

Group A Group B Group C Group D Group E
Rank 1
C1
E1
Rank 2


E2
Rank 3



Rank 4




Rank 5






NOTE:
After second thought, the 4th and 5th elimination in the beginning actually assumes these bottom two are the lowest of all.  This may be dangerous, as we split the race into groups, each group is independent each other.

For example, after 5 races performed, the speed of horses (in Miles/hour and ordered according to the rank) in
group A:  [20.0, 19.0, 18.0, 17.0, 16.0]
group B:  [25.0, 24.8, 24.6, 24.4, 24.2]

It is obvious, B4 and B5 (the bottom two) are still faster than even the fastest in group A.  If we eliminate indiscriminately against all 4th and 5th winners, B4 and B5 are got rid of, while the actual losers (all horses in group A) are picked up (at least A1, A2, A3 are selected, where they shouldn't be).  Now you see the problem?

It is similar to say, the top-3 smartest men among 5-idiots better than 4th and 5th place of smartest men among 5-smart men.

I dunno why the explanations for the issue on the Internet fall into the same fallacy (by the way, this question is one of Google's interview question, I believe).

Monday, August 28, 2017

Essential Open Source Projects for Software Development

I've crossed some these jargon recently when looking at job openings.  Interesting.  Apparently, everything we bought in the past to help full cycle software development now are available in open source (I mean, there are opensource alternatives for almost all the proprietary tools).

Here I list some of them:


  • To do code review (ala Code Collaborator): Gerrit
  • Version control: git, svn, cvs
  • Generic Scheduler for automation: Jenkins
  • Software test automation: Selenium WebDriver
  • Compiler set: GCC
  • Rich C++ Libraries: Boost
  • Dataplane SDK/library (Collection of libs & driver for fast data processing): DPDK
  • Data Packet Sniffer & Analyzer: Wireshark
  • Network Traffic Generator: Ostinato
  • Embedded Operating System: OpenWRT, etc.
  • Embedded Real-Time Operating System: FreeRTOS
  • Full package of tools and libs to build whole Linux and its packages (apps): Yocto
  • Opensource Electronic based on AVR chips: Arduino
  • IoT Development board (running OpenWRT): Onion
  • IoT Connectivity protocol framework: MQTT (pronounced as 'Mosquitto')
  • IoT devices: ESP32, Arduino
  • JTAG, ICE, OCD: OpenOCD

Saturday, August 26, 2017

The Repo of Apple Code

Interesting to find the official Apple code released as opensources:

https://opensource.apple.com/

The most interesting one to me is the almost complete code of MacOS (probably the only thing not there is the GUI subsystems):

https://opensource.apple.com/release/macos-10124.html

Thursday, August 24, 2017

Linux ARM on QEMU

Many times we want to develop a generic device driver for ARM processor, but we want first to test it on an emulator.  In this case, the emulator we're gonna use is ARM.

Here's the content of my script to build Linux for generic ARM (Versatile/PB (ARM926EJ-S). According to https://www.kernel.org/doc/Documentation/arm/Booting, Typically the physical RAM does not start at address zero. LOADADDR specifies the address where the kernel image will be located by U-Boot and is stored in the U-Boot header by the mkimage utility. Typically the load address (for placement in memory) is also the start address (for execution). Note that the uImage file is typically just the (self-extracting, compressed) zImage file with the U-Boot wrapper.

ARCH=arm

core=$(nproc)
make ARCH=$ARCH versatile_defcocnfig
make -j${core} ARCH=$ARCH CROSS_COMPILE=arm-linux-gnueabihf- uImage LOADADDR=0x80008000


There is a much easier way: using Yocto.

The following steps would be everything needed to run Linux arm on Qemu:


1.  git clone git://git.yoctoproject.org/poky

2.  cd poky

3.  git checkout -b pyro origin/pyro

4.  Edit ./build/conf/local.conf and add:

    INHERIT += "rm_work"

    Uncomment (and comment out the x32 one):
     MACHINE ?= "qemuarm"

    exit from editor

5.  From shell, type:
 
    . ./oe-init-build-env

    bitbake core-image-sato

or, if we want to build everything:

  bitbake world 

6.  Once a particular image (in this case is qemuarm) has been built, we can
    test it with command:

        runqemu qemuarm


The target files are located in ${yocto_dir}/build.  In my case it is in ${HOME}/poky.  For example, in my case:

./qemuarm-poky-linux-gnueabi/linux-yocto/4.10.17+gitAUTOINC+e92bd55409_3926e38630-r0/deploy-linux-yocto/zImage


Sunday, August 20, 2017

Are US tech companies so picky nowadays?

For last few months I have been looking for technical job in my area of expertise and experience.  I know, it is harder to find jobs in low-level or embedded system compared to application software, but I never realize even when I got some interviews, there are so picky.  A small mistake (not necessarily to judge that I will not be able to do the job), that's it.  You're done.  Next applicant, please.

I am still endeavoring to land my future job. Meanwhile, I am brushing my skills and learn some new stuff.  For example, I now know there is an opensource doing dataplane packet processing.  I also now relearning  Linux kernel and driver, Bluetooth and Wifi.

Thursday, August 17, 2017

Some videos of My Embedded projects I have done

Couple of projects done during my spare time:

ATMega32 interfacing to MAX7219 7seg via SPI 

PIC18F9520 doing ultrasonic ranging 

I wish I made video for the PIC-based IoT sensors I made (part of Computer Engineering project in my master program; It was able to push temperatur and humidty data to 'remote' server periodically, which then display it on the web)

Thursday, August 10, 2017

FizzBuzz Analysis

Recently I was asked this kind of question. As I was told not to worry too much about it and I got many other questions to answer within short time, I did not pay attention to much on it and just scribbled it on a piece of paper, no time to mentally test the algorithm.

At home I realized I did not complete the answer (blame that to the recruiter).  Anyway, the fizz buzz interview question is to ask interviewee to write a code to print number from 1 to 100, but for every multiplication of 3 to print string "FIZZ" (or something), for every multiplication of 5 to print string "BUZZ", and for every multiplication of 3 and 5 (which is another way to say multiplication of 15) to print "FIZZBUZZ".

Here's my working code in C++:

#include <iostream>

using namespace std;

const static char *s1 = "FIZZ";
const static char *s2 = "BUZZ";

int main()
{
    for(int i=1; i<=100; i++)
    {
        if (i% 3*5==0)
        {
            cout << s1 << s2 << endl;
            continue;
        }
        if (i % 3 == 0)
        {
            cout << s1 << endl;
            continue;
        }
        if (i % 5 == 0)
        {
            cout << s2 << endl;
            continue;
        }

        cout << i << endl;
    }
}

No matter whay you do, the optimal solution is always O(n) (unless you're crazy enough to just print manually without loop, such as cout << "1\n2\FIZZ\n4\BUZZ\n...|").

Some silly optimization can be performed in printing the string, though.

#include <iostream>

using namespace std;

const static string s = "FizzBuzz";

int main()
{
    for(int i=1; i<=100; i++)
    {
        if (i%15==0)
        {
            cout << s << endl;
            continue;
        }
        if (i % 3 == 0)
        {
            cout << s.substr(0,3) << endl;
            continue;
        }
        if (i % 5 == 0)
        {
            cout << s.substr(4) << endl;
            continue;
        }

        cout << i << endl;
    }
}

Another thought is to eliminate mod 15.  For example:

#include <iostream>

using namespace std;

const static string s = "FizzBuzz";

int main()
{
    int m;
    for(int i=1; i<=100; i++)
    {
        m = 0;
        if (i%3==0)
        {
            cout << s.substr(0,3);
            m++;
        }
        if (i % 5 == 0)
        {
            cout << s.substr(4);
            m++;
        }
        if (m > 0)
            cout << endl;
        else
            cout << i << endl;
    }
}

When I tested it (upper limit set to 1000 and perform "time <program>", the last algorithm shows some speedup.

The last few lines can be optimized to be:

        if (m == 0)
            cout << i;
        cout << endl;

Another tiny optimization is instead of doing post increment (i++), we do preincrement (++i).  This saves tiny bit (no copy to extra variable internally).

Last, we can write it down in Assembly if we want.  The following is suitable for embedded device:

main:
leal 4(%esp), %ecx
andl    $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %edi
pushl %esi
pushl %ebx
pushl %ecx

subl $8, %esp
movl $1, %ebx         ; EBX=1
movl $3, %esi         ; ESI=3
movl $5, %edi         ; EDI=5
jmp CHECK_FIZZ

FOR_I_NEXT:
movl %ebx, %eax       ; eax stores current value of i
cltd
idivl %edi             ; i / 5, result in eax and mod in edx
testl %edx, %edx       ; i % 5
jne PRINT_I          ; i % 5 != 0 -> jump to PRINT_I

CHECK_FUZZ:
subl $8, %esp
pushl stdout
pushl 'f'
call _IO_putc
popl %edx
popl %ecx
pushl stdout
pushl 'i'
call _IO_putc
popl %eax
popl %edx
pushl stdout
pushl 'z'
call _IO_putc
popl %ecx
popl %eax
pushl stdout
pushl 'z'
call _IO_putc
addl $16, %esp

PRINT_CR:
subl   $8, %esp
pushl stdout
pushl '\n'
call _IO_putc
incl %ebx
addl $16, %esp
cmpl $101, %ebx           ; i == 101 ?
je MAIN_EXIT            ; if (i ==101) goto MAIN_EXIT

CHECK_FIZZ:
movl %ebx, %eax
cltd
idivl %esi
testl %edx, %edx           ; i % 3 == 0
jne FOR_I_NEXT
subl $8, %esp             ; if (i%3==0):
pushl stdout
pushl 'b'
call _IO_putc
popl %eax
popl %edx
pushl stdout
pushl 'u'
call _IO_putc
popl %ecx
popl %eax
pushl stdout
pushl 'z'
call _IO_putc
popl %eax
popl %edx
pushl stdout
pushl 'z'
call _IO_putc
movl %ebx, %eax
cltd
idivl %edi
addl $16, %esp
testl %edx, %edx
jne PRINT_CR
jmp CHECK_FUZZ

PRINT_I:
pushl %eax
pushl %ebx
pushl $.LC0
pushl $1
call __printf_chk
addl $16, %esp
jmp PRINT_CR
MAIN_EXIT:
xorl   %eax, %eax
leal -16(%ebp), %esp
popl %ecx
popl %ebx
popl %esi
popl %edi
popl %ebp
leal -4(%ecx), %esp
ret