The slowest most pointless AVR program ever

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

Here's a bit of extreme silliness I wrote over the weekend...

So first I wrote this AVR program:

#include 

__attribute__((noinline)) void mydelay(void) {
	volatile uint16_t i;
	for (i = 0; i < 23; i++);
}

int main(void) {
	DDRB = 0xFF;
	PORTB = 0xAA;
	while(1) {
		PORTB ^= 0xFF;
		mydelay();
	}
}

A standard "LED flasher". The reason why the delay only counts to 23 will become apparent later ;-) (I started with a delay count of 10,000 then changed my mind). I built the code (-Os for mega16) and got:

:100000000C942A000C9434000C9434000C943400AA
:100010000C9434000C9434000C9434000C94340090
:100020000C9434000C9434000C9434000C94340080
:100030000C9434000C9434000C9434000C94340070
:100040000C9434000C9434000C9434000C94340060
:100050000C94340011241FBECFE5D4E0DEBFCDBF29
:100060000E944C000C9456000C940000DF93CF9338
:1000700000D0CDB7DEB71A82198205C089819A8176
:1000800001969A83898389819A814797B8F30F9063
:100090000F90CF91DF9108958FEF87BB8AEA88BBDD
:1000A00088B3809588BB0E943600FACFF894FFCFC2
:00000001FF

also this:

Disassembly of section .text:

00000000 <__vectors>:
   0:	0c 94 2a 00 	jmp	0x54	; 0x54 <__ctors_end>
   4:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
[snippety snip snip]

00000054 <__ctors_end>:
  54:	11 24       	eor	r1, r1
  56:	1f be       	out	0x3f, r1	; 63
  58:	cf e5       	ldi	r28, 0x5F	; 95
  5a:	d4 e0       	ldi	r29, 0x04	; 4
  5c:	de bf       	out	0x3e, r29	; 62
  5e:	cd bf       	out	0x3d, r28	; 61
  60:	0e 94 4c 00 	call	0x98	; 0x98 
64: 0c 94 56 00 jmp 0xac ; 0xac <_exit> 00000068 <__bad_interrupt>: 68: 0c 94 00 00 jmp 0 ; 0x0 <__vectors> 0000006c : #include __attribute__((noinline)) void mydelay(void) { 6c: df 93 push r29 6e: cf 93 push r28 70: 00 d0 rcall .+0 ; 0x72 72: cd b7 in r28, 0x3d ; 61 74: de b7 in r29, 0x3e ; 62 volatile uint16_t i; for (i = 0; i < 23; i++); 76: 1a 82 std Y+2, r1 ; 0x02 78: 19 82 std Y+1, r1 ; 0x01 7a: 05 c0 rjmp .+10 ; 0x86 7c: 89 81 ldd r24, Y+1 ; 0x01 7e: 9a 81 ldd r25, Y+2 ; 0x02 80: 01 96 adiw r24, 0x01 ; 1 82: 9a 83 std Y+2, r25 ; 0x02 84: 89 83 std Y+1, r24 ; 0x01 86: 89 81 ldd r24, Y+1 ; 0x01 88: 9a 81 ldd r25, Y+2 ; 0x02 8a: 47 97 sbiw r24, 0x17 ; 23 8c: b8 f3 brcs .-18 ; 0x7c } 8e: 0f 90 pop r0 90: 0f 90 pop r0 92: cf 91 pop r28 94: df 91 pop r29 96: 08 95 ret 00000098
: int main(void) { DDRB = 0xFF; 98: 8f ef ldi r24, 0xFF ; 255 9a: 87 bb out 0x17, r24 ; 23 PORTB = 0xAA; 9c: 8a ea ldi r24, 0xAA ; 170 9e: 88 bb out 0x18, r24 ; 24 while(1) { PORTB ^= 0xFF; a0: 88 b3 in r24, 0x18 ; 24 a2: 80 95 com r24 a4: 88 bb out 0x18, r24 ; 24 mydelay(); a6: 0e 94 36 00 call 0x6c ; 0x6c aa: fa cf rjmp .-12 ; 0xa0

Then I converted the .hex back to .bin with:

E:\avr\default>avr-objcopy -I ihex -O binary test.hex test.bin

E:\avr\default>

Then I wrote this PC program to convert .bin files back into .c source. I would just have used "xxd -i" except that I wanted an array of uint16_t:

#include 
#include 
#include 

typedef int bool;
#define false ((bool)0)
#define true  (!false)

FILE * fin;
FILE * fout;
char * p;
unsigned int n;
bool first = true;

int main(int argc, char * argv[]) {
        char buffer[256];
        int count;

        if (argc != 2) {
                printf("usage: makeavrc file.bin\n");
                exit(0);
        }
        strcpy(buffer, argv[1]);
        if (strchr(buffer, '.')) {
                p = strchr(buffer, '.');
                *p = 0;
        }
        strcat(buffer, ".c");
        fin = fopen(argv[1], "rb");
        fout = fopen(buffer, "wt");
        strcpy(buffer, "unsigned int ");
        strcat(buffer, argv[1]);
        p = strchr(buffer, '.');
        if (p) {
                *p = 0;
        }
        strcat(buffer, "[] = {\n");
        count = 0;
        while(!feof(fin)) {
                fread(&n, 2, 1, fin);
                if (!feof(fin)) {
                        fprintf(fout, "%s 0x%04X", first ? buffer: (count == 0) ? ",\n" : ",", n);
                        first = false;
                        count++;
                        if (count == 8) {
                                count = 0;
                        }
                }
        }
        fprintf(fout, "\n};\n");
        fclose(fin);
        fclose(fout);
}

which I used as:

E:\avr\default>makeavrc test.bin

E:\avr\default>type test.c
unsigned int test[] = {
 0x940C, 0x002A, 0x940C, 0x0034, 0x940C, 0x0034, 0x940C, 0x0034,
 0x940C, 0x0034, 0x940C, 0x0034, 0x940C, 0x0034, 0x940C, 0x0034,
 0x940C, 0x0034, 0x940C, 0x0034, 0x940C, 0x0034, 0x940C, 0x0034,
 0x940C, 0x0034, 0x940C, 0x0034, 0x940C, 0x0034, 0x940C, 0x0034,
 0x940C, 0x0034, 0x940C, 0x0034, 0x940C, 0x0034, 0x940C, 0x0034,
 0x940C, 0x0034, 0x2411, 0xBE1F, 0xE5CF, 0xE0D4, 0xBFDE, 0xBFCD,
 0x940E, 0x004C, 0x940C, 0x0056, 0x940C, 0x0000, 0x93DF, 0x93CF,
 0xD000, 0xB7CD, 0xB7DE, 0x821A, 0x8219, 0xC005, 0x8189, 0x819A,
 0x9601, 0x839A, 0x8389, 0x8189, 0x819A, 0x9747, 0xF3B8, 0x900F,
 0x900F, 0x91CF, 0x91DF, 0x9508, 0xEF8F, 0xBB87, 0xEA8A, 0xBB88,
 0xB388, 0x9580, 0xBB88, 0x940E, 0x0036, 0xCFFA, 0x94F8, 0xCFFF
};

Then I incorporated that in the main target AVR program (still work in progress):

#include 

unsigned int test[] = {
 0x940C, 0x002A, 0x940C, 0x0034, 0x940C, 0x0034, 0x940C, 0x0034,
 0x940C, 0x0034, 0x940C, 0x0034, 0x940C, 0x0034, 0x940C, 0x0034,
 0x940C, 0x0034, 0x940C, 0x0034, 0x940C, 0x0034, 0x940C, 0x0034,
 0x940C, 0x0034, 0x940C, 0x0034, 0x940C, 0x0034, 0x940C, 0x0034,
 0x940C, 0x0034, 0x940C, 0x0034, 0x940C, 0x0034, 0x940C, 0x0034,
 0x940C, 0x0034, 0x2411, 0xBE1F, 0xE5CF, 0xE0D4, 0xBFDE, 0xBFCD,
 0x940E, 0x004C, 0x940C, 0x0056, 0x940C, 0x0000, 0x93DF, 0x93CF,
 0xD000, 0xB7CD, 0xB7DE, 0x821A, 0x8219, 0xC005, 0x8189, 0x819A,
 0x9601, 0x839A, 0x8389, 0x8189, 0x819A, 0x9747, 0xF3B8, 0x900F,
 0x900F, 0x91CF, 0x91DF, 0x9508, 0xEF8F, 0xBB87, 0xEA8A, 0xBB88,
 0xB388, 0x9580, 0xBB88, 0x940E, 0x0036, 0xCFFA, 0x94F8, 0xCFFF
};

volatile uint32_t regPC;
volatile uint16_t opcode;
volatile uint32_t jdest;
volatile uint16_t src, dst;
volatile int16_t offset;

// The AVR memory...
volatile union {
	struct {
		uint8_t R[32];
		uint8_t SFR[61];
		union {
			struct {
				uint8_t rSPL;
				uint8_t rSPH;
			};
			uint16_t rSP;
		};
		union {
			struct {
				uint8_t C:1;
				uint8_t Z:1;
				uint8_t N:1;
				uint8_t V:1;
				uint8_t S:1;
				uint8_t H:1;
				uint8_t T:1;
				uint8_t I:1;
			};
			uint8_t all;
		} rSREG;
		uint8_t ram2[1024];
	};
	uint8_t RAM[32 + 64 + 1024];
} avr;

int main(void) {
	regPC = 0;
	for (offset = 0; offset < 61; offset++) {
		avr.SFR[offset] = 0xDD;
	}
	for (offset = 0; offset < 1024; offset++) {
		avr.ram2[offset] = 0xEE;
	}
	while (1) {
		opcode = test[regPC];
		if (opcode == 0x0000) { // NOP
			// do nowt
		}
		else if ((opcode & 0xFE0E) == 0x940C) { // JMP
			// decode
			jdest = ((opcode & 0x01F0) >> 3) | (opcode & 1);
			jdest <<= 16UL;
			jdest |= test[regPC+1];
			// action
			regPC = (jdest - 1); // -1 because it's incremented below
		}
		else if ((opcode & 0xFC00) == 0x2400) { // EOR
			// decode
			src = (opcode & 0x000F) | ((opcode & 0x0200) >> 5);
			dst = (opcode & 0x01F0) >> 4;
			// action
			avr.R[dst] = avr.R[src] ^ avr.R[dst];
			avr.rSREG.all = SREG;
		}
		else if ((opcode & 0xF800) == 0xB800) { // OUT
			// decode
			dst = (opcode & 0x0600) >> 5;
			dst |= (opcode & 0x000F);
			src = (opcode & 0x01F0) >> 4;
			// add the IO to RAM offset
			dst += 0x20;
			// action
			avr.RAM[dst] = avr.R[src];
		}
		else if ((opcode & 0xF800) == 0xB000) { // IN
			// decode
			src = (opcode & 0x0600) >> 5;
			src |= (opcode & 0x000F);
			dst = (opcode & 0x01F0) >> 4;
			// add the IO to RAM offset
			src += 0x20;
			// action
			avr.R[dst] = avr.RAM[src];
		}
		else if ((opcode & 0xF000) == 0xE000) { // LDI
			// decode
			dst = (opcode & 0x00F0) >> 4;
			dst += 16; // only upper 16 registers
			src = (opcode & 0x0F00) >> 4;
			src |= (opcode & 0x000F);
			// action
			avr.R[dst] = src;
		}
		else if ((opcode & 0xFE0E) == 0x940E) { // CALL
			// decode
			jdest = ((opcode & 0x01F0) >> 3) | (opcode & 1);
			jdest <<= 16UL;
			jdest |= test[regPC+1];
			// action
			avr.rSP -= 2;
			avr.RAM[avr.rSP + 1] = (regPC + 2) & 0xFF;
			avr.RAM[avr.rSP + 2] = (regPC + 2) >> 8;
			regPC = jdest - 1;
		}
		else if (opcode == 0x9508) { // RET
			// action
			regPC = avr.RAM[avr.rSP + 1];
			regPC |= (avr.RAM[avr.rSP + 2] >>8);
			regPC--;	// simply because there's a +1 below
			avr.rSP += 2;
		}
		else if ((opcode & 0xFE0F) == 0x920F) { // PUSH
			// decode
			src = (opcode & 0x01F0) >> 4;
			// action
			avr.rSP -= 1;
			avr.RAM[avr.rSP + 1] = avr.R[src];
		}
		else if ((opcode & 0xFE0F) == 0x900F) { // POP
			// decode
			dst = (opcode & 0x01F0) >> 4;
			// action
			avr.rSP += 1;
			avr.R[dst] = avr.RAM[avr.rSP];
		}
		else if ((opcode & 0xF000) == 0xD000) { // RCALL
			// decode
			offset = opcode & 0x0FFF;
			if (offset & 0x0800) {
				offset = 0x1000 - offset;
				offset *= -1;
			}
			// action
			avr.rSP -= 2;
			avr.RAM[avr.rSP + 1] = (regPC + 2) & 0xFF;
			avr.RAM[avr.rSP + 2] = (regPC + 2) >> 8;
			regPC = regPC + offset; // no +1 because regPC++ below
		}
		else if ((opcode & 0xF000) == 0xC000) { // RJMP
			// decode
			offset = opcode & 0x0FFF;
			if (offset & 0x0800) {
				offset = 0x1000 - offset;
				offset *= -1;
			}
			// action
			regPC = regPC + offset; // no +1 because regPC++ below
		}
		else if ((opcode & 0xFE0F) == 0x9400) { // COM
			// decode
			src = (opcode & 0x01F0) >> 4;
			// action
			avr.R[src] = 255 - avr.R[src];
			avr.rSREG.all = SREG;
		}
		else if ((opcode & 0xD208) == 0x8208) { // STD Y+q, Rn
			// decode
			src = (opcode & 0x01F0) >> 4;
			dst = opcode & 7;
			dst |= (opcode & 0x0C00) >> 6;
			dst |= (opcode & 0x2000) >> 8;
			// action
			avr.RAM[(avr.R[29]<<8) + avr.R[28] + dst] = avr.R[src];
		}
		else if ((opcode & 0xD208) == 0x8008) { // LDD Rn, Y+q
			// decode
			dst = (opcode & 0x01F0) >> 4;
			src = opcode & 7;
			src |= (opcode & 0x0C00) >> 6;
			src |= (opcode & 0x2000) >> 8;
			// action
			avr.R[dst] = avr.RAM[(avr.R[29]<<8) + avr.R[28] + src];
		}
		else if ((opcode & 0xF000) == 0x5000) { // SUBI
			// decode
			dst = (opcode & 0x00F0) >> 4;
			dst += 16; // offset to R16..R32
			src = (opcode & 0x0F00) >> 8;
			src |= (opcode & 0x000F);
			// action
			avr.R[dst] = avr.R[dst] - src;
			avr.rSREG.all = SREG;
		}
		else if ((opcode & 0xF000) == 0x4000) { // SBCI 
			// decode
			dst = (opcode & 0x00F0) >> 4;
			dst += 16; // offset to R16..R32
			src = (opcode & 0x0F00) >> 8;
			src |= (opcode & 0x000F);
			// action
			avr.R[dst] = avr.R[dst] - src - avr.rSREG.C;
			avr.rSREG.all = SREG;
		}
		else if ((opcode & 0xFC07) == 0xF400) { // BRCC
			// decode
			offset = (opcode & 0x03F8) >> 3;
			if (offset & 0x40) {
				// 2's complement if -ve
				offset = 0x80 - offset;
				offset *= -1;
			}
			// action
			if (!avr.rSREG.C) {
				regPC = regPC + offset; //+1 done by regPC++ below.
			}
		}
		else if ((opcode & 0xFC07) == 0xF000) { // BRCS
			// decode
			offset = (opcode & 0x03F8) >> 3;
			if (offset & 0x40) {
				// 2's complement if -ve
				offset = 0x80 - offset;
				offset *= -1;
			}
			// action
			if (avr.rSREG.C) {
				regPC = regPC + offset; //+1 done by regPC++ below.
			}
		}
		else if ((opcode & 0xFF00) == 0x9600) { // ADIW
			int16_t * p;
			// decode
			dst = (opcode & 0x0030) >> 4;
			offset = (opcode & 0x00C0) >> 2;
			offset |= (opcode & 0x000F);
			// action
			p = (int16_t *)&avr.R[24];
			p += dst;
			*p += offset;
			avr.rSREG.all = SREG;
		}
		else if ((opcode & 0xFF00) == 0x9700) { // SBIW
			int16_t * p;
			// decode
			dst = (opcode & 0x0030) >> 4;
			offset = (opcode & 0x00C0) >> 2;
			offset |= (opcode & 0x000F);
			// action
			p = (int16_t *)&avr.R[24];
			p += dst;
			*p -= offset;
			avr.rSREG.all = SREG;
		}
		regPC++;
	}
}

That's built for a mega32 which is very like the original target mega16 but has 2K of SRAM so there's enough room for a complete copy of the mega16 memory map within its own SRAM. So far I have implemented just enough opcodes to actually run the original AVR program I wrote but the key thing is that it's executing from RAM not code flash!

Yes, indeed, it's an AVR emulator and it's about the slowest bit of AVR code you could ever hope to meet. The original code hit a breakpoint on the COM R24 instruction every 457 cycles. The simulated code executes the COM instruction every 74,198 cycles - so it's 162 times slower than a real AVR on this short test !!

Admittedly that's built -O0 (for reasons I haven't yet determined the STD Y+q,Rd was not behaving right when optimised so I had no choice).

I was prompted to have a crack at this by yet another thread the other day looking for a way to "run programs from SD card". If one emulates an AVR using an AVR you could - as I did here - use the same build tools to build the program to be emulated and given that the opcode fetch is currently just:

	while (1) {
		opcode = test[regPC];

that could just as easily be:

	while (1) {
		opcode = read_2bytes_fromSD_at_offset_(regPC);

One thing about doing this is that you actually get a much deeper understanding about the way the AVR core must actually be doing it's job (like how to calculate negative offsets in RJMPs etc).

I think I might continue but I can see that the only practical way to do this would probably to be using inline Asm (which I was trying to avoid) though I suppose if you used a 20MHz 1284P it'd currently have plenty of room to emulated 164/324/644 at about 125kHz - so you probably wouldn't want to be setting your LED delays to much more than 23!

BTW at present the emulated program does not get to touch the AVR hardware at all but clearly all of the SFRs apart from SPL/SPH and SREG could "write through" to the real SFR addresses on the host CPU and get those LEDs flashing (slowly!).

==========================================================================================

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

Cliff... I'm thinking you could use a hobby!

:)

--greg
Still learning, don't shout at me, educate me.
Starting the fire is easy; the hardest part is learning how to keep the flame!

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

Before you lay claim to the slowest, I suppose one needs to compare to the AVRStudio simulator. ;)

In a past life I did a Forth kernel, and got the dispatch loop down to less than 10 x86 instructions. However, the Forth "virtual machine" is much smaller than the AVR opcode decode; all the AVR registers will need SRAM operations; the decoding is complex. And in my Forth I used the address of the "operator" routine as its opcode so I skinnied that down.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Quote:

In a past life I did a Forth kernel, and got the dispatch loop down to less than 10 x86 instructions.

On a PDP11 it was 2 instructions ! (my 3rd year thesis was about the use of Forth and comparison of various implementations I made - after Uni I almost started a full time job programming Forth)

I guess a threaded interpreted language or Java or some language generating P-code might be better ways to have non flash programs on an AVR - but the key thing here is that you just write "normal" AVR programs using the same favourite tool you always do then you should be able to run the resultant binary on the AVR.

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

Cliff, I'm utterly impressed, but you need to get a life :)

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

I ported the figforth public domain forth to the company 6800 and 6809 boards back in the 80s. Some of the techs really liked forth, but I couldnt understand it very well. Must be a leftbrain-rightbrain thing?

Imagecraft compiler user

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

Quote:

The simulated code executes the COM instruction every 74,198 cycles - so it's 162 times slower than a real AVR on this short test !!

Just buy a PIC :-)

