The two most obvious characteristics of an assembly language are:
- It is specific to a particular CPU architecture.
- There is a one-to-one correspondence between assembly language commands and machine code instructions (once you strip out comments, labels and, assembler directives and code comments).
By contrast, a high-level language will have the following characteristics:
- It is portable to some degree i.e. it can be compiled to run on several different target platforms.
- 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.