Assembler 'org' directive. - Page 1
Forum section: electronics / general

author: paulca
author_rank: Super Contributor
timestamp: June 08, 2025, 11:14:28 pm
content: I expected this:Code:.org 0x0000RET.org 0x0002RETto produce this:Code:C900C9It doesn't.It produces:Code:C9C9Can someone explain to me how this is useful to... ANYONE?Any suggestions on making "ORG" have purpose when laying out memory, that doesn't require a block fill for each and every single one of them?

author: Analog Kid
author_rank: Super Contributor
timestamp: June 08, 2025, 11:33:07 pm
content: What assembler?Pretty sure MASM wouldn't do that. That's just wrong, even if 0 is a NOP.

author: paulca
author_rank: Super Contributor
timestamp: June 08, 2025, 11:41:07 pm
content: Quote from: Analog Kid on June 08, 2025, 11:33:07 pmIt's both z80asm and sjasmplus.Both of their manuals state that "ORG" will not allocate anything, nor produce anything in the output.If I open the "list" file, the asm does know what I intend, but it doesn't output in the "bin" file that way.eg:Code:0000 restart_0:0000 c3 00 01 jp reboot00030003 ; Later these can vector into ROM routines0003 ; Or user RAM0003 ORG 0x00080008 restart_1:0008 c9 RET00090009 ORG 0x00100010 restart_2:0010 c9 RET00110011 ORG 0x00180018 restart_3:0018 c9 RET0019 ORG 0x00200020 restart_4:0020 c9 RET0021 ORG 0x00280028 restart_5:0028 c9 RET0029 ORG 0x00300030 restart_6:0030 c9 RET0031 ORG 0x00380038 restart_7:0038 ; General MODE 1 Interrupt0038 mode1_int_restart:0038 ; Empty00380038 ; NMI Interupt0038 ORG 0x00660066 nmi_restart:0066 ; Empty00660066 ORG 0x01000100 reboot:If it just filled in the gaps with 0 and did this in the ".bin" file I'd be golden.What I get is this:Code:0000000 00c3 c901 c9c9 c9c9 31c9 fdff fe3e 47edEDIT: Apologies this is byte order flipped, but it's late.Garbage.Too many rabbit holes send me down the whole linker route and defining my own ABI. I have no target platform. I just want verbatim raw bytes per address.The other option is to ...Generate my "block" ROM structure in one ASM process. Then use the file rewriting directives to "FPOS" code in.I don't mind for large blocks, but for vectors and resets it might be better to manually nop; pad.Surely this has to be easier, right?

author: ejeffrey
author_rank: Super Contributor
timestamp: June 09, 2025, 12:04:35 am
content: I haven't used those platforms for years but different assemblers and platforms work differently. On some assemblers, especially those where the standard output file is a simple binary (like CP/M or DOS COM files), the org directive doesn't what you are asking. Instead, org defines the address that the OS will load the binary into memory. In that case the only thing it did is change how jump and load labels are interpreted.On these systems you would only ever use a single .org directive. Once we moved beyond a single source per program, linkers took care of that offset and org is used to position relative to the current section start.

author: Analog Kid
author_rank: Super Contributor
timestamp: June 09, 2025, 12:24:12 am
content: Looks like those are demented assemblers.Any other choices available to you?

author: radiolistener
author_rank: Super Contributor
timestamp: June 09, 2025, 01:49:17 am
content: Quote from: paulca on June 08, 2025, 11:14:28 pmThis is incorrect. 0x00 is usually NOP, but there is no NOP in your code.ORG don't produce code, it just setup a new instruction pointer.This is what happens in your code:Code:.org 0x0000 ; the next instruction should be placed at 0x0000RET ; this instruction is placed at 0x0000.org 0x0002 ; the next instruction should be placed at 0x0002RET ; this instruction is placed at 0x0002there are just two ret's, so the compilation result will be:0x0000: 0xc90x0002: 0xc9so, the correct result is just two 0xc9, but their addresses are not sequential, first one is placed at 0x0000 and second one is placed at 0x0002.If compiler fills 0x00 between 0xc9 - this is serious bug of the compiler.Quote from: paulca on June 08, 2025, 11:41:07 pmThe BIN output format does not support multiple ORG directives. Technically, the compiler should reject such input and avoid generating a BIN file, however, some compilers may not report an error and instead produce a BIN file with undefined or incorrect content. In some implementations, filling the gaps with zeros in BIN for this case is not a bug but a documented behavior, since the BIN format lacks a mechanism to represent address gaps, unlike formats such as HEX.If your code contains multiple ORG directives, then you need to use the HEX output format instead of BIN.

