Absolute address in assembly
Buddha
Posts: 25
Is it possible to obtain the absolute address for a location in Hub RAM in assembly?
In Spin, there is the "Object Address Plus" operator that returns the actual location as a constant, but there doesn't seem to be a method to get that same information directly in assembly.
As an example case, let's say I want to write an assembly program that will restart itself into all of the COGs:
The problem with this code is the MOV statement, which will only load the offset address of the assembly code, and not get the absolute address of the initial instruction. Is there a way to get the real address in assembly without using Spin code?
In Spin, there is the "Object Address Plus" operator that returns the actual location as a constant, but there doesn't seem to be a method to get that same information directly in assembly.
As an example case, let's say I want to write an assembly program that will restart itself into all of the COGs:
PUB Main coginit(0, @Entry, 0) ' Restart the first COG to run assembly code DAT Entry MOV Startup, #@Entry ' Load startup address SHL Startup, #4 ' Shift into address field for COGINIT OR Startup, #8 ' Set COG # > 7 so a new COG will be started COGINIT Startup ' Initialize a new COG to also run this same code :Loop ADD Counter, #1 ' Increment the counter JMP #:Loop ' Loop forever Counter RES 1 ' Counter variable Startup RES 1 ' New COG startup variable
The problem with this code is the MOV statement, which will only load the offset address of the assembly code, and not get the absolute address of the initial instruction. Is there a way to get the real address in assembly without using Spin code?
Comments
It makes sense that instanced data would have a specific address at runtime, but code loaded from the EEPROM isn't going to be moving around in Hub RAM... So it seems like there should be a compile-time method to determine absolute addresses.
Here's a trick I found. Make an obvious value, like all A's, followed by something you're interested in.
(spin code)
DAT
org
tostart
some assembly code...
register0 long $AAAAAAAA
register1 long @tostart
Compile, then go to the Propeller tool menu "Run" -> "Compile Current" -> "View Info". You'll see your AAAAAAAA, followed by the value that the compiler assigned to your register1. If you add a NOP before your "tostart" code, you'll see the value of your register1 increase by 4, as it should.
But I think you have to add $10 to the contents of your variable if you want the absolute hub RAM address, because it looks like there are 16 bytes ($10, 4 longs) of header info that come first in hub RAM. (The first long in the header is clkfreq)
It's not that hard to match up those hex values with your assembly, and see exactly how your assembly is coded, using the assembly tables in the documentation, like this: (using the Windows calculator to convert between hex and binary)
0a24 a0fc = mov start, #@entry = A0FC0A24 = 101000 0011 1111 000000101 000100100
mov immed dest src
With that said, I'm having all kinds of trouble getting an assembly program to start an assembly cog. Spin starting a Spin cog works well, and Spin starting an assembly cog works well, but I just can't figure out how to get assembly to reliably start an assembly cog.
Here's some tests I've been working with. This is supposed to start assembly cogs, which then display an LED corresponding to their cog id. From Spin, the assembly works great. But not from assembly. As I play around with values, I do occasionally get working results, but nothing reliable.
Can anyone see what's wrong here?
David
' cogstart.spin
'
' Propeller board with individual segments of 7-segment LED display connected to IO pins 0-6
'
' works perfectly with spin starting assembly cogs
' only semi-working with spin starting assembly which starts assembly cog
'
_clkmode = xinput + pll16x ' use external clock multiplied by 16 -> 64 mHz
_xinfreq = 4_000_000 ' external rate is 4 MHz
PUB Main
{
' working demo of spin starting asm cog. (Starting cog zero wipes out this Spin program)
coginit(1, @tostart, 0)
coginit(2, @tostart, 0)
coginit(3, @tostart, 0)
coginit(4, @tostart, 0)
coginit(5, @tostart, 0)
coginit(6, @tostart, 0)
coginit(7, @tostart, 0)
coginit(0, @tostart, 0)
}
' working demo which automatically selects all available cogs (leaving out cog 0),
' and so will light all but the zeroeth segment of the display:
' repeat
' cognew(@tostart, 0)
' semi-working demo of assem starting assem cog...
' start assembly cog 1
coginit(1, @starter, 0)
repeat ' if we ever get this far. just pause.
DAT
org
nop
tostart
cogid id ' Get the cogid: a value from 0-7
shl bitmask, id ' build a bitmask
or dira, bitmask ' select segment based on cog id
or outa, bitmask ' set that bit -> turn on one segment of 7-segment display (0-6)
:loop jmp #:loop ' them wait.
id long $0
bitmask long $1
'
org
starter
' mov register1, #@tostart
shl register1, #4
' andn register1, #%1111 ' try lots of variations
' or register1, #$0 ' on manipulating register1...
' or register1, #$1
or register1, #$2
' or register1, #$4
' or register1, #$A
coginit register1
nop
coginit register1
nop
' coginit register1
' coginit register3
:loop jmp #:loop
' configure coginit argument:
' if bit 3 is 1 then bits 0-2 specify cog.
' bits 4-18 specify address in Hub RAM of assembly routine to start.
'register1 long %10_0000 + %0010
register0 long $AAAAAAAA
register1 long @tostart
'register2 long %110 '%10_0000 + %0011
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Chip Gracey
Parallax, Inc.