4
\$\begingroup\$

I watched Ben eater's video about a building computer based on 6502 microprocessor and I'm stuck at Part-3 ( Assembly language VS Machine code). He was programing a 32k AT28C256 EEPROM and he programmed it by assembly to blink a LED.

Here is his code: enter image description here

He uploaded code to EEPROM using Minipro TL866.

And here my question comes up:
32k EEPROM is only 0000-7fff address but he programed

>.org $8000

It tells the assembler that the code start at address 8000 right? And I think code will save starting at 8000 but this EEPROM can't save more than 7fff address so what address it will save?
In his video I understand that there are address decoders so when the 6502 chip sends address 8000 up above it will be decoded and it enable EEPROM and send address 0000 to EEPROM, so it means that code starts saving at address 0000 but when EEPROM is programmed with Minipro there are not any address decoders so what address it will be?

\$\endgroup\$
7
  • 1
    \$\begingroup\$ I have viewed Ben's videos, but frequently memory decoding is used to move various bits of memory to where you want them, in particular so they don't conflict. For example you might have RAM from 0x0000 and EEPROM from 0x8000 to 0xFFFF. This can be done by taking a higher order address bit than the 15 bits used to address the EEPROM and enabling the EEPROM (in this case A15) and using that to enable the EEPROM while addressing the EEPROM with the lower order bits A0..A14. \$\endgroup\$ Jan 2, 2023 at 4:32
  • \$\begingroup\$ Thank you. But I don't understand this part he uploaded code to eeprom by Minipro right . how Minipro would save code ? because it can't save code at address 8000 s? \$\endgroup\$
    – Heroz
    Jan 2, 2023 at 6:07
  • 1
    \$\begingroup\$ There might have been several ways Ben used to write data to the right addresses. The most obvious one is that he configured the EEPROM programmer to map the block of data starting at 0x8000 to physical address 0x0000 of the EEPROM when programming it. \$\endgroup\$
    – StarCat
    Jan 2, 2023 at 7:26
  • \$\begingroup\$ Is it about defining start address in Minipro gui ? Because I think starting address config in minipro gui is eeprom point of view address but .org is CPU point of view address \$\endgroup\$
    – Heroz
    Jan 2, 2023 at 13:16
  • \$\begingroup\$ My memory is that there are interrupt vectors in low memory on the 6502 and thus that has to be RAM if you want any flexibility. Thus the EEPROM must have been relocated and 0x8000 is the logical place to put it. \$\endgroup\$ Jan 3, 2023 at 1:11

6 Answers 6

6
\$\begingroup\$

Any block of physical memory can be "mapped" to appear at any position in the microprocessor's address space. As an example, take a small 1kB ROM/EPROM/EEPROM device, which has 10 address pins, allowing external circuitry to select any one of 1024 (0x0400) bytes inside the device, prior to reading data. This binary address will be anything between b0000000000 (=0x0000) and b1111111111 (=0x03FF).

Now we will connect those ten address pins directly to the lower ten address lines of a 6502's 16 bit address bus, A0 to A9, and (here's the important part) leave lines A10 to A15 unconnected.

Clearly, the microprocessor can place any value it likes on lines A10 to A15, and this will have absolutely no effect on which byte of memory is accessed. Only A0 to A9 have any influence. This has an interesting consequence, from the point of view of the microprocessor.

The effect is that the same block of 1kB of ROM appears at 64 different locations in the entire address space. The bottom 10 bits of the address bus repeatedly loop through 0x0, 0x1, 0x2 ... 0x3FF, 0x0, 0x1, 0x2 ... 0x3FF, again and again, 64 times, as the entire address bus counts from 0x0000 to 0xFFFF.

In other words, the first byte of the 1kB memory block can be accessed by the microprocessor at addresses 0x0000, 0x0400, 0x0800, 0x0C00, 0x1000, 0x1400 and so on, until 0xFC00. The second byte appears at 0x0001, 0x0401, 0x0801 etc.

Applying this same principle to your 32kB EEPROM, which has 15 address pins A0 to A14 connected to the corresponding pins of the microprocessor's address bus, with A15 being completely unconnected and ignored, you can see that the entire EEPROM contents will be duplicated (from the processor's perspective) twice, at 0x0000-0x7FFF, and again at 0x8000-0xFFFF.

The same program in that ROM will be present at 0x0000 and 0x8000, as far as the microprocessor can see.

The main takeaway here is that the address of the program in ROM memory, from the perspective of the EEPROM itself, and from the EEPROM programmer, is always 0x0000 to 0x7FFF, and yet from the point of view of the processor that program can be "mapped" to appear anywhere in the entire 64kB address space, depending on how it's wired to the address bus, and how the memory's "chip enable/select" pins respond to the upper address bus bits (called "address decoding").

The ".org" directive tells the assembler how to construct machines code to make it work at a specific location within the microprocessor's entire address space, but does not it any way determine or change the physical location in the device that stores it. So, when you write the assembly code, you are writing code to work at a specific place in the overall address space, but you can store that very code anywhere in the EEPROM, as long as you wire the EEPROM to appear at the appropriate place in that space.