/Bingo

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

BTW Cliff what you did is exactly what a friend of mine did to play SID musics from his C64 on his Amiga, he emulated the 6502 on his 68000 (this was almost 20 years ago).

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

Quote:
I'm thinking you could use a hobby!
A few children would also be very effective.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

But how can that execute?

The original code has dedicated RAM addresses coded in the bin file, and the emulator runs code in RAM. How do you ensure that these two RAM areas don't overlap and the original program does not garbage the code to be executed by writing to RAM?

avrfreaks does not support Opera. Profile inactive.

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

Reminiscent of Mel (http://www.cs.utah.edu/~elb/folklore/mel.html)

Edit, wrong link first time.

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

SprinterSB wrote:
But how can that execute?

The original code has dedicated RAM addresses coded in the bin file, and the emulator runs code in RAM. How do you ensure that these two RAM areas don't overlap and the original program does not garbage the code to be executed by writing to RAM?

Look at the union "avr". The RAM is also emulated.

Stefan Ernst

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

Ah, Mel... In my office, we say, Real Programmers use 'vi' and only 'vi'...

BTW Cliff, ALL of our Legacy applications are p-Code TIL and run on *ix ;) Fast, efficient and backed by C-ISAM. Our flag-ship product/service was written using 'vi' and this TIL, and was brought to market in less than 8 weeks after coding started. By comparison (I 'may' have mentioned this before) the re-write using .NET has overrun by over 3 years now and still not even in UAT!

Good on ya!!!

ps: you still need to get out more :)

--greg
Still learning, don't shout at me, educate me.
Starting the fire is easy; the hardest part is learning how to keep the flame!

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

Quote:

ALL of our Legacy applications are p-Code TIL and run on *ix

Quote:
What does TIL stand for?
Your abbreviation search returned 19 meanings

Rank Abbr. Meaning
***** TIL Truth in Lending
***** TIL Today I Learned (online slang)
***** TIL Tromsø Idrettslag (Norwegian soccer club)
**** TIL Tilefish (FAO fish species code)
**** TIL Tumor-Infiltrating Lymphocyte
**** TIL Time in Lieu
**** TIL Technical Information Library
**** TIL Transition to Independent Living
*** TIL Transparent Intensional Logic (natural language processing)
*** TIL Tata Infotech Limited
*** TiL Trust In Luton (Luton Town FC, England)
*** TIL Technical Information Letter
** TIL Terminal Indicate List
** TIL Threaded Interpretive Language (programming languages)
** TIL Trouble in Lab (Sprint PCS report)
** TIL Tactical Internet Laboratory
** TIL Transportation Information Library
** TIL Turbo Inter-Leaver/Inter-Leaving
* TIL Times Internet Limited (India)
Note: We have 100 other definitions for TIL in our Acronym Attic


Quote:
Possible Meanings Rank
Timken India Ltd *****
The Israel Lobby *****
Tobacco Industry Litigation *****
TUMOR INFILTRATING LYMPHOCYTES ****
Times Internet Ltd ****
Tjekker Iblandt LYST ****
Transitional Independent Living ****
Tata Infotech Ltd ****
Tech Info Library ****
The Industry Leader ****
Titan Industries Limited ****
Technology for Independent Living ****
Telesat International Ltd ***
Tablets India Ltd ***
tenant interim lease ***
Teaching Indigenous Languages ***
Temple Israel Library (Lawrence, NY) ***
Technology Integration Laboratory ***
Tata Industries Limited ***
Tablets India Limited ***
Teaching International Languages ***
Testi Italiani in Linea ***
Technology in Learning ***
Trade and Investment Limpopo ***
Tourism Ireland Limited ***
Trade Ideas Limited ***
Trypsin Inhibitory Like ***
Target Identification in Lupus ***
Tensar International Limited ***
Theoretical Inquiries in Law ***
Tata International Limited ***
Targeting Innovation Limited ***
Timken India Limited ***
Technisonic Industries Ltd ***
Tumour Infiltrating Lymphocytes ***
Technical Info Library ***
CD8(+)tumour-infiltrating lymphocytes **
LAK)/tumor-infiltrating lymphocytes **
Therapy Intensity Level **
thermo-inhalation lesions **
thyroid immune liquor **
thyroid infiltrating leukocytes **
thyroid infiltrating lymphocytes **
Tissue infiltrating lymphocytes **
tissue-infiltrating lymphocytes **
Tumor infilitration lymphocytes **
tumor infiltrated lymphocytes **
tumor infiltrating leukocytes **
tumor infiltration lymphocyte **
tumor-infiltrating lymphocytes **
tumor-infiltrating leukocytes **
tumor-infiltrating-lymphocyte **
tumour infiltrating leukocytes **
tumour-infiltrating leukocytes **
tumour-infiltrating lymphocyte **
tumour-intrinsic lymphocytes **
Trade and Investment Liberalisation **
Toromont Industries Ltd **
Targeted Institutional Links **
tumor infiltrating T lymphocytes **
tumor-infiltrating lymphoreticular cells **
tumor-infiltrating T lymphocyte **
tumor-infiltrating T lymphocytes **
tumor-infiltrating T-lymphocytes **
tumour infiltrating T-lymphocytes **
tumour-infiltrating T lymphocytes **
Tata Industries Ltd **
Tata Infomedia Limited **
Taking IT Local **
delta+ infiltrating lymphocytes **
tumor-derived lymphocyte **
Tumor Infiltrating Leukocyte **
Tractors India Ltd **
Technology International Ltd **
Total International Limited **
Tech Information Library **
adenocarcinoma-infiltrating lymphocytes **
melanoma-infiltrating lymphocytes **
Tumour Infiltrating Leucocytes **
The Indomitable Lions **
The Internet Letter **
Tcl Ice Library **
The Teaching Indigenous Languages **
Technology Independent Logic **
Teaching Information Line **
Technical Integration Leader **
The Internet Lottery **
Trauma Institut Leipzig **
Technical Information Letters **
The Tcl ICE Library *
Tradition Intarsie Liaison *
The Isles of Langerhans *
Thornton Information Line *
Temple Isaiah Lexington *
Techman Ireland Limited *
Temple Isaiah in Lexington *
Thai Infoservers and Libraries *
The Internet Lunchcounter *
Thumbnail Images and Links *
Tobin International Limited *

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Quote:

