11 ms·
A 23-byte “hello, world” program assembled with DEBUG.EXE in MS-DOS
- colejohnson66 4y agoImpressive. Is there a reason to jump over the string instead of just having the string after the program? Seems like one could save two bytes doing that.
- donio 4y agodebug.exe is single pass and doesn't do labels so by having the string first you know its address later.
- vore 4y agoYou can compute the address of the string yourself like a two-pass assembler would, though, so that shouldn't be limiting.
- Agingcoder 4y agoThe COM executable gets loaded by DOS at address 100h, so the first bytes have to be executable code, if memory serves me well?
- vore 4y agoI think OP is saying what if you wrote it as: mov ah, 9 mov dx, offset helloworld int 21 mov ah, 0 int 21 .helloworld: db 'hello, world', d, A, '$'
- dmitrygr 4y agoYou are right and sister comment to this one is wrong. Thusly: MOV AH, 9 MOV DX, str INT 21 MOV AH, 0 INT 21 Str: DB 'hello, world', d, A, '$'
- q-big 4y agoThis program can be simplified further: MOV AH, 9 MOV DX, str INT 21 RET str: DB 'hello, world', d, A, '$' Why can MOV AH, 0 INT 21 be replaced by RET? Here is the answer: https://stackoverflow.com/a/60805758 https://stackoverflow.com/a/60805758 UPDATE: Under https://news.ycombinator.com/item?id=33398592 https://news.ycombinator.com/item?id=33398592 userbinator posted an additional possible optimization.
- ralferoo 4y agoNot a size optimisation, but a performance optimisation... INT 21 RET can be replaced with JP 5
- blueflow 4y agoThat mnemonic would be "JMP" on x86. At address 5 in the PSP is the CP/M-style syscall entry point, it is not advised to not use it because it relies on the A20 line being short-circuited to zero, which cannot be relied on in later MS-DOS versions. I don't see why this would be faster than using RET directly and having the INT 20 opcode at address 0 do the work.
- jart 4y agoJump parity? But according to http://www.fysnet.net/yourhelp.htm http://www.fysnet.net/yourhelp.htm the parity flag is only set in DOS 7.1+
- susam 4y agoI wrote this about 20 years ago during my university days. I happened to stumble upon it today in my archives and thought of sharing it on GitHub. I was still learning microprocessors back then. While browsing the C:\Windows directory, I fortuitously happened to discover the DEBUG.EXE program. Turned out it was available on any standard installation of MS-DOS as well as Windows 98. That chance encounter helped me to dive into the world of assembly language programming much before the coursework introduced me to more popular assemblers. Since I was still learning the x86 CPUs, the intention here was not to save bytes but instead to have something working. I believe I picked up the style of having the string at the top and jumping over it from other similar code I had come across in those days. You are right of course. Here is a complete example that moves the string to the bottom and saves two bytes: C:\>DEBUG -A 1165:0100 MOV AH, 9 1165:0102 MOV DX, 10B 1165:0105 INT 21 1165:0107 MOV AH, 0 1165:0109 INT 21 1165:010B DB 'hello, world', D, A, '$' 1165:011A -G hello, world Program terminated normally -N HELLO.COM -R CX CX 0000 :1A -W Writing 0001A bytes -Q C:\>HELLO hello, world C:\> I have now updated the GitHub repository with this updated source code and binary. Thank you for the nice comment!
- owl57 4y agoCurious. These other programmers probably learned the habit from some even older code. Jumping over data isn't a very obvious way of organising code, so probably it served some purpose many years ago. Maybe someone here knows what was that purpose?
- deleted 4y ago[deleted]
- userbinator 4y agoI remember seeing that in old Asm books too. My best guess is that it avoids having too many forward references, which would take up precious memory in the systems of the time and perhaps reach the limit of the assembler sooner.
- _the_inflator 4y ago
- pizza234 4y agoThis is an ASM program with a very standard structure (including the standard printing API: http://spike.scu.edu.au/~barry/interrupts.html#ah09 http://spike.scu.edu.au/~barry/interrupts.html#ah09) using a very standard tool (DEBUG.exe, common at the time for quick debugging); I'm confused why this is impressive.
- alrlroipsp 4y agoMost if not all ANCIENT assembers did a 1-pass translation of input source and thus would not recognize tokens that was referenced but not yet declared. Hence you declare your variables at the start of the program and jump past them.
- rep_lodsb 4y agoAny "real" assembler has to support forward references, otherwise you couldn't even jump past the initial data section, except by using an absolute address! Minimizing the number of forward references may make the assembly run faster or use less memory, but usually it's just a convention to make the source code more neatly organized.
- marginalia_nu 4y agoDEBUG.EXE is some necronomicon-tier dark magic.
- Narishma 4y agoWhat do you mean? It's a simple straightforward debug tool, or a monitor as it used to be called in 8-bit systems.
- userbinator 4y agoPC magazines used to publish source code listings for little utilities in the form of DEBUG scripts.
- techsupporter 4y agoI painstakingly entered so many bytes into DEBUG from the back pages of PC Magazine. It was an amazing experience when they added the ability to pay $4.95 or something cheap (for the time) to access "ZiffNet" as a walled-off section of CompuServe and download the utilities from there. For those who want to re-live the moment, look at this PDF starting on internal page 873 (file page 894): http://vtda.org/books/Computing/OperatingSystems/DOSPowerTools_PaulSomerson.pdf http://vtda.org/books/Computing/OperatingSystems/DOSPowerToo...
- blueflow 4y agoThe debugger and the 8086 instruction set is well documented, much better than the "modern" software that i have to work with at dayjob. Its not magic.
- marginalia_nu 4y agoIt's not the assembly language that I feel is like dark magic, but rather having a prompt where you essentially have almost unfettered access to the system, and have basically kernel privileges as long as you understand the hardware well enough (because MS-DOS memory protection is a bit lol). If that isn't dark magic I don't know what is.
- userbinator 4y agoYou can just use ret at the end, saving 3 bytes. Also, the initial value of bp is 09xx on every version of MS-DOS since 4.0, so you can also start off with an xchg ax,bp to save another byte. xchg ax,bp mov dx,107 int 21 ret db "hello, world" 0d 0a '$' 22 bytes, of which 15 is the message.
- EvanAnderson 4y agoI was going to suggest the RET but I didn't remember the BP being set to save the additional byte. Nice, albeit sacrificing some compatibility. For anybody interested some DOS default register values documented: http://www.fysnet.net/yourhelp.htm http://www.fysnet.net/yourhelp.htm
- userbinator 4y agoIt's an old demoscene trick, so perhaps a bit obscure but somewhat common in the sizecoding community.
- jart 4y agoWow from feedback to commit in 30 minutes. https://github.com/susam/hello/commit/36fa08e7cafb7c5268b651831f2d255a0862dcef https://github.com/susam/hello/commit/36fa08e7cafb7c5268b651...
- arthurcolle 4y agoseems like a reasonable pace on a Sunday
- susam 4y agoUpdated the repository to use your RET suggestion. Thanks!
- ithinkso 4y agoWith no attribution nevertheless :) (/s) I love how, without the 'hello, world' message itself, 25% of your entire HELLO.ASM codebase is from a random HN comment Edit: I saw the new commit, HN is lovely
- ivoras 4y agoCrazy that once upon a time just sequences of machine code were written to files, without any headers, or checksums, or basically any modern metadata. Just plain machine code, to be directly fed into the CPU. Cue lamentations of today's complexity mixed with feelings of life being great because of it.
- pizza234 4y agoYes, although this was for COM files only, which were limited to (64k - 0x100) bytes. EXEs didn't have this limitation, but they indeed had a header.
- WalterBright 4y agoFun fact. The COM file size was limited to 64K. The program size was not. Most COM memory models set CS=DS=SS=ES. However, the Zortech C compiler, would have CS set differently. CS pointed to the start of the code segment, DS to the start of the data segment. As long as the code segment plus the initialized data of the data segment was less than 64K, you could have a considerably larger runtime footprint (up to 128K) and still be a COM file with all near pointers.
- stkdump 4y agoActually, .com files can be larger. I don't know how exactly it was handled, but I distincly remember seeing versions of edit.com >64k
- andrewshadura 4y agoThey were .exe internally.
- WalterBright 4y agoI'm not sure when it started, but the .com and .exe extensions were ignored, and the loader looked at the first two bytes to see if it was "MZ" and was an exe file.
- secondcoming 4y agoThis brings back memories! Many years ago I used debug.exe create a bootable floppy that did nothing but display my name on the screen when booted from. I peed a little when it finally worked. Why did MS stop shipping it???
- 0xbadc0de5 4y agoSimilar - many years ago, I used DEBUG.EXE to assemble the samples from "The Little Black Book of Computer Viruses" I found in the local library and started a long journey into computer security. I also used it to modify the COMMAND.COM on my all of my high school's DOS media lab and library computers to display rude alternatives to "Please insert floppy disk" and "Insufficient disk space" - which in those days were frequently displayed. (hey, I was a teenager).
- bombcar 4y agoDEBUG.COM begat DEBUG.EXE which did not survive the move to x64 on Windows.
- 300bps 4y agoBrings back memories of 35 or so years ago compiling C with Turbo-C 1.5 using the Tiny model to create an .EXE and then running the DOS command: EXE2BIN program.exe program.com Apps were super tiny doing this. Nice to see someone taking it to the extreme with Assembly.
- jijji 4y agoahhh a bit of time travel to the mid-1980's ....love it
- gscott 4y agoI had an old full height RLL hard drive if I moved the computer the drive would loose sector zero and I would have to low level format it using a command in debug.
- userbinator 4y agoWas that "g =c800:5" or "g =c800:0" ? It's probably been over 3 decades since I had to use that command, but I still remember it for some reason...
- 4m1rk 4y agoSo nostalgic. All the work is done by 21h interrupt though. 10h is faster. And the fastest is to put characters in 0xB800 memory address. How do I remember that number without looking it up?
- userbinator 4y ago...and B000 on the MDA (mode 7), which was to allow both an MDA and CGA to coexist in the same system. Eventually the VGA took B800 even for its text modes, and PCs started booting in mode 3 by default. I'm not sure how the UEFI stuff works, but even the latest GPUs still have a VGA-compatible controller in them: https://01.org/sites/default/files/documentation/intel-gfx-prm-osrc-rkl-vol04-displayengine_0.pdf https://01.org/sites/default/files/documentation/intel-gfx-p...
- 4m1rk 4y agoOh yeah I have a vague memory of B000. I was more interested in mode 13 (256 colors 360x200). I think it was starting from A000. I'm proud that I found that address by trial and error. I was able to put pixels on screen using `int 10h` but it wasn't fast enough to render my animations. I read in a translated (into Persian) tech book that there's something called screen memory but no mention of the address itself so I started from 0 and checked all blocks. I knew I didn't need to check every single byte. I was going like 0000, 0100, 0200, ... until I saw a few pixels on screen! Of course after a few crashes.
- aricz 4y agomode 13h was 320x200 resolution with 256 colors. A000h was the top left pixel.
- alrlroipsp 4y agoMeh. I still remember my ICQ #.
- amelius 4y agoGetting close to the Kolmogorov complexity of hello world, I guess. But a bit disappointing that it is still longer than the length of the string. https://en.wikipedia.org/wiki/Kolmogorov_complexity https://en.wikipedia.org/wiki/Kolmogorov_complexity
- ataylor284_ 4y agoAh, debug. I used debug extensively for machine code programming on DOS. I couldn't afford MASM and certainly didn't have access to free assemblers. My first hello world program was almost exactly this. However, I always ended my programs with INT 20 instead of RET, or INT 21 with AH = 0. RET worked both for standalone programs or in debug, but there was one exception: if you ran your code repeatedly in debug with G 100. It would work the first time, but IIRC the stack wasn't reinitialized to return to the correct place on subsequent G commands. I spent a lot of time typing, running and patching in a single session and INT 20 always worked correctly.
- neilv 4y agoEventually, Borland came out with the Turbo Debugger and Turbo Assembler, "only" $100, which was a steal compared to Microsoft tools.
- shric 4y agoAH = 0? I only recall AH = 4C? That would exit the program with AL being the exit value. I'm not aware of AH = 0? Sadly, I used to write these kinds of programs with a hex editor, so "B4 4C CD 21" is etched into my brain even though "CD 20" was shorter.
- ataylor284_ 4y ago4C is correct. My INT 21 function codes memory is a bit fuzzy.
- bonzini 4y agoAH=4C is new in MSDOS 2.0, AH=0 dates back to CP/M (the low DOS calls are compatible with CP/M, hence the use of FCBs for example) and always sets the exit status to 0.
- aricz 4y agoint 20h for .com, and ah=4ch; int 21h; for .exe
- feisuzhu 4y ago
- indianmouse 4y agoThis was like a time travel!! Good old days of writing small utilities directly using Debug and modifying the disk sectors etc etc!!! The Ralf Brown's Interrupt List was the bible for me... It went on until late 2000s is when I moved out of ASM to C. Now fiddling with Golang and Rust, but anytime would prefer C or ASM or C with ASM. Please don't jump on me as I've been primarily in the System's programming and in Information Security for my entire life. No other lang supports me like C / ASM.
- xtracto 4y ago> The Ralf Brown's Interrupt List was the bible for me... It went on until late 2000s is when I moved out of ASM to C. I have that printed and bound somewhere in my parent's house! great memories.
- deleted 4y ago[deleted]
- DeathArrow 4y agoWhy Debug.exe instead of Masm or Tasm?
- MonkeyClub 4y agoDEBUG.EXE is part of the base system. Essentially, from MS DOs 2.0 onwards, with DEBUG.EXE you had an “adequate” development environment. No LSP, though :)
- BayAreaEscapee 4y agoDebug ships with DOS itself, so it's available on every system. Masm and other assemblers have to be obtained and installed separately.
- dancek 4y agoFor these kinds of things, there's a wiki nowadays: http://www.sizecoding.org/wiki/Main_Page http://www.sizecoding.org/wiki/Main_Page It's a very welcoming scene. Highly recommended, though I'll warn you that sizecoding can be an addictive hobby.
- Briggs958 4y ago[dead]
- jamesdhutton 4y agoFor anyone unfamiliar with DOS programming who might be trying to understand the code, you will find it helpful to know that INT 21h makes a call to the DOS API, and the value in AH indicates which API function to call, in this case the "display string" function. See https://en.wikipedia.org/wiki/DOS_API https://en.wikipedia.org/wiki/DOS_API
- pantulis 4y agoWhen did DEBUG become an .EXE file instead of a .COM file itself?
- alrlroipsp 4y agoIirc MS-DOS 5 and older was DEBUG.COM. Many shipped apps in MS-DOS 6 had became .EXE:s. One limit of a .COM is that it is one segment size (64 kilobytes) max file size.
- hilbert42 4y ago23 bytes is a 'long' program, my shortest is 17 bytes: mov eax, $c08e0040 Beat that!