[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [avr-gcc-list] Problems with avrtest simulatorand avr-gcc testsuite.
From: |
Georg-Johann Lay |
Subject: |
Re: [avr-gcc-list] Problems with avrtest simulatorand avr-gcc testsuite. |
Date: |
Tue, 26 Apr 2011 18:28:13 +0200 |
User-agent: |
Thunderbird 2.0.0.24 (X11/20100302) |
Paulo Marques schrieb:
> Georg-Johann Lay wrote:
>> Paulo Marques schrieb:
>>> Georg-Johann Lay wrote:
>>>> Paulo Marques schrieb:
[...]
>>> IIRC this is called "exit.c" and not "avrtest_helpers.h" because of the
>>> way the testsuite works...
>> The .exp I found for avrtest+atmega just mentions exit.c in ldflags.
>> IMO including exit.c won't work because this file is not prepared for
>> including. Besides that test programs should not depend on the
>> particular implementation of exit/abort, i.e. these functions should
>> always be CALLed and not inlined/macro expanded.
>
> I stand corrected. I'm the one who includes the file for a quick test on
> a program that is intended for a real avr and I just want to test it on
> the simulator for some reason.
>
>> exit.c gets compiled on-the-fly, so together with -O0 and the "naked"
>> for abort resp. exit gives odd code because they rely on frame pointer.
>>
>> The right way would be to use an exit-amtega128.o that was compiled
>> with, say, -Os and make abort/exit "noreturn" instead of "naked".
>>
>> Thus, I now use a slightly different setup:
>>
>> * avrtest: Disable getchar when reading from STDIO_PORT
>> * atmega128-sim.exp: use exit-atmega128.o instead of exit.c directly
>> * Reduce namespace pollution in exit.c, build exit-*.o, see attached
>
> I like those changes. exit.c looks cleaner and the Makefile looks a lot
> cleaner.
>
> Eric, Georg-Johann seems to be motivated to improve avrtest and run the
> testsuite. Maybe he should be given sourceforge access to commit the
> improvements directly? Georg, do you have a sourceforge account?
I didn't intend to open an other constructions site ;-)
But I did some more cleanups like moving all the magic numbers to one
file: avrtest.h
This could also contain code for your counters.
I wonder if it is possible to get tick counts *without* changing the
source, just by specifying command line options to avrtest like
-tickcount=some_function
and let avrtest count the ticks for, say, the first time it executes
some_function. It has all information (provided the function is not
inlined).
Johann
#ifndef AVRTEST_H
#define STDIN_PORT_ADDR 0x52
#define STDOUT_PORT_ADDR 0x52
#define EXIT_PORT_ADDR 0x4F
#define ABORT_PORT_ADDR 0x49
#ifdef IN_AVRTEST
/* Remove define for STDIN_PORT resp. STDOUT_PORT
to avoid avrtest to take special actions when
these locations are accessed. */
/* Avoid hang of avrtest during gcc testsuite
by *not* defining STDIN_PORT */
// #define STDIN_PORT STDIN_PORT_ADDR
#define STDOUT_PORT STDOUT_PORT_ADDR
#define EXIT_PORT EXIT_PORT_ADDR
#define ABORT_PORT ABORT_PORT_ADDR
#else
#define STDIN_PORT (*((volatile unsigned char*) STDIN_PORT_ADDR))
#define STDOUT_PORT (*((volatile unsigned char*) STDOUT_PORT_ADDR))
#define EXIT_PORT (*((volatile unsigned char*) EXIT_PORT_ADDR))
#define ABORT_PORT (*((volatile unsigned char*) ABORT_PORT_ADDR))
#endif /* IN_AVRTEST */
#define AVRTEST_H
#endif /* AVRTEST_H */
/* Copyright (c) 2004, Bjoern Haase
All rights reserved.
Anatoly Sokolov added code to make more tests PASS.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of the copyright holders nor the names of
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. */
#include <stdlib.h>
#include <stdio.h>
#include "avrtest.h"
static int putchar_exit_c(char, FILE*) __attribute__((no_instrument_function));
static void init_exit_c(void) __attribute__ ((section
(".init8"),naked,no_instrument_function,used));
static int putchar_exit_c (char c, FILE *stream)
{
(void) stream;
STDOUT_PORT = c;
return 0;
}
static void init_exit_c (void)
{
static FILE file_exit_c;
file_exit_c.put = putchar_exit_c;
file_exit_c.get = NULL;
file_exit_c.flags = _FDEV_SETUP_WRITE;
file_exit_c.udata = 0;
stderr = stdout = &file_exit_c;
}
/* This defines never returning functions exit() and abort() */
void __attribute__ ((noreturn))
exit (int code)
{
EXIT_PORT = code;
for(;;);
}
void __attribute__ ((noreturn))
abort (void)
{
ABORT_PORT = 1;
for(;;);
}
CFLAGS=-O3 -fomit-frame-pointer
WARN=-W -Wall -Wno-unused-parameter
CC=gcc
AVRGCC=avr-gcc
all: avrtest exit-atmega128.o
avrtest: avrtest.c avrtest.h Makefile
$(CC) $(WARN) $(CFLAGS) $< -o $@
$(CC) $(WARN) $(CFLAGS) $< -o address@hidden -DLOG_DUMP
exit-%.o: dejagnuboards/exit.c avrtest.h Makefile
$(AVRGCC) -c -Os -mmcu=$* -I. $< -o $@
.PHONY: clean
clean:
rm -f *.o avrtest avrtest_log
/*
****************************************************************************
*
* avrtest - A simple simulator for the Atmel AVR family of microcontrollers
* designed to test the compiler
* Copyright (C) 2001, 2002, 2003 Theodore A. Roth, Klaus Rudolph
* Copyright (C) 2007 Paulo Marques
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
****************************************************************************
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
//
---------------------------------------------------------------------------------
// configuration values (in bytes).
#define MAX_RAM_SIZE 64 * 1024
#define MAX_FLASH_SIZE 256 * 1024 // Must be at least 128KB
// ---------------------------------------------------------------------------
// register and port definitions
#define SREG 0x5F
#define SPH 0x5E
#define SPL 0x5D
#define EIND 0x5C
#define RAMPZ 0x5B
#define IOBASE 0x20
// ports used for application <-> simulator interactions
#define IN_AVRTEST
#include "avrtest.h"
#define REGX 26
#define REGY 28
#define REGZ 30
#define FLAG_I 0x80
#define FLAG_T 0x40
#define FLAG_H 0x20
#define FLAG_S 0x10
#define FLAG_V 0x08
#define FLAG_N 0x04
#define FLAG_Z 0x02
#define FLAG_C 0x01
struct arch_desc {
// Name of the architecture.
const char *name;
// True if PC is 3 bytes, false if only 2 bytes.
unsigned char pc_3bytes;
// True if the architecture has EIND related insns (EICALL/EIJMP).
unsigned char has_eind;
};
// List of supported archs with their features.
const struct arch_desc arch_descs[] =
{
{
.name = "avr51",
.pc_3bytes = 0,
.has_eind = 0
},
{
.name = "avr6",
.pc_3bytes = 1,
.has_eind = 1
},
{ NULL, 0, 0}
};
const struct arch_desc *arch = &arch_descs[0];
enum decoder_operand_masks {
/** 2 bit register id ( R24, R26, R28, R30 ) */
mask_Rd_2 = 0x0030,
/** 3 bit register id ( R16 - R23 ) */
mask_Rd_3 = 0x0070,
/** 4 bit register id ( R16 - R31 ) */
mask_Rd_4 = 0x00f0,
/** 5 bit register id ( R00 - R31 ) */
mask_Rd_5 = 0x01f0,
/** 3 bit register id ( R16 - R23 ) */
mask_Rr_3 = 0x0007,
/** 4 bit register id ( R16 - R31 ) */
mask_Rr_4 = 0x000f,
/** 5 bit register id ( R00 - R31 ) */
mask_Rr_5 = 0x020f,
/** for 8 bit constant */
mask_K_8 = 0x0F0F,
/** for 6 bit constant */
mask_K_6 = 0x00CF,
/** for 7 bit relative address */
mask_k_7 = 0x03F8,
/** for 12 bit relative address */
mask_k_12 = 0x0FFF,
/** for 22 bit absolute address */
mask_k_22 = 0x01F1,
/** register bit select */
mask_reg_bit = 0x0007,
/** status register bit select */
mask_sreg_bit = 0x0070,
/** address displacement (q) */
mask_q_displ = 0x2C07,
/** 5 bit register id ( R00 - R31 ) */
mask_A_5 = 0x00F8,
/** 6 bit IO port id */
mask_A_6 = 0x060F
};
//
---------------------------------------------------------------------------------
// some helpful types and constants
#define EXIT_STATUS_EXIT 0
#define EXIT_STATUS_ABORTED 1
#define EXIT_STATUS_TIMEOUT 2
typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned int dword;
typedef unsigned long long ddword;
typedef struct {
byte data_index;
byte oper1;
word oper2;
} decoded_op;
#define OP_FUNC_TYPE void __attribute__((fastcall))
typedef OP_FUNC_TYPE (*opcode_func)(int,int);
typedef struct {
opcode_func func;
int size;
int cycles;
const char *hreadable;
} opcode_data;
//
---------------------------------------------------------------------------------
// auxiliary lookup tables
enum {
avr_op_index_dummy = 0,
avr_op_index_ADC = 1, avr_op_index_ADD, avr_op_index_ADIW,
avr_op_index_SBIW,
avr_op_index_AND, avr_op_index_ANDI, avr_op_index_ASR,
avr_op_index_BCLR, avr_op_index_BLD, avr_op_index_BRBC,
avr_op_index_BRBS, avr_op_index_BSET, avr_op_index_BST,
avr_op_index_CALL, avr_op_index_CBI, avr_op_index_COM,
avr_op_index_CP, avr_op_index_CPC, avr_op_index_CPI,
avr_op_index_CPSE, avr_op_index_DEC, avr_op_index_EICALL,
avr_op_index_EIJMP, avr_op_index_ELPM, avr_op_index_ELPM_Z,
avr_op_index_ELPM_Z_incr,avr_op_index_EOR, avr_op_index_ESPM,
avr_op_index_FMUL, avr_op_index_FMULS, avr_op_index_FMULSU,
avr_op_index_ICALL, avr_op_index_IJMP, avr_op_index_ILLEGAL,
avr_op_index_IN, avr_op_index_INC, avr_op_index_JMP,
avr_op_index_LDD_Y, avr_op_index_LDD_Z, avr_op_index_LDI,
avr_op_index_LDS, avr_op_index_LD_X, avr_op_index_LD_X_decr,
avr_op_index_LD_X_incr, avr_op_index_LD_Y_decr, avr_op_index_LD_Y_incr,
avr_op_index_LD_Z_decr, avr_op_index_LD_Z_incr, avr_op_index_LPM,
avr_op_index_LPM_Z, avr_op_index_LPM_Z_incr,avr_op_index_LSR,
avr_op_index_MOV, avr_op_index_MOVW, avr_op_index_MUL,
avr_op_index_MULS, avr_op_index_MULSU, avr_op_index_NEG,
avr_op_index_NOP, avr_op_index_OR, avr_op_index_ORI,
avr_op_index_OUT, avr_op_index_POP, avr_op_index_PUSH,
avr_op_index_RCALL, avr_op_index_RET, avr_op_index_RETI,
avr_op_index_RJMP, avr_op_index_ROR, avr_op_index_SBC,
avr_op_index_SBCI, avr_op_index_SBI, avr_op_index_SBIC,
avr_op_index_SBIS, avr_op_index_SBRC, avr_op_index_SBRS,
avr_op_index_SLEEP, avr_op_index_SPM, avr_op_index_STD_Y,
avr_op_index_STD_Z, avr_op_index_STS, avr_op_index_ST_X,
avr_op_index_ST_X_decr, avr_op_index_ST_X_incr, avr_op_index_ST_Y_decr,
avr_op_index_ST_Y_incr, avr_op_index_ST_Z_decr, avr_op_index_ST_Z_incr,
avr_op_index_SUB, avr_op_index_SUBI, avr_op_index_SWAP,
avr_op_index_WDR,
};
extern const opcode_data opcode_func_array[];
//
---------------------------------------------------------------------------------
// vars that hold core definitions
// configured flash size
static unsigned int flash_addr_mask;
// maximum number of executed instructions (used as a timeout)
static dword max_instr_count;
// Initialize sram from .data segment.
static int flag_initialize_sram;
// filename of the file being executed
static const char *program_name;
static unsigned int program_size;
//
---------------------------------------------------------------------------------
// vars that hold simulator state. These are kept as global vars for simplicity
// cycle counter
static dword program_cycles;
// cpu_data is used to store registers, ioport values and actual SRAM
static byte cpu_data[MAX_RAM_SIZE];
static int cpu_PC;
// flash
static byte cpu_flash[MAX_FLASH_SIZE];
static decoded_op decoded_flash[MAX_FLASH_SIZE/2];
//
---------------------------------------------------------------------------------
// log functions
#ifdef LOG_DUMP
static char log_data[256];
char *cur_log_pos = log_data;
static void log_add_data(char *data)
{
int len = strlen(data);
memcpy(cur_log_pos, data, len);
cur_log_pos += len;
}
static void log_add_instr(const char *instr)
{
char buf[32];
if (arch->pc_3bytes)
sprintf(buf, "%06x: %-5s ", cpu_PC * 2, instr);
else
sprintf(buf, "%04x: %-5s ", cpu_PC * 2, instr);
log_add_data(buf);
}
static void log_add_data_mov(const char *format, int addr, int value)
{
char buf[32], adname[16];
if (addr < 32) {
sprintf(adname, "R%d", addr);
} else {
switch (addr) {
case SREG: strcpy(adname, "SREG"); break;
case SPH: strcpy(adname, "SPH"); break;
case SPL: strcpy(adname, "SPL"); break;
default:
if (addr < 256)
sprintf(adname, "%02x", addr);
else
sprintf(adname, "%04x", addr);
}
}
sprintf(buf, format, adname, value);
log_add_data(buf);
}
static void log_dump_line(void)
{
*cur_log_pos = '\0';
puts(log_data);
cur_log_pos = log_data;
}
#else
// empty placeholders to keep the rest of the code clean while allowing the
compiler to
// optimize these calls away
static void log_add_instr(const char *instr)
{}
static void log_add_data_mov(const char *format, int addr, int value)
{}
static void log_dump_line(void)
{}
#endif
static const char *exit_status_text[] = {
[EXIT_STATUS_EXIT] = "EXIT",
[EXIT_STATUS_ABORTED] = "ABORTED",
[EXIT_STATUS_TIMEOUT] = "TIMEOUT"
};
static void leave(int status, const char *reason)
{
// make sure we print the last log line before leaving
log_dump_line();
printf("\n"
" exit status: %s\n"
" reason: %s\n"
" program: %s\n"
"exit address: %06x\n"
"total cycles: %u\n\n",
exit_status_text[status], reason,
program_name ? program_name : "-not set-",
cpu_PC * 2, program_cycles);
exit(0);
}
//
---------------------------------------------------------------------------------
// ioport / ram / flash, read / write entry points
// lowest level memory accessors
static int data_read_byte_raw(int address)
{
// add code here to handle special events
#ifdef STDIN_PORT
if (address == STDIN_PORT)
return getchar();
#endif
return cpu_data[address];
}
static void data_write_byte_raw(int address, int value)
{
// add code here to handle special events
switch (address) {
#ifdef STDOUT_PORT
case STDOUT_PORT:
putchar(value);
return;
#endif
case EXIT_PORT:
leave(value ? EXIT_STATUS_ABORTED : EXIT_STATUS_EXIT,
"exit function called");
break;
case ABORT_PORT:
leave(EXIT_STATUS_ABORTED, "abort function called");
break;
}
// default action, just store the value
cpu_data[address] = value;
}
static int flash_read_byte(int address)
{
address &= flash_addr_mask;
// add code here to handle special events
return cpu_flash[address];
}
// mid-level memory accessors
// read a byte from memory / ioport / register
static int data_read_byte(int address)
{
int ret = data_read_byte_raw(address);
log_add_data_mov("(%s)->%02x ", address, ret);
return ret;
}
// write a byte to memory / ioport / register
static void data_write_byte(int address, int value)
{
log_add_data_mov("(%s)<-%02x ", address, value);
data_write_byte_raw(address, value);
}
// get_reg / put_reg are just placeholders for read/write calls where we can
// be sure that the adress is < 32
static byte get_reg(int address)
{
log_add_data_mov("(%s)->%02x ", address, cpu_data[address]);
return cpu_data[address];
}
static void put_reg(int address, byte value)
{
log_add_data_mov("(%s)<-%02x ", address, value);
cpu_data[address] = value;
}
static int get_word_reg(int address)
{
int ret = cpu_data[address] | (cpu_data[address + 1] << 8);
log_add_data_mov("(%s)->%04x ", address, ret);
return ret;
}
static void put_word_reg(int address, int value)
{
log_add_data_mov("(%s)<-%04x ", address, value & 0xFFFF);
cpu_data[address] = value;
cpu_data[address + 1] = value >> 8;
}
// read a word from memory / ioport / register
static int data_read_word(int address)
{
int ret = data_read_byte_raw(address) | (data_read_byte_raw(address +
1) << 8);
log_add_data_mov("(%s)->%04x ", address, ret);
return ret;
}
// write a word to memory / ioport / register
static void data_write_word(int address, int value)
{
value &= 0xffff;
log_add_data_mov("(%s)<-%04x ", address, value);
data_write_byte_raw(address, value & 0xFF);
data_write_byte_raw(address + 1, value >> 8);
}
//
---------------------------------------------------------------------------------
// flag manipulation functions
static void update_flags(int flags, int new_values)
{
int sreg = data_read_byte(SREG);
sreg = (sreg & ~flags) | new_values;
data_write_byte(SREG, sreg);
}
static int get_carry(void)
{
return (data_read_byte(SREG) & FLAG_C) != 0;
}
// fast flag update tables to avoid conditional branches on frequently used
operations
#define FUT_ADD8_RES 0x01FF
#define FUT_ADD8_V2_80 0x0200
#define FUT_ADD8_V1_80 0x0400
#define FUT_ADD8_V2_08 0x0800
#define FUT_ADD8_V1_08 0x1000
static byte flag_update_table_add8[8192];
#define FUT_ADD_SUB_INDEX(v1,v2,res) \
((((v1) & 0x08) << 9) | (((v2) & 0x08) << 8) | (((v1) & 0x80) << 3) |
(((v2) & 0x80) << 2) | ((res) & 0x1FF))
#define FUT_ADDSUB16_INDEX(v1, res) \
( ((((v1) >> 8) & 0x80) << 3) | (((res) >> 8) & 0x1FF))
#define FUT_SUB8_RES 0x01FF
#define FUT_SUB8_V2_80 0x0200
#define FUT_SUB8_V1_80 0x0400
#define FUT_SUB8_V2_08 0x0800
#define FUT_SUB8_V1_08 0x1000
static byte flag_update_table_sub8[8192];
static byte flag_update_table_ror8[512];
static byte flag_update_table_inc[256];
static byte flag_update_table_dec[256];
static byte flag_update_table_logical[256];
static byte update_zns_flags(int result, byte sreg)
{
if ((result & 0xFF) == 0x00)
sreg |= FLAG_Z;
if (result & 0x80)
sreg |= FLAG_N;
if (((sreg & FLAG_N) != 0) != ((sreg & FLAG_V) != 0))
sreg |= FLAG_S;
return sreg;
}
static void init_flag_update_tables(void)
{
int i, result, sreg;
// build flag update table for 8 bit addition
for (i = 0; i < 8192; i++) {
result = i & FUT_ADD8_RES;
sreg = 0;
if ((!(i & FUT_ADD8_V1_80) == !(i & FUT_ADD8_V2_80)) &&
(!(result & 0x80) != !(i & FUT_ADD8_V1_80)))
sreg |= FLAG_V;
if (result & 0x100)
sreg |= FLAG_C;
if (((i & 0x1800) == 0x1800) || ((result & 0x08) == 0 && ((i &
0x1800) != 0x0000)))
sreg |= FLAG_H;
sreg = update_zns_flags(result, sreg);
flag_update_table_add8[i] = sreg;
}
// build flag update table for 8 bit subtraction
for (i = 0; i < 8192; i++) {
result = i & FUT_SUB8_RES;
sreg = 0;
if ((!(result & 0x80) == !(i & FUT_SUB8_V2_80)) && (!(i &
FUT_SUB8_V1_80) != !(i & FUT_SUB8_V2_80)))
sreg |= FLAG_V;
if (result & 0x100)
sreg |= FLAG_C;
if (((i & 0x1800) == 0x1800) || ((result & 0x08) == 0 && ((i &
0x1800) != 0x0000)))
sreg |= FLAG_H;
sreg = update_zns_flags(result, sreg);
flag_update_table_sub8[i] = sreg;
}
// build flag update table for 8 bit rotation
for (i = 0; i < 512; i++) {
result = i >> 1;
sreg = 0;
if (i & 0x01)
sreg |= FLAG_C;
if (((result & 0x80) != 0) != ((sreg & FLAG_C) != 0))
sreg |= FLAG_V;
sreg = update_zns_flags(result, sreg);
flag_update_table_ror8[i] = sreg;
}
// build flag update table for increment
for (i = 0; i < 256; i++) {
sreg = (i == 0x80) ? FLAG_V : 0;
sreg = update_zns_flags(i, sreg);
flag_update_table_inc[i] = sreg;
}
// build flag update table for decrement
for (i = 0; i < 256; i++) {
sreg = (i == 0x7F) ? FLAG_V : 0;
sreg = update_zns_flags(i, sreg);
flag_update_table_dec[i] = sreg;
}
// build flag update table for logical operations
for (i = 0; i < 256; i++) {
sreg = update_zns_flags(i, 0);
flag_update_table_logical[i] = sreg;
}
}
//
---------------------------------------------------------------------------------
// helper functions
static void add_program_cycles(dword cycles)
{
program_cycles += cycles;
}
static void push_byte(int value)
{
int sp = data_read_word(SPL);
// temporary hack to disallow growing the stack over the reserved
register area
if (sp < 0x60)
leave(EXIT_STATUS_ABORTED, "stack pointer overflow");
data_write_byte(sp, value);
data_write_word(SPL, sp - 1);
}
static int pop_byte(void)
{
int sp = data_read_word(SPL);
sp++;
data_write_word(SPL, sp);
return data_read_byte(sp);
}
static void push_PC(void)
{
int sp = data_read_word(SPL);
// temporary hack to disallow growing the stack over the reserved
register area
if (sp < 0x60)
leave(EXIT_STATUS_ABORTED, "stack pointer overflow");
data_write_byte(sp, cpu_PC & 0xFF);
sp--;
data_write_byte(sp, (cpu_PC >> 8) & 0xFF);
sp--;
if (arch->pc_3bytes) {
data_write_byte(sp, (cpu_PC >> 16) & 0xFF);
sp--;
}
data_write_word(SPL, sp);
}
static void pop_PC(void)
{
int sp = data_read_word(SPL);
if (arch->pc_3bytes) {
sp++;
cpu_PC = data_read_byte(sp) << 16;
} else
cpu_PC = 0;
sp++;
cpu_PC |= data_read_byte(sp) << 8;
sp++;
cpu_PC |= data_read_byte(sp);
data_write_word(SPL, sp);
}
// perform the addition and set the appropriate flags
static void do_addition_8(int rd, int rr, int carry)
{
int value1, value2, sreg, result;
value1 = get_reg(rd);
value2 = get_reg(rr);
result = value1 + value2 + carry;
sreg = flag_update_table_add8[FUT_ADD_SUB_INDEX(value1, value2,
result)];
update_flags(FLAG_H | FLAG_S | FLAG_V | FLAG_N | FLAG_Z | FLAG_C, sreg);
put_reg(rd, result & 0xFF);
}
// perform the subtraction and set the appropriate flags
static int do_subtraction_8(int value1, int value2, int carry, int use_carry)
{
int sreg, result = value1 - value2 - carry;
sreg = flag_update_table_sub8[FUT_ADD_SUB_INDEX(value1, value2,
result)];
if (use_carry && ((data_read_byte(SREG) & FLAG_Z) == 0))
sreg &= ~FLAG_Z;
update_flags(FLAG_H | FLAG_S | FLAG_V | FLAG_N | FLAG_Z | FLAG_C, sreg);
return result & 0xFF;
}
static void store_logical_result(int rd, int result)
{
put_reg(rd, result);
update_flags(FLAG_S | FLAG_V | FLAG_N | FLAG_Z,
flag_update_table_logical[result]);
}
/* 10q0 qq0d dddd 1qqq | LDD */
static void load_indirect(int rd, int ind_register, int adjust, int
displacement_bits)
{
//TODO: use RAMPx registers to address more than 64kb of RAM
int ind_value = get_word_reg(ind_register);
if (adjust < 0)
ind_value += adjust;
put_reg(rd, data_read_byte(ind_value + displacement_bits));
if (adjust > 0)
ind_value += adjust;
if (adjust)
put_word_reg(ind_register, ind_value);
}
static void store_indirect(int rd, int ind_register, int adjust, int
displacement_bits)
{
//TODO: use RAMPx registers to address more than 64kb of RAM
int ind_value = get_word_reg(ind_register);
if (adjust < 0)
ind_value += adjust;
data_write_byte(ind_value + displacement_bits, get_reg(rd));
if (adjust > 0)
ind_value += adjust;
if (adjust)
put_word_reg(ind_register, ind_value);
}
static void load_program_memory(int rd, int use_RAMPZ, int incr)
{
int address = get_word_reg(REGZ);
if (use_RAMPZ)
address |= data_read_byte(RAMPZ) << 16;
put_reg(rd, flash_read_byte(address));
if (incr) {
address++;
put_word_reg(REGZ, address & 0xFFFF);
if (use_RAMPZ)
data_write_byte(RAMPZ, address >> 16);
}
}
static void skip_instruction_on_condition(int condition)
{
int size;
if (condition) {
size = opcode_func_array[decoded_flash[cpu_PC].data_index].size;
cpu_PC += size;
add_program_cycles(size);
}
}
static void branch_on_sreg_condition(int rd, int rr, int flag_value)
{
if (((data_read_byte(SREG) & rr) != 0) == flag_value) {
int delta = rd;
if (delta & 0x40) delta |= 0xFFFFFF80;
cpu_PC += delta;
add_program_cycles(1);
}
}
static void rotate_right(int rd, int value, int top_bit)
{
if (top_bit)
value |= 0x100;
put_reg(rd, value >> 1);
update_flags(FLAG_S | FLAG_V | FLAG_N | FLAG_Z | FLAG_C,
flag_update_table_ror8[value]);
}
static void do_multiply(int rd, int rr, int signed1, int signed2, int shift)
{
int v1, v2, result, sreg;
v1 = signed1 ? ((signed char) get_reg(rd)) : get_reg(rd);
v2 = signed2 ? ((signed char) get_reg(rr)) : get_reg(rr);
result = (v1 * v2) & 0xFFFF;
sreg = 0;
if (result & 0x8000)
sreg |= FLAG_C;
if (shift)
result <<= 1;
if (result == 0)
sreg |= FLAG_Z;
update_flags(FLAG_Z | FLAG_C, sreg);
put_word_reg(0, result);
}
// handle illegal instructions
static OP_FUNC_TYPE avr_op_ILLEGAL(int rd, int rr)
{
char buf[128];
sprintf(buf, "illegal opcode %04x", rr);
leave(EXIT_STATUS_ABORTED, buf);
}
//
---------------------------------------------------------------------------------
// opcode execution functions
/* opcodes with no operands */
/* 1001 0101 0001 1001 | EICALL */
static OP_FUNC_TYPE avr_op_EICALL(int rd, int rr)
{
if (arch->has_eind) {
push_PC();
cpu_PC = data_read_word(REGZ) | (data_read_byte(EIND) << 16);
}
else {
avr_op_ILLEGAL(0,0x9519);
}
}
/* 1001 0100 0001 1001 | EIJMP */
static OP_FUNC_TYPE avr_op_EIJMP(int rd, int rr)
{
if (arch->has_eind) {
cpu_PC = data_read_word(REGZ) | (data_read_byte(EIND) << 16);
}
else {
avr_op_ILLEGAL(0,0x9419);
}
}
/* 1001 0101 1101 1000 | ELPM */
static OP_FUNC_TYPE avr_op_ELPM(int rd, int rr)
{
load_program_memory(0, 1, 0);
}
/* 1001 0101 1111 1000 | ESPM */
static OP_FUNC_TYPE avr_op_ESPM(int rd, int rr)
{
avr_op_ILLEGAL(0, 0x95F8);
//TODO
}
/* 1001 0101 0000 1001 | ICALL */
static OP_FUNC_TYPE avr_op_ICALL(int rd, int rr)
{
push_PC();
cpu_PC = get_word_reg(REGZ);
if (arch->pc_3bytes)
add_program_cycles(1);
}
/* 1001 0100 0000 1001 | IJMP */
static OP_FUNC_TYPE avr_op_IJMP(int rd, int rr)
{
cpu_PC = get_word_reg(REGZ);
}
/* 1001 0101 1100 1000 | LPM */
static OP_FUNC_TYPE avr_op_LPM(int rd, int rr)
{
load_program_memory(0, 0, 0);
}
/* 0000 0000 0000 0000 | NOP */
static OP_FUNC_TYPE avr_op_NOP(int rd, int rr)
{
}
/* 1001 0101 0000 1000 | RET */
static OP_FUNC_TYPE avr_op_RET(int rd, int rr)
{
pop_PC();
if (arch->pc_3bytes)
add_program_cycles(1);
}
/* 1001 0101 0001 1000 | RETI */
static OP_FUNC_TYPE avr_op_RETI(int rd, int rr)
{
avr_op_RET(rd,rr);
update_flags(FLAG_I, FLAG_I);
}
/* 1001 0101 1000 1000 | SLEEP */
static OP_FUNC_TYPE avr_op_SLEEP(int rd, int rr)
{
// we don't have anything to wake us up, so just pretend we wake up
immediately
}
/* 1001 0101 1110 1000 | SPM */
static OP_FUNC_TYPE avr_op_SPM(int rd, int rr)
{
avr_op_ILLEGAL(0,0x95E8);
//TODO
}
/* 1001 0101 1010 1000 | WDR */
static OP_FUNC_TYPE avr_op_WDR(int rd, int rr)
{
// we don't have a watchdog, so do nothing
}
/* opcodes with two 5-bit register (Rd and Rr) operands */
/* 0001 11rd dddd rrrr | ADC or ROL */
static OP_FUNC_TYPE avr_op_ADC(int rd, int rr)
{
do_addition_8(rd,rr,get_carry());
}
/* 0000 11rd dddd rrrr | ADD or LSL */
static OP_FUNC_TYPE avr_op_ADD(int rd, int rr)
{
do_addition_8(rd,rr,0);
}
/* 0010 00rd dddd rrrr | AND or TST */
static OP_FUNC_TYPE avr_op_AND(int rd, int rr)
{
int result = get_reg(rd) & get_reg(rr);
store_logical_result(rd,result);
}
/* 0001 01rd dddd rrrr | CP */
static OP_FUNC_TYPE avr_op_CP(int rd, int rr)
{
do_subtraction_8(get_reg(rd), get_reg(rr), 0, 0);
}
/* 0000 01rd dddd rrrr | CPC */
static OP_FUNC_TYPE avr_op_CPC(int rd, int rr)
{
do_subtraction_8(get_reg(rd), get_reg(rr), get_carry(), 1);
}
/* 0001 00rd dddd rrrr | CPSE */
static OP_FUNC_TYPE avr_op_CPSE(int rd, int rr)
{
skip_instruction_on_condition(get_reg(rd) == get_reg(rr));
}
/* 0010 01rd dddd rrrr | EOR or CLR */
static OP_FUNC_TYPE avr_op_EOR(int rd, int rr)
{
int result = get_reg(rd) ^ get_reg(rr);
store_logical_result(rd,result);
}
/* 0010 11rd dddd rrrr | MOV */
static OP_FUNC_TYPE avr_op_MOV(int rd, int rr)
{
put_reg(rd, get_reg(rr));
}
/* 1001 11rd dddd rrrr | MUL */
static OP_FUNC_TYPE avr_op_MUL(int rd, int rr)
{
do_multiply(rd,rr,0,0,0);
}
/* 0010 10rd dddd rrrr | OR */
static OP_FUNC_TYPE avr_op_OR(int rd, int rr)
{
int result = get_reg(rd) | get_reg(rr);
store_logical_result(rd, result);
}
/* 0000 10rd dddd rrrr | SBC */
static OP_FUNC_TYPE avr_op_SBC(int rd, int rr)
{
put_reg(rd, do_subtraction_8(get_reg(rd), get_reg(rr), get_carry(), 1));
}
/* 0001 10rd dddd rrrr | SUB */
static OP_FUNC_TYPE avr_op_SUB(int rd, int rr)
{
put_reg(rd, do_subtraction_8(get_reg(rd), get_reg(rr), 0, 0));
}
/* opcode with a single register (Rd) as operand */
/* 1001 010d dddd 0101 | ASR */
static OP_FUNC_TYPE avr_op_ASR(int rd, int rr)
{
int value = get_reg(rd);
rotate_right(rd, value, value & 0x80);
}
/* 1001 010d dddd 0000 | COM */
static OP_FUNC_TYPE avr_op_COM(int rd, int rr)
{
int result = (~get_reg(rd)) & 0xFF;
put_reg(rd, result);
update_flags(FLAG_S | FLAG_V | FLAG_N | FLAG_Z | FLAG_C,
flag_update_table_logical[result] | FLAG_C);
}
/* 1001 010d dddd 1010 | DEC */
static OP_FUNC_TYPE avr_op_DEC(int rd, int rr)
{
int result = (get_reg(rd) - 1) & 0xFF;
put_reg(rd, result);
update_flags(FLAG_S | FLAG_V | FLAG_N | FLAG_Z,
flag_update_table_dec[result]);
}
/* 1001 000d dddd 0110 | ELPM */
static OP_FUNC_TYPE avr_op_ELPM_Z(int rd, int rr)
{
load_program_memory(rd, 1, 0);
}
/* 1001 000d dddd 0111 | ELPM */
static OP_FUNC_TYPE avr_op_ELPM_Z_incr(int rd, int rr)
{
load_program_memory(rd, 1, 1);
}
/* 1001 010d dddd 0011 | INC */
static OP_FUNC_TYPE avr_op_INC(int rd, int rr)
{
int result = (get_reg(rd) + 1) & 0xFF;
put_reg(rd, result);
update_flags(FLAG_S | FLAG_V | FLAG_N | FLAG_Z,
flag_update_table_inc[result]);
}
/* 1001 000d dddd 0000 | LDS */
static OP_FUNC_TYPE avr_op_LDS(int rd, int rr)
{
//TODO:RAMPD
put_reg(rd, data_read_byte(rr));
}
/* 1001 000d dddd 1100 | LD */
static OP_FUNC_TYPE avr_op_LD_X(int rd, int rr)
{
load_indirect(rd, REGX, 0, 0);
}
/* 1001 000d dddd 1110 | LD */
static OP_FUNC_TYPE avr_op_LD_X_decr(int rd, int rr)
{
load_indirect(rd, REGX, -1, 0);
}
/* 1001 000d dddd 1101 | LD */
static OP_FUNC_TYPE avr_op_LD_X_incr(int rd, int rr)
{
load_indirect(rd, REGX, 1, 0);
}
/* 1001 000d dddd 1010 | LD */
static OP_FUNC_TYPE avr_op_LD_Y_decr(int rd, int rr)
{
load_indirect(rd, REGY, -1, 0);
}
/* 1001 000d dddd 1001 | LD */
static OP_FUNC_TYPE avr_op_LD_Y_incr(int rd, int rr)
{
load_indirect(rd, REGY, 1, 0);
}
/* 1001 000d dddd 0010 | LD */
static OP_FUNC_TYPE avr_op_LD_Z_decr(int rd, int rr)
{
load_indirect(rd, REGZ, -1, 0);
}
/* 1001 000d dddd 0010 | LD */
static OP_FUNC_TYPE avr_op_LD_Z_incr(int rd, int rr)
{
load_indirect(rd, REGZ, 1, 0);
}
/* 1001 000d dddd 0100 | LPM Z */
static OP_FUNC_TYPE avr_op_LPM_Z(int rd, int rr)
{
load_program_memory(rd, 0, 0);
}
/* 1001 000d dddd 0101 | LPM Z+ */
static OP_FUNC_TYPE avr_op_LPM_Z_incr(int rd, int rr)
{
load_program_memory(rd, 0, 1);
}
/* 1001 010d dddd 0110 | LSR */
static OP_FUNC_TYPE avr_op_LSR(int rd, int rr)
{
rotate_right(rd, get_reg(rd), 0);
}
/* 1001 010d dddd 0001 | NEG */
static OP_FUNC_TYPE avr_op_NEG(int rd, int rr)
{
put_reg(rd, do_subtraction_8(0, get_reg(rd), 0, 0));
}
/* 1001 000d dddd 1111 | POP */
static OP_FUNC_TYPE avr_op_POP(int rd, int rr)
{
put_reg(rd, pop_byte());
}
/* 1001 001d dddd 1111 | PUSH */
static OP_FUNC_TYPE avr_op_PUSH(int rd, int rr)
{
push_byte(get_reg(rd));
}
/* 1001 010d dddd 0111 | ROR */
static OP_FUNC_TYPE avr_op_ROR(int rd, int rr)
{
rotate_right(rd, get_reg(rd), get_carry());
}
/* 1001 001d dddd 0000 | STS */
static OP_FUNC_TYPE avr_op_STS(int rd, int rr)
{
//TODO:RAMPD
data_write_byte(rr, get_reg(rd));
}
/* 1001 001d dddd 1100 | ST */
static OP_FUNC_TYPE avr_op_ST_X(int rd, int rr)
{
store_indirect(rd, REGX, 0, 0);
}
/* 1001 001d dddd 1110 | ST */
static OP_FUNC_TYPE avr_op_ST_X_decr(int rd, int rr)
{
store_indirect(rd, REGX, -1, 0);
}
/* 1001 001d dddd 1101 | ST */
static OP_FUNC_TYPE avr_op_ST_X_incr(int rd, int rr)
{
store_indirect(rd, REGX, 1, 0);
}
/* 1001 001d dddd 1010 | ST */
static OP_FUNC_TYPE avr_op_ST_Y_decr(int rd, int rr)
{
store_indirect(rd, REGY, -1, 0);
}
/* 1001 001d dddd 1001 | ST */
static OP_FUNC_TYPE avr_op_ST_Y_incr(int rd, int rr)
{
store_indirect(rd, REGY, 1, 0);
}
/* 1001 001d dddd 0010 | ST */
static OP_FUNC_TYPE avr_op_ST_Z_decr(int rd, int rr)
{
store_indirect(rd, REGZ, -1, 0);
}
/* 1001 001d dddd 0001 | ST */
static OP_FUNC_TYPE avr_op_ST_Z_incr(int rd, int rr)
{
store_indirect(rd, REGZ, 1, 0);
}
/* 1001 010d dddd 0010 | SWAP */
static OP_FUNC_TYPE avr_op_SWAP(int rd, int rr)
{
int value = get_reg(rd);
put_reg(rd, ((value << 4) & 0xF0) | ((value >> 4) & 0x0F));
}
/* opcodes with a register (Rd) and a constant data (K) as operands */
/* 0111 KKKK dddd KKKK | CBR or ANDI */
static OP_FUNC_TYPE avr_op_ANDI(int rd, int rr)
{
int result = get_reg(rd) & rr;
store_logical_result(rd, result);
}
/* 0011 KKKK dddd KKKK | CPI */
static OP_FUNC_TYPE avr_op_CPI(int rd, int rr)
{
do_subtraction_8(get_reg(rd), rr, 0, 0);
}
/* 1110 KKKK dddd KKKK | LDI or SER */
static OP_FUNC_TYPE avr_op_LDI(int rd, int rr)
{
put_reg(rd, rr);
}
/* 0110 KKKK dddd KKKK | SBR or ORI */
static OP_FUNC_TYPE avr_op_ORI(int rd, int rr)
{
int result = get_reg(rd) | rr;
store_logical_result(rd, result);
}
/* 0100 KKKK dddd KKKK | SBCI */
/* 0101 KKKK dddd KKKK | SUBI */
static OP_FUNC_TYPE avr_op_SBCI_SUBI(int rd, int rr, int carry, int use_carry)
{
put_reg(rd, do_subtraction_8(get_reg(rd), rr, carry, use_carry));
}
static OP_FUNC_TYPE avr_op_SBCI(int rd, int rr)
{
avr_op_SBCI_SUBI(rd, rr, get_carry(), 1);
}
static OP_FUNC_TYPE avr_op_SUBI(int rd, int rr)
{
avr_op_SBCI_SUBI(rd, rr, 0, 0);
}
/* opcodes with a register (Rd) and a register bit number (b) as operands */
/* 1111 100d dddd 0bbb | BLD */
static OP_FUNC_TYPE avr_op_BLD(int rd, int rr)
{
int value = get_reg(rd);
if (data_read_byte(SREG) & FLAG_T)
value |= rr;
else
value &= ~rr;
put_reg(rd, value);
}
/* 1111 101d dddd 0bbb | BST */
static OP_FUNC_TYPE avr_op_BST(int rd, int rr)
{
int bit = get_reg(rd) & rr;
update_flags(FLAG_T, bit ? FLAG_T : 0);
}
/* 1111 110d dddd 0bbb | SBRC */
static OP_FUNC_TYPE avr_op_SBRC(int rd, int rr)
{
skip_instruction_on_condition(!(get_reg(rd) & rr));
}
/* 1111 111d dddd 0bbb | SBRS */
static OP_FUNC_TYPE avr_op_SBRS(int rd, int rr)
{
skip_instruction_on_condition(get_reg(rd) & rr);
}
/* opcodes with a relative 7-bit address (k) and a register bit number (b) as
operands */
/* 1111 01kk kkkk kbbb | BRBC */
static OP_FUNC_TYPE avr_op_BRBC(int rd, int rr)
{
branch_on_sreg_condition(rd, rr, 0);
}
/* 1111 00kk kkkk kbbb | BRBS */
static OP_FUNC_TYPE avr_op_BRBS(int rd, int rr)
{
branch_on_sreg_condition(rd, rr, 1);
}
/* opcodes with a 6-bit address displacement (q) and a register (Rd) as
operands */
/* 10q0 qq0d dddd 1qqq | LDD */
static OP_FUNC_TYPE avr_op_LDD_Y(int rd, int rr)
{
load_indirect(rd, REGY, 0, rr);
}
/* 10q0 qq0d dddd 0qqq | LDD */
static OP_FUNC_TYPE avr_op_LDD_Z(int rd, int rr)
{
load_indirect(rd, REGZ, 0, rr);
}
/* 10q0 qq1d dddd 1qqq | STD */
static OP_FUNC_TYPE avr_op_STD_Y(int rd, int rr)
{
store_indirect(rd, REGY, 0, rr);
}
/* 10q0 qq1d dddd 0qqq | STD */
static OP_FUNC_TYPE avr_op_STD_Z(int rd, int rr)
{
store_indirect(rd, REGZ, 0, rr);
}
/* opcodes with a absolute 22-bit address (k) operand */
/* 1001 010k kkkk 110k | JMP */
static OP_FUNC_TYPE avr_op_JMP(int rd, int rr)
{
cpu_PC = rr | (rd << 16);
}
/* 1001 010k kkkk 111k | CALL */
static OP_FUNC_TYPE avr_op_CALL(int rd, int rr)
{
push_PC();
cpu_PC = rr | (rd << 16);
if (arch->pc_3bytes)
add_program_cycles(1);
}
/* opcode with a sreg bit select (s) operand */
/* BCLR takes place of CL{C,Z,N,V,S,H,T,I} */
/* BSET takes place of SE{C,Z,N,V,S,H,T,I} */
/* 1001 0100 1sss 1000 | BCLR */
static OP_FUNC_TYPE avr_op_BCLR(int rd, int rr)
{
update_flags(rd, 0);
}
/* 1001 0100 0sss 1000 | BSET */
static OP_FUNC_TYPE avr_op_BSET(int rd, int rr)
{
update_flags(rd, rd);
}
/* opcodes with a 6-bit constant (K) and a register (Rd) as operands */
/* 1001 0110 KKdd KKKK | ADIW */
static OP_FUNC_TYPE avr_op_ADIW(int rd, int rr)
{
int svalue, evalue, sreg;
svalue = get_word_reg(rd);
evalue = svalue + rr;
put_word_reg(rd, evalue);
sreg = flag_update_table_add8[FUT_ADDSUB16_INDEX(svalue, evalue)];
sreg &= ~FLAG_H;
if ((evalue & 0xFFFF) != 0x0000)
sreg &= ~FLAG_Z;
update_flags(FLAG_S | FLAG_V | FLAG_N | FLAG_Z | FLAG_C, sreg);
}
/* 1001 0111 KKdd KKKK | SBIW */
static OP_FUNC_TYPE avr_op_SBIW(int rd, int rr)
{
int svalue, evalue, sreg;
svalue = get_word_reg(rd);
evalue = svalue - rr;
put_word_reg(rd, evalue);
sreg = flag_update_table_sub8[FUT_ADDSUB16_INDEX(svalue, evalue)];
sreg &= ~FLAG_H;
if ((evalue & 0xFFFF) != 0x0000)
sreg &= ~FLAG_Z;
update_flags(FLAG_S | FLAG_V | FLAG_N | FLAG_Z | FLAG_C, sreg);
}
/* opcodes with a 5-bit IO Addr (A) and register bit number (b) as operands */
/* 1001 1000 AAAA Abbb | CBI */
static OP_FUNC_TYPE avr_op_CBI(int rd, int rr)
{
data_write_byte(rd, data_read_byte(rd) & ~(rr));
}
/* 1001 1010 AAAA Abbb | SBI */
static OP_FUNC_TYPE avr_op_SBI(int rd, int rr)
{
data_write_byte(rd, data_read_byte(rd) | rr);
}
/* 1001 1001 AAAA Abbb | SBIC */
static OP_FUNC_TYPE avr_op_SBIC(int rd, int rr)
{
skip_instruction_on_condition(!(data_read_byte(rd) & rr));
}
/* 1001 1011 AAAA Abbb | SBIS */
static OP_FUNC_TYPE avr_op_SBIS(int rd, int rr)
{
skip_instruction_on_condition(data_read_byte(rd) & rr);
}
/* opcodes with a 6-bit IO Addr (A) and register (Rd) as operands */
/* 1011 0AAd dddd AAAA | IN */
static OP_FUNC_TYPE avr_op_IN(int rd, int rr)
{
put_reg(rd, data_read_byte(rr));
}
/* 1011 1AAd dddd AAAA | OUT */
static OP_FUNC_TYPE avr_op_OUT(int rd, int rr)
{
data_write_byte(rr, get_reg(rd));
}
/* opcodes with a relative 12-bit address (k) operand */
/* 1100 kkkk kkkk kkkk | RJMP */
static OP_FUNC_TYPE avr_op_RJMP(int rd, int rr)
{
int delta = rr;
if (delta & 0x800) delta |= 0xFFFFF000;
// special case: endless loop usually means that the program has ended
if (delta == -1)
leave(EXIT_STATUS_EXIT, "infinite loop detected (normal exit)");
cpu_PC += delta;
}
/* 1101 kkkk kkkk kkkk | RCALL */
static OP_FUNC_TYPE avr_op_RCALL(int rd, int rr)
{
int delta = rr;
if (delta & 0x800) delta |= 0xFFFFF000;
push_PC();
cpu_PC += delta;
if (arch->pc_3bytes)
add_program_cycles(1);
}
/* opcodes with two 4-bit register (Rd and Rr) operands */
/* 0000 0001 dddd rrrr | MOVW */
static OP_FUNC_TYPE avr_op_MOVW(int rd, int rr)
{
put_word_reg(rd, get_word_reg(rr));
}
/* 0000 0010 dddd rrrr | MULS */
static OP_FUNC_TYPE avr_op_MULS(int rd, int rr)
{
do_multiply(rd, rr, 1, 1, 0);
}
/* opcodes with two 3-bit register (Rd and Rr) operands */
/* 0000 0011 0ddd 0rrr | MULSU */
static OP_FUNC_TYPE avr_op_MULSU(int rd, int rr)
{
do_multiply(rd, rr, 1, 0, 0);
}
/* 0000 0011 0ddd 1rrr | FMUL */
static OP_FUNC_TYPE avr_op_FMUL(int rd, int rr)
{
do_multiply(rd, rr, 0, 0, 1);
}
/* 0000 0011 1ddd 0rrr | FMULS */
static OP_FUNC_TYPE avr_op_FMULS(int rd, int rr)
{
do_multiply(rd, rr, 1, 1, 1);
}
/* 0000 0011 1ddd 1rrr | FMULSU */
static OP_FUNC_TYPE avr_op_FMULSU(int rd, int rr)
{
do_multiply(rd, rr, 1, 0, 1);
}
//
---------------------------------------------------------------------------------
// ELF loader.
typedef uint16_t Elf32_Half;
typedef uint32_t Elf32_Word;
typedef uint32_t Elf32_Addr;
typedef uint32_t Elf32_Off;
#define EI_NIDENT 16
typedef struct
{
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
Elf32_Half e_type; /* Object file type */
Elf32_Half e_machine; /* Architecture */
Elf32_Word e_version; /* Object file version */
Elf32_Addr e_entry; /* Entry point virtual address */
Elf32_Off e_phoff; /* Program header table file offset */
Elf32_Off e_shoff; /* Section header table file offset */
Elf32_Word e_flags; /* Processor-specific flags */
Elf32_Half e_ehsize; /* ELF header size in bytes */
Elf32_Half e_phentsize; /* Program header table entry size */
Elf32_Half e_phnum; /* Program header table entry count */
Elf32_Half e_shentsize; /* Section header table entry size */
Elf32_Half e_shnum; /* Section header table entry count */
Elf32_Half e_shstrndx; /* Section header string table index */
} Elf32_Ehdr;
typedef struct
{
Elf32_Word p_type; /* Segment type */
Elf32_Off p_offset; /* Segment file offset */
Elf32_Addr p_vaddr; /* Segment virtual address */
Elf32_Addr p_paddr; /* Segment physical address */
Elf32_Word p_filesz; /* Segment size in file */
Elf32_Word p_memsz; /* Segment size in memory */
Elf32_Word p_flags; /* Segment flags */
Elf32_Word p_align; /* Segment alignment */
} Elf32_Phdr;
#define EI_CLASS 4
#define ELFCLASS32 1
#define EI_DATA 5
#define ELFDATA2LSB 1
#define EI_VERSION 5
#define EV_CURRENT 1
#define ET_EXEC 2
#define EM_AVR 0x53
#define PT_LOAD 1
#define DATA_VADDR 0x800000
static Elf32_Half get_elf32_half (Elf32_Half *v)
{
unsigned char *p = (unsigned char *)v;
return p[0] | (p[1] << 8);
}
static Elf32_Word get_elf32_word (Elf32_Word *v)
{
unsigned char *p = (unsigned char *)v;
return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
}
static void load_elf(FILE *f)
{
Elf32_Ehdr ehdr;
Elf32_Phdr phdr[16];
int nbr_phdr;
int i;
size_t res;
rewind(f);
if (fread(&ehdr, sizeof(ehdr), 1, f) != 1)
leave(EXIT_STATUS_ABORTED, "can't read ELF header");
if (ehdr.e_ident[EI_CLASS] != ELFCLASS32
|| ehdr.e_ident[EI_DATA] != ELFDATA2LSB
|| ehdr.e_ident[EI_VERSION] != EV_CURRENT)
leave(EXIT_STATUS_ABORTED, "bad ELF header");
if (get_elf32_half (&ehdr.e_type) != ET_EXEC
|| get_elf32_half(&ehdr.e_machine) != EM_AVR
|| get_elf32_word(&ehdr.e_version) != EV_CURRENT
|| get_elf32_half(&ehdr.e_phentsize) != sizeof (Elf32_Phdr))
leave(EXIT_STATUS_ABORTED, "ELF file is not an AVR executable");
nbr_phdr = get_elf32_half(&ehdr.e_phnum);
if ((unsigned)nbr_phdr > sizeof(phdr)/sizeof(*phdr))
leave(EXIT_STATUS_ABORTED, "ELF file contains too many PHDR");
if (fseek (f, get_elf32_word(&ehdr.e_phoff), SEEK_SET) != 0)
leave(EXIT_STATUS_ABORTED, "ELF file truncated");
res = fread(phdr, sizeof(Elf32_Phdr), nbr_phdr, f);
if (res != (size_t)nbr_phdr)
leave(EXIT_STATUS_ABORTED, "can't read PHDRs of ELF file");
for (i = 0; i < nbr_phdr; i++) {
Elf32_Addr addr = get_elf32_word(&phdr[i].p_paddr);
Elf32_Addr vaddr = get_elf32_word(&phdr[i].p_vaddr);
Elf32_Word filesz = get_elf32_word(&phdr[i].p_filesz);
Elf32_Word memsz= get_elf32_word(&phdr[i].p_memsz);
if (get_elf32_word (&phdr[i].p_type) != PT_LOAD)
continue;
if (filesz == 0)
continue;
/* if (verbose)
printf ("Load 0x%06x - 0x%06x\n",
(unsigned)addr, (unsigned)(addr + memsz)); */
if (addr + memsz > MAX_FLASH_SIZE)
leave(EXIT_STATUS_ABORTED,
"program too big to fit in flash");
if (fseek (f, get_elf32_word(&phdr[i].p_offset), SEEK_SET) != 0)
leave(EXIT_STATUS_ABORTED, "ELF file truncated");
if (fread(cpu_flash + addr, filesz, 1, f) != 1)
leave(EXIT_STATUS_ABORTED, "ELF file truncated");
/* Also copy in SRAM. */
if (flag_initialize_sram && vaddr >= DATA_VADDR)
memcpy(cpu_data + vaddr - DATA_VADDR,
cpu_flash + addr, filesz);
/* No need to clear memory. */
if ((unsigned)(addr + memsz) > program_size)
program_size = addr + memsz;
}
}
// ----------------------------------------------------------------------------
// parse command line arguments
static void load_to_flash(const char *filename)
{
FILE *fp;
char buf[EI_NIDENT];
size_t len;
fp = fopen(filename, "rb");
if (!fp)
leave(EXIT_STATUS_ABORTED, "can't open program file");
len = fread(buf, 1, sizeof (buf), fp);
if (len == sizeof(buf)
&& buf[0] == 0x7f
&& buf[1] == 'E'
&& buf[2] == 'L'
&& buf[3] == 'F') {
load_elf(fp);
}
else {
rewind(fp);
program_size = fread(cpu_flash, 1, MAX_FLASH_SIZE, fp);
}
fclose(fp);
}
static void usage (void)
{
const struct arch_desc *d;
printf("usage: avrtest [-d] [-m MAXCOUNT] [-mmcu=ARCH] program\n");
printf("Options:\n"
" -d Initialize SRAM from .data (for ELF program)\n"
" -m MAXCOUNT Execute at most MAXCOUNT instructions\n"
" -mmcu=ARCH Select instruction set for ARCH\n"
" ARCH is one of:");
for (d = arch_descs; d->name; d++)
printf ("%c%s", (d == arch_descs) ? ' ' : ',', d->name);
printf ("\n");
leave(EXIT_STATUS_ABORTED, "command line error");
}
static void parse_args(int argc, char *argv[])
{
int i;
//max_instr_count = 1000000000;
max_instr_count = 0;
// parse command line arguments
for (i = 1; i < argc; i++) {
// Use naive but very portable method to decode arguments.
if (strcmp(argv[i], "-d") == 0) {
flag_initialize_sram = 1;
}
else if (strcmp(argv[i], "-m") == 0) {
i++;
if (i >= argc)
usage();
max_instr_count = strtoul(argv[i], NULL, 0);
}
else if (strncmp(argv[i], "-mmcu=", 6) == 0) {
const struct arch_desc *d;
for (d = arch_descs; d->name; d++)
if (strcmp(argv[i] + 6, d->name) == 0) {
arch = d;
break;
}
if (d->name == NULL)
usage();
}
else {
if (program_name != NULL)
usage();
program_name = argv[i];
}
}
// setup default values
flash_addr_mask = (arch->pc_3bytes ? MAX_FLASH_SIZE : (1 << 17)) - 1;
if (program_name == NULL)
usage();
load_to_flash(program_name);
if (program_size & (~flash_addr_mask)) {
fprintf(stderr, "program is too large (size: %u, max: %u)\n",
program_size, flash_addr_mask + 1);
leave(EXIT_STATUS_ABORTED, "program too large");
}
}
//
---------------------------------------------------------------------------------
// flash pre-decoding functions
const opcode_data opcode_func_array[] = {
[avr_op_index_dummy] = {NULL,0,0,NULL}, // dummy entry to guarantee
that "zero" is an invalid function
[avr_op_index_ADC] = { avr_op_ADC, 1, 1, "ADC" },
[avr_op_index_ADD] = { avr_op_ADD, 1, 1, "ADD" },
[avr_op_index_ADIW] = { avr_op_ADIW, 1, 2, "ADIW" },
[avr_op_index_SBIW] = { avr_op_SBIW, 1, 2, "SBIW" },
[avr_op_index_AND] = { avr_op_AND, 1, 1, "AND" },
[avr_op_index_ANDI] = { avr_op_ANDI, 1, 1, "ANDI" },
[avr_op_index_ASR] = { avr_op_ASR, 1, 1, "ASR" },
[avr_op_index_BCLR] = { avr_op_BCLR, 1, 1, "BCLR" },
[avr_op_index_BLD] = { avr_op_BLD, 1, 1, "BLD" },
[avr_op_index_BRBC] = { avr_op_BRBC, 1, 1, "BRBC" }, //
may need extra cycles
[avr_op_index_BRBS] = { avr_op_BRBS, 1, 1, "BRBS" }, //
may need extra cycles
[avr_op_index_BSET] = { avr_op_BSET, 1, 1, "BSET" },
[avr_op_index_BST] = { avr_op_BST, 1, 1, "BST" },
[avr_op_index_CALL] = { avr_op_CALL, 2, 4, "CALL" }, //
may need extra cycles
[avr_op_index_CBI] = { avr_op_CBI, 1, 2, "CBI" },
[avr_op_index_COM] = { avr_op_COM, 1, 1, "COM" },
[avr_op_index_CP] = { avr_op_CP, 1, 1, "CP" },
[avr_op_index_CPC] = { avr_op_CPC, 1, 1, "CPC" },
[avr_op_index_CPI] = { avr_op_CPI, 1, 1, "CPI" },
[avr_op_index_CPSE] = { avr_op_CPSE, 1, 1, "CPSE" }, //
may need extra cycles
[avr_op_index_DEC] = { avr_op_DEC, 1, 1, "DEC" },
[avr_op_index_EICALL] = { avr_op_EICALL, 1, 4, "EICALL" },
[avr_op_index_EIJMP] = { avr_op_EIJMP, 1, 2, "EIJMP" },
[avr_op_index_ELPM] = { avr_op_ELPM, 1, 3, "ELPM" },
[avr_op_index_ELPM_Z] = { avr_op_ELPM_Z, 1, 3, "ELPM Z" },
[avr_op_index_ELPM_Z_incr]={ avr_op_ELPM_Z_incr, 1, 3, "ELPM Z+" },
[avr_op_index_EOR] = { avr_op_EOR, 1, 1, "EOR" },
[avr_op_index_ESPM] = { avr_op_ESPM, 1, 1, "ESPM" },
[avr_op_index_FMUL] = { avr_op_FMUL, 1, 2, "FMUL" },
[avr_op_index_FMULS] = { avr_op_FMULS, 1, 2, "FMULS" },
[avr_op_index_FMULSU] = { avr_op_FMULSU, 1, 2, "FMULSU" },
[avr_op_index_ICALL] = { avr_op_ICALL, 1, 3, "ICALL" }, //
may need extra cycles
[avr_op_index_IJMP] = { avr_op_IJMP, 1, 2, "IJMP" },
[avr_op_index_ILLEGAL] = { avr_op_ILLEGAL, 1, 1, "ILLEGAL" },
[avr_op_index_IN] = { avr_op_IN, 1, 1, "IN" },
[avr_op_index_INC] = { avr_op_INC, 1, 1, "INC" },
[avr_op_index_JMP] = { avr_op_JMP, 2, 3, "JMP" },
[avr_op_index_LDD_Y] = { avr_op_LDD_Y, 1, 2, "LDD r,Y+q" },
[avr_op_index_LDD_Z] = { avr_op_LDD_Z, 1, 2, "LDD r,Z+q" },
[avr_op_index_LDI] = { avr_op_LDI, 1, 1, "LDI" },
[avr_op_index_LDS] = { avr_op_LDS, 2, 2, "LDS" },
[avr_op_index_LD_X] = { avr_op_LD_X, 1, 2, "LD r,X" },
[avr_op_index_LD_X_decr] = { avr_op_LD_X_decr, 1, 2, "LD r,-X" },
[avr_op_index_LD_X_incr] = { avr_op_LD_X_incr, 1, 2, "LD r,X+" },
[avr_op_index_LD_Y_decr] = { avr_op_LD_Y_decr, 1, 2, "LD r,-Y" },
[avr_op_index_LD_Y_incr] = { avr_op_LD_Y_incr, 1, 2, "LD r,Y+" },
[avr_op_index_LD_Z_decr] = { avr_op_LD_Z_decr, 1, 2, "LD r,-Z" },
[avr_op_index_LD_Z_incr] = { avr_op_LD_Z_incr, 1, 2, "LD r,Z+" },
[avr_op_index_LPM] = { avr_op_LPM, 1, 3, "LPM" },
[avr_op_index_LPM_Z] = { avr_op_LPM_Z, 1, 3, "LPM Z" },
[avr_op_index_LPM_Z_incr]= { avr_op_LPM_Z_incr, 1, 3, "LPM Z+" },
[avr_op_index_LSR] = { avr_op_LSR, 1, 1, "LSR" },
[avr_op_index_MOV] = { avr_op_MOV, 1, 1, "MOV" },
[avr_op_index_MOVW] = { avr_op_MOVW, 1, 1, "MOVW" },
[avr_op_index_MUL] = { avr_op_MUL, 1, 2, "MUL" },
[avr_op_index_MULS] = { avr_op_MULS, 1, 2, "MULS" },
[avr_op_index_MULSU] = { avr_op_MULSU, 1, 2, "MULSU" },
[avr_op_index_NEG] = { avr_op_NEG, 1, 1, "NEG" },
[avr_op_index_NOP] = { avr_op_NOP, 1, 1, "NOP" },
[avr_op_index_OR] = { avr_op_OR, 1, 1, "OR" },
[avr_op_index_ORI] = { avr_op_ORI, 1, 1, "ORI" },
[avr_op_index_OUT] = { avr_op_OUT, 1, 1, "OUT" },
[avr_op_index_POP] = { avr_op_POP, 1, 2, "POP" },
[avr_op_index_PUSH] = { avr_op_PUSH, 1, 2, "PUSH" },
[avr_op_index_RCALL] = { avr_op_RCALL, 1, 3, "RCALL" }, //
may need extra cycles
[avr_op_index_RET] = { avr_op_RET, 1, 4, "RET" }, //
may need extra cycles
[avr_op_index_RETI] = { avr_op_RETI, 1, 4, "RETI" }, //
may need extra cycles
[avr_op_index_RJMP] = { avr_op_RJMP, 1, 2, "RJMP" },
[avr_op_index_ROR] = { avr_op_ROR, 1, 1, "ROR" },
[avr_op_index_SBC] = { avr_op_SBC, 1, 1, "SBC" },
[avr_op_index_SBCI] = { avr_op_SBCI, 1, 1, "SBCI" },
[avr_op_index_SBI] = { avr_op_SBI, 1, 2, "SBI" },
[avr_op_index_SBIC] = { avr_op_SBIC, 1, 1, "SBIC" }, //
may need extra cycles
[avr_op_index_SBIS] = { avr_op_SBIS, 1, 1, "SBIS" }, //
may need extra cycles
[avr_op_index_SBRC] = { avr_op_SBRC, 1, 1, "SBRC" }, //
may need extra cycles
[avr_op_index_SBRS] = { avr_op_SBRS, 1, 1, "SBRS" }, //
may need extra cycles
[avr_op_index_SLEEP] = { avr_op_SLEEP, 1, 1, "SLEEP" },
[avr_op_index_SPM] = { avr_op_SPM, 1, 1, "SPM" },
[avr_op_index_STD_Y] = { avr_op_STD_Y, 1, 2, "STD Y+q,r" },
[avr_op_index_STD_Z] = { avr_op_STD_Z, 1, 2, "STD Z+q,r" },
[avr_op_index_STS] = { avr_op_STS, 2, 2, "STS" },
[avr_op_index_ST_X] = { avr_op_ST_X, 1, 2, "ST X,r" },
[avr_op_index_ST_X_decr] = { avr_op_ST_X_decr, 1, 2, "ST -X,r" },
[avr_op_index_ST_X_incr] = { avr_op_ST_X_incr, 1, 2, "ST X+,r" },
[avr_op_index_ST_Y_decr] = { avr_op_ST_Y_decr, 1, 2, "ST -Y,r" },
[avr_op_index_ST_Y_incr] = { avr_op_ST_Y_incr, 1, 2, "ST Y+,r" },
[avr_op_index_ST_Z_decr] = { avr_op_ST_Z_decr, 1, 2, "ST -Z,r" },
[avr_op_index_ST_Z_incr] = { avr_op_ST_Z_incr, 1, 2, "ST Z+,r" },
[avr_op_index_SUB] = { avr_op_SUB, 1, 1, "SUB" },
[avr_op_index_SUBI] = { avr_op_SUBI, 1, 1, "SUBI" },
[avr_op_index_SWAP] = { avr_op_SWAP, 1, 1, "SWAP" },
[avr_op_index_WDR] = { avr_op_WDR, 1, 1, "WDR" },
};
static int decode_opcode(decoded_op *op, word opcode1, word opcode2)
{
int decode;
// start with default arguments
op->oper1 = 0;
op->oper2 = 0;
/* opcodes with no operands */
switch (opcode1) {
case 0x9519: return avr_op_index_EICALL; /* 1001 0101
0001 1001 | EICALL */
case 0x9419: return avr_op_index_EIJMP; /* 1001 0100
0001 1001 | EIJMP */
case 0x95D8: return avr_op_index_ELPM; /* 1001 0101
1101 1000 | ELPM */
case 0x95F8: return avr_op_index_ESPM; /* 1001 0101
1111 1000 | ESPM */
case 0x9509: return avr_op_index_ICALL; /* 1001 0101
0000 1001 | ICALL */
case 0x9409: return avr_op_index_IJMP; /* 1001 0100
0000 1001 | IJMP */
case 0x95C8: return avr_op_index_LPM; /* 1001 0101
1100 1000 | LPM */
case 0x0000: return avr_op_index_NOP; /* 0000 0000
0000 0000 | NOP */
case 0x9508: return avr_op_index_RET; /* 1001 0101
0000 1000 | RET */
case 0x9518: return avr_op_index_RETI; /* 1001 0101
0001 1000 | RETI */
case 0x9588: return avr_op_index_SLEEP; /* 1001 0101
1000 1000 | SLEEP */
case 0x95E8: return avr_op_index_SPM; /* 1001 0101
1110 1000 | SPM */
case 0x95A8: return avr_op_index_WDR; /* 1001 0101
1010 1000 | WDR */
}
// since there are a lot of opcodes that use these parameters, we
// extract them here to simplify each opcode decoding logic
op->oper1 = ((opcode1 >> 4) & 0x1F);
op->oper2 = (opcode1 & 0x0F) | ((opcode1 >> 5) & 0x0010);
/* opcodes with two 5-bit register (Rd and Rr) operands */
decode = opcode1 & ~(mask_Rd_5 | mask_Rr_5);
switch ( decode ) {
case 0x1C00: return avr_op_index_ADC; /* 0001 11rd dddd
rrrr | ADC or ROL */
case 0x0C00: return avr_op_index_ADD; /* 0000 11rd dddd
rrrr | ADD or LSL */
case 0x2000: return avr_op_index_AND; /* 0010 00rd dddd
rrrr | AND or TST */
case 0x1400: return avr_op_index_CP; /* 0001 01rd dddd
rrrr | CP */
case 0x0400: return avr_op_index_CPC; /* 0000 01rd dddd
rrrr | CPC */
case 0x1000: return avr_op_index_CPSE; /* 0001 00rd dddd
rrrr | CPSE */
case 0x2400: return avr_op_index_EOR; /* 0010 01rd dddd
rrrr | EOR or CLR */
case 0x2C00: return avr_op_index_MOV; /* 0010 11rd dddd
rrrr | MOV */
case 0x9C00: return avr_op_index_MUL; /* 1001 11rd dddd
rrrr | MUL */
case 0x2800: return avr_op_index_OR; /* 0010 10rd dddd
rrrr | OR */
case 0x0800: return avr_op_index_SBC; /* 0000 10rd dddd
rrrr | SBC */
case 0x1800: return avr_op_index_SUB; /* 0001 10rd dddd
rrrr | SUB */
}
/* opcodes with a single register (Rd) as operand */
decode = opcode1 & ~(mask_Rd_5);
switch (decode) {
case 0x9405: return avr_op_index_ASR; /* 1001 010d
dddd 0101 | ASR */
case 0x9400: return avr_op_index_COM; /* 1001 010d
dddd 0000 | COM */
case 0x940A: return avr_op_index_DEC; /* 1001 010d
dddd 1010 | DEC */
case 0x9006: return avr_op_index_ELPM_Z; /* 1001 000d
dddd 0110 | ELPM */
case 0x9007: return avr_op_index_ELPM_Z_incr; /* 1001 000d
dddd 0111 | ELPM */
case 0x9403: return avr_op_index_INC; /* 1001 010d
dddd 0011 | INC */
case 0x9000: /* 1001 000d dddd 0000
| LDS */
op->oper2 = opcode2; return avr_op_index_LDS;
case 0x900C: return avr_op_index_LD_X; /* 1001 000d
dddd 1100 | LD */
case 0x900E: return avr_op_index_LD_X_decr; /* 1001 000d
dddd 1110 | LD */
case 0x900D: return avr_op_index_LD_X_incr; /* 1001 000d
dddd 1101 | LD */
case 0x900A: return avr_op_index_LD_Y_decr; /* 1001 000d
dddd 1010 | LD */
case 0x9009: return avr_op_index_LD_Y_incr; /* 1001 000d
dddd 1001 | LD */
case 0x9002: return avr_op_index_LD_Z_decr; /* 1001 000d
dddd 0010 | LD */
case 0x9001: return avr_op_index_LD_Z_incr; /* 1001 000d
dddd 0001 | LD */
case 0x9004: return avr_op_index_LPM_Z; /* 1001 000d
dddd 0100 | LPM */
case 0x9005: return avr_op_index_LPM_Z_incr; /* 1001 000d
dddd 0101 | LPM */
case 0x9406: return avr_op_index_LSR; /* 1001 010d
dddd 0110 | LSR */
case 0x9401: return avr_op_index_NEG; /* 1001 010d
dddd 0001 | NEG */
case 0x900F: return avr_op_index_POP; /* 1001 000d
dddd 1111 | POP */
case 0x920F: return avr_op_index_PUSH; /* 1001 001d
dddd 1111 | PUSH */
case 0x9407: return avr_op_index_ROR; /* 1001 010d
dddd 0111 | ROR */
case 0x9200: /* 1001 001d dddd 0000
| STS */
op->oper2 = opcode2; return avr_op_index_STS;
case 0x920C: return avr_op_index_ST_X; /* 1001 001d
dddd 1100 | ST */
case 0x920E: return avr_op_index_ST_X_decr; /* 1001 001d
dddd 1110 | ST */
case 0x920D: return avr_op_index_ST_X_incr; /* 1001 001d
dddd 1101 | ST */
case 0x920A: return avr_op_index_ST_Y_decr; /* 1001 001d
dddd 1010 | ST */
case 0x9209: return avr_op_index_ST_Y_incr; /* 1001 001d
dddd 1001 | ST */
case 0x9202: return avr_op_index_ST_Z_decr; /* 1001 001d
dddd 0010 | ST */
case 0x9201: return avr_op_index_ST_Z_incr; /* 1001 001d
dddd 0001 | ST */
case 0x9402: return avr_op_index_SWAP; /* 1001 010d
dddd 0010 | SWAP */
}
/* opcodes with a register (Rd) and a constant data (K) as operands */
op->oper1 = ((opcode1 >> 4) & 0xF) | 0x10;
op->oper2 = (opcode1 & 0x0F) | ((opcode1 >> 4) & 0x00F0);
decode = opcode1 & ~(mask_Rd_4 | mask_K_8);
switch ( decode ) {
case 0x7000: return avr_op_index_ANDI; /* 0111 KKKK dddd
KKKK | CBR or ANDI */
case 0x3000: return avr_op_index_CPI; /* 0011 KKKK dddd
KKKK | CPI */
case 0xE000: return avr_op_index_LDI; /* 1110 KKKK dddd
KKKK | LDI or SER */
case 0x6000: return avr_op_index_ORI; /* 0110 KKKK dddd
KKKK | SBR or ORI */
case 0x4000: return avr_op_index_SBCI; /* 0100 KKKK dddd
KKKK | SBCI */
case 0x5000: return avr_op_index_SUBI; /* 0101 KKKK dddd
KKKK | SUBI */
}
/* opcodes with a register (Rd) and a register bit number (b) as
operands */
op->oper1 = ((opcode1 >> 4) & 0x1F);
op->oper2 = 1 << (opcode1 & 0x0007);
decode = opcode1 & ~(mask_Rd_5 | mask_reg_bit);
switch ( decode ) {
case 0xF800: return avr_op_index_BLD; /* 1111 100d dddd
0bbb | BLD */
case 0xFA00: return avr_op_index_BST; /* 1111 101d dddd
0bbb | BST */
case 0xFC00: return avr_op_index_SBRC; /* 1111 110d dddd
0bbb | SBRC */
case 0xFE00: return avr_op_index_SBRS; /* 1111 111d dddd
0bbb | SBRS */
}
/* opcodes with a relative 7-bit address (k) and a register bit number
(b) as operands */
op->oper1 = ((opcode1 >> 3) & 0x7F);
decode = opcode1 & ~(mask_k_7 | mask_reg_bit);
switch ( decode ) {
case 0xF400: return avr_op_index_BRBC; /* 1111 01kk
kkkk kbbb | BRBC */
case 0xF000: return avr_op_index_BRBS; /* 1111 00kk
kkkk kbbb | BRBS */
}
/* opcodes with a 6-bit address displacement (q) and a register (Rd) as
operands */
op->oper1 = ((opcode1 >> 4) & 0x1F);
op->oper2 = (opcode1 & 0x7) | ((opcode1 >> 7) & 0x18) | ((opcode1 >> 8)
& 0x20);
decode = opcode1 & ~(mask_Rd_5 | mask_q_displ);
switch ( decode ) {
case 0x8008: return avr_op_index_LDD_Y; /* 10q0 qq0d dddd
1qqq | LDD */
case 0x8000: return avr_op_index_LDD_Z; /* 10q0 qq0d dddd
0qqq | LDD */
case 0x8208: return avr_op_index_STD_Y; /* 10q0 qq1d dddd
1qqq | STD */
case 0x8200: return avr_op_index_STD_Z; /* 10q0 qq1d dddd
0qqq | STD */
}
/* opcodes with a absolute 22-bit address (k) operand */
op->oper1 = (opcode1 & 1) | ((opcode1 >> 3) & 0x3E);
op->oper2 = opcode2;
decode = opcode1 & ~(mask_k_22);
switch ( decode ) {
case 0x940E: return avr_op_index_CALL; /* 1001 010k kkkk
111k | CALL */
case 0x940C: return avr_op_index_JMP; /* 1001 010k kkkk
110k | JMP */
}
/* opcode1 with a sreg bit select (s) operand */
op->oper1 = 1 << ((opcode1 >> 4) & 0x07);
decode = opcode1 & ~(mask_sreg_bit);
switch ( decode ) {
/* BCLR takes place of CL{C,Z,N,V,S,H,T,I} */
/* BSET takes place of SE{C,Z,N,V,S,H,T,I} */
case 0x9488: return avr_op_index_BCLR; /* 1001 0100 1sss
1000 | BCLR */
case 0x9408: return avr_op_index_BSET; /* 1001 0100 0sss
1000 | BSET */
}
/* opcodes with a 6-bit constant (K) and a register (Rd) as operands */
op->oper1 = ((opcode1 >> 3) & 0x06) + 24;
op->oper2 = ((opcode1 & 0xF) | ((opcode1 >> 2) & 0x30));
decode = opcode1 & ~(mask_K_6 | mask_Rd_2);
switch ( decode ) {
case 0x9600: return avr_op_index_ADIW; /* 1001 0110 KKdd
KKKK | ADIW */
case 0x9700: return avr_op_index_SBIW; /* 1001 0111 KKdd
KKKK | SBIW */
}
/* opcodes with a 5-bit IO Addr (A) and register bit number (b) as
operands */
op->oper1 = ((opcode1 >> 3) & 0x1F) + IOBASE;
op->oper2 = 1 << (opcode1 & 0x0007);
decode = opcode1 & ~(mask_A_5 | mask_reg_bit);
switch ( decode ) {
case 0x9800: return avr_op_index_CBI; /* 1001 1000 AAAA
Abbb | CBI */
case 0x9A00: return avr_op_index_SBI; /* 1001 1010 AAAA
Abbb | SBI */
case 0x9900: return avr_op_index_SBIC; /* 1001 1001 AAAA
Abbb | SBIC */
case 0x9B00: return avr_op_index_SBIS; /* 1001 1011 AAAA
Abbb | SBIS */
}
/* opcodes with a 6-bit IO Addr (A) and register (Rd) as operands */
op->oper1 = ((opcode1 >> 4) & 0x1F);
op->oper2 = ((opcode1 & 0x0F) | ((opcode1 >> 5) & 0x30)) + IOBASE;
decode = opcode1 & ~(mask_A_6 | mask_Rd_5);
switch ( decode ) {
case 0xB000: return avr_op_index_IN; /* 1011 0AAd dddd
AAAA | IN */
case 0xB800: return avr_op_index_OUT; /* 1011 1AAd dddd
AAAA | OUT */
}
/* opcodes with a relative 12-bit address (k) operand */
op->oper2 = opcode1 & 0xFFF;
decode = opcode1 & ~(mask_k_12);
switch ( decode ) {
case 0xD000: return avr_op_index_RCALL; /* 1101 kkkk kkkk
kkkk | RCALL */
case 0xC000: return avr_op_index_RJMP; /* 1100 kkkk kkkk
kkkk | RJMP */
}
/* opcodes with two 4-bit register (Rd and Rr) operands */
decode = opcode1 & ~(mask_Rd_4 | mask_Rr_4);
switch ( decode ) {
case 0x0100:
op->oper1 = ((opcode1 >> 4) & 0x0F) << 1;
op->oper2 = (opcode1 & 0x0F) << 1;
return avr_op_index_MOVW; /* 0000 0001 dddd rrrr
| MOVW */
case 0x0200:
op->oper1 = ((opcode1 >> 4) & 0x0F) | 0x10;
op->oper2 = (opcode1 & 0x0F) | 0x10;
return avr_op_index_MULS; /* 0000 0010 dddd rrrr
| MULS */
}
/* opcodes with two 3-bit register (Rd and Rr) operands */
op->oper1 = ((opcode1 >> 4) & 0x07) | 0x10;
op->oper2 = (opcode1 & 0x07) | 0x10;
decode = opcode1 & ~(mask_Rd_3 | mask_Rr_3);
switch ( decode ) {
case 0x0300: return avr_op_index_MULSU; /* 0000 0011 0ddd
0rrr | MULSU */
case 0x0308: return avr_op_index_FMUL; /* 0000 0011 0ddd
1rrr | FMUL */
case 0x0380: return avr_op_index_FMULS; /* 0000 0011 1ddd
0rrr | FMULS */
case 0x0388: return avr_op_index_FMULSU; /* 0000 0011 1ddd
1rrr | FMULSU */
}
op->oper2 = opcode1;
return avr_op_index_ILLEGAL;
}
static void decode_flash(void)
{
word opcode1, opcode2;
unsigned int i;
for (i = 0; i < program_size; i += 2) {
opcode1 = cpu_flash[i] | (cpu_flash[i + 1] << 8);
opcode2 = cpu_flash[i + 2] | (cpu_flash[i + 3] << 8);
decoded_flash[i / 2].data_index =
decode_opcode(&decoded_flash[i / 2], opcode1, opcode2);
}
}
//
---------------------------------------------------------------------------------
// main execution loop
static void do_step(void)
{
decoded_op dop;
const opcode_data *data;
// fetch decoded instruction
dop = decoded_flash[cpu_PC];
if (!dop.data_index)
leave(EXIT_STATUS_ABORTED, "program counter out of program
space");
// execute instruction
data = &opcode_func_array[dop.data_index];
log_add_instr(data->hreadable);
cpu_PC += data->size;
add_program_cycles(data->cycles);
data->func(dop.oper1, dop.oper2);
log_dump_line();
}
static void execute(void)
{
dword count;
program_cycles = 0;
cpu_PC = 0;
if (!max_instr_count) {
for (;;)
do_step();
} else {
for (count = 0; count < max_instr_count; count++)
{
do_step();
}
leave(EXIT_STATUS_TIMEOUT, "instruction count limit reached");
}
}
// main: as simple as it gets
int main(int argc, char *argv[])
{
parse_args(argc, argv);
init_flag_update_tables();
decode_flash();
execute();
return 0;
}
- Re: [avr-gcc-list] Problems with avrtest simulator and avr-gcc testsuite., (continued)
- Re: [avr-gcc-list] Problems with avrtest simulatorand avr-gcc testsuite., Weddington, Eric, 2011/04/19
- Re: [avr-gcc-list] Problems with avrtest simulatorand avr-gcc testsuite., Paulo Marques, 2011/04/19
- Re: [avr-gcc-list] Problems with avrtest simulatorand avr-gcc testsuite., Georg-Johann Lay, 2011/04/19
- Re: [avr-gcc-list] Problems with avrtest simulatorand avr-gcc testsuite., Paulo Marques, 2011/04/20
- Re: [avr-gcc-list] Problems with avrtest simulatorand avr-gcc testsuite., Georg-Johann Lay, 2011/04/20
- Re: [avr-gcc-list] Problems with avrtest simulatorand avr-gcc testsuite., Paulo Marques, 2011/04/20
- Re: [avr-gcc-list] Problems with avrtest simulatorand avr-gcc testsuite.,
Georg-Johann Lay <=
- Re: [avr-gcc-list] Problems with avrtest simulator and avr-gcc testsuite., Weddington, Eric, 2011/04/28