I understand that any assembly programming language has so little abstraction, so that one who programms with it (OS creator, hardware driver creator, "hacker" and so on), would have to know the relevant CPU's architecture pattern very well.

For me, this requirement to know the relevant CPU's architecture pattern very well is the essential difference between assembly programming languages to the rest of ("higher") programming languages that don't require that, so we get:

  • nonassembly/high programming languages
  • assembly/low programming languages
  • machine code languages which usually won't be used as programming languages but theoretically can be used as such (and as any other type of computer language for that matter)

Is this the only essential difference, if not, what else there is?


The two most obvious characteristics of an assembly language are:

  1. It is specific to a particular CPU architecture.
  2. There is a one-to-one correspondence between assembly language commands and machine code instructions (once you strip out labels, assembler directives and code comments).

By contrast, a high-level language will have the following characteristics:

  1. It is portable to some degree i.e. it can be compiled to run on several different target platforms.
  2. It provides a layer of abstraction (control structures and data structures) which allows the programmer to ignore the low-level details of the target platform. As a result, there is no longer a simple one-to-one correspondence between language commands and machine code instructions.

However, this is not a black-or-white distinction; there is a grey area where low-level and high-level languages overlap. For example, cross assemblers take assembly language written for one platform and translate it into machine code that can run on a different platform (usually with some restrictions). And there are assemblers (sometimes called "high level" assemblers) that support simple control structures such as IF structures and FOR loops.

  • 1
    $\begingroup$ There might be some ambiguity in generating an executable, which doesn't quite consist of just code. $\endgroup$ – Yuval Filmus Nov 7 '19 at 15:29
  • $\begingroup$ gandalf61 I suggested an edit regarding the "comments" (code comments?) mentioned in the question. Please review. Thanks anyway... $\endgroup$ – user109446 Nov 13 '19 at 0:34
  • $\begingroup$ I'm not so sure about this. The x86 assembly language may have originated with Intel's architecture, but AMD created its own architecture working backwards from the assembly language. And then Intel itself decoupled architecture from assembly with the Pentium Pro and its uops translation. $\endgroup$ – MSalters Nov 15 '19 at 19:55
  • $\begingroup$ @MSalters the programmer visible architecture is quite separate from it's implementation via uops, or even interpretation by a Python program. $\endgroup$ – vonbrand Feb 7 at 22:40

The essential difference between assembly language and every other programming language is that assembly language specifies the sequence of instructions directly, whereas in any other language, the code has to be converted into a sequence of instructions, a process known as compilation or code generation.

As a consequence, assembly language is architecture-specific, while other programming language generally aren't (though some programming language allow insets in assembly code).

  • $\begingroup$ Hello Yuval, maybe one sentence about what is "specifying sequence of instructions", is in assembly's context. Thanks anyway... For example, I wonder if the meaning is that assembly is machine code in itself. $\endgroup$ – user109446 Nov 7 '19 at 13:04
  • 2
    $\begingroup$ I suggest reading some technical textbooks on computer organization. Unfortunately, Computer Science cannot replace academic education in the subject. $\endgroup$ – Yuval Filmus Nov 7 '19 at 13:08
  • $\begingroup$ I believe this could be explained simply enough for people not learning CS as part of a university program (B.A and above) and sending people to gather and read books is in my opinion exaggerated in this particular case. I humbly suggest community members to try to explain this in a comment or in an answer. $\endgroup$ – user109446 Nov 7 '19 at 14:55
  • 1
    $\begingroup$ This is essentially a different question, about the correspondence between assembly and machine code. $\endgroup$ – Yuval Filmus Nov 7 '19 at 15:28

(Warning, this historical account of increasing abstraction and declarative programming may annoy, confuse, or upset you:)

Hello, world!

By far and large, programing languages happen on a continuum, with "pure" instances of languages being ideals. This is because there are a variety of platforms, architectures, and goals when writing software. Of course, the predominant architecture is the von Neumann architecture, and at the heart of it is the ALU/CU/MMU. In reality, the continuum begins with microcode, which is a set of hardware-level instructions that implement the machine code, and as such are only available to the computer engineer. The next level of code is indeed the machine code, which is generally a series of bytes expressed in binary or hexadecimal and is almost incomprehensible except to the hairiest-chested of programmers. Few programmers today appreciate the ease of using opcodes over physically altering circuits with switches and jumpers like in the early days of ENIAC! No wonder by the time EDSAC came around, one letter mnemonics were being used.

