code: /**
   * Copyright (c) 2004-2005, Regents of the University of California
   * All rights reserved.
   *
   * 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 University of California, Los Angeles nor the
   * names of its 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.
   */
  
  package avrora.syntax;
  
  import avrora.arch.legacy.*;
  import avrora.core.*;
  import avrora.syntax.atmel.AtmelParser;
  import cck.parser.AbstractParseException;
  import cck.parser.AbstractToken;
  import cck.text.StringUtil;
  import cck.text.Verbose;
  import java.io.FileInputStream;
  import java.io.FileNotFoundException;
  import java.util.*;
  
  /**
   * The <code>Module</code> class collects together the instructions and data into an AVR assembly program.
   *
   * @author Ben L. Titzer
   */
  public class Module implements Context {
  
      public final HashMap<String, LegacyRegister> definitions;
      public final HashMap<String, Integer> constants;
      public final HashMap<String, Item> labels;
      public final AVRErrorReporter ERROR;
  
      public final boolean caseSensitivity;
      public final boolean useByteAddresses;
  
      protected Seg segment;
      protected Seg programSegment;
      protected Seg dataSegment;
      protected Seg eepromSegment;
  
      public Program newprogram;
  
      private List<Item> itemList;
  
      static Verbose.Printer modulePrinter = Verbose.getVerbosePrinter("loader");
  
      private static final SyntacticOperand[] NO_OPERANDS = { };
      private SourceMapping sourceMapping;
  
      protected class Seg {
  
          private final String name;
          private final boolean acceptsInstrs;
          private final boolean acceptsData;
  
          int lowest_address;
          int highest_address;
  
          int cursor;
          final int align;
  
          Seg(String n, int a, int o, boolean i, boolean d) {
              name = n;
              acceptsInstrs = i;
              acceptsData = d;
              align = a;
              lowest_address = highest_address = cursor = o;
          }
  
          public Module getModule() {
              return Module.this;
          }
  
          public String getName() {
              return name;
          }
  
          public void writeDataBytes(ASTNode loc, int baddr, byte[] b) {
              if (acceptsData) newprogram.writeProgramBytes(b, baddr);
              else ERROR.DataCannotBeInSegment(name, loc);
          }
  
          public void writeDataByte(ASTNode loc, int baddr, byte b) {
              if (acceptsData) newprogram.writeProgramByte(b, baddr);
              else ERROR.DataCannotBeInSegment(name, loc);
          }
  
          public void writeInstr(AbstractToken loc, int baddr, LegacyInstr i) {
              if (acceptsInstrs) newprogram.writeInstr(i, baddr);
              else ERROR.InstructionCannotBeInSegment(name, loc);
          }
  
          public void addLabel(String name, int vma_addr, int lma_addr, int size) {
              sourceMapping.newLocation(this.name, name, vma_addr, lma_addr, size);
          }
  
          public void setOrigin(int org) {
              cursor = org;
              if (org < lowest_address) lowest_address = org;
              if (org > highest_address) highest_address = org;
          }
  
          public int getCurrentAddress() {
              return cursor;
          }
  
          public void advance(int dist) {
              cursor = align(cursor + dist, align);
              if (cursor > highest_address) highest_address = cursor;
          }
      }
  
  
      public Module(boolean cs, boolean ba) {
          caseSensitivity = cs;
          useByteAddresses = ba;
  
          definitions = new HashMap<String, LegacyRegister>();
          constants = new HashMap<String, Integer>();
          labels = new HashMap<String, Item>();
  
          programSegment = new Seg(".text", 2, 0, true, true);
          dataSegment = new Seg(".data", 1, 32, false, false);
          eepromSegment = new Seg(".eeprom", 1, 0, false, false);
  
          segment = programSegment;
          itemList = new LinkedList<Item>();
  
          addGlobalConstants();
  
          ERROR = new AVRErrorReporter();
      }
  
  
      // .def directive
      public void addDefinition(AbstractToken name, AbstractToken rtok) {
          modulePrinter.println(".def " + labelName(name) + " = " + labelName(rtok));
          addItem(new Item.RegisterAlias(segment, name, rtok));
      }
  
      // .equ directive
      public void addConstant(AbstractToken name, Expr val) {
          modulePrinter.println(".equ " + labelName(name) + " = " + val);
          addItem(new Item.NamedConstant(segment, name, val));
      }
  
      // .dseg directive
      public void enterDataSegment() {
          modulePrinter.println("enter segment: data");
          segment = dataSegment;
      }
  
      // .cseg directive
      public void enterProgramSegment() {
          modulePrinter.println("enter segment: program");
          segment = programSegment;
      }
  
      // .eseg directive
      public void enterEEPROMSegment() {
          modulePrinter.println("enter segment: eeprom");
          segment = eepromSegment;
      }
  
      private void print(String what, ASTNode where) {
          String addr = StringUtil.addrToString(segment.getCurrentAddress());
          modulePrinter.println(segment.getName() + " @ " + addr + ": " + what + " on line " + where.getLeftMostToken().beginLine);
      }
  
      private void print(String what, AbstractToken where) {
          String addr = StringUtil.addrToString(segment.getCurrentAddress());
          modulePrinter.println(segment.getName() + " @ " + addr + ": " + what + " on line " + where.beginLine);
      }
  
      // .db directive
      public void addDataBytes(ExprList l) {
          print("addDataBytes", l);
          addItem(new Item.InitializedData(segment, l, 1));
      }
  
      // .dw directive
      public void addDataWords(ExprList l) {
          print("addDataWords", l);
          addItem(new Item.InitializedData(segment, l, 2));
      }
  
      // .dd directive
      public void addDataDoubleWords(ExprList l) {
          print("addDataDoubleWords", l);
          addItem(new Item.InitializedData(segment, l, 4));
      }
  
      // .org directive
      public void setOrigin(Expr.Constant c) {
          int result = c.evaluate(segment.getCurrentAddress(), this);
          modulePrinter.println("setOrigin(" + c + ") -> " + result);
          segment.setOrigin(result);
      }
  
      // .byte directive
      public void reserveBytes(Expr e, Expr f) {
          // TODO: fill section with particular value
          int result = e.evaluate(segment.getCurrentAddress(), this);
          modulePrinter.println("reserveBytes(" + e + ") -> " + result);
          addItem(new Item.UninitializedData(segment, result));
      }
  
      // .include directive
      public void includeFile(AbstractToken fname) throws AbstractParseException {
          try {
              modulePrinter.println("includeFile(" + fname.image + ')');
              String fn = StringUtil.trimquotes(fname.image);
              AtmelParser parser = new AtmelParser(new FileInputStream(fn), this, fn);
              // TODO: handle infinite include recursion possibility
              parser.Module();
          } catch (FileNotFoundException e) {
              ERROR.IncludeFileNotFound(fname);
          }
      }
  
  
      // <instruction>
      public void addInstruction(String variant, AbstractToken name) {
          String v = StringUtil.quote(variant);
          print(StringUtil.embed("addInstr", v), name);
          SyntacticOperand[] o = NO_OPERANDS;
          makeInstr(variant, name, o);
      }
  
      // <instruction> <operand>
      public void addInstruction(String variant, AbstractToken name, SyntacticOperand o1) {
          String v = StringUtil.quote(variant);
          print(StringUtil.embed("addInstr", v, o1), name);
          SyntacticOperand[] o = { o1 };
          makeInstr(variant, name, o);
      }
  
      // <instruction> <operand> <operand>
      public void addInstruction(String variant, AbstractToken name, SyntacticOperand o1, SyntacticOperand o2) {
          String v = StringUtil.quote(variant);
          print(StringUtil.embed("addInstr", v, o1, o2), name);
          SyntacticOperand[] o = { o1, o2 };
          makeInstr(variant, name, o);
      }
  
      // <instruction> <operand> <operand> <operand>
      public void addInstruction(String variant, AbstractToken name, SyntacticOperand o1, SyntacticOperand o2, SyntacticOperand o3) {
          String v = StringUtil.quote(variant);
          print(StringUtil.embed("addInstr", v, o1, o2, o3), name);
          SyntacticOperand[] o = { o1, o2, o3 };
          makeInstr(variant, name, o);
      }
  
      // <label>
      public void addLabel(AbstractToken name) {
          Item.Label li = new Item.Label(segment, name);
          addItem(li);
          labels.put(name.image.toLowerCase(), li);
      }
  
      private void makeInstr(String variant, AbstractToken name, SyntacticOperand[] o) {
          LegacyInstrProto proto = LegacyInstrSet.getPrototype(variant);
          addItem(new Item.Instruction(segment, variant, name, proto, o));
      }
  
  
      public Program build() {
          newprogram = new Program(LegacyArchitecture.INSTANCE, programSegment.lowest_address, programSegment.highest_address);
  
          sourceMapping = new SourceMapping(newprogram);
          newprogram.setSourceMapping(sourceMapping);
          for (Item pos : itemList) {
              simplify(pos);
          }
  
          return newprogram;
      }
  
      protected void simplify(Item i) {
          Item.Instruction instr = null;
          if (i instanceof Item.Instruction) instr = (Item.Instruction)i;
  
          try {
              i.simplify();
          } catch (LegacyInstr.ImmediateRequired e) {
              ERROR.ConstantExpected((SyntacticOperand)e.operand);
          } catch (LegacyInstr.InvalidImmediate e) {
              ERROR.ConstantOutOfRange(instr.operands[e.number - 1], e.value, StringUtil.interval(e.low, e.high));
          } catch (LegacyInstr.InvalidRegister e) {
              ERROR.IncorrectRegister(instr.operands[e.number - 1], e.register, e.set.toString());
          } catch (LegacyInstr.RegisterRequired e) {
              ERROR.RegisterExpected((SyntacticOperand)e.operand);
          } catch (LegacyInstr.WrongNumberOfOperands e) {
              ERROR.WrongNumberOfOperands(instr.name, e.found, e.expected);
          }
      }
  
      public void addVariable(String name, int value) {
          constants.put(labelName(name), new Integer(value));
      }
  
      public void addRegisterName(String name, AbstractToken reg) {
          LegacyRegister register = LegacyRegister.getRegisterByName(reg.image);
          if (register == null) ERROR.UnknownRegister(reg);
  
          definitions.put(labelName(name), register);
      }
  
      public LegacyRegister getRegister(AbstractToken tok) {
          String name = labelName(tok);
          LegacyRegister reg = LegacyRegister.getRegisterByName(name);
          if (reg == null) reg = (LegacyRegister)definitions.get(name);
  
          if (reg == null) ERROR.UnknownRegister(tok);
          return reg;
      }
  
      public int getVariable(AbstractToken tok) {
          String name = labelName(tok);
  
          Integer v = (Integer)constants.get(name);
          if (v == null) {
              Item.Label li = (Item.Label)labels.get(name);
              if (li == null) ERROR.UnknownVariable(tok);
              if (li.segment == programSegment && !useByteAddresses) return li.getByteAddress() >> 1;
              else return li.getByteAddress();
          } else return v.intValue();
      }
  
      public SyntacticOperand.Expr newOperand(Expr e) {
          return new SyntacticOperand.Expr(e, useByteAddresses);
      }
  
      public SyntacticOperand.Register newOperand(AbstractToken tok) {
          return new SyntacticOperand.Register(tok);
      }
  
      protected void addItem(Item i) {
          itemList.add(i);
          segment.advance(i.itemSize());
      }
  
      public static int align(int val, int width) {
          if (val % width == 0) return val;
          return val + (width - (val % width));
      }
  
      private String labelName(AbstractToken tok) {
          if (caseSensitivity) return tok.image;
          else return tok.image.toLowerCase();
      }
  
      private String labelName(String n) {
          if (caseSensitivity) return n;
          else return n.toLowerCase();
      }
  
  
      private void addGlobalConstants() {
          // TODO: pull out machine-specific constants to somewhere.
          constant("RAMEND", 4095);
  
          // TODO: use numbers definition in IORegisterConstants
          ioreg("UCSR1C", 0x9D);
          ioreg("UDR1", 0x9C);
          ioreg("UCSR1A", 0x9B);
          ioreg("UCSR1B", 0x9A);
          ioreg("UBRR1L", 0x99);
          ioreg("UBRR1H", 0x98);
  
          ioreg("UCSR0C", 0x95);
  
          ioreg("UBRR0H", 0x90);
  
          ioreg("TCCR3C", 0x8C);
          ioreg("TCCR3A", 0x8B);
          ioreg("TCCR3B", 0x8A);
          ioreg("TCNT3H", 0x89);
          ioreg("TCNT3L", 0x88);
          ioreg("OCR3AH", 0x87);
          ioreg("OCR3AL", 0x86);
          ioreg("OCR3BH", 0x85);
          ioreg("OCR3BL", 0x84);
          ioreg("OCR3CH", 0x83);
          ioreg("OCR3CL", 0x82);
          ioreg("ICR3H", 0x81);
          ioreg("ICR3L", 0x80);
  
          ioreg("ETIMSK", 0x7D);
          ioreg("ETIFR", 0x7C);
  
          ioreg("TCCR1C", 0x7A);
          ioreg("OCR1CH", 0x79);
          ioreg("OCR1CL", 0x78);
  
          ioreg("TWCR", 0x74);
          ioreg("TWDR", 0x73);
          ioreg("TWAR", 0x72);
          ioreg("TWSR", 0x71);
          ioreg("TWBR", 0x70);
          ioreg("OSCCAL", 0x6F);
  
          ioreg("XMCRA", 0x6D);
          ioreg("XMCRB", 0x6C);
  
          ioreg("EICRA", 0x6A);
  
          ioreg("SPMCSR", 0x68);
  
          ioreg("PORTG", 0x65);
          ioreg("DDRG", 0x64);
          ioreg("PING", 0x63);
          ioreg("PORTF", 0x62);
          ioreg("DDRF", 0x61);
  
          ioreg("SREG", 0x3F);
          ioreg("SPH", 0x3E);
          ioreg("SPL", 0x3D);
          ioreg("XDIV", 0x3C);
          ioreg("RAMPZ", 0x3B);
          ioreg("EICRB", 0x3A);
          ioreg("EIMSK", 0x39);
          ioreg("EIFR", 0x38);
          ioreg("TIMSK", 0x37);
          ioreg("TIFR", 0x36);
          ioreg("MCUCR", 0x35);
          ioreg("MCUCSR", 0x34);
          ioreg("TCCR0", 0x33);
          ioreg("TCNT0", 0x32);
          ioreg("OCR0", 0x31);
          ioreg("ASSR", 0x30);
          ioreg("TCCR1A", 0x2F);
          ioreg("TCCR1B", 0x2E);
          ioreg("TCNT1H", 0x2D);
          ioreg("TCNT1L", 0x2C);
          ioreg("OCR1AH", 0x2B);
          ioreg("OCR1AL", 0x2A);
          ioreg("OCR1BH", 0x29);
          ioreg("OCR1BL", 0x28);
          ioreg("ICR1H", 0x27);
          ioreg("ICR1L", 0x26);
          ioreg("TCCR2", 0x25);
          ioreg("TCNT2", 0x24);
          ioreg("OCR2", 0x23);
          ioreg("OCDR", 0x22);
          ioreg("WDTCR", 0x21);
          ioreg("SFIOR", 0x20);
          ioreg("EEARH", 0x1F);
          ioreg("EEARL", 0x1E);
          ioreg("EEDR", 0x1D);
          ioreg("EECR", 0x1C);
          ioreg("PORTA", 0x1B);
          ioreg("DDRA", 0x1A);
          ioreg("PINA", 0x19);
          ioreg("PORTB", 0x18);
          ioreg("DDRB", 0x17);
          ioreg("PINB", 0x16);
          ioreg("PORTC", 0x15);
          ioreg("DDRC", 0x14);
          ioreg("PINC", 0x13);
          ioreg("PORTD", 0x12);
          ioreg("DDRD", 0x11);
          ioreg("PIND", 0x10);
          ioreg("SPDR", 0x0F);
          ioreg("SPSR", 0x0E);
          ioreg("SPCR", 0x0D);
          ioreg("UDR0", 0x0C);
          ioreg("UCSR0A", 0x0B);
          ioreg("UCSR0B", 0x0A);
          ioreg("UBRR0L", 0x09);
          ioreg("ACSR", 0x08);
          ioreg("ADMUX", 0x07);
          ioreg("ADCSRA", 0x06);
          ioreg("ADCH", 0x05);
          ioreg("ADCL", 0x04);
          ioreg("PORTE", 0x03);
          ioreg("DDRE", 0x02);
          ioreg("PINE", 0x01);
          ioreg("PINF", 0x00);
      }
  
      private void constant(String name, int value) {
          constants.put(name.toLowerCase(), new Integer(value));
      }
  
      private void ioreg(String name, int offset) {
          constants.put(name.toLowerCase(), new Integer(offset));
      }
  
  }
  