Python Assembler Query - RAM labels

Using, learning, programming and modding the Gigatron and anything related.
Forum rules
Be nice. No drama.
pdr0663
Posts: 7
Joined: 13 Mar 2020, 10:26

Python Assembler Query - RAM labels

Post by pdr0663 » 19 Mar 2020, 13:27

Team,

I'm writing some software ( Forth ) where i need to address RAM locations. I can see that the assembler allows labels for ROM locations, but how do we label RAM locations?

Where data is trampolined from EPROM into RAM, how do we keep track of these by labels, and how to retrieve the labels?

Thanks,

Paul

qwertyface
Posts: 14
Joined: 16 Jul 2019, 09:19

Re: Python Assembler Query - RAM labels

Post by qwertyface » 19 Mar 2020, 16:58

There isn't any such mechanism - asm.py doesn't really know much about memory at all. Depending on what you are trying to achieve you might be able to use the way in which the hi() and lo() functions work - they evaluate to 0, but also record the ROM offset where they were called, and the label they were passed. At the end of assembly, the high or low byte of the label is added to the operand of the instruction. This means that you can do a certain amount of arithmetic with labels.

E.g. if Python encounters

Code: Select all

ld(lo("label") - pc())
at address 0x1234 the ld function sees an argument of -0x1234 (0 - 0x1234). Later the low-byte of the address of the label will be added, so what you actually get in the rom is is the address of the label relative to the program counter. It's nice that what this means what it looks like it means - but don't be fooled!

Code: Select all

lo("label2") - lo("label1")
does not give the offset between the two labels - it gives the sum of the low-bytes of the two labels!

You could add multiple constants or labels and use the same label multiple times, to do more complicated things, but addition is your only building block.

All of that said, I don't think that labels are likely to be very useful for what you're talking about. I think you probably need to have your understanding of memory layout in Python (or your head and documentation), and not expect asm.py to help you.

User avatar
marcelk
Posts: 474
Joined: 13 May 2018, 08:26

Re: Python Assembler Query - RAM labels

Post by marcelk » 19 Mar 2020, 22:54

pdr0663 wrote:
19 Mar 2020, 13:27
I'm writing some software ( Forth ) where i need to address RAM locations. I can see that the assembler allows labels for ROM locations, but how do we label RAM locations?

Where data is trampolined from EPROM into RAM, how do we keep track of these by labels, and how to retrieve the labels?
At the core of the assembler, we have

Code: Select all

define(name, value)
to define any integer value, regardless of the meaning: ROM address, RAM address, button layout, ...

By extension,

Code: Select all

label(name)
is just a macro around define() that takes its value from pc(). pc() returns the current ROM location we're assembling to. But it all ends up in the same symbol table.