author: Analog Kid
author_rank: Super Contributor
timestamp: June 09, 2025, 02:03:11 am
content: Wellll, all I can say is that MASM does exactly the right thing here. From this code:Code:ORG1000HRETORG1002HRETI get this executable code:(this was a Windows console program)Obviously it's got to fill the space in between withsomething, so zeroes are the obvious choice.

author: radiolistener
author_rank: Super Contributor
timestamp: June 09, 2025, 02:10:38 am
content: Quote from: Analog Kid on June 09, 2025, 02:03:11 amI just wonder what it produces for this code:Code:ORG0FFFFHRETORG8000HRET

author: Analog Kid
author_rank: Super Contributor
timestamp: June 09, 2025, 02:14:05 am
content: Quote from: radiolistener on June 09, 2025, 02:10:38 amSorry, I don't have the time to scroll through that much of a debug listing.Life's too damn short for that.

author: radiolistener
author_rank: Super Contributor
timestamp: June 09, 2025, 02:25:27 am
content: Quote from: Analog Kid on June 09, 2025, 02:03:11 amThe compiler should not make assumptions or insert instructions that are not explicitly present in the source code. Using multiple ORG directives when generating binary output is considered undefined behavior. While some compilers may handle such cases in their own way (e.g., by padding with zeros), this behavior is implementation-defined and should not be relied upon. Other compilers may handle it differently or even report an error.Actually this is not so much a compiler behavior as it is a limitation of the linker and binary output format. The BIN format lacks the ability to represent address gaps or discontinuous memory layouts as defined by multiple ORG directives, making it impossible to faithfully preserve the structure specified in the source code.

author: brucehoult
author_rank: Super Contributor
timestamp: June 09, 2025, 02:35:12 am
content: Quote from: Analog Kid on June 09, 2025, 02:03:11 amNo, the first part is not obvious.Using a proper executable format such as IHEX or ELF the bytes that you specify (in your case two RET) will be written into RAM at the addresses you specify, and the rest of RAM will be left untouched.BIN format doesn't know what address it should be loaded at, it's just raw bytes. You should get the same bytes regardless of that .ORG you use. Converting something with more than one block of data into BIN is completely entitled to simply concatenate the blocks together.

author: radiolistener
author_rank: Super Contributor
timestamp: June 09, 2025, 02:40:32 am
content: just tried your code withzasmwith output to HEX:test_org.asm:Code:org 0x1000retorg 0x1002rettest_org.hex:Code::01000000C936:01000200C934:00000001FFtest_org.lst:Code:1000:org 0x10001000: C9retorg 0x10021002: C9ret; +++ segments +++#CODE = $1000 = 4096, size = $0003 = 3; +++ global symbols +++_end = $1003 = 4099 test_org.asm:1 (unused)_size = $0003 = 3 test_org.asm:1 (unused)_z80_ = $0001 = 1 test_org.asm:1 (unused)total time: 0.0006 sec.no errorsAs you can see there is no zero filling between ORG directives.

