|I'll try to answer your original question, it can be tough getting to grips with all the concepts involved in this. I can give you a real example of "binary programming" from something I have worked on. Intel CPUs have some special instructions called streaming multimedia instructions, which are high performance instructions for processing data in memory. But the time I did the work these instructions were not supported by the C compiler I was using, which was gcc. That being the case, the only way to get the cpu to run those instructions was to code them as direct binary data in the source of my program. Let's have a bit of background to explain what I mean. When you write some source code in any native compiled language, that is, code that executes directly on the machines CPU, such as assembly language or C, the compiler will translate the source code of your program into a binary object code that the CPU is able to process. For example, let's say you have written in C "int b = 12"; the equivalent in assembly might be something like "mov eax, 12". In fact, when your C code is compiled, it is usually translated into assembly first, before being translated to binary. When the assembly is itself translated, the output is no longer readable as text, but is a sequence of binary byte values. These values encode the assembly instruction in a sequence that the CPU instruction decoder is able to understand to actually execute the instructions. Let's have an example. Here is a tiny program written in C source code:-
Code:
#include <stdio.h>
int main(int argc, char *argv[]) {
int b = 5;
b = b * 2;
printf("value of b is %d\n", b);
}
You can compile this and run it as follows:-
Code:
$ cc -o t t.c
./t
value of b is 10
So far so good, we wrote a little program and ran it. We can also ask the compiler to output the assembly language it generated during compilation, which is a step that you don't normally bother to look at. To do this compile the source program again but this time using the -S option, as follows:-
That step creates a file called t.s, which contains the assembly language source code generated from compiling your C source code. This is still not in a form that the cpu can run, but it is getting closer. It is still human-readable and is still a type of source code, called assembly language. Let's have a look at t.s:-
Code:
$ cat t.s
.text
.file "t.c"
.globl main # -- Begin function main
.p2align 4, 0x90
.type main,@function
main: # @main
.cfi_startproc
# %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
subq $32, %rsp
movabsq $.L.str, %rax
movl %edi, -4(%rbp)
movq %rsi, -16(%rbp)
movl $5, -20(%rbp)
movl -20(%rbp), %edi
shll $1, %edi
movl %edi, -20(%rbp)
movl -20(%rbp), %esi
movq %rax, %rdi
movb $0, %al
callq printf
xorl %esi, %esi
movl %eax, -24(%rbp) # 4-byte Spill
movl %esi, %eax
addq $32, %rsp
popq %rbp
retq
.Lfunc_end0:
.size main, .Lfunc_end0-main
.cfi_endproc
# -- End function
.type .L.str,@object # @.str
.section .rodata.str1.1,"aMS",@progbits,1
.L.str:
.asciz "value of b is [%d]\n"
.size .L.str, 20
.ident "FreeBSD clang version 6.0.1 (tags/RELEASE_601/final 335540) (based on LLVM 6.0.1)"
.section ".note.GNU-stack","",@progbits
Crikey! Now you can see why we prefer to write the original program in C or another high-level language. Just imagine if you had to type all that in just to write a simple program to double a constant. I won't go into how this works in detail but you can assume that somewhere in there are instructions to make a variable, assign the value 5 to it, then double it, then call the printf() function to write the value to standard output. In fact we can see the line that is equivalent to the assignment in "int b=5"; it is the assembly line "movl $5, -20(%rbp)"; and if you dig your way through it you might get an idea of which parts of the assembley code roughly correspond to the rest of the C source code.
But we still haven't got to binary code yet. To do that we must invoke the assember stage of the compiler, which is done by using the -c option to the cc command. This stage of compilation will translate the C to assembly, and then translate the assembly language and convert it to something called object code, which is the binary equivalent.
After running this it will be found that a file called t.o has been created, which is known as object code. And finally this file does contain the binary version of the instructions that were in the assembly file, which in turn were generated from the original C source code. And we can look at the contents of the t.o file using a binary file viewer such as the hexdump program, as follows:-
Code:
$ hexdump t.o
0000000 457f 464c 0102 0901 0000 0000 0000 0000
0000010 0001 003e 0001 0000 0000 0000 0000 0000
0000020 0000 0000 0000 0000 0268 0000 0000 0000
0000030 0000 0000 0040 0000 0000 0040 000a 0001
0000040 4855 e589 8348 20ec b848 0000 0000 0000
0000050 0000 7d89 48fc 7589 c7f0 ec45 0005 0000
0000060 7d8b c1ec 01e7 7d89 8bec ec75 8948 b0c7
0000070 e800 0000 0000 f631 4589 89e8 48f0 c483
0000080 5d20 76c3 6c61 6575 6f20 2066 2062 7369
0000090 5b20 6425 0a5d 0000 7246 6565 5342 2044
00000a0 6c63 6e61 2067 6576 7372 6f69 206e 2e36
00000b0 2e30 2031 7428 6761 2f73 4552 454c 5341
00000c0 5f45 3036 2f31 6966 616e 206c 3333 3535
00000d0 3034 2029 6228 7361 6465 6f20 206e 4c4c
00000e0 4d56 3620 302e 312e 0029 0000 0000 0000
00000f0 0014 0000 0000 0000 7a01 0052 7801 0110
.... (truncated for brevity)
Now finally we can see the binary data that was created by the assembly pass. This is no longer in a form that you can read or understand, but it does make perfect sense to the CPU when it comes to execute it. Somewhere in this binary data is a series of bytes that represents our original assembly code that was in turn generated from C code; and this series of binary data will be executed by the CPU. Believe it or not there is one further stage that must be performed before this file can actually be loaded and run by the operating system, which is called linkage. This step combines our small object file with system library files so that the printf() function and certain other essential functions can be accessed. I have only skimmed over the surface of the whole process here, but hopefully you get the rough idea.
So what exactly is the binary data that is in the object file? It consists of a series of instructions called opcodes, and their parameters. In the example given the assembly word "mov" is the assembly code representation of one type of opcode, and that will always generate a specific binary value to represent the "mov" (move) instruction to the CPU. The entire set of opcodes understood by a cpu is called the instruction set. If you rummage around the intel website at some time you will find that you can download books that describe the entire instruction set that a particular processor supports; for example, you will find one book that describes the "Intel 80386 instruction set", one for the pentium, etc.
Now, if you had a binary editor, you could actually write the object file directly by writing the values from the hexdump into a file, which would be another way to write the instructions for the cpu; actual binary programming; of course this would be far too laborious to ever do in practice, and you would have to look up every opcode in the instruction set book, work out what its parameters are, and write the correct sequence into the file. It would be almost impossible for a human to get it right and would be extremely time-consuming.
So when might we ever encounter a need to program in binary? As processors become more complex over the years, additional instructions are added to the instruction set, while the earlier instructions are retained for backwards compatibility. And sometimes instructions are added to a processor but are not added to the compiler or assembler by the programmers who develop the tools; the reason might be a lack of time, or the instructions are very rarely used, or any number of other factors. In those circumstances if you want to use the instructions that the compiler does not support, your only option is to write them directly as binary data into your source code, and both the assembler and C compiler provide a special syntax to allow you to do that. Going back to my original example of the steaming multimedia instructions, the version of gcc I was using at the time did not support them, so there was no way to access those instructions from C or assembly source code. However it is possible to embed snippets of assembler within C code, using something called "inline assembly language", and within one of those snippets it is possible to hard-code binary values that are to be executed directly by the CPU. In this was it was possible to write some code that used the streaming multimedia instructions, despite them not being available on the CPU. There is one other time that you might need to do "binary programming", which is when you have an executable program that you don't have the source code for; imagine you have shipped this program to lots of customers who have started reporting a bug to you. But the guy who wrote the program has left the company and you have lost the source code. Believe it or not this does happen in the real world. So you get your team to debug the program using something called a disassembler, which converts the binary back to assembly language, and you find the bug, and you work out how to fix it; but how do you give this fix to all your customers? You can use something called a binary patch, which which will edit the binary code of the executable file in situ on the customers system. If you ever have to do this you are getting in deep ;-) But that's another example of real-world "binary programming". Don't worry, your university will never mention that!
So back to your original question about whether a university course would cover this. I would expect a good university course to explain the concepts of computer architecture, cpu opcodes and instruction sets, how the CPU works in terms of binary instruction decode, and assembly language programming; with practical programming sessions most likely using a simple example cpu like a Z80 or 6800 to develop your knowledge. As with everything, working through real problems is far more valuable than just learning principles from a book. If you are lucky they will have actual hardware kits, such a Z80 development system, that will allow you to practice this and do exercises. Perhaps this is more likely to be encountered in an electronics or embedded systems course rather than in computer science, but even a computer science degree should cover some of this stuff in the first year, IMHO. Of course in the real world you will almost never do "binary programming" except under the rare kinds of circumstances I have described. But it's worth having a strong grip on how computers really work, when you come to work on higher level projects. Anyway hopefully that's give you a few ideas, feel free to ask questions if you like.