Some "sacred" name-value pairs are also kept in interface.json. For those, define() checks if these aren't violated and warns if that is the case. These values aren't allowed to change, ever, because that causes incompatible GT1 files. (And GT1 files shouldn't make assumptions about values not defined in interface.json...)
pdr0663 wrote: Where data is trampolined from EPROM into RAM, how do we keep track of these by labels, and how to retrieve the labels?
I'm not sure I fully follow. Can you give an example?

pdr0663
Posts: 7
Joined: 13 Mar 2020, 10:26

Re: Python Assembler Query - RAM labels

Post by pdr0663 » 19 Mar 2020, 23:29

Marcel,

Thanks your explanation is clear. I may need to customize asm.py or the traditional assembler.

Just one last question. In your "trampoline" routines it seems that you are transferring data from ROM to RAM (I'm not familar with trampolining as a term) for lookup tables and the like. Strange thing to me, is that you're using "LD" $00 opcodes to do this. The best I can make out is that this opcode loads the value in D into the accumulator and that's it. Don't subsequent LDs just overwrite the previous ones? I expected to see some variant of the [Y, X++] load instuction to do this, however it seems the target for this instruction can only be OUT.

Can you perhaps explain trampolining a little...? :oops:

Paul

User avatar
marcelk
Posts: 474
Joined: 13 May 2018, 08:26

Re: Python Assembler Query - RAM labels

Post by marcelk » 20 Mar 2020, 07:49

I split the topic for easier later reference: viewtopic.php?f=4&t=218

TL;DR:

Those long sequences of 'ld $dd' instructions in the file storage area of the EPROM aren't executed in sequence at all. They are all single-instruction subroutines that each carry a single data byte.

pdr0663
Posts: 7
Joined: 13 Mar 2020, 10:26

Re: Python Assembler Query - RAM labels

Post by pdr0663 » 26 Mar 2020, 08:51

Marcel,

Can we manipulate the _romSize variable on the fly to "ORG" to other ROM locations?

Paul

User avatar
marcelk
Posts: 474
Joined: 13 May 2018, 08:26

Re: Python Assembler Query - RAM labels

Post by marcelk » 26 Mar 2020, 09:49

This is a long time ago....

Internally the ROM bytes are kept in two lists, _rom0 and _rom1 for the even and odd bytes.

Code: Select all

_romSize = 0
_rom0, _rom1 = [], []
This goes back to the breadboard with two 28C64 chips:

ROM0_ROM1.jpeg
ROM0_ROM1.jpeg (75.43 KiB) Viewed 133 times

The way it is now, _rom0 and _rom1 start out empty and the assembler appends a byte to each for every emitted instruction. _romSize is the current size and increments. At the end (when calling end()...) and before writing out to file, a second pass resolves the open references, we merge the lists and we fill the rest with "Gigatron!" padding.

There's no real reason to have it this way, we could also start with a single _rom array and pad it at startup. It's just not like that now.

We also have a GitHub discussion on how to deal with higher level code generation. psr/qwertyface mentioned he wanted to be able to retract code in a backtracking like fashion. This is useful when hitting a page boundary. That should be as simple as lowering _romSize and truncating _rom0 and _rom1. But apparently there are some complications with the emitted labels. I haven't dived into it. That work is in his own repository, and when he emerges we can see what changes to asm.py we really need to take back.

When I hit a page boundary in the kernel, I just manually rearrange the source code and don't think about it. This may also happen with the ROM files that are included from the command line (some are application-specific SYS functions). Sometimes I have to rearrange them manually (in the Makefile) to make it fit again. It's not ideal, but not enough of an itch either (yet).

qwertyface
Posts: 14
Joined: 16 Jul 2019, 09:19

Re: Python Assembler Query - RAM labels

Post by qwertyface » 26 Mar 2020, 21:37

The issue I had was trying to do label arithmetic in a position independent way - e.g. the sort of thing I was talking about further up the thread.

The motivation was a bit hard to explain (it's to do with how I do branching within Forth threads), but I needed to emit ld instructions with the operand being the address of a label relative to the program counter. This was initially easy, I could just do ld(lo(label) - pc()), which emitted the right code.

However as you describe, I needed to avoid Forth threads crossing pages - and in this case all of these calls to the asm.py functions are coming from a Forth interpreter, which is just processing a Forth file. It can't really see how long a definition is going to be in advance; it doesn't really "know" that it's processing a definition at all - it just called the ':' function, then called the 'COMPILE,' function and so on, which in turn call these functions. So I can't really detect page crossing from within Forth, and I can't really measure the length of the definition from Python - all I can tell is when I've gone too far. I guess I could perhaps in some way capture and restore the state of the Forth interpreter - I must admit that hadn't occurred to me until now.

Anyway, what I thought I could do to do is to have : save the state of _romSize, and have COMPILE, recognise when I've gone too far, pick up the code generated since _romSize was last saved, and push it forward on to the next page. However now the operands that were relative to pc() are all wrong!

I hacked around this for my purposes, by storing and replaying the functions and their operands, not the generated bytes, but I think a real solution would allow probably be for asm.py to leave things in a position independent format until the last minute.

pdr0663
Posts: 7
Joined: 13 Mar 2020, 10:26

Re: Python Assembler Query - RAM labels

Post by pdr0663 » 27 Mar 2020, 03:32

qwertyface,

For nomal execution, the compiled Forth words are lists of 16-bit addresses, so you would load the X and Y regs and do an absolute jump I assume.
For BRANCH, can use a 16-bit absolute address again. For 0BRANCH or ?BRANCH etc the branch target could be inter-page, which is where I guess the problem is. Gigatron supports relative conditional branches only.

I had the same problem when writing a Forth for the PDP-8. Branches could only be intra-page. Absolute branches could only be done indirectly via a zero-page vector. The assembler was kind enough to highlight the branch error, and as Marcel mentioned, I adjusted the source so the page fault was rectified. Perhaps ASM.py can be modded to warn on page faults for branches. Moving the source is a bit painful, but the memory losses can be minimal if you're careful.

Paul

User avatar
marcelk
Posts: 474
Joined: 13 May 2018, 08:26

Re: Python Assembler Query - RAM labels

Post by marcelk » 27 Mar 2020, 06:06

pdr0663 wrote:
27 Mar 2020, 03:32
Perhaps ASM.py can be modded to warn on page faults for branches.
That's a good point. There's a small dilemma here between chopping off the high byte when resolving symbols, vs. requiring the high byte to be zero or match the page number. We chop off the high byte now because that has many uses.

Code: Select all

def end():
  ...
  for name, where in _refsL:
    if name in _symbols:
      _rom1[where] += _symbols[name] # adding allows some label tricks
      _rom1[where] &= 255
   ...
I'm now just randomly brainstorming that we can tweak that last line a bit. For example, have a less relaxed treatment when the symbol name starts with a dot.

P.S.: You probably have noticed already, just for completeness and to be be sure we're on the same page [pun intended]: native code can safely cross a ROM page by just running off the end. Sometimes this is undesirable. We then guard against unintentional ROM page crossings with:

Code: Select all

 align(0x100, size=256)
when aligning the program counter with a new ROM page. The second argument sets a code emission limit. Assembly halts when exceeding that number of words.

Post Reply