5 ms·
I 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 l
by susam 4y ago
I 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 agoMaybe also better for linking the files. At least this was the reason I did it on Amiga when using absolute addresses. I only had to remember the start of the address area even when recompiling.
- bombcar 4y agoThis and the hard coding address comments are the key - forward looking references were expensive (and early enough, impossible) whereas you already knew where your data points were as they came before.
- jstanley 4y agoI wrote a compiler for a small machine that did this so that it could output the string content straight away without having to buffer it in memory.
- jmole 4y agoyou can hard-code values if you know their address in memory. If the data section comes first, then it doesn't move around if your code size changes.
- lboc 4y agoI've seen it done fairly often in mainframe assembler - embed an 'eyecatcher' string near the top of your program and jump to the real entry point. Used as an aid for debugging when reading dumps I believe. Not generally program data though, ususally metadata like program name, author, date assembled etc.
- kragen 4y agoA friend of mine used to program the PDP-8 a lot. The PDP-8 didn't have a stack; to call a subroutine, the JMS FOO instruction would store the return address at (the effective address computed from) FOO, then continue execution at the following word. So to return, the called subroutine would do an indirect jump using the address stored in the memory word before the beginning of its code. I was surprised when he told me how people normally passed arguments. They would put them after the JMS instruction in the caller. So, the callee would indirect from the "return address" stored in FOO to retrieve its arguments, incrementing the return address after each one. So the code to call FOO with arguments 1 and 2 would be (in Intel syntax) JMS FOO DW 1 DW 2 [code to run after returning] Mark Smotherman explains this in more detail, using PDP-8-compatible syntax, in https://people.computing.clemson.edu/~mark/subroutines/pdp8.html https://people.computing.clemson.edu/~mark/subroutines/pdp8..... It seems sort of intuitive to do things this way; if you're writing atan2(3, 4), it's nice to be able to translate this to jms atan2 3 4 rather than, say, mov $3, %rdi; mov $4, %rsi; call atan2. Maybe someone had this habit in their assembly and adapted it to the 8086's calling convention by inserting MOVs and a JMP instruction? You can also imagine that a compiler might be somewhat simpler if it generated code in the order in which it encountered the expressions. Gas lets you switch back and forth between .text and .data, so that if your compiler does this, you still don't have to insert JMP instructions. However, a few years ago when I wrote Ur-Scheme, I did do this for lambda expressions: the code for the lambda expression is generated inline in the middle of its containing code, which then has to contain a jump past it.
- ale42 4y agoOne case where it has a use is on partition (or floppy disk) boot sectors. First instruction is a jump to skip the disk metadata (actually a BIOS parameter block), which is in a fixed place. See https://en.wikipedia.org/wiki/Volume_boot_record https://en.wikipedia.org/wiki/Volume_boot_record
- Narishma 4y agoDEBUG.EXE is present in all versions of MS-DOS since the very first.
- WelcomeShorty 4y agoNo: https://github.com/microsoft/MS-DOS/blob/master/v1.25/bin/DEBUG.COM https://github.com/microsoft/MS-DOS/blob/master/v1.25/bin/DE...
- Narishma 4y agoIt was a .COM file back then but still the same program.