So, in Ben's example, he's writing code to be assembled and run at position 0x8000, but that code is stored at position zero in the EEPROM, and the EEPROM is connected to "appear" (or be enabled) at position 0x8000 (and perhaps also 0x0000, if A15 is ignored as I explained above).

The EEPROM programmer has no idea of how the program works or was assembled, doesn't care if it's designed to run at 0x8000, or 0xABCD; all it cares about is that the file you are writing to the memory device begins at the device's own 0x0000 location. It's up to you to wire that EEPROM device up so that it appears at 0x8000 from the processor's perspective.

As for what's actually stored in the EEPROM, given the program you've shown us, if I recall correctly (30 year old memories being dragged up here), the first .org will not generate data. The following program segment appears at 0x0000 in the file. The second .org $FFFC will generate almost 32kBytes of zeros, then the last .equ will place 0x00 and 0x80 in locations 0x7FFC and 0x7FFD respectively. That's the end of the file.

\$\endgroup\$
5
  • 2
    \$\begingroup\$ Since a system with no visible RAM would be fairly useless, it's quite unlikely that A15 would be entirely ignored. More likely it selects between RAM & (EP)ROM. (It's common to feed the top few address lines into an IC like the 74HC238 to select a particular device; so it doesn't add significantly to the price of the whole computer - and makes some of the software cheaper.) \$\endgroup\$ Jan 2, 2023 at 23:59
  • 1
    \$\begingroup\$ @MartinKealey is right - Ben Eater uses a NAND for address decoding in this part, using A15 HIGH to enable the EEPROM. There’s no RAM yet, but 16K of it does get mapped into $0000-3FFF eventually. \$\endgroup\$ Jan 3, 2023 at 0:49
  • \$\begingroup\$ @Simon: you're correct, the first .org doesn't generate padding with the assembler they used. The OP showed a hexdump of the 32KiB flat binary in an earlier question, Why does first address equal 0000 when .org 8000 in assembly? \$\endgroup\$ Jan 3, 2023 at 4:43
  • \$\begingroup\$ I suggested ignoring A15 for the purpose of illustrating how a memory block can be mapped anywhere, even multiple times, and I agree with you all; some kind of decoding is very probably necessary, just to separate ROM, RAM and IO. A15 is super important, considering the interrupt vectors being so high in the address space, and the stack being so low. \$\endgroup\$ Jan 3, 2023 at 21:55
  • \$\begingroup\$ I forget - can the stack be relocated in the 6502? \$\endgroup\$ Jan 3, 2023 at 21:58
5
\$\begingroup\$

The way memory addressing is done on a 6502 (and many other processors) is that there are 16 address lines from the CPU, that is enough to address 64k of memory directly. But to be able to do that you would need 64k of memory with 16 address lines, and this is rarely what you would use.

Instead you usually have memory in smaller chunks, such as the 32k EEPROM mentioned. As an example, the Commodore 64 computer which used a form of the 6502 (a 6510) had two 8k ROM chips for the operating kernal and BASIC language, a 4k ROM for character generation, a 1k x 4 RAM for color memory, along with 64k of DRAM and some I/O mapped into the memory space.

The CPU address lines are put through some decoding logic that generates signals to enable each memory IC depending on the address range, so where each ROM appears in the address space depends on how the address lines are decoded.

With a 6502, ROM is usually put somewhere high in memory because the last couple of memory addresses ($FFFE, $FFFF) are used as a vector to tell the CPU where it should start executing instructions. When the CPU starts up it reads these addresses and whatever data it finds there is loaded into the program counter and code execution starts there. Some other processors such as the Z80 put the ROM at the beginning of memory and start execution at address 0x0000.

The assembler needs to know where in the CPU's address space the ROM is going to be located so that it can use the correct addresses for any labels in the code that reference locations in the ROM itself. For example, if the ROM is to be mapped from $8000 to $FFFF and there is some code like this:

MYLABEL LDA #$00
        ---some code here
        JMP MYLABEL

The compiler needs to know what address that label represents, so the instruction

.ORG $8000

will tell the compiler that the code is starting at $8000 so when an address in the ROM is referenced through a label it will be able to calculate the correct address, if the .ORG address and the address the ROM is physically mapped to do not match the assembled code would reference the wrong location.

The alternative would be to make the code relocatable, that is, write the code so there are no absolute references like this, all address references would be relative. Then the code can be put anywhere in memory and still work.

In some 6502 computers where the address decoding is made as simple as possible, a memory IC can appear in multiple places. A ROM might be able to be addressed at any multiple of 8k for example, so you could use address $2000, $4000, $6000, etc. in your code and they'd all access the same ROM location. This was often done for computers that didn't need a lot of memory so it didn't matter if these redundant 'ghost' addresses existed.

Ben Eater goes into the decoding logic in part 2 of his 6502 series,so you might want to re-watch that one.

If you get into designing 6502 computers you'll find that the main part of it is designing the address decoding logic.

\$\endgroup\$
3
\$\begingroup\$

The org directive basically tells the compiler that the code must be generated to work when the 32k memory is located in CPU memory space starting from $8000.

