The Art of Assembly Language Programming (1996)
phatcode.net
phatcode.net
So, if you buy the book (like I did), please be aware of this.
GNU GAS syntax is roughly as repugnant, but became popular only because of GNU/Linux. Deviating from the official docs has caused much divisiveness and confusion.
No idea how it was called.
Fully agree with GAS syntax being a pain.
Basically imagine a C subset where variables are the registers, mov is = , expressions are Dest = Reg1 op Reg2 and so forth.
I can gladly be corrected if it was another vendor.
AT&T syntax predates GNU and x86, and spread just fine without Linux.
> For example, one might test the carry flag after an addition to determine if an unsigned overflow has occurred using code like the following:
add eax, 5
jnc NoOverflow
<< code to execute if overflow occurs >>
NoOverflow:
> Although this code is straightforward, you would be surprised how many students cannot visualize this code. On the other hand, if you feed them some pseudo code like: add eax, 5
if( the carry flag is set ) then
<< code to execute if overflow occurs >>
endif
> Those same students won't have any problems understanding this code.I too find reading assembly a hindrance to understanding the intent and behavior of the code, and I mentally map assembly to high-level behaviors similar to structured programming. If HLA is more accessible and productive for regular programmers as a tool to read/write reasonably-efficient code on architectures without good C compilers, I don't consider that repugnant but empowering. (Admittedly it's more disconnected from the output binary instructions than regular assembly.)
(Sidenote: are assemblers designed around simplicity of parsing and speed at processing compiler-produced assembly, or user experience for humans writing assembly, or is the latter more common on architectures without good C compilers?)
That really depends on the assembler. Each one is different. Some support a degree of structured programming, while others do not even allow for relocatable addresses.
I'm having trouble understanding the difficulty in the example. If "jnc" doesn't suit then there's "jc" to do the opposite.
If the difficulty is the conceptual leap to unstructured branching then I'd say that's what the teacher's job is - teach the students how to think about it.
"jump_if_condition_is_met" or "jump_short_if_not_carry" (or which ever one it maps to) is much better and clearer than "jnc"
add eax, 5
jnc {
nop
}
And it would automatically create the labels for you, syntactically, it was equivalent to what you've written above, but with labels like ".auto_brace_X".You could also do the typical "if-else" branching with slightly extended syntax like:
add eax, 5
jnc {
<carry is set>
}:jmp {
<carry is not set>
}
<continue>
Which allowed easy extension to "do-while" loops: {
<some loop>
dec ecx
}:jnz
The trickiest part is remembering that all branches are "unless" branches instead of "if" branches, and it obviously only works for simple constructs and can make refactoring and optimization a pain; however, there was plenty of code that benefited from using this. ; find next task in circular list Next(6 cycles) Top(14 cycles)
{
add.w #10, r10 ;2 increment pointer to next task area
cmp.w #mms_task.end, r10 ;2 are we at the end of the task list?
jnz { ;2 no? start next task
.init: eint ;1 ensure interrupts are enabled
bis.w #MMS_EACH, &mms_task.state ;5 ensure EACH state flag is set
mov.w #mms_task.list, r10 ;2 restart at top of list
}
; build state and check against task Skip(12 cycles) Take(31 cycles)
mov.b &IFG2, r8 ;3 r8 = ifg2 interrupt flags
and.w #(MMS_TX+MMS_RX), r8 ;1 get only TX and RX ready flags
mov.w &mms_task.state, r9 ;3 r9 = current task state
bis.w r8, r9 ;1 add TX and TX flags to global state
; determine if this task needs to run
bit.w @r10, r9 ;2 task request flags & system flags
}:jz ;2 no matches? find next task
; load the task state and return into it
mov.w r10, &mms_task.ptr ;4 set current task area to r10
mov.w @r10+, r12 ;2 r12 = task[0] (request flags)
mov.w @r10+, r8 ;2 r8 = task[1] (PC)
mov.w @r10+, r4 ;2 r4 = task[2]
mov.w @r10+, r5 ;2 r5 = task[3]
mov.b @r10+, r6 ;2 r6 = task[4] (low byte)
mov.b @r10, r7 ;2 r7 = task[4] (high byte)
mov.w #0, r10 ;1 clear r10 pointer for safety
mov.w r8, PC ;2 "return" into the yield1. the real meat of ASM doesn't really depend on the Assembler, so one can (and ends up) coding the exercise with the Assembler of their choice
2. people have freely contributed the translation of the listings/programs to all the major Assemblers, so even if one doesn't want to code their own version, they can just use the contributed versions
Certainly, there are some chapters expressly dedicated to MASM functionalities, so those are wasted.
As a matter of fact, I worked through it on Linux/NASM, and wasn't really bothered, as the book is really good quality.
Not for the legions of developers doing Windows and XBox games.
I guess most of it is still relevant, but I'd rather not deal with DOS to follow along, and would prefer working on Linux.
Also, the x86 instruction set seems daunting to pick up for a beginner. Would it be better learning on a 4/8-bit or toy machine first?
The full x86 ISA is daunting, I'm sure, but for a beginner looking to get a sense of what asm is, you don't engage with the entirety of the ISA. The PFTGU text is great specifically because it assumes an audience of beginners who want to learn the basics of how asm operates to enable core programming concepts. It's not aiming to be exhaustive like the linked text.
Edit: And also importantly, it's also (legitimately) available for free online.
The x86 instruction set isn't daunting, the 16-bit x86 memory model is, speaking as someone who learned x86 assembly at a young age. It is easier to work on Linux, because segmentation is a non-issue and you have a flat address space.
The x86 instruction set is daunting and it can appear quite arbitrary unless one knows the near 50 years of historical baggage it carries along with it. But I think if you have some experience with an 8-bit CPU, plus just kind of accept the weirdness of it, it's not that bad (especially the 64-bit stuff, which in a way, is simpler, even if half the registers are named, and half are numbered).
https://www.amazon.com/Inner-Loops-Sourcebook-Software-Devel...
now you're essentially programming in basic
Snark aside, yes, people program entire operating systems and programs and games in assembly. Famously, Rollercoaster Tycoon was written entirely in assembly.
Microsoft Macro Assembler for example offers:
• Records/structs, bitfields
• Typed labels/pointers. Intel in particular introduced this feature.
• Procedure blocks which allow locally scoped labels within them
• The automatic allocation of local variables onto the stack.
• if/then blocks, for loop blocks
• Memory model directives
• Macros, equates, etc.
Other assemblers like NASM omit some of these features, as introducing versions of these features which are completely compatible with the MASM ones would be difficult. For example, NASM allows MASM-like procedure blocks, but they are not block-scoped. They're just a notation for the programmer.
You are probably thinking of the assembling process of pieces of machine code - through the assembler. But the assembler also translates symbolics into machine code, in the goal to join the different pieces of code and data - so it's more than just concatenating. To define the memory addresses to be encoded for jumps, for example, you must have defined them - so, in the assembler (e.g. concatenating subroutines) you imply the assembly (i.e. naming pieces of machine code, symbolically treated).
Not sure about the merits of calling the ideal processors by special names.