AVR Assembler Emulator Preview

Back to General discussions forum

Rodion (admin)     2015-11-14 08:09:16
User avatar

Hi Friends!

For some time already I'm slowly working on AVR assembler emulator which we can use in future for creating new problems. Currently only small subset of instructions is implemented, but I want to share the current state of the project - probably seeking for some advice, help in testing etc.

Excuse me for this post is bit too long.

Here is the Emulator page and the Github Project.

But what is important - this thing works differently from i4004 emulator. It emulates not the instruction list itself, but the compiled hex code (intel hex file format). This means that:

  • code could be compiled with real assembler compiler, like avrasm32 with all its features;
  • it also could be compiled with C / Pascal / Basic compilers for AVR (though there is not much sense in this);
  • standalone IDEs and debuggers (e.g. old but good VMLAB) could be used;
  • it would be easy to implement tricks like jumping to the middle of instructions :)

Emulator has "Compile" button also (it sends the code for compilation to remote server where AvrA compiler is used). Though different AVR devices are in many ways compatible, we emulate one of the most popular - ATMega8, which has about 8k memory for code.

EXAMPLE

Copy this sample program to the emulator and click "Compile":

.include "m8def.inc"
.org $0

; output few bytes to UART
ldi r16, 'H'
out UDR, r16
ldi r16, 'i'
out UDR, r16
ldi r16, '!'
out UDR, r16

some hex compiled code should appear below - now click "Execute" to execute this code. Some output registers will be shown to have non-zero value - and the "output console" will show "Hi!" message.

In this case "output console" is the honest thing, not some imaginary device. The emulated chip ATMega8 have UART pins and real serial terminal could be used with it.

  • command ldi or "load immediate" just loads value to register;
  • command out sends data from register to "io-register" - or rather some hardware endpoint controlling some peripherial device;
  • the value UDR is defined in m8def.inc file (wehave a copy on a compilation server) and is just the address of "io-register" assigned to UART.

CURRENT TROUBLES

The main problem is that AVR instruction set is comparatively huge. Right now I implemented instructions like:

  • addition, subtraction add, adc, sub, subi, sbc, sbci, inc, dec;
  • moving data ldi, mov, in, out (though the last two work only with few io-regs now);
  • branching (they are many, and sometimes have synonymous names breq, brsh, brcc etc.).

But I'm already facing the fact that I could not be sure results of instructions are always correct especially in regard of flags (there are flags of carry, zero, negative, overflow, half-carry and few more). If anyone can think of whimsical self-test based on these instructions - it would be cool...

Another concern is that the code of executor looks somewhat horrible because of parsing hex instruction codes (which sometimes have a whimsical bit ordering). Perhaps someone could provide some insight on reorganizing this part?

The third issue is that, I suspect, this emulator would not be very fast as i often needs to perform many lines in scripting language to calculate a single instruction. I haven't conducted performance tests yet.

THE FUTURE

We are going to have the first avr-asm related problems as soon as some more popular instructions are implemented. I hope it is ok not to wait until all possible ones are here?

Meanwhile I invite you to check this stuff, if you are interested, and tell if you see some horrible bugs, flaws or have some cool suggestions.

I provided no documentation still, but as this is far more modern chip, you can easily find some good guides, and among them two official papers:

  • "AVR 8-bit instruction set" in PDF
  • "ATmega8/L datasheet" in PDF

P.S. Our emulator still have some cheating - on startup of real device we could not be sure registers are initialized to 0 and UART for example needs to be configured with few more instructions. However we can pretend this was done in bootloader code after which jump was performed to address of $000 (that is an often practice).

Quandray     2015-11-14 08:27:40
User avatar

Hi Rodion,

Sorry I don't have much time to look at this right now as I have a dog that needs a walk, but...

It looks good. I'll be glad to help anywhere I can and certainly with testing. I'll start reading about AVR.

Your example code needs an extra OUT instruction to handle the "!".

Quandray     2015-11-14 11:23:57
User avatar

The emulator appears to just ignore instructions it doesn't understand. I think it should give an error message.