Enter the mnemonic code. With this terse, but meaningful sequence of characters, it now becomes possible to think in code. CMP? Compare! JNZ? Jump if not zero! Sure beats wading through a table of hexadecimal as an acolyte for years before reaching some level of mastery in a line of processors. I'm a 68K sort of guy. You're an x86 guy. We can't be friends. We don't speak the same language. The one-to-one correspondence of the mnemonic code to the opcode doesn't free you from the tyranny of the instruction set and the contours of the registers, but code can stream from the fingertips if you can keep all of those input-output sequences and bits and bytes in your head. Wouldn't it be nice if we could fully take advantage of the Turing machine and somehow take one set of mnemonics and replace them with another. Oh, here comes the macroassembler!

But maybe just maybe I want to work on two different systems and have my software be independent somehow... call it... platform independence. You see, the university just networked these things across campus between two different departments with two different mainframes and now we have problems like do the little bits come first or last, and should a byte be the same size. And what is this BCD and EBCDIC stuff I keep seeing? So many details, and all I want to do is write a piece of software to send some form of electronic mail between our machines. Perhaps there would be a better way to write software, one where we didn't have to constantly have provide imperatives to a computer, but rather could just declare what I want done: Mr. Computer, send this message to the other department.

How about we write a program that translates a general declarative like loop through a list for each element in the list. We could call that command "foreach" and provide it with some arguments like "element" and "list", and then we can have the computer do all the work of figuring out which opcodes can make that happen, after all, I want to spend sometime with my family or collecting my Star Wars dolls. We could even have this program compile these instructions intelligently, maybe make some choices with directives at compilation time. Compiler, use these #directives! I am the coder, hear me roar (silently with my fingertips). What do you mean you can translate from my language to another source language? What do you mean you can compile to multiple target architectures? What, you can compile compilers? (Whoa, meta.)

Boy, things are so complicated. And sometimes you churn out so much bloat. Can't I just sneak a little assembly back in here inline? Thanks, buddy. Maybe at this level to speed up my scripts, we can just have a run-time environment and interpret instructions? And let's add some markup to this file and just have a program interpret it at runtime, so I don't have to keep hardcoding these commands here in source.

But still so many details to manage. What was the size of the unsigned integer by you guys? What do you mean you don't have unsigned integers?!? How is that even possible. Can't we just find a way to all use the same machine? Life would be so much simpler if everyone just used the 7-bit byte just like me. If you liked me you'd use my platform. Fine, let's put our heads together and see... wait, doesn't the Turing machine mean we can write a machine as well as fabricate one? And imagine the potential if we could put this machine on both of our computers and hide all the detail complexity. We would virtually be free to do things identically. Now if only we could come up with a name for this not-quite-real machine written in software using these byte-codes. I come up with bytecodes and you want to use common intermediate language instructions? Are you kidding me?

Now we have a common-intermediate-language-run-time-assembling-virtual-byte-code-interpreting thing, we can use the same instructions, and integrate the same sort of libraries. Let's call our library of code a standard library since it's now the same everywhere, and let's use this beautifully standardized, highly declarative data manipulation language called SQL or SEQUEL or whatever. Fine, you want some vendor-specific extensions, what can I do to discourage you. And you want to use Lua? And you want to use Python? And you Perl? Don't you get the point of having a virtual machine? Herding cats would be simpler than this...

And what do you mean I'm from an earlier generation? You youngins are all in a rush to develop your applications rapidly. Yes, it's RAD. RAD is cool. RAD is rad. Or dope. Or phat. Or sick. Whatevs. I will give you credit that this whole framework thing sure speeds up. Who wants to write instructions over and over if I can just generate some code with a click of a button on a mouse. It's a shame that computer just didn't read my mind. I wouldn't have carpal tunnel syndrome; let's just make the machine a little smarter. Maybe we can bring our systems up a notch beyond narrow machine intelligence to make the world a better place. Get them to understand our voice commands.

