From the outset, electronics was always about hooking up more and more devices in order to acomplish a specification requirement. With the advent of transistors in 1948, it was possible to make very small switches. This lead to smaller designs. Some clever dudes found that you could actually stick a bunch of these transistors on a single lump of silicon (glass). This means you can have a whole array of switches. You can arrange them to perform basic boolean logic such. If you stick enough of these together you can build a nand gate (which is an inverted and gate). And with a collection of nands you can produce all sorts of clever combinatorial logic. Such as if door open and car going, then sound alarm. If you arrange your gates cleverly you can make a state machine. This allows the machine to effectively run a simple program by going through a bunch of states - rather like lines of a program. If you arrange this state machine, you can make a microcontroller to perform quite involved calculations and control complex systems, such as a car engine. If you then make that state machine reprogrammable, you end up with a microprocessor.

Firmware to control the interior light fader in my car!
We used to be happy with a bunch of logic chips to perform a function. But these days you can throw most of them away and use a processor. Then you just write the code you want in that and it will act like the gates you needed. This is called the firmware. Above is a sample of what this looks like. This is assembler. Assembler is where you instruct the chip explicitly where to put everything. Such as movelw d'30' means literally move decimal value of 30 into the working register - which means exactly what it says! Assembler is great because you can see what happens on every tick, and you can guarantee a design will do that on a certain tick. But it can get complicated and you can go nuts trying to find a firmware bug. Complicated if-then-else statements can get tricky.

Firmware written in C - for the USB board. Bit more readable and more manageable.
Fortunately with modern processors, you can get compilers to worry about the assembler and you can instruct it in a higher language like C. Its a little bit easier to program the thing in C, but I would say you don't appreciate C until you knew the assembly listing, because if you approach it this way, then you can see a correlation line for line. You can have black box bits of code which you can then just bung on the latest project. In this case I spent 2 weeks head scratching getting the USB bit to work on my board. Now it works, in future all I do is #include <usb.h> and it will all work out dandy.
When constructing designs on a new platform (processor) I often find that it takes longer to figure out the bugs in the compiler, and argue with command lines with 30 swiches in order to get it to compile correctly, then it can be a pain to get the tools to talk to your target chip. You need often need a complex array of tools and utilities to get the task done. But its usually the same process and only experience can help you!