⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 avr.cpp

📁 plc软件的源代码 支持PIC ATMEGA单片机
💻 CPP
📖 第 1 页 / 共 4 页
字号:
//-----------------------------------------------------------------------------
// Copyright 2007 Jonathan Westhues
//
// This file is part of LDmicro.
// 
// LDmicro 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 3 of the License, or
// (at your option) any later version.
// 
// LDmicro 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 LDmicro.  If not, see <http://www.gnu.org/licenses/>.
//------
//
// An AVR assembler, for our own internal use, plus routines to generate
// code from the ladder logic structure, plus routines to generate the
// runtime needed to schedule the cycles.
// Jonathan Westhues, Oct 2004
//-----------------------------------------------------------------------------
#include <windows.h>
#include <stdio.h>
#include <setjmp.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>

#include "ldmicro.h"
#include "intcode.h"

// not complete; just what I need
typedef enum AvrOpTag {
    OP_VACANT,
    OP_ADC,
    OP_ADD,
    OP_ASR,
    OP_BRCC,
    OP_BRCS,
    OP_BREQ,
    OP_BRGE,
    OP_BRLO,
    OP_BRLT,
    OP_BRNE,
    OP_CBR,
    OP_CLC,
    OP_CLR,
    OP_COM,
    OP_CP,
    OP_CPC,
    OP_DEC,
    OP_EOR,
    OP_ICALL,
    OP_IJMP,
    OP_INC,
    OP_LDI,
    OP_LD_X,
    OP_MOV,
    OP_OUT,
    OP_RCALL,
    OP_RET,
    OP_RETI,
    OP_RJMP,
    OP_ROR,
    OP_SEC,
    OP_SBC,
    OP_SBCI,
    OP_SBR,
    OP_SBRC,
    OP_SBRS,
    OP_ST_X,
    OP_SUB,
    OP_SUBI,
    OP_TST,
    OP_WDR,
} AvrOp;

typedef struct AvrInstructionTag {
    AvrOp       op;
    DWORD       arg1;
    DWORD       arg2;
} AvrInstruction;

#define MAX_PROGRAM_LEN 128*1024
static AvrInstruction AvrProg[MAX_PROGRAM_LEN];
static DWORD AvrProgWriteP;

// For yet unresolved references in jumps
static DWORD FwdAddrCount;

// Fancier: can specify a forward reference to the high or low octet of a
// 16-bit address, which is useful for indirect jumps.
#define FWD_LO(x) ((x) | 0x20000000)
#define FWD_HI(x) ((x) | 0x40000000)

// Address to jump to when we finish one PLC cycle
static DWORD BeginningOfCycleAddr;

// Address of the multiply subroutine, and whether we will have to include it
static DWORD MultiplyAddress;
static BOOL MultiplyUsed;
// and also divide
static DWORD DivideAddress;
static BOOL DivideUsed;

// For EEPROM: we queue up characters to send in 16-bit words (corresponding
// to the integer variables), but we can actually just program 8 bits at a
// time, so we need to store the high byte somewhere while we wait.
static DWORD EepromHighByte;
static DWORD EepromHighByteWaitingAddr;
static int EepromHighByteWaitingBit;

// Some useful registers, unfortunately many of which are in different places
// on different AVRs! I consider this a terrible design choice by Atmel.
static DWORD REG_TIMSK;
static DWORD REG_TIFR;
#define REG_OCR1AH  0x4b
#define REG_OCR1AL  0x4a
#define REG_TCCR1A  0x4f
#define REG_TCCR1B  0x4e
#define REG_SPH     0x5e
#define REG_SPL     0x5d
#define REG_ADMUX   0x27
#define REG_ADCSRA  0x26
#define REG_ADCL    0x24
#define REG_ADCH    0x25

static DWORD REG_UBRRH;
static DWORD REG_UBRRL;
static DWORD REG_UCSRB;
static DWORD REG_UCSRA;
static DWORD REG_UDR;

#define REG_OCR2    0x43
#define REG_TCCR2   0x45

#define REG_EEARH   0x3f
#define REG_EEARL   0x3e
#define REG_EEDR    0x3d
#define REG_EECR    0x3c