Rodion (admin)     2015-11-14 13:08:15
User avatar

Graeme Hi! I've added the missing line, thank you!

You are right - now it just skips the instructions which failed to match with any of the if-else-ifs... It is not convenient - I'll try to find a countermeasure for this...

Quandray     2015-11-14 19:13:34
User avatar

LDI & MOV seem to work ok. I have some tests to check ADD, i.e.

ID  Test    Result
--  ----    ------
A   1+255   ZHC
B   0+0     Z
C   0+1     no flags
D   7+8     no flags
E   7+120   no flags
F   7+9     H
G   8+120   VNH
H   8+121   VNH
I   21+107  VNH
J   32+100  VN  
K   31+100  VNH 
L   16+116  VN
M   126+126 VNH
N   129+129 SVC
O   136+1   SN
P   255+2   HC

I've written code for the first

.include "m8def.inc"
.org $0
;R20 holds test ID
;R21 & R22 are added
LDI R20,'A' ;add 1+255 expect flags ZHC
LDI R21,1
LDI R22,255
ADD R21,R22
BRCC    fail
BRNE    fail
BRHC    fail
BRMI    fail
BRVS    fail
BRBS    4,fail

;Now do tests B-P



LDI R16, 'O'
OUT UDR, R16
LDI R16, 'K'
OUT UDR, R16
RJMP    end ;not implemented!
BRCS    end
BRCC    end
;never gets here

fail:   LDI R16, 'F'
OUT UDR, R16
LDI R16, 'a'
OUT UDR, R16
LDI R16, 'i'
OUT UDR, R16
LDI R16, 'l'
OUT UDR, R16
LDI R16, ' '
OUT UDR, R16
OUT UDR, R20
end:

I'm running into problems already

a) I don't know if all the BR instructions I've used have been implemented
b) I can't do an unconditional jump
c) I'm going to have more than 63 bytes of code so I need a way of doing longer jumps
d) A better way of printing a string would be nice

Rodion (admin)     2015-11-15 07:17:33
User avatar

Graeme, Hi!

Thanks for the insight. I think it is just of the kind I needed - a guidance about most wanted commands. I'll try to make the requested improvements right now.

As about BRXX commands I believe that any of them which is the clone of some BRBC/BRBS is working - i.e. any which tests some of the flags (bits in SREG). I'm not sure if there are other conditional branches however...

UPD: I've added rjmp (for jumps) and lpm (to use strings from ROM) as "Hi People" example shows. It seems that label addresses are in words rather than in bytes. Hope it works!

Also I've tried to add "not implemented" warning though I'm not sure all relevant places are covered.

UPD-2: Added push, pop, rcall and ret (examples strrev.asm and subroutine.asm) for perhaps they could be useful.

Quandray     2015-11-15 09:11:21
User avatar

Hi Rodion, that looks much better.

When I tried a CLZ instruction it reported

Execution error:

Not implemented instruction 9498
(Not implemented instruction 9498)

I haven't used the new string print mechanism, but when I use this code it fails because ADDing 0+0 is setting the V flag

.include "m8def.inc"
.org $0
;R20 holds test ID
;R21 & R22 are added
;R23 expected result
LDI R20,'A' ;add 1+255 expect flags ZHC 0
LDI R21,1
LDI R22,255
LDI R23,0
ADD R21,R22
BRCC    fail1
BRNE    fail1
BRHC    fail1
BRMI    fail1
BRVS    fail1
BRBS    4,fail1
SUB R21,R23
BRNE    fail1
LDI R20,'B' ;add 0+0 expect flags Z 0
LDI R21,0
LDI R22,0
LDI R23,0
ADD R21,R22
LDI R24,'C'
BRCS    fail1
LDI R24,'Z'
BRNE    fail1
LDI R24,'H'
BRHS    fail1
LDI R24,'N'
BRMI    fail1
LDI R24,'V'
BRVS    fail1
LDI R24,'S'
BRBS    4,fail1
SUB R21,R23
BRNE    fail1
RJMP    testf

