What makes a computer the wonderful and flexible tool that it is, is the ability to give it a varying sequence of instructions which tell it what to do. There is complete and total freedom in designing a program. There are many, many ways to do the same thing. Some are faster and some are smaller. Some programs are more mathematically elegant. But what is common in all programs is the set of instructions than can be used for a particular machine.
Each type of computer has it's own machine language. Machine language instructions are just a group of bits which have a particular meaning to that machine - the same pattern of bits would most likely have a different meaning to another computer.
For example the bit pattern:
0001110101010101
might tell one computer to add the number in one memory location to another. For another computer it might be meaningless.
This is called machine language programming, but it is difficult to see the forest because the trees are in the way. It is difficult to keep track of where data is and to remember what one part of a program did, and in fact to distinguish between instructions and data at all.
After some time, Assembly Language was invented. This allowed the above instruction to be expressed as:
ADD A,data2
There would be a program called an ASSEMBLER which would translate your assembly language program into the machine language for your computer. The assembler would read as input a file containing an assembly language program and create another file with that same program in machine language (plus some additional information for use by the system). In C the keyword asm tells the compiler that what follows is assembly language statements. This is compiler and computer dependent and not portable.
Assembly language was a great improvement over machine language, you no longer had to know the particular bit patterns to ADD or MULT (multiply) and usually no longer had to keep track of where something is, but most of the time you had to explicitly know how big (i.e. how much memory) was occupied by particular items of data.
Assembly language replaced direct machine language, but still it was not easy to organize the program in a way which was easy to understand and modify. But the biggest problems centered about the fact that an assembly language program could only run on the machine type for which it was written. This implied that the programmer had to intimately know the hardware and be concerned with details and limitations of the machine not relevant to the program to be written.
This caused the next evolutionary step to be taken. The next step is called higher level languages. They are called higher level, because usually one statement in the higher level language would be translated (i.e. compiled) into more than one machine language instruction. In assembly language usually one assembly language statement produced one machine language instruction, although this is a slight over-simplification.
The first high level languages, such as COBOL and FORTRAN, allowed the programmer to concentrate on the program and not on the details of the computer it was to run on. It also produced a program which, with none or minor changes, would run on other machines which had a compiler for that high level language.
High level languages also moved the focus from cleverly using the machine instructions of a particular computer to instead develop the best method of solving a program - finding an algorithm which gave the fastest or smallest program.
Nevertheless, there are still features of every computer which are different from other computers. Access to these features may be useful to get the fastest program or to use some special feature of the computer. When writing an operating system, which must control all aspects of a computer, there is no choice but to use assembly language for the very low level control and management of the machine. But these assembly language sections can be made accessible to the portions of the operating system written in higher level language.