author: paulca
author_rank: Super Contributor
timestamp: June 09, 2025, 08:25:44 am
content: It's a chicken and the egg problem.The ASM I am trying to write is the over all memory map as defined from the ROM chip point of view. It is that which defines, for example "USER_BASE" where user_main goes. user_main in my case is (currently at) 0x1000. Right in the middle of ROM.In particular, there are "hardcoded" addresses in the CPU protocol, such as reset vectors and interrupt vectors. These have to go in the right place.I did some more thinking. My resistance to "self padding" with declarations is ... not valid. I have no choice. I need to lay out a full 0x8000 bytes into a HEX file and flash it to the ROM.OR... I don't use "Full chip erase" and I align my memory regions to the flash sectors, I write my stuff in blocks, then I map those blocks to chip sectors. I write my programmer to take multiple files with a "Partition descriptor" and flash each into it's own memory location.The advantage being, when I am in "user land" I only have one asm file with one ORG and I only need to erase and write on sector. As my programmer is a slow DIY job I'm afraid to optimise, this is a good thing.On 00 being "NOP". To be honest this is a mistake and unless I see a reason not to, i intend to "pad" out with "HALT". That or borrow from STM32 and fill it all with a "RESET vector call" (a single byte opcode) that results in an infinite loop.... where at least if you had a debugger you can get the stack trace of how it ended up there. I put myself to sleep last night thinking about if it was a good idea to attempt to write the last few entries off the stack to the LCD screen. However, usually when you end up in the "Hard Fault" handler there are reasons that might also be a bad idea. ie. It's very likely the code hit a point where state was corrupted and it couldn't continue. As a programmer, it's like something I would do, is directly call the "hard_fault" handler as a way of raising a "RuntimeException" or a "IQuitSodThisForAGameOfDarts".

author: paulca
author_rank: Super Contributor
timestamp: June 09, 2025, 09:05:24 am
content: If I "Do nothing", regards padding and write the sparse object files "as is" just to the correct addresses in flash...The padding will consist of 0xFF.Interestingly this is:rst 38h ffA Mode 1 interrupt - Restart vector 7.Not ideal, but could be worse.EDIT: I own that ROM vector. I get to decide. Needs investigating, but if I end up in the RST7 handler and the interrupt flags are NOT set, then I know someone sent us there. If we make "that" process, of calling the CPU interrput handler directly ILLEGAL, then I can simply test the interrupt flags and if not set, jump to the hard_fault handler. Getting to where I wanted to go, without having to 00 the flash every time.

author: paulca
author_rank: Super Contributor
timestamp: June 09, 2025, 09:49:28 am
content: On "what I expected" being incorrect.It does make sense I was headed up the wrong tree. I had a growing feeling saying, "you are trying to dig a screw out with a spade.... find the screwdriver instead." - in other words, what I was trying to do what not in the capabilities of the tools I was using. Suggesting either what I was trying to do was wrong, or the tool was wrong.The .org statement does nothing, but change the assembler state, regards the "base vector" of any labels.The only place it will really be used in instructions is in the likes of:LD A,(my_label)In this case, "my_label" will be (last ORG address + instruction counter delta) of where "my_label" is actually anchored in the code.Other than in directives that's about all it does.If I want to split memory regions along those "ORG" definitions, I need to do that somehow/somewhere else. Be that the full formal linker defs or in the ROM partitioning while making the "ASCI-HEX" file for the programmer.

author: bingo600
author_rank: Super Contributor
timestamp: June 09, 2025, 10:17:21 am
content: I just tried for fun to build from sourcepasmohttps://pasmo.speccy.org/#downz88dkgit clone --recursivehttps://github.com/z88dk/z88dk.gitTest Source (asm-tst.asm)Code:org 0x1000retorg 0x1002retpasmo:Code::~/tmp/z80-asm/pasmo/pasmo-0.5.5$ ./pasmo asm-tst.asm asm-tst.bin:~/tmp/z80-asm/pasmo/pasmo-0.5.5$ od -x asm-tst.bin0000000 00c9 00c90000003pasmo:Code:./pasmo --hex asm-tst.asm asm-tst.hexcat asm-tst.hex:03100000C900C95B:00000001FFz88dk:Code::~/tmp/z80-asm/z88dk/bin$ ./z88dk-z80asm -b asm-tst.asmasm-tst.asm:4: error: ORG redefined^---- org 0x1002^---- org 4098z88dk uses another method to do "multi placement"https://github.com/z88dk/z88dk/issues/247z88dk "regions" seems nice.But if using simple "ORG" pasmo seems to do what you want/Bingo

author: DiTBho
author_rank: Super Contributor
timestamp: June 09, 2025, 10:17:57 am
content: Org is different even in GNU as