Look at the union "avr". The RAM is also emulated.

Indeed it is. That's why the host needs (preferably) to be a "family" chip but with x2 or x4 the emulated chip's RAM so there's room for an entire copy of the RAM (and other stuff).

In fact the only "clever bit" in the whole thing is the avr{} union which is formed of 32 AVR registers, 61 (in this case) SFRs followed by SPL, SPH and SREG (so really 64 SFRs I guess), then 1024 (in this case) bytes of RAM. The LDD's and STD's (and as yet unimplemented STS/LDS etc) really can access any of the 32 AVR registers, the SFRs (inc SP and SREG) and any location in RAM).

In fact when implementing it the watch window in AVR Studio is really useful as it shows all facets of that multi-layered onion - things like watching the CALL/RET stack (the RCALL .+0) to create 'i' in mydelay(), and the PUSH/POPs are particularly intriguing.

It's just vaguely possibly that when it does CALL/RCALL I might be stacking the 2 bytes in the wrong order but as long as things "unwind" in the same way I guess it's OK - well OK until you hit one of those mad programs that actually starts to look at its own return address on the stack - at which point it could be in for a shock.

(Lee I'm sure you really know TIL = Threaded Interpreted Language - presumably Forth - what other ones are there?)

PS BTW this particular LED flasher has no hard coded RAM addresses in fact - the only RAM usage (apart from CALL return addresses and various PUSH's is the 'i' variable and that is created on the stack too)

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

To make things "better", I think I'd start with a smart decode, beginning with the matrices posted https://www.avrfreaks.net/index.p...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Lee,

Thanks, in part I've actually been using this page from the avr-gcc manual:

http://sourceware.org/binutils/d...

which is very similar.

I will try "stream lining" it to improve performance (maybe an initial switch() on the top nibble to reduce the if/else cascade) but the single cascade suffices for the time being while I have so few opcodes implemented.

(I am intrigued by the way the opcode patterns and the source/dest bits within them are split - it seems just about the most inefficient way they could possibly have done things - but I guess this kind of thing doesn't matter in "instantaneous" VHDL?)

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

clawson wrote:
(I am intrigued by the way the opcode patterns and the source/dest bits within them are split - it seems just about the most inefficient way they could possibly have done things - but I guess this kind of thing doesn't matter in "instantaneous" VHDL?)
If you get hand on A's and V's diploma/PhD thesis or whatever started the thing, I am quite confident you'll find the answer there. My personal guess is that they simply kept changing the pattern until it resulted in the smallest 90S1200.

JW

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

Quote:
What does TIL stand for?
Your abbreviation search returned 19 meanings
Indeed, however, anyone bothering to read the previous posts to put abbreviations in context would have read...
Quote:
I guess a threaded interpreted language or Java or some language generating P-code might

Long day at the office Lee?

Cliff, Forth is not the only TIL (still?) in use. There were sufficient clues in my post for any net savvy user to identify the language. Granted 'Sculptor 4G'L is no longer as popular as it was 15-20 years ago, but it's still about in sufficient numbers for the authors to keep developing it. Can we say the same about 'Informix'?

My apologies for pulling the tread Off Topic.

--greg
Still learning, don't shout at me, educate me.
Starting the fire is easy; the hardest part is learning how to keep the flame!

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

How close is postscript to forth?

Imagecraft compiler user

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

Quote:

Before you lay claim to the slowest, I suppose one needs to compare to the AVRStudio simulator.

Or the startup of AVR Studio 5....

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Cliff,

as said before:
You really should get a hobby. I had hoped the cats would keep you from doing things like this in the weekend, but they probably lay on both sides of the screen watching what you where typing.
How do you find time to do things like this. Does the wife have other outdoor hobbies????

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

Used to use Forth all the time on 8080's 8085's and Z80's. Was just right for machine control like we were doing. I'd hate to try to write a business app in Forth.

Yes, the "Opcode" was just the address of the next string of opcodes which started with a jump to the opcode scanner if it wasn't actually machine code.

Wrote a CP/M Bios in Forth once, mainly to prove it could be done. The resulting bios was pretty small, and ran fast. Engineer decided he didn't want any FORTH code in HIS system He was an ASSEMBLY ONLY type. Strangely, his systems always had instructions like, "Don't press a key while..." as the interrupt would derail his delicate code and bomb the system.

The largest known prime number: 282589933-1

It's easy to stop breaking the 10th commandment! Break the 8th instead. 

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

OK
OK
OK
OK
OK
OK
OK

The largest known prime number: 282589933-1

It's easy to stop breaking the 10th commandment! Break the 8th instead. 

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

Just a small update to the sim. I used the opcode matrix that Lee linked to to implement a switch on the top nibble of the opcode to split the decode into smaller groups (some usefully have just one entry and hence no further need to mask/test the opcode pattern);

int main(void) {
	regPC = 0;
	for (offset = 0; offset < 61; offset++) {
		avr.SFR[offset] = 0xDD;
	}
	for (offset = 0; offset < 1024; offset++) {
		avr.ram2[offset] = 0xEE;
	}
	while (1) {
		opcode = test[regPC];
		switch(opcode >> 12) {
			case 0x0:
				// NOP, MOVW, MULS, MULSU, FMUL, FMULS, FMULSU, CPC, SBC, ADD
				if (opcode == 0x0000) { // NOP
					// do nowt
				}
				break;

			case 0x1:
				// CPSE, CP, SUB, ADC
				break;

			case 0x2:
				// AND, EOR, OR, MOV
				if ((opcode & 0xFC00) == 0x2400) { // EOR
					// decode
					src = (opcode & 0x000F) | ((opcode & 0x0200) >> 5);
					dst = (opcode & 0x01F0) >> 4;
					// action
					avr.R[dst] = avr.R[src] ^ avr.R[dst];
					avr.rSREG.all = SREG;
				}
				break;

			case 0x3:
				// CPI
				break;

			case 0x4:
				// SBCI
				{ // SBCI 
					// decode
					dst = (opcode & 0x00F0) >> 4;
					dst += 16; // offset to R16..R32
					src = (opcode & 0x0F00) >> 8;
					src |= (opcode & 0x000F);
					// action
					avr.R[dst] = avr.R[dst] - src - avr.rSREG.C;
					avr.rSREG.all = SREG;
				}
				break;

			case 0x5:
				// SUBI
				{ // SUBI
					// decode
					dst = (opcode & 0x00F0) >> 4;
					dst += 16; // offset to R16..R32
					src = (opcode & 0x0F00) >> 8;
					src |= (opcode & 0x000F);
					// action
					avr.R[dst] = avr.R[dst] - src;
					avr.rSREG.all = SREG;
				}
				break;

			case 0x6:
				// ORI
				break;

			case 0x7:
				// ANDI
				break;

			case 0x8:
			case 0xA:
				// LD Z, LD Y, LDD Y, LDD Z, STD Y, STD Z
				if ((opcode & 0xD208) == 0x8208) { // STD Y+q, Rn
					// decode
					src = (opcode & 0x01F0) >> 4;
					dst = opcode & 7;
					dst |= (opcode & 0x0C00) >> 6;
					dst |= (opcode & 0x2000) >> 8;
					// action
					avr.RAM[(avr.R[29]<<8) + avr.R[28] + dst] = avr.R[src];
				}
				else if ((opcode & 0xD208) == 0x8008) { // LDD Rn, Y+q
					// decode
					dst = (opcode & 0x01F0) >> 4;
					src = opcode & 7;
					src |= (opcode & 0x0C00) >> 6;
					src |= (opcode & 0x2000) >> 8;
					// action
					avr.R[dst] = avr.RAM[(avr.R[29]<<8) + avr.R[28] + src];
				}
				break;

			case 0x9:
				// Everything else !!
				if ((opcode & 0xFE0E) == 0x940C) { // JMP
					// decode
					jdest = ((opcode & 0x01F0) >> 3) | (opcode & 1);
					jdest <<= 16UL;
					jdest |= test[regPC+1];
					// action
					regPC = (jdest - 1); // -1 because it's incremented below
				}
				else if ((opcode & 0xFE0E) == 0x940E) { // CALL
					// decode
					jdest = ((opcode & 0x01F0) >> 3) | (opcode & 1);
					jdest <<= 16UL;
					jdest |= test[regPC+1];
					// action
					avr.rSP -= 2;
					avr.RAM[avr.rSP + 1] = (regPC + 2) & 0xFF;
					avr.RAM[avr.rSP + 2] = (regPC + 2) >> 8;
					regPC = jdest - 1;
				}
				else if (opcode == 0x9508) { // RET
					// action
					regPC = avr.RAM[avr.rSP + 1];
					regPC |= (avr.RAM[avr.rSP + 2] >>8);
					regPC--;	// simply because there's a +1 below
					avr.rSP += 2;
				}
				else if ((opcode & 0xFE0F) == 0x920F) { // PUSH
					// decode
					src = (opcode & 0x01F0) >> 4;
					// action
					avr.rSP -= 1;
					avr.RAM[avr.rSP + 1] = avr.R[src];
				}
				else if ((opcode & 0xFE0F) == 0x900F) { // POP
					// decode
					dst = (opcode & 0x01F0) >> 4;
					// action
					avr.rSP += 1;
					avr.R[dst] = avr.RAM[avr.rSP];
				}
				else if ((opcode & 0xFE0F) == 0x9400) { // COM
					// decode
					src = (opcode & 0x01F0) >> 4;
					// action
					avr.R[src] = 255 - avr.R[src];
					avr.rSREG.all = SREG;
				}
				else if ((opcode & 0xFF00) == 0x9600) { // ADIW
					int16_t * p;
					// decode
					dst = (opcode & 0x0030) >> 4;
					offset = (opcode & 0x00C0) >> 2;
					offset |= (opcode & 0x000F);
					// action
					p = (int16_t *)&avr.R[24];
					p += dst;
					*p += offset;
					avr.rSREG.all = SREG;
				}
				else if ((opcode & 0xFF00) == 0x9700) { // SBIW
					int16_t * p;
					// decode
					dst = (opcode & 0x0030) >> 4;
					offset = (opcode & 0x00C0) >> 2;
					offset |= (opcode & 0x000F);
					// action
					p = (int16_t *)&avr.R[24];
					p += dst;
					*p -= offset;
					avr.rSREG.all = SREG;
				}
				break;

			case 0xB:
				// IN, OUT
				if ((opcode & 0xF800) == 0xB800) { // OUT
					// decode
					dst = (opcode & 0x0600) >> 5;
					dst |= (opcode & 0x000F);
					src = (opcode & 0x01F0) >> 4;
					// add the IO to RAM offset
					dst += 0x20;
					// action
					avr.RAM[dst] = avr.R[src];
				}
				else { // IN
					// decode
					src = (opcode & 0x0600) >> 5;
					src |= (opcode & 0x000F);
					dst = (opcode & 0x01F0) >> 4;
					// add the IO to RAM offset
					src += 0x20;
					// action
					avr.R[dst] = avr.RAM[src];
				}
				break;

			case 0xC:
				// RJMP
				{ // RJMP
					// decode
					offset = opcode & 0x0FFF;
					if (offset & 0x0800) {
						offset = 0x1000 - offset;
						offset *= -1;
					}
					// action
					regPC = regPC + offset; // no +1 because regPC++ below
				}
				break;

			case 0xD:
				// RCALL
				{ // RCALL
					// decode
					offset = opcode & 0x0FFF;
					if (offset & 0x0800) {
						offset = 0x1000 - offset;
						offset *= -1;
					}
					// action
					avr.rSP -= 2;
					avr.RAM[avr.rSP + 1] = (regPC + 2) & 0xFF;
					avr.RAM[avr.rSP + 2] = (regPC + 2) >> 8;
					regPC = regPC + offset; // no +1 because regPC++ below
				}
				break;

			case 0xE:
				// LDI
				{ // LDI
					// decode
					dst = (opcode & 0x00F0) >> 4;
					dst += 16; // only upper 16 registers
					src = (opcode & 0x0F00) >> 4;
					src |= (opcode & 0x000F);
					// action
					avr.R[dst] = src;
				}
				break;

			case 0xF:
				// BRBS, BRBC, BLD, BST, SBRC, SBRS
				if ((opcode & 0xFC07) == 0xF400) { // BRCC
					// decode
					offset = (opcode & 0x03F8) >> 3;
					if (offset & 0x40) {
						// 2's complement if -ve
						offset = 0x80 - offset;
						offset *= -1;
					}
					// action
					if (!avr.rSREG.C) {
						regPC = regPC + offset; //+1 done by regPC++ below.
					}
				}
				else if ((opcode & 0xFC07) == 0xF000) { // BRCS
					// decode
					offset = (opcode & 0x03F8) >> 3;
					if (offset & 0x40) {
						// 2's complement if -ve
						offset = 0x80 - offset;
						offset *= -1;
					}
					// action
					if (avr.rSREG.C) {
						regPC = regPC + offset; //+1 done by regPC++ below.
					}
				}
				break;
		}
		regPC++;
	}
}

That dropped the time between COM opcodes to 47,104 cycles - almost doubling performance in one fell swoop. So on 20MHz that would get to about 200kHz now.

It's just a bugger that so many of the opcodes start with 0x9??? so I need to further split those intelligently rather than having a cascade of if/else.

=============================================================================

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

Quote:

That dropped the time between COM opcodes to 47,104 cycles

As an exercise it sounds like your aim is pretty much "straight C". With your toolchain, to get more speed I'd perhaps use small jump tables and "goto *frog;", skipping switch().

I dabbled with the "op code charts" and indeed it doesn't lend itself to an obvious straightforward decode.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Just had the maddest idea ever - build the simulator code then feed it as input to the simulator - but it's gonna need an awful lot more of the opcodes to be implemented before I could hope to run it. :? :evil:

(oh and have that simulate the simulator .. ad infinitum)

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

Quote:

build the simulator code then feed it as input to the simulator

Hmmm--the next challenge will be to do something useful. Consider the simulators in AVRStudio. Indeed it is an age-old question how to feed it with stimuli such as a UART character stream or external/pinchange interrupt. But a user would expect at the least timer interrupt flags to trigger and invoke the associated ISR.

So you need to maintain AVR peripherals...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Then you simulate your simulator simulating a larson scanner!

The largest known prime number: 282589933-1

It's easy to stop breaking the 10th commandment! Break the 8th instead. 

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

Quote:

So you need to maintain AVR peripherals...

Oh I fully intend that - and interrupts. The "host" will have ISRs for every interrupt that just pass control to the simulated code's own vector entries in the usual way. And as I said before for the SFRs apart from SPL/SPH/SREG I intend to allow read/write through to the real AVR hardware. So the simulated code should be able to start a timer with interrupts and have it caught inside the ISR - only thing is that something like a timer is going to run at 20MHz (or whatever) yet the handling code will be running 100+ times slower - so that's going to be kind of tricky!

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

Quote:
It's just a bugger that so many of the opcodes start with 0x9??? so I need to further split those intelligently rather than having a cascade of if/else.

You could make another switch statement on the second nibble(from the left) and then inside that have a limited amount of elseif checks.

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

clawson wrote:
Quote:

So you need to maintain AVR peripherals...

Oh I fully intend that - and interrupts. [...]

Then just give it a GDB server interface... :wink:

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Just an update - more opcodes done now though there's still problems with getting SREG which make me think I'll have to give in and execute each opcode with inline asm :-(

#include 
#include 

uint16_t two_word_opcode(uint16_t opcode);
uint16_t read_opcode(void);
uint16_t read_next_opcode(void);

unsigned int test[] PROGMEM = {
 0x940C, 0x002A, 0x940C, 0x0047, 0x940C, 0x0047, 0x940C, 0x0047,
 0x940C, 0x0047, 0x940C, 0x0047, 0x940C, 0x0047, 0x940C, 0x0047,
 0x940C, 0x0047, 0x940C, 0x0047, 0x940C, 0x0047, 0x940C, 0x0047,
 0x940C, 0x0047, 0x940C, 0x0047, 0x940C, 0x0047, 0x940C, 0x0047,
 0x940C, 0x0047, 0x940C, 0x0047, 0x940C, 0x0047, 0x940C, 0x0047,
 0x940C, 0x0047, 0x2411, 0xBE1F, 0xE5CF, 0xE0D8, 0xBFDE, 0xBFCD,
 0xE011, 0xE6A0, 0xE0B0, 0xE8EE, 0xE0FD, 0xC002, 0x9005, 0x920D,
 0x31A0, 0x07B1, 0xF7D9, 0xE015, 0xE1A0, 0xE0B1, 0xC001, 0x921D,
 0x38A0, 0x07B1, 0xF7E1, 0x940E, 0x0049, 0x940C, 0x06C5, 0x940C,
 0x0000, 0x92EF, 0x92FF, 0x930F, 0x931F, 0x93CF, 0x93DF, 0x9210,
 0x0578, 0x9210, 0x0579, 0x9210, 0x057A, 0x9210, 0x057B, 0x9210,
 0x057D, 0x9210, 0x057C, 0xED2D, 0xC010, 0x91E0, 0x057C, 0x91F0,
 0x057D, 0x5FE0, 0x4FFE, 0xA320, 0x9180, 0x057C, 0x9190, 0x057D,
 0x9601, 0x9390, 0x057D, 0x9380, 0x057C, 0x9180, 0x057C, 0x9190,
 0x057D, 0x97CD, 0xF354, 0x9210, 0x057D, 0x9210, 0x057C, 0xEE2E,
 0xC010, 0x91E0, 0x057C, 0x91F0, 0x057D, 0x59E0, 0x4FFE, 0x8320,
 0x9180, 0x057C, 0x9190, 0x057D, 0x9601, 0x9390, 0x057D, 0x9380,
 0x057C, 0x9180, 0x057C, 0x9190, 0x057D, 0x5080, 0x4094, 0xF34C,
 0xE000, 0xE110, 0xE8F0, 0x2EEF, 0x2CF1, 0x9180, 0x0578, 0x9190,
 0x0579, 0x91A0, 0x057A, 0x91B0, 0x057B, 0x0F88, 0x1F99, 0x5A80,
 0x4F9F, 0x01FC, 0x8180, 0x8191, 0x9390, 0x0577, 0x9380, 0x0576,
 0x9180, 0x0576, 0x9190, 0x0577, 0x2F89, 0x2799, 0x9582, 0x708F,
 0x3089, 0x0591, 0xF409, 0xC1CC, 0x308A, 0x0591, 0xF4A8, 0x3084,
 0x0591, 0xF409, 0xC075, 0x3085, 0x0591, 0xF430, 0x9700, 0xF139,
 0x9702, 0xF009, 0xC5EE, 0xC028, 0x3085, 0x0591, 0xF409, 0xC0B7,
 0x9708, 0xF009, 0xC5E6, 0xC0FB, 0x308C, 0x0591, 0xF409, 0xC495,
 0x308D, 0x0591, 0xF440, 0x308A, 0x0591, 0xF409, 0xC0F0, 0x970B,
 0xF009, 0xC5D7, 0xC403, 0x308E, 0x0591, 0xF409, 0xC501, 0x308E,
 0x0591, 0xF408, 0xC4A8, 0x970F, 0xF009, 0xC5CB, 0xC53C, 0x9180,
 0x0576, 0x9190, 0x0577, 0xC5C5, 0x9180, 0x0576, 0x9190, 0x0577,
 0x7080, 0x7F9C, 0x5080, 0x4294, 0xF009, 0xC5BB, 0x9120, 0x0576,
 0x9130, 0x0577, 0x9180, 0x0576, 0x9190, 0x0577, 0x7080, 0x7092,
 0xE065, 0x9596, 0x9587, 0x956A, 0xF7E1, 0x702F, 0x7030, 0x2B82,
 0x2B93, 0x9390, 0x057F, 0x9380, 0x057E, 0x9180, 0x0576, 0x9190,
 0x0577, 0x7F80, 0x7091, 0xE054, 0x9596, 0x9587, 0x955A, 0xF7E1,
 0x9390, 0x0575, 0x9380, 0x0574, 0x91A0, 0x0574, 0x91B0, 0x0575,
 0x91E0, 0x057E, 0x91F0, 0x057F, 0x5FE0, 0x4FFE, 0x8190, 0x91E0,
 0x0574, 0x91F0, 0x0575, 0x5FE0, 0x4FFE, 0x8180, 0x2789, 0xC31B,
 0x9180, 0x0576, 0x9190, 0x0577, 0x7F80, 0x7090, 0xE044, 0x9596,
 0x9587, 0x954A, 0xF7E1, 0x9390, 0x0575, 0x9380, 0x0574, 0x9180,
 0x0574, 0x9190, 0x0575, 0x9640, 0x9390, 0x0575, 0x9380, 0x0574,
 0x9180, 0x0576, 0x9190, 0x0577, 0x7080, 0x709F, 0x2F89, 0x2799,
 0x9390, 0x057F, 0x9380, 0x057E, 0x9120, 0x057E, 0x9130, 0x057F,
 0x9180, 0x0576, 0x9190, 0x0577, 0x708F, 0x7090, 0x2B82, 0x2B93,
 0x9390, 0x057F, 0x9380, 0x057E, 0x91A0, 0x0574, 0x91B0, 0x0575,
 0x91E0, 0x0574, 0x91F0, 0x0575, 0x5FE0, 0x4FFE, 0x8190, 0x9120,
 0x057E, 0x9130, 0x057F, 0x9180, 0x016F, 0x7081, 0x1B92, 0x1B98,
 0x5FA0, 0x4FBE, 0x939C, 0xB78F, 0x9380, 0x016F, 0xC532, 0x9180,
 0x0576, 0x9190, 0x0577, 0x7F80, 0x7090, 0xE024, 0x9596, 0x9587,
 0x952A, 0xF7E1, 0x9390, 0x0575, 0x9380, 0x0574, 0x9180, 0x0574,
 0x9190, 0x0575, 0x9640, 0x9390, 0x0575, 0x9380, 0x0574, 0x9180,
 0x0576, 0x9190, 0x0577, 0x7080, 0x709F, 0x2F89, 0x2799, 0x9390,
 0x057F, 0x9380, 0x057E, 0x9120, 0x057E, 0x9130, 0x057F, 0x9180,
 0x0576, 0x9190, 0x0577, 0x708F, 0x7090, 0x2B82, 0x2B93, 0x9390,
 0x057F, 0x9380, 0x057E, 0x91A0, 0x0574, 0x91B0, 0x0575, 0x91E0,
 0x0574, 0x91F0, 0x0575, 0x5FE0, 0x4FFE, 0x8120, 0x9180, 0x057E,
 0x9190, 0x057F, 0x1B28, 0x5FA0, 0x4FBE, 0x932C, 0xCFB4, 0x9180,
 0x0576, 0x9190, 0x0577, 0x7088, 0x7D92, 0x5088, 0x4892, 0xF009,
 0xC051, 0x0000, 0x9180, 0x0576, 0x9190, 0x0577, 0x7F80, 0x7091,
 0xE0A4, 0x9596, 0x9587, 0x95AA, 0xF7E1, 0x9390, 0x057F, 0x9380,
 0x057E, 0x9180, 0x0576, 0x9190, 0x0577, 0x7087, 0x7090, 0x9390,
 0x0575, 0x9380, 0x0574, 0x9120, 0x0574, 0x9130, 0x0575, 0x9180,
 0x0576, 0x9190, 0x0577, 0x7080, 0x709C, 0xE0F6, 0x9596, 0x9587,
 0x95FA, 0xF7E1, 0x2B82, 0x2B93, 0x9390, 0x0575, 0x9380, 0x0574,
 0x9120, 0x0574, 0x9130, 0x0575, 0x9180, 0x0576, 0x9190, 0x0577,
 0x7080, 0x7290, 0x2F89, 0x2799, 0x2B82, 0x2B93, 0x9390, 0x0575,
 0x9380, 0x0574, 0x9120, 0x012D, 0x9130, 0x012C, 0x9180, 0x0574,
 0x9190, 0x0575, 0x2F72, 0xE060, 0x01DB, 0x0FA3, 0x1DB1, 0x0FA8,
 0x1FB9, 0xC338, 0x9180, 0x0576, 0x9190, 0x0577, 0x7088, 0x7D92,
 0x5088, 0x4890, 0xF009, 0xC485, 0x9180, 0x0576, 0x9190, 0x0577,
 0x7F80, 0x7091, 0xE074, 0x9596, 0x9587, 0x957A, 0xF7E1, 0x9390,
 0x0575, 0x9380, 0x0574, 0x9180, 0x0576, 0x9190, 0x0577, 0x7087,
 0x7090, 0x9390, 0x057F, 0x9380, 0x057E, 0x9120, 0x057E, 0x9130,
 0x057F, 0x9180, 0x0576, 0x9190, 0x0577, 0x7080, 0x709C, 0xE066,
 0x9596, 0x9587, 0x956A, 0xF7E1, 0x2B82, 0x2B93, 0x9390, 0x057F,
 0x9380, 0x057E, 0x9120, 0x057E, 0x9130, 0x057F, 0x9180, 0x0576,
 0x9190, 0x0577, 0x7080, 0x7290, 0x2F89, 0x2799, 0x2B82, 0x2B93,
 0x9390, 0x057F, 0x9380, 0x057E, 0x91A0, 0x0574, 0x91B0, 0x0575,
 0x9120, 0x012D, 0x9130, 0x012C, 0x9180, 0x057E, 0x9190, 0x057F,
 0x2FD2, 0xE0C0, 0x01FE, 0x0FE3, 0x1DF1, 0x0FE8, 0x1FF9, 0xC2DE,
 0x9180, 0x0576, 0x9190, 0x0577, 0x708E, 0x7F9E, 0x508C, 0x4994,
 0xF009, 0xC059, 0x9180, 0x0576, 0x9190, 0x0577, 0x9120, 0x0576,
 0x9130, 0x0577, 0x7F80, 0x7091, 0xE043, 0x9596, 0x9587, 0x954A,
 0xF7E1, 0x7021, 0x7030, 0x2B82, 0x2B93, 0xE0A0, 0xE0B0, 0x9380,
 0x0570, 0x9390, 0x0571, 0x93A0, 0x0572, 0x93B0, 0x0573, 0x9180,
 0x0570, 0x9190, 0x0571, 0x91A0, 0x0572, 0x91B0, 0x0573, 0x01DC,
 0x2799, 0x2788, 0x9380, 0x0570, 0x9390, 0x0571, 0x93A0, 0x0572,
 0x93B0, 0x0573, 0x9120, 0x0570, 0x9130, 0x0571, 0x9140, 0x0572,
 0x9150, 0x0573, 0x9180, 0x0578, 0x9190, 0x0579, 0x91A0, 0x057A,
 0x91B0, 0x057B, 0x9601, 0x1DA1, 0x1DB1, 0x0F88, 0x1F99, 0x5A80,
 0x4F9F, 0x01FC, 0x8180, 0x8191, 0xE0A0, 0xE0B0, 0x2B82, 0x2B93,
 0x2BA4, 0x2BB5, 0x9380, 0x0570, 0x9390, 0x0571, 0x93A0, 0x0572,
 0x93B0, 0x0573, 0xC091, 0x9180, 0x0576, 0x9190, 0x0577, 0x708E,
 0x7F9E, 0x508E, 0x4994, 0xF009, 0xC093, 0x9180, 0x0576, 0x9190,
 0x0577, 0x9120, 0x0576, 0x9130, 0x0577, 0x7F80, 0x7091, 0xE0E3,
 0x9596, 0x9587, 0x95EA, 0xF7E1, 0x7021, 0x7030, 0x2B82, 0x2B93,
 0xE0A0, 0xE0B0, 0x9380, 0x0570, 0x9390, 0x0571, 0x93A0, 0x0572,
 0x93B0, 0x0573, 0x9180, 0x0570, 0x9190, 0x0571, 0x91A0, 0x0572,
 0x91B0, 0x0573, 0x01DC, 0x2799, 0x2788, 0x9380, 0x0570, 0x9390,
 0x0571, 0x93A0, 0x0572, 0x93B0, 0x0573, 0x9120, 0x0570, 0x9130,
 0x0571, 0x9140, 0x0572, 0x9150, 0x0573, 0x9180, 0x0578, 0x9190,
 0x0579, 0x91A0, 0x057A, 0x91B0, 0x057B, 0x9601, 0x1DA1, 0x1DB1,
 0x0F88, 0x1F99, 0x5A80, 0x4F9F, 0x01FC, 0x8180, 0x8191, 0xE0A0,
 0xE0B0, 0x2B82, 0x2B93, 0x2BA4, 0x2BB5, 0x9380, 0x0570, 0x9390,
 0x0571, 0x93A0, 0x0572, 0x93B0, 0x0573, 0x9180, 0x016D, 0x9190,
 0x016E, 0x9702, 0x9390, 0x016E, 0x9380, 0x016D, 0x91E0, 0x016D,
 0x91F0, 0x016E, 0x9180, 0x0578, 0x9190, 0x0579, 0x91A0, 0x057A,
 0x91B0, 0x057B, 0x5F8E, 0x5EEF, 0x4FFE, 0x8380, 0x91E0, 0x016D,
 0x91F0, 0x016E, 0x9180, 0x0578, 0x9190, 0x0579, 0x91A0, 0x057A,
 0x91B0, 0x057B, 0x9602, 0x1DA1, 0x1DB1, 0x2F89, 0x2F9A, 0x2FAB,
 0x27BB, 0x5EEE, 0x4FFE, 0x8380, 0x9180, 0x0570, 0x9190, 0x0571,
 0x91A0, 0x0572, 0x91B0, 0x0573, 0x9701, 0x09A1, 0x09B1, 0xC329,
 0x9180, 0x0576, 0x9190, 0x0577, 0x5088, 0x4995, 0xF009, 0xC046,
 0x91E0, 0x016D, 0x91F0, 0x016E, 0x5EEF, 0x4FFE, 0x8180, 0xE090,
 0xE0A0, 0xE0B0, 0x9380, 0x0578, 0x9390, 0x0579, 0x93A0, 0x057A,
 0x93B0, 0x057B, 0x9180, 0x0578, 0x9190, 0x0579, 0x91A0, 0x057A,
 0x91B0, 0x057B, 0x91E0, 0x016D, 0x91F0, 0x016E, 0x5EEE, 0x4FFE,
 0x81E0, 0x9380, 0x0578, 0x9390, 0x0579, 0x93A0, 0x057A, 0x93B0,
 0x057B, 0x9180, 0x0578, 0x9190, 0x0579, 0x91A0, 0x057A, 0x91B0,
 0x057B, 0x9701, 0x09A1, 0x09B1, 0x9380, 0x0578, 0x9390, 0x0579,
 0x93A0, 0x057A, 0x93B0, 0x057B, 0x9180, 0x016D, 0x9190, 0x016E,
 0x9602, 0x9390, 0x016E, 0x9380, 0x016D, 0xC2E3, 0x9180, 0x0576,
 0x9190, 0x0577, 0x708F, 0x7F9E, 0x508F, 0x4992, 0xF531, 0x9180,
 0x0576, 0x9190, 0x0577, 0x7F80, 0x7091, 0xE0F4, 0x9596, 0x9587,
 0x95FA, 0xF7E1, 0x9390, 0x057F, 0x9380, 0x057E, 0x9180, 0x016D,
 0x9190, 0x016E, 0x9701, 0x9390, 0x016E, 0x9380, 0x016D, 0x91A0,
 0x016D, 0x91B0, 0x016E, 0x91E0, 0x057E, 0x91F0, 0x057F, 0x5FE0,
 0x4FFE, 0x8180, 0x5EAF, 0x4FBE, 0xC166, 0x9180, 0x0576, 0x9190,
 0x0577, 0x708F, 0x7F9E, 0x508F, 0x4990, 0xF509, 0x9180, 0x0576,
 0x9190, 0x0577, 0x7F80, 0x7091, 0xE0E4, 0x9596, 0x9587, 0x95EA,
 0xF7E1, 0x9390, 0x0575, 0x9380, 0x0574, 0x9180, 0x016D, 0x9190,
 0x016E, 0x9601, 0x9390, 0x016E, 0x9380, 0x016D, 0x91A0, 0x0574,
 0x91B0, 0x0575, 0x91E0, 0x016D, 0x91F0, 0x016E, 0xC137, 0x9180,
 0x0576, 0x9190, 0x0577, 0x708F, 0x7F9E, 0x5080, 0x4994, 0xF4F9,
 0x9180, 0x0576, 0x9190, 0x0577, 0x7F80, 0x7091, 0xE074, 0x9596,
 0x9587, 0x957A, 0xF7E1, 0x9390, 0x057F, 0x9380, 0x057E, 0x91A0,
 0x057E, 0x91B0, 0x057F, 0x91E0, 0x057E, 0x91F0, 0x057F, 0x5FE0,
 0x4FFE, 0x8180, 0x9580, 0x5FA0, 0x4FBE, 0x938C, 0xCD2C, 0x9180,
 0x0576, 0x9190, 0x0577, 0x7080, 0x5080, 0x4996, 0xF5F1, 0x9180,
 0x0576, 0x9190, 0x0577, 0x7380, 0x7090, 0xE064, 0x9596, 0x9587,
 0x956A, 0xF7E1, 0x9390, 0x0575, 0x9380, 0x0574, 0x9180, 0x0576,
 0x9190, 0x0577, 0x7C80, 0x7090, 0x9596, 0x9587, 0x9596, 0x9587,
 0x9390, 0x057D, 0x9380, 0x057C, 0x9120, 0x057C, 0x9130, 0x057D,
 0x9180, 0x0576, 0x9190, 0x0577, 0x708F, 0x7090, 0x2B82, 0x2B93,
 0x9390, 0x057D, 0x9380, 0x057C, 0x91E0, 0x0574, 0x91F0, 0x0575,
 0x0FEE, 0x1FFF, 0x5DE8, 0x4FFE, 0x9120, 0x057C, 0x9130, 0x057D,
 0x8180, 0x8191, 0x0F82, 0x1F93, 0xC046, 0x9180, 0x0576, 0x9190,
 0x0577, 0x7080, 0x5080, 0x4997, 0xF009, 0xC213, 0x9180, 0x0576,
 0x9190, 0x0577, 0x7380, 0x7090, 0xE034, 0x9596, 0x9587, 0x953A,
 0xF7E1, 0x9390, 0x0575, 0x9380, 0x0574, 0x9180, 0x0576, 0x9190,
 0x0577, 0x7C80, 0x7090, 0x9596, 0x9587, 0x9596, 0x9587, 0x9390,
 0x057D, 0x9380, 0x057C, 0x9120, 0x057C, 0x9130, 0x057D, 0x9180,
 0x0576, 0x9190, 0x0577, 0x708F, 0x7090, 0x2B82, 0x2B93, 0x9390,
 0x057D, 0x9380, 0x057C, 0x91E0, 0x0574, 0x91F0, 0x0575, 0x0FEE,
 0x1FFF, 0x5DE8, 0x4FFE, 0x9120, 0x057C, 0x9130, 0x057D, 0x8180,
 0x8191, 0x1B82, 0x0B93, 0x8391, 0x8380, 0xCC9D, 0x9180, 0x0576,
 0x9190, 0x0577, 0x7080, 0x7F98, 0x5080, 0x4B98, 0xF5C1, 0x9180,
 0x0576, 0x9190, 0x0577, 0x7080, 0x7096, 0xE0A5, 0x9596, 0x9587,
 0x95AA, 0xF7E1, 0x9390, 0x0575, 0x9380, 0x0574, 0x9120, 0x0574,
 0x9130, 0x0575, 0x9180, 0x0576, 0x9190, 0x0577, 0x708F, 0x7090,
 0x2B82, 0x2B93, 0x9390, 0x0575, 0x9380, 0x0574, 0x9180, 0x0576,
 0x9190, 0x0577, 0x7F80, 0x7091, 0xE0F4, 0x9596, 0x9587, 0x95FA,
 0xF7E1, 0x9390, 0x057F, 0x9380, 0x057E, 0x9180, 0x0574, 0x9190,
 0x0575, 0x9680, 0x9390, 0x0575, 0x9380, 0x0574, 0xC037, 0x9180,
 0x0576, 0x9190, 0x0577, 0x7080, 0x7096, 0xE0E5, 0x9596, 0x9587,
 0x95EA, 0xF7E1, 0x9390, 0x057F, 0x9380, 0x057E, 0x9120, 0x057E,
 0x9130, 0x057F, 0x9180, 0x0576, 0x9190, 0x0577, 0x708F, 0x7090,
 0x2B82, 0x2B93, 0x9390, 0x057F, 0x9380, 0x057E, 0x9180, 0x0576,
 0x9190, 0x0577, 0x7F80, 0x7091, 0xE074, 0x9596, 0x9587, 0x957A,
 0xF7E1, 0x9390, 0x0575, 0x9380, 0x0574, 0x9180, 0x057E, 0x9190,
 0x057F, 0x9680, 0x9390, 0x057F, 0x9380, 0x057E, 0x91A0, 0x0574,
 0x91B0, 0x0575, 0x91E0, 0x057E, 0x91F0, 0x057F, 0x5FE0, 0x4FFE,
 0x8180, 0x5FA0, 0x4FBE, 0x938C, 0xC14C, 0x9180, 0x0576, 0x9190,
 0x0577, 0x709F, 0x9390, 0x057D, 0x9380, 0x057C, 0x9180, 0x057C,
 0x9190, 0x057D, 0xFF93, 0xC121, 0x9180, 0x057C, 0x9190, 0x057D,
 0x01F8, 0x1BE8, 0x0BF9, 0x93F0, 0x057D, 0x93E0, 0x057C, 0x9180,
 0x057C, 0x9190, 0x057D, 0x9590, 0x9581, 0x4F9F, 0x9390, 0x057D,
 0x9380, 0x057C, 0xC10A, 0x9180, 0x0576, 0x9190, 0x0577, 0x709F,
 0x9390, 0x057D, 0x9380, 0x057C, 0x9180, 0x057C, 0x9190, 0x057D,
 0xFF93, 0xC016, 0x9180, 0x057C, 0x9190, 0x057D, 0x0198, 0x1B28,
 0x0B39, 0x9330, 0x057D, 0x9320, 0x057C, 0x9180, 0x057C, 0x9190,
 0x057D, 0x9590, 0x9581, 0x4F9F, 0x9390, 0x057D, 0x9380, 0x057C,
 0x9180, 0x016D, 0x9190, 0x016E, 0x9702, 0x9390, 0x016E, 0x9380,
 0x016D, 0x91E0, 0x016D, 0x91F0, 0x016E, 0x9180, 0x0578, 0x9190,
 0x0579, 0x91A0, 0x057A, 0x91B0, 0x057B, 0x5F8E, 0x5EEF, 0x4FFE,
 0x8380, 0x91E0, 0x016D, 0x91F0, 0x016E, 0x9180, 0x0578, 0x9190,
 0x0579, 0x91A0, 0x057A, 0x91B0, 0x057B, 0x9602, 0x1DA1, 0x1DB1,
 0x2F89, 0x2F9A, 0x2FAB, 0x27BB, 0x5EEE, 0x4FFE, 0x8380, 0xC0B5,
 0x9180, 0x0576, 0x9190, 0x0577, 0x7F80, 0x7090, 0xE054, 0x9596,
 0x9587, 0x955A, 0xF7E1, 0x9390, 0x0575, 0x9380, 0x0574, 0x9180,
 0x0574, 0x9190, 0x0575, 0x9640, 0x9390, 0x0575, 0x9380, 0x0574,
 0x9180, 0x0576, 0x9190, 0x0577, 0x7080, 0x709F, 0xE044, 0x9596,
 0x9587, 0x954A, 0xF7E1, 0x9390, 0x057F, 0x9380, 0x057E, 0x9120,
 0x057E, 0x9130, 0x057F, 0x9180, 0x0576, 0x9190, 0x0577, 0x708F,
 0x7090, 0x2B82, 0x2B93, 0x9390, 0x057F, 0x9380, 0x057E, 0x91E0,
 0x0574, 0x91F0, 0x0575, 0x9180, 0x057E, 0x9190, 0x057F, 0x5FE0,
 0x4FFE, 0x8380, 0xC08E, 0x9180, 0x0576, 0x9190, 0x0577, 0x7087,
 0x7F9C, 0x5080, 0x4F94, 0xF581, 0x9180, 0x0576, 0x9190, 0x0577,
 0x7F88, 0x7093, 0xE033, 0x9596, 0x9587, 0x953A, 0xF7E1, 0x9390,
 0x057D, 0x9380, 0x057C, 0x9180, 0x057C, 0x9190, 0x057D, 0xFF86,
 0xC016, 0x9180, 0x057C, 0x9190, 0x057D, 0x01F7, 0x1BE8, 0x0BF9,
 0x93F0, 0x057D, 0x93E0, 0x057C, 0x9180, 0x057C, 0x9190, 0x057D,
 0x9590, 0x9581, 0x4F9F, 0x9390, 0x057D, 0x9380, 0x057C, 0x9180,
 0x016F, 0xFD80, 0xC056, 0xC039, 0x9180, 0x0576, 0x9190, 0x0577,
 0x7087, 0x7F9C, 0x5080, 0x4F90, 0xF009, 0xC04B, 0x9180, 0x0576,
 0x9190, 0x0577, 0x7F88, 0x7093, 0xE023, 0x9596, 0x9587, 0x952A,
 0xF7E1, 0x9390, 0x057D, 0x9380, 0x057C, 0x9180, 0x057C, 0x9190,
 0x057D, 0xFF86, 0xC016, 0x9180, 0x057C, 0x9190, 0x057D, 0x0197,
 0x1B28, 0x0B39, 0x9330, 0x057D, 0x9320, 0x057C, 0x9180, 0x057C,
 0x9190, 0x057D, 0x9590, 0x9581, 0x4F9F, 0x9390, 0x057D, 0x9380,
 0x057C, 0x9180, 0x016F, 0xFF80, 0xC01C, 0x9120, 0x0578, 0x9130,
 0x0579, 0x9140, 0x057A, 0x9150, 0x057B, 0x9180, 0x057C, 0x9190,
 0x057D, 0x27AA, 0xFD97, 0x95A0, 0x2FBA, 0x0F82, 0x1F93, 0x1FA4,
 0x1FB5, 0x9380, 0x0578, 0x9390, 0x0579, 0x93A0, 0x057A, 0x93B0,
 0x057B, 0x9180, 0x0578, 0x9190, 0x0579, 0x91A0, 0x057A, 0x91B0,
 0x057B, 0x9601, 0x1DA1, 0x1DB1, 0x9380, 0x0578, 0x9390, 0x0579,
 0x93A0, 0x057A, 0x93B0, 0x057B, 0xC9D0, 0x94F8, 0xCFFF, 0x940C,
 0x002A, 0x940C, 0x0034, 0x940C, 0x0034, 0x940C, 0x0034, 0x940C,
 0x0034, 0x940C, 0x0034, 0x940C, 0x0034, 0x940C, 0x0034, 0x940C,
 0x0034, 0x940C, 0x0034, 0x940C, 0x0034, 0x940C, 0x0034, 0x940C,
 0x0034, 0x940C, 0x0034, 0x940C, 0x0034, 0x940C, 0x0034, 0x940C,
 0x0034, 0x940C, 0x0034, 0x940C, 0x0034, 0x940C, 0x0034, 0x940C,
 0x0034, 0x2411, 0xBE1F, 0xE5CF, 0xE0D4, 0xBFDE, 0xBFCD, 0x940E,
 0x004C, 0x940C, 0x0056, 0x940C, 0x0000, 0x93DF, 0x93CF, 0xD000,
 0xB7CD, 0xB7DE, 0x821A, 0x8219, 0xC005, 0x8189, 0x819A, 0x9601,
 0x839A, 0x8389, 0x8189, 0x819A, 0x9747, 0xF3B8, 0x900F, 0x900F,
 0x91CF, 0x91DF, 0x9508, 0xEF8F, 0xBB87, 0xEA8A, 0xBB88, 0xB388,
 0x9580, 0xBB88, 0x940E, 0x0036, 0xCFFA, 0x94F8, 0xCFFF
};


volatile uint32_t regPC;
volatile uint16_t opcode;
volatile uint32_t jdest;
volatile uint16_t src, dst;
volatile int16_t offset;

// The AVR memory...
volatile union {
	struct {
		uint8_t R[32];
		uint8_t SFR[61];
		union {
			struct {
				uint8_t rSPL;
				uint8_t rSPH;
			};
			uint16_t rSP;
		};
		union {
			struct {
				uint8_t C:1;
				uint8_t Z:1;
				uint8_t N:1;
				uint8_t V:1;
				uint8_t S:1;
				uint8_t H:1;
				uint8_t T:1;
				uint8_t I:1;
			};
			uint8_t all;
		} rSREG;
		uint8_t ram2[1024];
	};
	uint8_t RAM[32 + 64 + 1024];
} avr;

int main(void) {
	regPC = 0;
	for (offset = 0; offset < 61; offset++) {
		avr.SFR[offset] = 0xDD;
	}
	for (offset = 0; offset < 1024; offset++) {
		avr.ram2[offset] = 0xEE;
	}
	while (1) {
		opcode = read_opcode();
		switch(opcode >> 12) {

			case 0x0:
				// NOP, MOVW, MULS, MULSU, FMUL, FMULS, FMULSU, CPC, SBC, ADD = LSL

				// 0000 0000 0000 0000   nop
				if (opcode == 0x0000) { // NOP
					// do nowt
				}

				// 0000 0001 dddd rrrr   movw    v,v
				else if ((opcode & 0x0F00) == 0x0100) { // MOVW
					dst = (opcode & 0x00F0) >> 4;
					dst *= 2; // they go up in pairs
					src = (opcode & 0x000F);
					src *= 2;
					avr.R[dst+1] = avr.R[src+1];
					avr.R[dst] = avr.R[src];
				}

				// 0000 0010 dddd rrrr   muls    d,d
				else if ((opcode & 0x0F00) == 0x0200) { // MULS
					// @@@
				}

				// 0000 0011 0ddd 0rrr   mulsu   a,a
				else if ((opcode & 0x0F88) == 0x0300) { // MULSU
					// @@@
				}

				// 0000 0011 0ddd 1rrr   fmul    a,a
				else if ((opcode & 0x0F88) == 0x0308) { // FMUL
					// @@@
				}

				// 0000 0011 1ddd 0rrr   fmuls   a,a
				else if ((opcode & 0x0F88) == 0x0380) { // FMULS
					// @@@
				}

				// 0000 0011 1ddd 1rrr   fmulsu  a,a
				else if ((opcode & 0x0F88) == 0x0388) { // FMULSU
					// @@@
				}

				// 0000 01rd dddd rrrr   cpc     r,r
				else if ((opcode & 0x0C00) == 0x0400) { // CPC
					uint8_t tmp;
					dst = (opcode & 0x01F0) >> 4;
					src = (opcode & 0x000F) | ((opcode & 0x0200) >> 5);
					tmp = avr.R[dst] - avr.R[src] - avr.rSREG.C;
					avr.rSREG.all = SREG;
				}

				// 0000 10rd dddd rrrr   sbc     r,r
				else if ((opcode & 0x0C00) == 0x0800) { // SBC
					dst = (opcode & 0x01F0) >> 4;
					src = (opcode & 0x000F) | ((opcode & 0x0200) >> 5);
					avr.R[dst] = avr.R[dst] - avr.R[src] - avr.rSREG.C;
					avr.rSREG.all = SREG;
				}

				// 0000 11rd dddd rrrr   add     r,r
				//         =             lsl     r
				else if ((opcode & 0x0C00) == 0x0C00) { // ADD = LSL
					dst = (opcode & 0x01F0) >> 4;
					src = (opcode & 0x000F) | ((opcode & 0x0200) >> 5);
					avr.R[dst] = avr.R[dst] + avr.R[src];
					avr.rSREG.all = SREG;
				}
				break;

			case 0x1:
				// CPSE, CP, SUB, ADC = ROL

				// 0001 00rd dddd rrrr   cpse    r,r
				if ((opcode & 0x0C00) == 0x0000) { // CPSE
					uint8_t tmp;
					dst = (opcode & 0x01F0) >> 4;
					src = (opcode & 0x000F) | ((opcode & 0x0200) >> 5);
					tmp = avr.R[dst] - avr.R[src];
					if (tmp == 0) {
						// this is tricky as need to know width of next instruction!
						if (two_word_opcode(read_next_opcode())) {
							regPC += 2;
						}
						else {
							regPC++;
						}
					}
				}

				// 0001 01rd dddd rrrr   cp      r,r
				if ((opcode & 0x0C00) == 0x0400) { // CP
					uint8_t tmp;
					dst = (opcode & 0x01F0) >> 4;
					src = (opcode & 0x000F) | ((opcode & 0x0200) >> 5);
					tmp = avr.R[dst] - avr.R[src];
					avr.rSREG.all = SREG;
				}

				// 0001 10rd dddd rrrr   sub     r,r
				if ((opcode & 0x0C00) == 0x0800) { // SUB
					dst = (opcode & 0x01F0) >> 4;
					src = (opcode & 0x000F) | ((opcode & 0x0200) >> 5);
					avr.R[dst] = avr.R[dst] - avr.R[src];
					avr.rSREG.all = SREG;
				}

				// 0001 11rd dddd rrrr   adc     r,r
				//         =             rol     r
				if ((opcode & 0x0C00) == 0x0C00) { // ADC = ROL
					dst = (opcode & 0x01F0) >> 4;
					src = (opcode & 0x000F) | ((opcode & 0x0200) >> 5);
					avr.R[dst] = avr.R[dst] + avr.R[src] + avr.rSREG.C;
					avr.rSREG.all = SREG;
				}
				break;

			case 0x2:
				// AND, EOR, OR, MOV

				// 0010 00rd dddd rrrr   and     r,r
				//         =             tst     r
				if ((opcode & 0xFC00) == 0x2000) { // AND = TST
					// decode
					src = (opcode & 0x000F) | ((opcode & 0x0200) >> 5);
					dst = (opcode & 0x01F0) >> 4;
					// action
					avr.R[dst] = avr.R[src] & avr.R[dst];
					avr.rSREG.all = SREG;
				}

				// 0010 01rd dddd rrrr   eor     r,r
				//         =             clr     r
				if ((opcode & 0xFC00) == 0x2400) { // EOR = CLR
					// decode
					src = (opcode & 0x000F) | ((opcode & 0x0200) >> 5);
					dst = (opcode & 0x01F0) >> 4;
					// action
					avr.R[dst] = avr.R[src] ^ avr.R[dst];
					avr.rSREG.all = SREG;
				}

				// 0010 10rd dddd rrrr   or      r,r
				if ((opcode & 0xFC00) == 0x2800) { // OR
					// decode
					src = (opcode & 0x000F) | ((opcode & 0x0200) >> 5);
					dst = (opcode & 0x01F0) >> 4;
					// action
					avr.R[dst] = avr.R[src] | avr.R[dst];
					avr.rSREG.all = SREG;
				}

				// 0010 11rd dddd rrrr   mov     r,r
				if ((opcode & 0xFC00) == 0x2C00) { // MOV
					// decode
					src = (opcode & 0x000F) | ((opcode & 0x0200) >> 5);
					dst = (opcode & 0x01F0) >> 4;
					// action
					avr.R[dst] = avr.R[src];
				}
				break;

			case 0x3:
				// CPI
				// 0011 KKKK dddd KKKK   cpi     d,M
				// decode
				src = (opcode & 0x00F0) >> 4;
				dst = (opcode & 0x0F00) >> 4;
				dst |= (opcode & 0x000F);
				uint8_t tmp;
				// action
				tmp = avr.R[src] - dst;
				avr.rSREG.all = SREG;
				break;

			case 0x4:
				// SBCI
				// 0100 KKKK dddd KKKK   sbci    d,M
				// decode
				dst = (opcode & 0x00F0) >> 4;
				dst += 16; // offset to R16..R32
				src = (opcode & 0x0F00) >> 8;
				src |= (opcode & 0x000F);
				// action
				avr.R[dst] = avr.R[dst] - src - avr.rSREG.C;
				avr.rSREG.all = SREG;
				break;

			case 0x5:
				// SUBI
				// 0101 KKKK dddd KKKK   subi    d,M
				// decode
				dst = (opcode & 0x00F0) >> 4;
				dst += 16; // offset to R16..R32
				src = (opcode & 0x0F00) >> 8;
				src |= (opcode & 0x000F);
				// action
				avr.R[dst] = avr.R[dst] - src;
				avr.rSREG.all = SREG;
				break;

			case 0x6:
				// ORI = SBR
				// 0110 KKKK dddd KKKK   ori     d,M
				//         =             sbr     d,M
				// decode
				dst = (opcode & 0x00F0) >> 4;
				dst += 16; // offset to R16..R32
				src = (opcode & 0x0F00) >> 8;
				src |= (opcode & 0x000F);
				// action
				avr.R[dst] = avr.R[dst] | src;
				avr.rSREG.all = SREG;
				break;

			case 0x7:
				// ANDI = CBR
				// 0111 KKKK dddd KKKK   andi    d,M
				//         =             cbr     d,n
				// decode
				dst = (opcode & 0x00F0) >> 4;
				dst += 16; // offset to R16..R32
				src = (opcode & 0x0F00) >> 8;
				src |= (opcode & 0x000F);
				// action
				avr.R[dst] = avr.R[dst] & src;
				avr.rSREG.all = SREG;
				break;

			case 0x8:
			case 0xA:
				// LD Z, LD Y, LDD Y, LDD Z, STD Y, STD Z

				// 
				if ((opcode & 0xD208) == 0x8208) { // STD Y+q, Rn
					// decode
					asm("nop\n");
					src = (opcode & 0x01F0) >> 4;
					dst = opcode & 7;
					dst |= (opcode & 0x0C00) >> 6;
					dst |= (opcode & 0x2000) >> 8;
					// action
					avr.RAM[(avr.R[29]<<8) + avr.R[28] + dst] = avr.R[src];
				}
				else if ((opcode & 0xD208) == 0x8008) { // LDD Rn, Y+q
					// decode
					dst = (opcode & 0x01F0) >> 4;
					src = opcode & 7;
					src |= (opcode & 0x0C00) >> 6;
					src |= (opcode & 0x2000) >> 8;
					// action
					avr.R[dst] = avr.RAM[(avr.R[29]<<8) + avr.R[28] + src];
				}
				break;

			case 0x9:
				// Everything else !!

				// 1001 000d dddd 0000   lds     r,i
				if ((opcode & 0xFE0F) == 0x9000) { // LDS
					//@@@
				}

				// 1001 000d dddd 010+   lpm     r,z
				else if ((opcode & 0xFE0E) == 0x9004) { // LPM
					//@@@
				}

				// 1001 000d dddd 011+   elpm    r,z
				else if ((opcode & 0xFE0E) == 0x9006) { // ELPM
					//@@@
				}

				// 1001 000r rrrr 1111   pop     r
				else if ((opcode & 0xFE0F) == 0x900F) { // POP
					// decode
					dst = (opcode & 0x01F0) >> 4;
					// action
					avr.rSP += 1;
					avr.R[dst] = avr.RAM[avr.rSP];
				}

				// 1001 001d dddd 0000   sts     i,r
				else if ((opcode & 0xFE0F) == 0x9200) { // STS
					//@@@
				}

				// 1001 001r rrrr 1111   push    r
				else if ((opcode & 0xFE0F) == 0x920F) { // PUSH
					// decode
					src = (opcode & 0x01F0) >> 4;
					// action
					avr.rSP -= 1;
					avr.RAM[avr.rSP + 1] = avr.R[src];
				}

				// 1001 0100 0000 1001   ijmp
				else if (opcode == 0x9409) { // IJMP
					//@@@
				}

				// 1001 0100 0001 1001   eijmp
				else if (opcode == 0x9419) { // EIJMP
					//@@@
				}

				// 1001 0100 0SSS 1000   bset    S
				//         =             sec
				//         =             sez
				//         =             sen
				//         =             sev
				//         =             ses
				//         =             seh
				//         =             set
				//         =             sei
				else if ((opcode & 0xFF8F) == 0x9408) { // BSET = SEC, SEZ, SEN, SEV, SES, SEH, SET, SEI
					//@@@
				}

				// 1001 0100 1SSS 1000   bclr    S
				//         =             clc
				//         =             clz
				//         =             cln
				//         =             clv
				//         =             cls
				//         =             clh
				//         =             clt
				//         =             cli
				else if ((opcode & 0xFF8F) == 0x9488) { // BCLR = CLC, CLZ, CLN, CLV, CLS, CLH, CLT, CLI
					//@@@
				}

				// 1001 0101 0000 1000   ret
				else if (opcode == 0x9508) { // RET
					// action
					regPC = avr.RAM[avr.rSP + 1];
					regPC |= (avr.RAM[avr.rSP + 2] >>8);
					regPC--;	// simply because there's a +1 below
					avr.rSP += 2;
				}

				// 1001 0101 0000 1001   icall
				else if (opcode == 0x9509) { // ICALL
					//@@@
				}

				// 1001 0101 0001 1000   reti
				else if (opcode == 0x9518) { // RETI
					//@@@
				}

				// 1001 0101 0001 1001   eicall
				else if (opcode == 0x9519) { // EICALL
					//@@@
				}

				// 1001 0101 1000 1000   sleep
				else if (opcode == 0x9588) { // SLEEP
					//@@@
				}

				// 1001 0101 1001 1000   break
				else if (opcode == 0x9598) { // BREAK
					// think we'll ignore this ;-)
				}

				// 1001 0101 1010 1000   wdr
				else if (opcode == 0x95A8) { // WDR
					//@@@
				}

				// 1001 0101 1100 1000   lpm     ?
				else if (opcode == 0x95C8) { // LPM
					//@@@
				}

				// 1001 0101 1100 1000   lpm     ?
				else if (opcode == 0x95D8) { // ELPM
					//@@@
				}

				// 1001 0101 1110 1000   spm
				else if (opcode == 0x95E8) { // SPM
					//@@@
				}

				// 1001 010h hhhh 110h   jmp     h
				else if ((opcode & 0xFE0E) == 0x940C) { // JMP
					// decode
					jdest = ((opcode & 0x01F0) >> 3) | (opcode & 1);
					jdest <<= 16UL;
					jdest |= read_next_opcode();
					// action
					regPC = (jdest - 1); // -1 because it's incremented below
				}

				// 1001 010h hhhh 111h   call    h
				else if ((opcode & 0xFE0E) == 0x940E) { // CALL
					// decode
					jdest = ((opcode & 0x01F0) >> 3) | (opcode & 1);
					jdest <<= 16UL;
					jdest |= read_next_opcode();
					// action
					avr.rSP -= 2;
					avr.RAM[avr.rSP + 1] = (regPC + 2) & 0xFF;
					avr.RAM[avr.rSP + 2] = (regPC + 2) >> 8;
					regPC = jdest - 1;
				}

				// 1001 010r rrrr 0000   com     r
				else if ((opcode & 0xFE0F) == 0x9400) { // COM
					// decode
					src = (opcode & 0x01F0) >> 4;
					// action
					avr.R[src] = 255 - avr.R[src];
					avr.rSREG.all = SREG;
				}

				// 1001 010r rrrr 0001   neg     r
				else if ((opcode & 0xFE0F) == 0x9401) { // NEG
					// decode
					src = (opcode & 0x01F0) >> 4;
					// action
					avr.R[src] = 0 - avr.R[src];
					avr.rSREG.all = SREG;
				}

				//1001 010r rrrr 0010   swap    r
				else if ((opcode & 0xFE0F) == 0x9402) { // SWAP
					// decode
					src = (opcode & 0x01F0) >> 4;
					// action
					uint8_t tmp;
					tmp = avr.R[src];
					avr.R[src] = ((tmp & 0x0F) << 4) | ((tmp & 0xF0) >> 4);
				}

				// 1001 010r rrrr 0011   inc     r
				else if ((opcode & 0xFE0F) == 0x9403) { // INC
					// decode
					src = (opcode & 0x01F0) >> 4;
					// action
					avr.R[src]++;
					avr.rSREG.all = SREG;
				}

				// 1001 010r rrrr 0101   asr     r
				else if ((opcode & 0xFE0F) == 0x9405) { // ASR
					// decode
					src = (opcode & 0x01F0) >> 4;
					// action
					uint8_t tmp, C;
					tmp = avr.R[src] & 0x80;
					C = avr.R[src] & 1;
					avr.R[src] >>= 1;
					avr.R[src] |= tmp;
					avr.rSREG.all = SREG;
					avr.rSREG.C = C;
				}

				// 1001 010r rrrr 0110   lsr     r	
				else if ((opcode & 0xFE0F) == 0x9406) { // LSR
					// decode
					src = (opcode & 0x01F0) >> 4;
					// action
					uint8_t C;
					C = avr.R[src] & 1;
					avr.R[src] >>= 1;
					avr.rSREG.all = SREG;
					avr.rSREG.C = C;
				}

				// 1001 010r rrrr 0111   ror     r
				else if ((opcode & 0xFE0F) == 0x9407) { // ROR
					// decode
					src = (opcode & 0x01F0) >> 4;
					// action
					uint8_t C;
					C = avr.R[src] & 1;
					avr.R[src] >>= 1;
					if (avr.rSREG.C) {
						avr.R[src] |= 0x80;
					}
					avr.rSREG.all = SREG;
					avr.rSREG.C = C;
				}

				// 1001 010r rrrr 1010   dec     r
				else if ((opcode & 0xFE0F) == 0x940A) { // DEC
					// decode
					src = (opcode & 0x01F0) >> 4;
					// action
					avr.R[src]--;
					avr.rSREG.all = SREG;
				}

				// 1001 0110 KKdd KKKK   adiw    w,K
				else if ((opcode & 0xFF00) == 0x9600) { // ADIW
					int16_t * p;
					// decode
					dst = (opcode & 0x0030) >> 4;
					offset = (opcode & 0x00C0) >> 2;
					offset |= (opcode & 0x000F);
					// action
					p = (int16_t *)&avr.R[24];
					p += dst;
					*p += offset;
					avr.rSREG.all = SREG;
				}

				// 1001 0111 KKdd KKKK   sbiw    w,K
				else if ((opcode & 0xFF00) == 0x9700) { // SBIW
					int16_t * p;
					// decode
					dst = (opcode & 0x0030) >> 4;
					offset = (opcode & 0x00C0) >> 2;
					offset |= (opcode & 0x000F);
					// action
					p = (int16_t *)&avr.R[24];
					p += dst;
					*p -= offset;
					avr.rSREG.all = SREG;
				}

				// 1001 1000 pppp psss   cbi     p,s
				else if ((opcode & 0xFF00) == 0x9800) { // CBI
					dst = (opcode & 0x00F8) >> 3;
					src = opcode & 0x0007;
					avr.SFR[dst] &= ~(1 << src);
				}

				// 1001 1001 pppp psss   sbic    p,s
				else if ((opcode & 0xFF00) == 0x9900) { // SBIC
					dst = (opcode & 0x00F8) >> 3;
					src = opcode & 0x0007;
					if (!(avr.SFR[dst] & (1 << src))) {
						if (two_word_opcode(read_next_opcode())) {
							regPC += 2;
						}
						else {
							regPC++;
						}
					}
				}

				// 1001 1010 pppp psss   sbi     p,s
				else if ((opcode & 0xFF00) == 0x9A00) { // SBI
					dst = (opcode & 0x00F8) >> 3;
					src = opcode & 0x0007;
					avr.SFR[dst] |= (1 << src);
				}

				// 1001 1011 pppp psss   sbis    p,s
				else if ((opcode & 0xFF00) == 0x9B00) { // SBIS
					dst = (opcode & 0x00F8) >> 3;
					src = opcode & 0x0007;
					if (avr.SFR[dst] & (1 << src)) {
						if (two_word_opcode(read_next_opcode())) {
							regPC += 2;
						}
						else {
							regPC++;
						}
					}
				}

				// 1001 11rd dddd rrrr   mul     r,r
				else if ((opcode & 0xFC00) == 0x9C00) { // MUL
					volatile uint16_t tmp;
					src = (opcode & 0x01F0) >> 4;
					dst = ((opcode & 0x0200) >> 5) | (opcode & 0x000F);
					//@@@ code ordering !!
					tmp = src * dst;
					*(volatile uint8_t *)&avr.rSREG.all = SREG;
					*(uint16_t *)&avr.R[0] = tmp;
				}
				break;

			case 0xB:
				// IN, OUT

				// 1011 1PPr rrrr PPPP   out     P,r
				if ((opcode & 0xF800) == 0xB800) { // OUT
					// decode
					dst = (opcode & 0x0600) >> 5;
					dst |= (opcode & 0x000F);
					src = (opcode & 0x01F0) >> 4;
					// action
					avr.SFR[dst] = avr.R[src];
				}

				// 1011 0PPd dddd PPPP   in      r,P
				else { // IN
					// decode
					src = (opcode & 0x0600) >> 5;
					src |= (opcode & 0x000F);
					dst = (opcode & 0x01F0) >> 4;
					// action
					avr.R[dst] = avr.SFR[src];
				}
				break;

			case 0xC:
				// 1100 LLLL LLLL LLLL   rjmp    L
				// RJMP
				// decode
				offset = opcode & 0x0FFF;
				if (offset & 0x0800) {
					offset = 0x1000 - offset;
					offset *= -1;
				}
				// action
				regPC = regPC + offset; // no +1 because regPC++ below
				break;

			case 0xD:
				// 1101 LLLL LLLL LLLL   rcall   L
				// RCALL
				// decode
				offset = opcode & 0x0FFF;
				if (offset & 0x0800) {
					offset = 0x1000 - offset;
					offset *= -1;
				}
				// action
				avr.rSP -= 2;
				avr.RAM[avr.rSP + 1] = (regPC + 2) & 0xFF;
				avr.RAM[avr.rSP + 2] = (regPC + 2) >> 8;
				regPC = regPC + offset; // no +1 because regPC++ below
				break;

			case 0xE:
				// 1110 KKKK dddd KKKK   ldi     d,M
				//		    =			 ser     d (K=FF)
				// LDI = SER (with src = 0xFF)
				// decode
				dst = (opcode & 0x00F0) >> 4;
				dst += 16; // only upper 16 registers
				src = (opcode & 0x0F00) >> 4;
				src |= (opcode & 0x000F);
				// action
				avr.R[dst] = src;
				break;

			case 0xF:
				// BRBS, BRBC, BLD, BST, SBRC, SBRS

				// 1111 00ll llll lsss   brbs    s,l
				//         =             brcs
				//         =             brlo
				//         =             breq
				//         =             brmi
				//         =             brvs
				//         =             brlt
				//         =             brhs
				//         =             brts
				//         =             brie
				if ((opcode & 0xFC00) == 0xF000) { // BRBS = BRCS/BRLO, BREQ, BRMI, BRVS, BRLT, BRHS, BRTS, BRIE
					// decode
					offset = (opcode & 0x03F8) >> 3;
					if (offset & 0x40) {
						// 2's complement if -ve
						offset = 0x80 - offset;
						offset *= -1;
					}
					src = opcode & 7;
					// action
					if (avr.rSREG.all & (1 << src)) {
						regPC = regPC + offset; //+1 done by regPC++ below.
					}
				}

				// 1111 01ll llll lsss   brbc    s,l
				//         =             brcc
				//         =             brsh
				//         =             brne
				//         =             brpl
				//         =             brvc
				//         =             brge
				//         =             brhc
				//         =             brtc
				//         =             brid
				else if ((opcode & 0xFC00) == 0xF400) { // BRBC = BRCC/BRSH, BRNE, BRPL, BRVC, BRGE, BRHC, BRTC, BRID
					// decode
					offset = (opcode & 0x03F8) >> 3;
					if (offset & 0x40) {
						// 2's complement if -ve
						offset = 0x80 - offset;
						offset *= -1;
					}
					src = opcode & 7;
					// action
					if (!(avr.rSREG.all & (1 << src))) {
						regPC = regPC + offset; //+1 done by regPC++ below.
					}
				}

				//1111 100d dddd 0sss   bld     r,s
				else if ((opcode & 0xFE08) == 0xF800) { // BLD
					dst = (opcode & 0x1F0) >> 4;
					src = opcode & 0x0007;
					if (avr.rSREG.T) {
						avr.R[dst] |= (1 << src);
					} else {
						avr.R[dst] &= ~(1 << src);
					}
				}

				// 1111 101d dddd 0sss   bst     r,s
				else if ((opcode & 0xFE08) == 0xFA00) { // BST
					src = (opcode & 0x1F0) >> 4;
					dst = opcode & 0x0007;
					if (avr.R[src] & (1 << dst)) {
						avr.rSREG.T = 1;
					} else {
						avr.rSREG.T = 0;
					}

				}

				// 1111 110r rrrr 0sss   sbrc    r,s
				else if ((opcode & 0xFE08) == 0xFC00) { // SBRC
					src = (opcode & 0x01F0) >> 4;
					dst = opcode & 0x0007;
					if (!(avr.R[src] & (1 << dst))) {
						if (two_word_opcode(read_next_opcode())) {
							regPC += 2;
						}
						else {
							regPC++;
						}
					}
				}

				// 1111 111r rrrr 0sss   sbrs    r,s
				else if ((opcode & 0xFE08) == 0xFE00) { // SBRS
					src = (opcode & 0x01F0) >> 4;
					dst = opcode & 0x0007;
					if (avr.R[src] & (1 << dst)) {
						if (two_word_opcode(read_next_opcode())) {
							regPC += 2;
						}
						else {
							regPC++;
						}
					}
				}
				break;
		}
		regPC++;
	}
}

uint16_t read_opcode(void) {
	return pgm_read_word(&test[regPC]);
}

uint16_t read_next_opcode(void) {
	return pgm_read_word(&test[regPC + 1]);
}

uint16_t two_word_opcode(uint16_t opcode) {
	uint16_t retval = 0;
	// there only are 4 opcodes witrh 2 words: LDS, STS, CALL and JMP
	// 1001 000d dddd 0000   lds     r,i
	// 1001 001d dddd 0000   sts     i,r
	// 1001 010h hhhh 111h   call    h
	// 1001 010h hhhh 110h   jmp     h
	opcode &= 0xFE0F;
	if ((opcode == 0x9000) ||	// lds
		(opcode == 0x9200) ||	// sts
		(opcode >= 0x940C) || (opcode <= 0x940F)) // JMP or CALL
	{
		retval = 1;
	}
	return retval;
}

Oh and my opcode list I've been using as a "master":

0000 0000 0000 0000   nop				1
0000 0001 dddd rrrr   movw    v,v		1
0000 0010 dddd rrrr   muls    d,d		1
0000 0011 0ddd 0rrr   mulsu   a,a		1
0000 0011 0ddd 1rrr   fmul    a,a		1
0000 0011 1ddd 0rrr   fmuls   a,a		1
0000 0011 1ddd 1rrr   fmulsu  a,a		1
0000 01rd dddd rrrr   cpc     r,r		1
0000 10rd dddd rrrr   sbc     r,r		1
0000 11rd dddd rrrr   add     r,r		1
        =             lsl     r

0001 00rd dddd rrrr   cpse    r,r		1
0001 01rd dddd rrrr   cp      r,r		1
0001 10rd dddd rrrr   sub     r,r		1
0001 11rd dddd rrrr   adc     r,r		1
        =             rol     r

0010 00rd dddd rrrr   and     r,r		1
        =             tst     r
0010 01rd dddd rrrr   eor     r,r		1
        =             clr     r
0010 10rd dddd rrrr   or      r,r		1
0010 11rd dddd rrrr   mov     r,r		1

0011 KKKK dddd KKKK   cpi     d,M		1

0100 KKKK dddd KKKK   sbci    d,M		1

0101 KKKK dddd KKKK   subi    d,M		1

0110 KKKK dddd KKKK   ori     d,M		1
        =             sbr     d,M

0111 KKKK dddd KKKK   andi    d,M		1
        =             cbr     d,n

100! 000d dddd ee-+   ld      r,e		1
100! 001r rrrr ee-+   st      e,r		1
10o0 oo0d dddd booo   ldd     r,b		1
10o0 oo1r rrrr booo   std     b,r		1

1001 000d dddd 0000   lds     r,i		2
1001 000d dddd 010+   lpm     r,z		1
1001 000d dddd 011+   elpm    r,z		1
1001 000r rrrr 1111   pop     r			1
1001 001d dddd 0000   sts     i,r		2
1001 001r rrrr 1111   push    r			1
1001 0100 0000 1001   ijmp				1
1001 0100 0001 1001   eijmp				1
1001 0100 0SSS 1000   bset    S			1
        =             sec
        =             sez
        =             sen
        =             sev
        =             ses
        =             seh
        =             set
        =             sei
1001 0100 1SSS 1000   bclr    S			1
        =             clc
        =             clz
        =             cln
        =             clv
        =             cls
        =             clh
        =             clt
        =             cli
1001 0101 0000 1000   ret				1
1001 0101 0000 1001   icall				1
1001 0101 0001 1000   reti				1
1001 0101 0001 1001   eicall			1
1001 0101 1000 1000   sleep				1
1001 0101 1001 1000   break				1
1001 0101 1010 1000   wdr				1
1001 0101 1100 1000   lpm     ?			1
1001 0101 1101 1000   elpm    ?			1
1001 0101 1110 1000   spm				1
1001 010h hhhh 110h   jmp     h			2
1001 010h hhhh 111h   call    h			2
1001 010r rrrr 0000   com     r			1
1001 010r rrrr 0001   neg     r			1
1001 010r rrrr 0010   swap    r			1
1001 010r rrrr 0011   inc     r			1
1001 010r rrrr 0101   asr     r			1
1001 010r rrrr 0110   lsr     r			1
1001 010r rrrr 0111   ror     r			1
1001 010r rrrr 1010   dec     r			1
1001 0110 KKdd KKKK   adiw    w,K		1
1001 0111 KKdd KKKK   sbiw    w,K		1
1001 1000 pppp psss   cbi     p,s		1
1001 1001 pppp psss   sbic    p,s		1
1001 1010 pppp psss   sbi     p,s		1
1001 1011 pppp psss   sbis    p,s		1
1001 11rd dddd rrrr   mul     r,r		1

1011 0PPd dddd PPPP   in      r,P		1
1011 1PPr rrrr PPPP   out     P,r		1

1100 LLLL LLLL LLLL   rjmp    L			1

1101 LLLL LLLL LLLL   rcall   L			1

1110 KKKK dddd KKKK   ldi     d,M		1
	    =			  ser     d (K=FF)

1111 00ll llll lsss   brbs    s,l		1
        =             brcs    l
        =             brlo    l
        =             breq    l
        =             brmi    l
        =             brvs    l
        =             brlt    l
        =             brhs    l
        =             brts    l
        =             brie    l
1111 01ll llll lsss   brbc    s,l		1
        =             brcc    l
        =             brsh    l
        =             brne    l
        =             brpl    l
        =             brvc    l
        =             brge    l
        =             brhc    l
        =             brtc    l
        =             brid    l
1111 100d dddd 0sss   bld     r,s		1
1111 101d dddd 0sss   bst     r,s		1
1111 110r rrrr 0sss   sbrc    r,s		1
1111 111r rrrr 0sss   sbrs    r,s		1

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

Quote:
I'll have to give in and execute each opcode with inline asm
Frankly, I would to the whole thing is asm. WRT sreg, there is no way in C to guarantee that sreg will not change between the last op you did and when you retrieve it.

Regards,
Steve A.

The Board helps those that help themselves.