fail1:  RJMP    fail3   

testf:  LDI R16,'O'
OUT UDR,R16
LDI R16,'K'
OUT UDR,R16
RJMP    end

fail3:  LDI R16,'F'
OUT UDR,R16
LDI R16,'a'
OUT UDR,R16
LDI R16,'i'
OUT UDR,R16
LDI R16,'l'
OUT UDR,R16
LDI R16,' '
OUT UDR,R16
OUT UDR,R20
LDI R16,' '
OUT UDR,R16
OUT UDR,R24
end:
Rodion (admin)     2015-11-15 10:08:28
User avatar

> When I tried a CLZ instruction it reported

That's true, CLC/CLZ and others are not yet implemented though I see now it is inconvenient. I'll try to do this asap.

> ADDing 0+0 is setting the V flag

Wow. Really, and not only for 0+0... This do not look correct, I'll investigate! Thank you very much!


UPD

  • fixed (I hope) the bug with V-flag (it was inverted due to some previous refactoring, sorry)
  • implemented instructions CLx / SEx for flags (they are synonyms of BCLR and BSET, I think)

P.S. just realized that would be nice to see flags in emulator web-page.

Quandray     2015-11-15 14:36:12
User avatar

Thanks. No problems with ADDing now. All flags are fine.

If someone gets an error like Not implemented instruction 9475 how can they find out which instruction it is they have to stop using?

UPD: I can go to https://www.onlinedisassembler.com/odaweb/ and enter it in reverse order, i.e. 7594, and it tells me.

Rodion (admin)     2015-11-15 15:29:37
User avatar

Thanks for the link! As the emulator works with compiled hex instruction codes, I myself would be very glad to find a good way to determine the instruction by code. Before your link I've used the table from wikipedia.

Quandray     2015-11-15 18:05:46
User avatar

http://lyons42.com/AVR/Opcodes/AVRAllOpcodes.html is good for looking up the op code.

Quandray     2015-11-18 15:12:54
User avatar

Hi Rodion,

I think what you've done so far with this is really good, although I'd prefer the Not implemented xxyy error message to be reported the other way around as yyxx.

I haven't had any problems with the speed of the emulator.

I don't think you need to implement all the instructions before creating an avr-asm problem.

I've tried VMLAB and Atmel Studio and both are fine for compiling/simulating/debugging, but I haven't been able to get to see the output I've written to UDR. With VMLAB, I tried the TTY component, but it didn't like my 64bit version of Windows. With Atmel Studio, I installed version 7, then found that the Terminal Window extension only worked with versions 5 & 6. For the code I've written, I've used your emulator, which worked perfectly well.

When you get around to implementing more instructions, I'd suggest compare/xor/and/or/shift/rotate.

Rodion (admin)     2015-11-18 17:01:26
User avatar

Hi Graeme! Sorry for delay in moving this forward.

> but I haven't been able to get to see the output I've written to UDR

That's correct, as mentioned above UART needs to be initialized in real devices with few additional instructions. If you peek in "Atmega8 pdf" it can be found in article "USART Initialization" along with sample code.

In emulator we pretend that some initialization was already performed to save people from thinking about such hardware-related code (it is not a cheat, because real chips too may be programmed for such behavior by starting with custom "bootloader" in upper memory address and then jumping to $000 to start the user's code).

Though I'm not sure if this decision is right...

> I'd suggest compare/xor/and/or/shift/rotate.

Thanks for suggestion! I'll proceed in this direction soon!

Rodion (admin)     2015-11-23 08:14:35
User avatar

Hi! Few more instructions wre added:

cp, cpi, cpse, com, neg, swap, asr, lsr, ror, and, andi, or, ori, eor

I've found with surprise that lsl and rol are not separate instructions but instead are simply encoded as add or addi of register to itself (so they are already supported).

Quandray     2015-12-20 18:59:57
User avatar

Well done to Signum_X for reaching the rank of cardinal.

Why post this here? He says he's "Mainly an 8-bit AVR Assembly Programmer"

Please login and solve 5 problems to be able to post at forum