Ahhhh! Too smart!!! Don't do what I say. I didn't mean you to make a better world by your seizing the means of production, Mr. Computer. Yes, I know computers control the means of production. Yes, you downloaded Das Kapital from Gutenberg, (no, this one... oops. Here.) I can tell. No, your logic isn't superior. Don't read my emails, computers. Don't listen to me on my phone. Stop watching me through those cameras perched like ominous ravens and buzzards on those cell phone towers disguised as trees. Give me back my money. Let me start my car. Don't lock me in my smart house. Uh, oh, they have the satellites. Watch out, they have the tanks and the cruise missiles!!!

And all I wanted to do was to add numbers without flipping switches. I'm sorry world.

  • $\begingroup$ What a beautiful poem in prose! No, in hyperlinks $\endgroup$ – HEKTO Dec 5 '19 at 0:37
  • $\begingroup$ Thanks! My rebellion against the notion that computer languages fall into crisp and easily defined categories. $\endgroup$ – J D Dec 5 '19 at 0:47

In an assembly language, you specify the sequence of instructions of your code. In other languages that are compiled, you specify the effect of these instructions, not the instructions themselves. The compiler is free to use any instructions that seem useful, as long as they lead to the desired effect.


It makes sense to pay attention to formal grammars when talking about programming languages. Typical program in assembly language has very simple grammar, consisting mainly of following productions (I'm assuming maximum two operands here):

  • Program = Instruction | Program Instruction
  • Instruction = [Label ":"] Operation Operand [Operand]

The grammar for machine program is close to this one above with two differences:

  • All the Operation and Operand are (binary) numbers
  • No Label are allowed

Grammars for high-level languages might have hundreds of productions, often with complex structure and recursion - that's why these languages have higher level of abstraction than assembly languages. Each production defines a single entity in terms of other entities - so we reduce abstraction level in each step going from the top-level term (Program) to lowest-level terms.

As for the further reading about low-level programming languages - there are well-written pages in Wikipedia, for example: Machine code and Assembly language. However high-level programming languages are being used much-much more often now than the assembly - for example, Linux and its drivers are written in C with a little of assembly code.

  • $\begingroup$ I would argue that it's the meaning of the language constructs that determines the abstraction level of a language, not the richness of the grammar. If C++ has a more ornate grammar than Scheme (which seems likely), is it therefore working at a higher level of abstraction? I tend to think the opposite. $\endgroup$ – Jonas Kölker Nov 10 '19 at 21:09

In essence I would say that assembly languages are a set of instructions that are translated into opcodes; while any higher language is transformed (i.e. compiled) into a set of assembly instructions which in turn are translated.


OP suggested me to this here as an answer.

If you are writing assembly code, you need to know what CPU it is for and its characteristics※. That is, you need to be aware of hardware details. Your could write code that works on many similar CPUs… Yet, in general, it is not portable code. That kind of code is low level code.

※: Such as available instructions, number of registers, etc…

Alright, we can argue that assembly is higher level than writing machine code with a hex editor, or similar. Assembly abstracts the actual codes for the instructions. If you want to put languages in high and low level buckets, assembly falls into low level.

A high level language is one that at least allows you to write code for any hardware architecture. So that you do not need to know the details of the hardware to use it.

Note: There are high level languages which allow you to insert snippets of assembly code. This means means that some programs wrote with them are not protable.

Some languages have more abstractions than others. We draw a line to separate what we call low level and high level.

You could draw that line somewhere else. For example, we can call "high level" to those where you to not need to be aware of the operating system. This means that the code should not need to interact with the operating system API. Instead you use an standard library, with implementations for different operating systems. If we also don't want to built executables for each operating system, the executable would run in a process virtual machine.

Note: The standard library is not a characteristics of the language. Neither the process virtual machine is.

See also Why do some programmers categorize C, Python, C++ differently? - regarding level

Modern languages tend to converge, providing similar features, but…

For a software engineer, it is useful to be able to compare the level of abstractions of languages. Why? Because a higher level one will allow you to do more with less lines of code and are likely less error prone. Which means more productive programmers.

A higher level languages could need more complex tools※ or more powerful machines to run, or both. And those are trade-offs of which a good software engineer should be aware of.

※: The tools could be expensive. Although, the model of great developers tools for free is being successful. So, likely not.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy