Cool initiative, but please don't add music to the videos. It makes me nervous and unable to focus on you talking. Such a waste of otherwise great content.
Thanks for the feedback. The videos I have on my YouTube channel to-date are all from my Twitch streams. For or better or worse, these streams capture my working habits: when the camera isn't running, I'm doing the exact same thing -- even talking to myself. :) For some reason, suffering from ADHD, music helps to focus my attention. I've never been able to explain why.
However, with all that said, I am producing non-stream content that will start airing on my YouTube channel soon (within the next 30 days). This content will be more structured and focus on a specific topics with a set lesson plans. These videos won't feature any background music. I hope you'll be able to enjoy these when they're available.
Started watching your x86 stuff from the beginning. The music issues at first are distracting but I really like it. Keep up the good work. I'll see if I can keep up but you do really stream the whole five hours!
I haven't seen your videos before, but they look neat. I glanced at one of your repos and was surprised to see .8 extensions! a86 was definitely my favorite flavor of x86 assembly in that era and d86 was pretty remarkable.
Absolutely agree: a86/d86 and later a386/d386 were my go-to assemblers for the early-to-mid PC era. Great macro facilities, library tools, listing files, and very fast.
a86 works great under DOSBOX but, sadly, d86 does not. I'm stuck using Turbo Debugger. Which, as tools of the time go, wasn't at all shabby and DOSBOX emulates it very well.
I wonder if the DOSBox developers are aware of this, and if you could interest them in fiddling around to see what the cause is. It could be something very simple.
(Based on the fact that D86 was last updated in 2000 I doubt the author is going to be too interested in tinkering with it.)
---
HOWEVER!
D86 works just fine in QEMU - and, even better, if you use a CPU idle program for DOS to make QEMU not chew 100% of one core, the idle program will continue to have an effect even while D86 is running.
This being said, I have no idea how to use D86 :) and so cannot say whether an idler program will impact anything.
I tested with the IDLE.COM in "VMAdditions.iso" (date 3 Aug 2004, cksum 281796710). The program is so small (128 bytes FTW) that I see no issue with just
I haven't tried QEMU but I have successfully run D86 under an MS-DOS instances running in Virtual Machine and Parallels. However, these environment aren't well tuned for games and sound emulation is more or less useless.
My guess is that the author of D86, Eric Issacson (http://www.eji.com/), made use of some of his inside knowledge from his Intel days. Specifically, I know he liked to use encodings of the AAM instruction with bases other than 10. Intel didn't document the opcode properly so history records it as only supporting base 10, but it in fact can support quite a few number bases. Anyhow, I would speculate that DOSBOX doesn't support such a flexible interpretation of the encodings and that's why it's throwing illegal instruction exceptions. Maybe I should patch DOSBOX on stream sometime and submit the patch to the maintainers.
> My guess is that the author of D86 ... made use of some of his inside knowledge from his Intel days.
I continue to be amazed at the amount of backward-compatibility inside the average Intel x86 CPU. If something stays out of long mode, it can still do all of that. Impressive, really.
And... I hadn't thought through to the point of considering what you were using D86 for :)
I agree, things like sound emulation leave a lot to be desired. I only use QEMU for its networking and HW-accel virtualization - DOSBox runs rings around it with eg Win3.1...
Have you tried PCem and 86Box?
> Maybe I should patch DOSBOX on stream sometime and submit the patch to the maintainers.
What are the main benefits of a(3)86 compared to free assemblers such as FASM or NASM? a386 costs a good chunk of money, so trying to understand the value proposition here.
Assembly language is really fun to write! I think that it's really important to know the basics of modern computer architecture and their ISAs. All abstractions are leaky and I don't think that you can be a truly proficient systems programmer if you don't know the basics of the hardware your code runs on.
Lately I have been writing a lot of code in x64 asm for fun. I've found that it's impressive how much functionality you can fit in such a small amount of space. Randall Hyde's book got me started but I think that a much better introduction would be Paul Carter's PC Assembly Language https://pacman128.github.io/pcasm/
I haven't done any assembly on intel chips. The Motorola 68k was so much easier to program I never even looked at anything else. I guess that has improved by now?
I was pretty much in the same boat. I did m68k asm in college and only barely looked at intel and felt a bit overwhelmed. Recently I got interested though and found some good youtube videos by this guy https://www.youtube.com/channel/UCq7dxy_qYNEBcHqQVCbc20w . After going through those videos I actually feel like I have a decent grasp on intel asm now. My only interest was trying to do some wacky things in inline asm in c which was a lot of fun.
It's not bad at all. You can always start with gcc -S to give you assembler output of a compilation, then modify as you want, or compare with the compiler.
As for an ISA, it's not clean but there's only so much you can do to an assembler language, so it's not that bad. And the docs are pretty good.
I've used that several times. One more thing I want: The C/C++ code as comments in the assembler file. Is there a gcc switch for that that I don't know about?
It's fairly dense nowadays by comparison. If you have the time, playing with DOSBOX or PCem with an emulation of the original IBM PC or XT is a less complex and more inviting introduction to ASM within the confines of the PC architecture.
(If you're following Paul Carter's book above, use something that supports protected mode, like a 386.)
You program a modern x86 in almost a RISC esque way with a couple of exceptions. For instance if you're just doing a read-modify-write that can be done in one instruction, that's preferred. You can do all of the work only allocating physical register file resources without allocating any architectural registers.
Just read "Register file" on Wikipedia, thank you. Would you kindly share any link that discusses the technique you explained? This is pretty new idea to me.
The original arcade Mortal Kombat (and MK2, MK3 and UMK3) were all written in assembly for the TMS34010 processor. No doubt ports of it to the Sega Genesis did use 68K assembler.
The TMS34010 was something like a CPU and 2D graphics processor combined. It has the ability to change its word size from anywhere between 1 and 32 bits with data addressable on bit boundaries. It's wild.
I still don’t understand how you can use basic data structure in assembly. I know strings are just allocating a blob of bytes and usually reserving the last for null terminator, but what about arrays? Is it just like in C where you allocate a blob of bytes but now you manually calculate where the index is based on item size? What about hash tables, that seems even more confusing... are they just not needed due to the nature of assembly?
If you know how to construct data structures in C, it's pretty straightforward to construct them in assembly language. In asm you tend to lose the benefit of arrays being a type. In x86 you have powerful addressing modes (the different ways you can calculate a memory address) which are normally encoded with modrm and sib bytes after the opcode. An example where eax, rbx, and rcx are registers:
; eax = rbx[rcx] where rbx points to the base of an array of dwords
mov eax, [rcx*4 + rbx]
; => 8B 04 8B
; where the first 8B is the opcode for this version of mov, 04 is the modrm byte saying
; to mov the value into eax as well as that there will be a sib (scale/index/base)
; byte following specifying the data source, and then 8B specifying everything in the []'s.
; If we were moving into ecx, the modrm byte would be 0C because a the 3-bit
; register specification portion of modrm would change to specify ecx.
If you are optimizing for size (which can be a fun exercise) there are other ways of iterating through "arrays" of bytes such as the various string instructions [1] (though these tend to be slower on modern processors because they are microcoded). This gets into designing your code to use registers which are used implicitly by small instructions reducing the need for modrm/sib bytes in the instruction bytes [2].
Another example might be:
lodsd ; eax = *rsi++ : "AD" (one byte opcode using implicit src/dst operands)
shl eax, 2 ; eax *= 4 : "C1 E0 02" where C1 specifying shl instruction
; E0 specifying eax and immediate constant
; 02 the constant to shift eax by (this is a mul by 4)
stosd ; *rdi++ = eax : "AB" store into destination (one byte opcode)
It's not hard to imagine that being in a loop to iterate over the contents of an array. Hash tables are just the way they are in C really.
Everything is just a blob of bytes when you get right down to it. Strings and arrays are just as you say. As another commenter notes, hash tables are big arrays and a function that tells you how to index into it. Linked lists are a bunch of different blobs, each of which has a sub-blob that tells you the address of the next blob. And so on.
They have this representation whether you're using assembly or not... it's just that if you're writing assembly, then you have to pay attention to their blobby nature. If you're writing in a higher level language, then that language will do the work for you of translating, say, my_object["my_key"] = my_value into the assembly instructions that deal with indexing into your giant array.
Yeah, seems like you have the right idea, for arrays. So if you think about a hash table, (a very simple one) that is just a big array, paired with a function that turns keys into indices. So you would define your function. It would spit out an index. And you would calculate the location of that index based on the start point of the array, and the size of the values.
>I know strings are just allocating a blob of bytes and usually reserving the last for null terminator, but what about arrays? Is it just like in C where you allocate a blob of bytes but now you manually calculate where the index is based on item size?
Yes, that is roughly it, although there is more to the topic, related to arrays and pointers, etc. In fact C's way of doing it is pretty low-level too and similar to assembly - it just adds the address of the origin of the array (i.e. the array name. which is a pointer to the start of the array) and the index times the size of the array element, to get to the memory location of the indexed item in the array.
A surprising point I read a while ago is that due to this, in C, the expression a[i] is equivalent to i[a], where a is the array name and i is the index (int variable). This is because C resolves a[i] to:
*(a + i),
meaning, dereference the pointer calculated by adding the offset i to the pointer a, and that expression:
*(a + i),
by the commutativity law of arithmetic, is the same as:
*(i + a),
which can be converted to i[a] by the same C resolution rule in reverse. I actually tried this out earlier when I first read it, on either Microsoft C compilers on Windows or gcc on Linux, or both, and it worked. Not sure, it might have changed for some reason now, maybe due to later versions of the C standard. Should try it out again.
The Kernighan and Ritchie book "The C Programming Language" is be a good introduction to how arrays and pointers work (and their inter-relationship). (Seeing your profile, I guess you may have read it, but just saying.) I had read the original version and the 2nd (ANSI C) version. Not sure if there is an updated version after that.
In the versions I read, they explain arrays and pointers in C pretty well (although you have to do some work yourself, it is not dumbed-down teaching like some you see nowadays).
>What about hash tables, that seems even more confusing...
The K&R book had a nice simple implementation of a hash table in C too. The hash function (from memory) was something like just adding up all the characters's int values (in the string used as the hash key) and doing a division modulo some prime number (like 31). There was other stuff for handling collisions and buckets and so on. And that was of course only a demo version (though it did work), there are more advanced versions.
(Sorry for a few multiple edits, I was not familiar with how asterisks are used in HN as markup, so had to edit it a few times.)
Here are a few links for others who might not know about how they are used:
> I know strings are just allocating a blob of bytes and usually reserving the last for null terminator
I know this is what string literals in C translate to, but is it actually how anyone does it these days? I thought there was general agreement that a size field is mostly superior to an elephant in Cairo.
The comparison with C is not too bad. Explore what your C compiler does out of simple C code (Array access, allocation, string access, linked lists), for instance online in https://godbolt.org/
if you write recursive macros and/or use computed offsets from some template structure someplace...it ends up looking pretty much like C. except you have to assign your own registers.
Just use a programmer's calculator or a software calculator (e.g. bc on Unix, RealCalc on Android) for the hex math (and for math in other number bases and conversions between bases too). Or even write one of your own as an exercise and then use it.
Do you even do any of the hex math when writing actual code in assembly?
I understand when you use something along the lines of a disassembler that requires you to actually see the addresses of the values. But even these are sophisticated enough to give you symbolic values instead.
A lot of my hobby programs are emulators or parsers for old file formats (think DOS-era games). Bitfields become important, and they're more convenient to express in hex than in decimal.
There are times where it feels convenient to express things in decimal and times where it seems better to use hex.
>Do you even do any of the hex math when writing actual code in assembly?
There are multiple reasons why it is useful or convenient to deal with data (whether memory addresses, data values, colors, etc.) in hex (including doing math on such hex values).
As I said in another comment in this thread, I have not done a lot of work in assembly language; but have done enough and read enough about it to know that it is useful, and not just a cool or retro thing that developers do.
The fact that two hex digits compactly represent a byte, the fact that one hex digit can represent a nibble/nybble [1], the fact that machine registers on devices attached to computers (such as printers, scanners, any other electro-mechanical equipment with a computer interface) are used to programmatically read and write data to and from those devices, and thereby get their status and manipulate and control them, are some of the reasons why hex and hex math and bit-twiddling in hex and binary is useful and still done.
Depends on what kind of applications you write in assembly (or even C), I guess.
I knew about Randall Hyde's book from a while ago, and some time later had seen Paul Carter's book site. I reviewed it briefly and it did look good. Planning to work on x86 assembly language as a hobby, at some point when I have some more free time, using those two books. I had only done some 6502 assembly programming earlier on home computers (Commodore 64 and BBC Micro, mainly), but liked it. Apart from the basic programming in assembly, trying to optimize the code using various tricks, whether for speed or size, using bits, flags, alternative instructions or addressing modes or other techniques, is fun too. Of course it can get more frustrating than high-level languages when you need to debug the errors ... but still worth it, IMO, and even the errors are a good way to improve your deduction / debugging / programming skills.
I really enjoyed learning x86 assembly in college, and I'd highly recommend others give it a try. If you focus on the core essentials (e.g. the reduced instruction set from the 8086), it's a small and fun toolkit.
Learning assembly and creating my own simulated processor using multiple layers of logic gates was one of the main cornerstones of my college education. I remember when things finally "clicked" and I finally understood how computers worked on a fundamental level, I was positively giddy for days.
Unfortunately, our technology has become increasingly complex. For a while I tried my hand at learning more about modern processors, but found it far too overwhelming. Maybe I just didn't know where to look for more approachable resources. Just taking a look at the reference manuals provided by Intel is enough to make anyone lose hope. I seriously doubt any single human is capable of reading and understanding that in any reasonable amount of time.
If you wanna go down into the metal, consider picking up a microcontroller and writing your own firmware. You can ditch the OS and start squeezing every ounce of power from the hardware. It's pretty crazy how little power a well configured microcontroller can get by with.
I'd also suggest learning WebAssembly! The text format is quite pleasant, and it does away with many of the issues you'd have to deal with when using real hardware. Even better, the spec [0] is actually quite readable for regular developers. That might let people get their feet a bit wet without having having to go into the deep end of the pool right away.
The NAND to Tetris textbook is a masterpiece. (The book is called The Elements of Computing Systems: Building a Modern Computer from First Principles. The authors are Nisan and Schocken).
I worked through the entire textbook by myself, doing all the projects, and having no previous knowledge of computer architecture or compilers. It was a challenging but smooth process. The difficulty level was just right and I didn't get frustrated.
The computer you build is designed brilliantly. It's as simple as it can be, while still being a real computer can be that can play video games.
Im studying CS right now (community college) and we have two low level courses. In the first one we use Nand2Tetris and also build a SAP-1 (simple as possible) computer following Ben Eaters YouTube series. One of the coolest classes I've ever taken. We all built an actual working computer on a fricken breadboard.
The second class is the one im taking right now on x86 assembly and its cool but definetly not as fun as building a computer from scratch.
If no ones has watched Ben Eaters videos I highly recommend them.
MC68000 assembly language was so much fun on the great Commodore Amiga. It gave you the opportunity not only to code quickly nice video effects, but also to learn a lot about what is the architecture of a computer, how the CPU works with other coprocesseors (there were many on the Amiga, among which the Blitter to copy data blocks / draw lines / fill shapes, and the amazing Copper to control the video).
I recently decided to code again some stuff because I wanted to write some articles for the retro section of a magazine, and it was so pleasant that I coded some more and documented it (here for those interested, in french at this time, but there are pictures: http://www.stashofcode.fr/category/retrocoding/).
I hope people still have the opportunity to have a look at assembly language at school. The article is interesting. It reminds me also that I always heard that coding in assembly language was hard. Well, I would not have say that back in 1996. x86 was a pain in the ass because of the lack of registers and the way memory was managed, but Mx68x00 was very simple indeed. IMO, what was a bit difficult was that if you wanted to code assembly, you had to learn about how what was around the CPU did work.
I can't remember which book I read on 6502 assembly (may have been Randall Hyde's, actually), but learning it was one of the most satisfying programming experiences I've ever had. Strongly recommended for coders of all types and styles, even if it's a dialect that's pretty far removed from the CPU of your standard environment. You'll never be confused by what is meant by a pointer or reference ever again, that's for sure!
And anyway I have this cool Apple II video codec to show for my efforts :)
If, as a novice, you wish to write assembly code:
- compile your code with "-O3 -S" and examine
- analyze it.
Assume that glibc for your architecture is going to be hand tuned (and exceptionally highly optimized)
Read and try to understand the native mem() and str() routines, as the folk who write these routings DEEPLY understand the micro-architecture and instruction set.
A counter example: all implementations of strlen() which evaluate a single character at a time are completely inept if the architecture has vector comparison capabilities.
If you can cut things down to something reasonably self-contained, https://godbolt.org/ is incredibly useful. For example, I was curious about the approximate inverse square root instruction, so I wrote some code: https://godbolt.org/g/qdfSj4
It's a lot easier to read that than the raw output from gcc. The highlighting between matching segments is nice, and the right-click menu has all sorts of goodies, like scrolling everything to the same place or showing the manual entry for some instruction. Plus, it's super easy to see the effect of switching flags or compilers.
Though to your point, apparently godbolt v0.0.1 was basically just:
What may seem to be "single character at a time" may not be. Recent chips from Intel can run the built-in string operations very quickly. It's as if they are internally vectorized by the hardware.
Memories! Thank you for digging this up. 1996 sounds about rigtht - that's when I actually read the thing, and that, alas, is around the last time I did any serious assembly. Switching to Linux, and later on to 64bit, and all sorts of complexities cropping up, nd portability becoming more of an issue, I sort of lost touch, but it was wonderful while it lasted. Squeezed some screaming speed and ridiculously microsized code out of 486's and early pentiums.
I stil squirm when I see anything javascripty labelled 'asm'. Because, you know, it's not.
Recalling when I was at college I used machine code to pre-fetch data into cache to improve an algorithm by 40x, even the professor did not believe that and asked me to explain all the hacking code I did and later on gave me the highest grade for that class. Yes ASM and machine-code are fantastic...
I figured out how to write some basic stuff, then I learned to use perf to performance tune. Being able to read a little assembly was a revolution for me.
I also chuckled about how C is non-portable; omg C++ is so much worse. In some sense, x86 asm is more portable than just about anything now, sure you can't run it on ARM, but your C++ code won't compile on ARM either and you'll be in #ifdef hell for two weeks to get it there, at which point you'll give up.
Why is C++ less portable than C? In my experience, non-portable code happens when the interaction with the OS is not well isolated. Since the major OS interfaces are C header files, I don't see why C++ makes it any worse.
I should've clarified: I meant portability of code so that is can be compiled by multiple compilers, VS, clang, gcc. The feature set for C++ is so vast that it's almost certain that you'll write code on for one compiler that doesn't compile on another.
The second edition introduces high-level assembly (HLA) though. I never found HLA to be that appealing. I'd be curious to know if anyone who has read the book found it to be useful in learning assembly. I used "Assembly Language for x86 processors" by Kip Irvine, and I thought it was pretty good.
I've had many an argument with Randy over HLA and I agree. His point is that it makes assembly and computer programming at a low level easier to understand but I just don't see it.
Writing assembly is tedious when the processor is smaller than the task at hand. Doing more than 8-bit math on a 6502 was a pain because you had to handle multi-byte addition yourself. Zero page - because no 16bit pointers. But when the processor isn't too small and the instruction set is well written with humans in mind (I look away from Intel here) it can be kind of nice. The MC68000 was a dream compared to earlier processors I'd used before.
On a related note, I learned first on an 8080 but with a different naming and syntax than the standard intel ASM. Maybe it's because that's what I learned first, but it still seems better to this day.
Not really, but I'm writing C code and reading the asm with the expectation of seeing exactly the asm I expect. About the only major thing I want from the compiler is register allocation and scheduling, but it's way more maintainable to let the compiler make these decisions based on its machine model than have me do it.
Note that I'm a heavy user of intrinsics and SIMD, so I'm writing a lot of code where I've pretty much already done instruction selection, often at algorithm design time.
In embedded, using a cheaper microcontroller or HW offloading can bring big benefits. The former might boost profit a lit across high volume of units sold. 8-16 bit MCU's still account for a huge chunk of what's sold with it being over a billion dollar market. People will use C and/or assembly. For same reason, there's even still a market for 4-bit MCU's used in things like watches and Gilette razors. Like to see demoscene take on 4bitters.
Past that, it's still good for leveraging hardware features directly, highly-optimized code, and in high-assurance to be sure binary matches source. For latter, you can verify compiler output or just write it by hand to be verifiable.
There are even so-called typed assembly languages that are safer than C. TALx86 and CoqASM that build on x86 come to mind. SIFTAL addresses non-interference that combats things like side channels on top of obvious stuff.
I wrote an implementation of coroutines for C in both x86-32 and x86-64 bit assembly [1] last year. It's amazing just how few instructions it actually took (by taking advantage of the calling convention, not all registers need to be saved).
Only for SIMD-optimized code. For remaining cases, you can help the compiler so it can generate very fast code (with current OoOE CPUs, the generated code may be suboptimal, but as fast as hand-tuned assembly because of the abundance of ALUs and load/store units in the CPU; also, current compilers are better than back in 1996).
Of course! Seriously, if you're good at it, want a job? (see appropriate post if so)
I think assembly has only gotten less popular in a relative sense. Sure, there are now many jobs doing high-level stuff, but the low-level stuff hasn't gone away. It really can't go away, because all that high-level stuff is built upon it.
I work on a local smartcard manufacturer company. My friends who are assigned to develop our smart card OS ocasionally dabble with assembly code. Most of the code is C, though.
I love assembly! I think if you know C/C++ it's not too difficult to learn. One thing I noticed is, it can be a bit rough coming back to an old project, I thought I had left enough comments on one of my projects (https://github.com/nemasu/asmttpd), but I was still scratching my head a bit in some places.
Its a fair metric that the amount of maintenance in coding support is proportional to the number of lines of code you write. Assembly code is an order of magnitude lines longer than higher level languages.
Otherwise I dont think Assembly is that difficult to learn or code.
I only got into assembly recently when programming the C64, its such a simple instruction set and there is so much documentation that it seemed to me much easier to get into. Also its fun to have to work around its limitations.
x86/amd64 assembly, along with common lisp, are two languages that I find pleasurable in itself to write in. Probably because there no artificial barriers to what is possible. There's nothing more infuriating than knowledge that something is possible but artificially prevented/hidden.
All of my previous streams are archived here: https://www.youtube.com/channel/UCaV77OIv89qfsnncY5J2zvg
I have around 23 videos now on YouTube covering both ARM 64-bit and x86 assembly language.
I have two projects on the schedule that are 100% assembly language:
- Let's Make an Arcade Game in MS-DOS: 100% x86 assembly language. I use DOSBOX and period tools for this project.
- Arcade Kernel Kit: 100% ARM AArch64 assembly language running on Raspberry Pi 3.
All of the code for these is available on Github: https://github.com/nybblesio
I'm also working on a game engine called Ryu: The Arcade Construction Kit where I'm writing my own assembler for classic arcade CPUs.
I'll be working on the x86 project again starting 2 April through 7 April.