author: paulca
author_rank: Super Contributor
timestamp: June 09, 2025, 11:06:24 am
content: Consider.... generate a bin file that contains nothing but"JP (Whoooooopsie)"object code. A single 8 byte opcode like RSTn.Then build my memory map over the top of that file. So ALL uninitialised ROM jumps to "Whooopsie" handler.... and if that RST happens to be RST7 it's opcode is.... 0xFF which is what the flash rom will initialise to anyway.It just means, as I mentioned above, I need to make sure I can (abuse) use that default RST vector that way, as it is shared with Mode1 interrupt.

author: bingo600
author_rank: Super Contributor
timestamp: June 09, 2025, 11:15:07 am
content: Somewhat unrelated , but have you seen this PI-Pico based eprom emulatorhttps://www.mikrocontroller.net/topic/577231#newhttp://mino-elektronik.de/Gemischtes/ROM-Simulator.htmlI'm 99% sure mino is on this forum too.

author: paulca
author_rank: Super Contributor
timestamp: June 09, 2025, 11:20:44 am
content: This is my ROM simulatorI do however use an STM32 to take the ASM output and put it on a read flash rom chip via a ZIF socket.Work, like started this thread does not pass the simulator test so I don't bother trying the hardware.It's like unit testing though. It only tests that the code behaves in the test harness. The real world always has surprises. So "integration" testing with partial or complete hardware systems has to happen eventually.Code:// ram_async.vmodule ram_async #( parameter init_file = "asm/rom.hex",parameter size = 32768)(input wire [14:0] addr,input wire nCE,input wire nOE,input wire nWE,inout wire [7:0] data,input wire clk);reg _nCE;reg _nOE;reg _nWE;reg [7:0] ram_data [0:size-1]; // 32kreg [7:0] io_buffer;reg [14:0] address_buffer;reg o_drive;initial beginio_buffer = 8'bz;o_drive = 0;_nCE = 1;_nOE = 1;_nWE = 1;$readmemh(init_file, ram_data);endalways @(posedge clk) begin_nCE <= nCE;_nOE <= nOE;_nWE <= nWE;address_buffer <= addr;if( ~_nWE ) beginio_buffer <= data;endendalways @(posedge clk) beginif(~_nOE & ~_nCE ) beginio_buffer <= ram_data;o_drive <= 1;end else begino_drive <= 0;endendalways @(negedge clk) beginif (~_nWE & ~_nCE) beginram_data <= io_buffer;o_drive <= 0;endendassign data = (o_drive) ? io_buffer : 8'bz;endmodule

author: bingo600
author_rank: Super Contributor
timestamp: June 09, 2025, 11:51:33 am
content: OKDidn't know you were using a fpga ... Well verilog.

author: SiliconWizard
author_rank: Super Contributor
timestamp: June 09, 2025, 04:00:55 pm
content: The TL;DR is that it highly depends on the assembler in question, there is no standard about "ORG" directives.As you saw, some assemblers will properly pad data when using multiple ORG directives in the same source file, and many won't. I suspect that the simpler ones just deal with one ORG directive per source file and ignore any subsequent ones.

author: ejeffrey
author_rank: Super Contributor
timestamp: June 09, 2025, 05:57:23 pm
content: Also, if you want to accurately place multiple blocks of data in a single binary and resolve their references...That is what linkers do.If you switch to a workflow where you put the objects in specific sections and write a linker script that puts those sections at the right location then you will get what you expect and never have to use the .org directive at all.

author: radiolistener
author_rank: Super Contributor
timestamp: June 09, 2025, 07:50:46 pm
content: Using $FF as a fill value for unused blocks is considered good practice, as FLASH and UV-erasable EEPROM typically represent unwritten memory with $FF. This approach helps preserve the memory's lifespan, since writing $00 sets all bits to zero, which results in maximum wear on the memory cells.You can use this way to do it:Code:org 0x1000retblock0_end:defs block1_begin-block0_end, $fforg 0x1010block1_begin:retresult.hex:Code::11000000C9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC96C:00000001FFresult.lst:Code:1000:org 0x10001000: C9ret1001:block0_end:1001: FFFFFFFFdefs block1_begin-block0_end, $ff1005: FF...org 0x10101010:block1_begin:1010: C9ret