📄 avr.cpp
字号:
//-----------------------------------------------------------------------------
// 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 + -