Which means the 32k memory is wired in hardware to CPU memory addresses $8000-$FFFF.

\$\endgroup\$
7
  • \$\begingroup\$ Thank you . But when loaded program to eeprom , eeprom only connected to Minipro and it can't load at address 8000 so what address it will be ? \$\endgroup\$
    – Heroz
    Jan 2, 2023 at 9:24
  • \$\begingroup\$ Or it means that with or without org binary output file be same. \$\endgroup\$
    – Heroz
    Jan 2, 2023 at 9:43
  • 2
    \$\begingroup\$ The chip only has addresses $0000-$7FFF. But in the context of running it in the system, the chip is at addresses $8000-$FFFF, and the code needs to know where in the memory the code is to use correct addresses. \$\endgroup\$
    – Justme
    Jan 2, 2023 at 10:46
  • \$\begingroup\$ So this is why when he loaded code in EEPROM by Minipro it started at 0000 . This is what I understand with or without org directive it loaded at 0000 chip's address right? \$\endgroup\$
    – Heroz
    Jan 2, 2023 at 10:52
  • \$\begingroup\$ @Heroz You can only program a 32768 bytes file into a 32768 byte memory and the compiler produces a 32768 byte file. The file could be smaller but then you would have to load it into higher address because the CPU boots from the end of the memory space. Without ORG the code would start at 0 and that is wrong because from the CPU point of view the memory is not at address 0. \$\endgroup\$
    – Justme
    Jan 2, 2023 at 10:55
2
\$\begingroup\$

It may be informative to consider how ORG works when you have a program stored not in ROM, but in a file.

A program file does not have a byte for every possible byte of the CPU address space.

Rather, it contains control blocks, program blocks, data blocks, and a start block.

Each data program or data block has an associated control block, which says where to find the program or data block in the file, how long it is, and which address it should start loading at into memory.

The start block says where to start running the program.

The effect of ORG is to set the loading address in a control block, for the code or data block that it refers to: the first byte of that code or data block will be loaded into memory at the address specified in the control block.


Likewise a ROM does not normally occupy the entire address space of the CPU, as that would leave no way to address other essential components.

But unlike a file, a ROM chip corresponds to a single combined code & init-data block, and there are no control blocks. Instead the ORG statement must agree with the "low" address that is established by the chip select circuitry, so that jump statements and other references will resolve correctly at run-time.

Or to look at that another way, the ORG statement conveys to the assembler where the ROM will appear in the CPU's address space, as established by the chip select circuitry.

Instead, it is usual for the ROM & RAM to take its address lines directly from the lowest of those provided by the CPU, while the other (higher) lines select which device (ROM, RAM, IO) is accessed. As a result, each device can only appear at an address that is some multiple of its own size.


In the example given, the CPU has 16 address lines, and the 32 KiB ROM has 15 address lines. IN that case the CPU's A0 ~ A14 would be connected directly to the ROM, while the CPU's A15 would be connected to the (very simple, two-way) chip select circuitry.

If instead there were an 8 KiB ROM, only A0 ~ A12 would be wired from the CPU to the ROM, while A13 ~ A15 would be wired into a one-of-eight chip selector.


More complex CPUs may have Memory Management Units that perform sophisticated address translation and access rights enforcement, in which case the ORG statement has to allow for whatever memory address mappings are loaded before that piece of code runs.

\$\endgroup\$
2
  • \$\begingroup\$ Only few visitors read comments at all. Therefore, if some information is only in a comment, it's like never written. You have enough formatting options and you can add a note to make your point clear in the answer. \$\endgroup\$ Feb 6, 2023 at 7:07
  • \$\begingroup\$ @thebusybee fine, I've deleted my footnotes, since you're just going to continue to object. Now let's see how long before someone else adds a comment being picky about how their system has a file format that doesn't match what I've said. This is just a useless waste of effort. \$\endgroup\$ Feb 6, 2023 at 7:34
1
\$\begingroup\$

If you define a label at the start of the program, and then jump to the label, the assembler has to know that the CPU thinks the label's address is $8000 so it writes the instruction JMP $8000 and not JMP $0000, and then the CPU jumps to the label which it thinks is at $8000. That's what the .org is for. It tells the assembler how the CPU will see the program.

It makes no difference to how the program is stored on the EEPROM.

I think in the example it makes no difference because there are no labels. Except the assembler does calculate the difference between $8000 and $fffc, but you could write $0000 and $7ffc and get the exact same program. That would be different if the program used any labels.

\$\endgroup\$
0
\$\begingroup\$

Let's think of an address like it was a page in a book. One byte - one page.

The size of the EEPROM is like the number of pages in one book. One book - one chip.

The book sits somewhere in a stack of books (read-only), notebooks (read-write), and unmapped address space (reams of coated blank paper).

You refer to any page in the stack by giving the consecutive page number in the stack. This page number may fall in any of the books, notebooks or blank paper reams.

ORG gives the position of the given address in the whole address space - in the stack of "books etc.". How this maps to an address in one book depends on how the books are set up in the stack (address space).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.