PC manipulations and strings

Go To Last Post
9 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

While debugging my code, I came across an idea to manipulate the PC. I fiddled with it in a separate file and got interesting results but on a microcontroller level, I couldn't really find a good use for it that couldn't be handled more clearly/simply with other instructions. (I was trying to apply the idea of dynamic functions at the time).

Now, I'm searching for some ideas on shrinking or compressing strings suitable for the microcontroller when I come across this post Strings in ROM using assembler. The post that caught my eye was a comment:

avrcandies wrote:
also, there is a trick described somewhere on how to put the text right in the code and manipulate the pointer (stack?) to access it directly after the instruction.

I've been searching this forum for the past couple of days and I can't find the post where the trick is. I'm really curious about this trick and how it's applied.

I'm just curious at the solution. Can anyone help me find it?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you use ASM look for after then LPM instruction
I GCC (winAVR) look after the use of PROGMEM.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The first place I saw this technique used was when I was working on the ROM code for the Sinclair Spectrum. The Z80 has RST instructions which are a single opcode CALL to locations 0008, 0010, 0018 and so on. To save code space these were used to call the most used functions (1byte rather than 3byte call) and, for example the "print string" function would be:

rst 18h
.db "hello world", 0
;code continues here

The handler would POP the return address (the address of 'h' in "hello") and start printing the characters from there onwarsds until it got to the end of string (which may have been 0, I can't remember, this was 30 years ago) and finally it would RET to the address it was now pointing to.

Exactly the same could be done with an AVR with a CALLed routine then picking up data immediately after the CALL with an LPM and adjusting the RET address.

Its an interesting technique and when you trade up to ARM or MIPS you'll find their C compilers doing something almost identical with inline constant data.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, and these 'tricks' are very awkward to disassemble automatically.

You had to recognise the techniques. Then search for them, creating new data sections. Then skip over for the next executable code.

I can remember fighting for every byte of ROM. Although you can do similar techniques in AVR, I doubt that you will be so tight for ROM. Anyway, there are normally more pleasant tricks to use first.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

All it really saves is the loading of an index register pair with the address of the data. If the AVR were like a 68000 with unhandled instruction trap then there would be added merit, like that Z80 RST, of a "short opcode" to save further bytes. But the AVR is going to require a CALL or RCALL. I suppose the latter is as short an opcode as you can get so maybe it doesn't matter but it really does mean that all you save is the index register setup.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
.def    a0              = r20           ;working registers
.def    a1              = r21
.def    a2              = r22
;-------------------------------------------------------------------------
;                               transmit byte
;-------------------------------------------------------------------------
;input: a0 = byte
;
putchar:                                ;wait until tx buffer empty
        sbis    UCSRA, UDRE
        rjmp    putchar
        out     UDR, a0
        ret
;-------------------------------------------------------------------------
;                       send zero terminated string after call
;-------------------------------------------------------------------------
puts:
        pop     zh                      ;get address after call
        pop     zl
        lsl     zl                      ;*2 to get byte pointer
        rol     zh
        rjmp    _tou2
_tou1:  rcall   putchar
_tou2:  lpm     a0, Z+
        tst     a0                      ;check zero byte
        brne    _tou1
        lsr     zh                      ;/2 to get word pointer
        ror     zl
        ijmp
;-------------------------------------------------------------------------

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes Peter that kind of code is normal for me aswell for things that needs to be small and so not fast, I guess it's a leftover for my (and your) 8051 code, where that kind of code is normal.
I often make a small interpreter to some simple 16 stuff this way.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I remember doing something similar in 8086 assembly back in the eighties, to produce the smallest possible "Hello, World!" program.

You basically use the call mechanism to push the address of the string onto the stack, while effectively jumping over the string itself.

Sid

Life... is a state of mind

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Great. That's actually a little more concise than what I was thinking so it makes an interesting read.

I can't really use strings in this way without additional acrobatics though :( Oh well, slot it away in the library for future self. :)