static int IntPc;

static void CompileFromIntermediate(void);

//-----------------------------------------------------------------------------
// Wipe the program and set the write pointer back to the beginning. Also
// flush all the state of the register allocators etc.
//-----------------------------------------------------------------------------
static void WipeMemory(void)
{
    memset(AvrProg, 0, sizeof(AvrProg));
    AvrProgWriteP = 0;
}

//-----------------------------------------------------------------------------
// Store an instruction at the next spot in program memory.  Error condition
// if this spot is already filled. We don't actually assemble to binary yet;
// there may be references to resolve.
//-----------------------------------------------------------------------------
static void Instruction(AvrOp op, DWORD arg1, DWORD arg2)
{
    if(AvrProg[AvrProgWriteP].op != OP_VACANT) oops();

    AvrProg[AvrProgWriteP].op = op;
    AvrProg[AvrProgWriteP].arg1 = arg1;
    AvrProg[AvrProgWriteP].arg2 = arg2;
    AvrProgWriteP++;
}

//-----------------------------------------------------------------------------
// Allocate a unique descriptor for a forward reference. Later that forward
// reference gets assigned to an absolute address, and we can go back and
// fix up the reference.
//-----------------------------------------------------------------------------
static DWORD AllocFwdAddr(void)
{
    FwdAddrCount++;
    return 0x80000000 | FwdAddrCount;
}

//-----------------------------------------------------------------------------
// Go back and fix up the program given that the provided forward address
// corresponds to the next instruction to be assembled.
//-----------------------------------------------------------------------------
static void FwdAddrIsNow(DWORD addr)
{
    if(!(addr & 0x80000000)) oops();

    DWORD i;
    for(i = 0; i < AvrProgWriteP; i++) {
        if(AvrProg[i].arg1 == addr) {
            AvrProg[i].arg1 = AvrProgWriteP;
        } else if(AvrProg[i].arg2 == FWD_LO(addr)) {
            AvrProg[i].arg2 = (AvrProgWriteP & 0xff);
        } else if(AvrProg[i].arg2 == FWD_HI(addr)) {
            AvrProg[i].arg2 = AvrProgWriteP >> 8;
        }
    }
}

//-----------------------------------------------------------------------------
// Given an opcode and its operands, assemble the 16-bit instruction for the
// AVR. Check that the operands do not have more bits set than is meaningful;
// it is an internal error if they do not. Needs to know what address it is
// being assembled to so that it generate relative jumps; internal error if
// a relative jump goes out of range.
//-----------------------------------------------------------------------------
static DWORD Assemble(DWORD addrAt, AvrOp op, DWORD arg1, DWORD arg2)
{
#define CHECK(v, bits) if((v) != ((v) & ((1 << (bits))-1))) oops()
    switch(op) {
        case OP_ASR:
            CHECK(arg1, 5); CHECK(arg2, 0);
            return (9 << 12) | (2 << 9) | (arg1 << 4) | 5;

        case OP_ROR:
            CHECK(arg1, 5); CHECK(arg2, 0);
            return (9 << 12) | (2 << 9) | (arg1 << 4) | 7;

        case OP_ADD:
            CHECK(arg1, 5); CHECK(arg2, 5);
            return (3 << 10) | ((arg2 & 0x10) << 5) | (arg1 << 4) |
                (arg2 & 0x0f);

        case OP_ADC:
            CHECK(arg1, 5); CHECK(arg2, 5);
            return (7 << 10) | ((arg2 & 0x10) << 5) | (arg1 << 4) |
                (arg2 & 0x0f);

        case OP_EOR:
            CHECK(arg1, 5); CHECK(arg2, 5);
            return (9 << 10) | ((arg2 & 0x10) << 5) | (arg1 << 4) |
                (arg2 & 0x0f);

        case OP_SUB:
            CHECK(arg1, 5); CHECK(arg2, 5);
            return (6 << 10) | ((arg2 & 0x10) << 5) | (arg1 << 4) |
                (arg2 & 0x0f);

        case OP_SBC:
            CHECK(arg1, 5); CHECK(arg2, 5);
            return (2 << 10) | ((arg2 & 0x10) << 5) | (arg1 << 4) |
                (arg2 & 0x0f);

        case OP_CP:
            CHECK(arg1, 5); CHECK(arg2, 5);
            return (5 << 10) | ((arg2 & 0x10) << 5) | (arg1 << 4) |
                (arg2 & 0x0f);

        case OP_CPC:
            CHECK(arg1, 5); CHECK(arg2, 5);
            return (1 << 10) | ((arg2 & 0x10) << 5) | (arg1 << 4) |
                (arg2 & 0x0f);

        case OP_COM:
            CHECK(arg1, 5); CHECK(arg2, 0);
            return (9 << 12) | (2 << 9) | (arg1 << 4);

        case OP_SBR:
            CHECK(arg1, 5); CHECK(arg2, 8);
            if(!(arg1 & 0x10)) oops();
            arg1 &= ~0x10;
            return (6 << 12) | ((arg2 & 0xf0) << 4) | (arg1 << 4) |
                (arg2 & 0x0f);

        case OP_CBR:
            CHECK(arg1, 5); CHECK(arg2, 8);
            if(!(arg1 & 0x10)) oops();
            arg1 &= ~0x10;
            arg2 = ~arg2;
            return (7 << 12) | ((arg2 & 0xf0) << 4) | (arg1 << 4) |
                (arg2 & 0x0f);

        case OP_INC:
            CHECK(arg1, 5); CHECK(arg2, 0);
            return (0x4a << 9) | (arg1 << 4) | 3;

        case OP_DEC:
            CHECK(arg1, 5); CHECK(arg2, 0);
            return (0x4a << 9) | (arg1 << 4) | 10;

        case OP_SUBI:
            CHECK(arg1, 5); CHECK(arg2, 8);
            if(!(arg1 & 0x10)) oops();
            arg1 &= ~0x10;
            return (5 << 12) | ((arg2 & 0xf0) << 4) | (arg1 << 4) |
                (arg2 & 0x0f);

        case OP_SBCI:
            CHECK(arg1, 5); CHECK(arg2, 8);
            if(!(arg1 & 0x10)) oops();
            arg1 &= ~0x10;
            return (4 << 12) | ((arg2 & 0xf0) << 4) | (arg1 << 4) |
                (arg2 & 0x0f);

        case OP_TST:
            CHECK(arg1, 5); CHECK(arg2, 0);
            return (8 << 10) | ((arg1 & 0x10) << 4) | ((arg1 & 0x10) << 5) |
                ((arg1 & 0xf) << 4) | (arg1 & 0xf);

        case OP_SEC:
            CHECK(arg1, 0); CHECK(arg2, 0);
            return 0x9408;

        case OP_CLC:
            CHECK(arg1, 0); CHECK(arg2, 0);
            return 0x9488;

        case OP_IJMP:
            CHECK(arg1, 0); CHECK(arg2, 0);
            return 0x9409;

        case OP_ICALL:
            CHECK(arg1, 0); CHECK(arg2, 0);
            return 0x9509;

        case OP_RJMP:
            CHECK(arg2, 0);
            arg1 = arg1 - addrAt - 1;
            if(((int)arg1) > 2047 || ((int)arg1) < -2048) oops();
            arg1 &= (4096-1);
            return (12 << 12) | arg1;

        case OP_RCALL:
            CHECK(arg2, 0);
            arg1 = arg1 - addrAt - 1;
            if(((int)arg1) > 2047 || ((int)arg1) < -2048) oops();
            arg1 &= (4096-1);
            return (13 << 12) | arg1;

        case OP_RETI:
            return 0x9518;

        case OP_RET:
            return 0x9508;

        case OP_SBRC:
            CHECK(arg1, 5); CHECK(arg2, 3);
            return (0x7e << 9) | (arg1 << 4) | arg2;

        case OP_SBRS:
            CHECK(arg1, 5); CHECK(arg2, 3);
            return (0x7f << 9) | (arg1 << 4) | arg2;

        case OP_BREQ:
            CHECK(arg2, 0);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -