/* * Simulator of microcontrollers (avr.cc) * * Copyright (C) 1999,99 Drotos Daniel, Talker Bt. * * To contact author send email to drdani@mazsola.iit.uni-miskolc.hu * */ /* This file is part of microcontroller simulator: ucsim. UCSIM 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. UCSIM 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 UCSIM; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*@1@*/ #include "ddconfig.h" #include #include #include #include "i_string.h" // prj #include "pobjcl.h" // sim #include "simcl.h" #include "memcl.h" // local #include "portcl.h" #include "avrcl.h" #include "glob.h" #include "regsavr.h" // Addresses are IRAM addresses! static struct name_entry sfr_tabl[]= { { CPU_ALL_AVR, 0x001a, "XL"}, { CPU_ALL_AVR, 0x001a, "XL" }, { CPU_ALL_AVR, 0x001b, "XH" }, { CPU_ALL_AVR, 0x001c, "YL" }, { CPU_ALL_AVR, 0x001d, "YH" }, { CPU_ALL_AVR, 0x001e, "ZL" }, { CPU_ALL_AVR, 0x001f, "ZH" }, { CPU_ALL_AVR, 0x0024, "ADCL" }, { CPU_ALL_AVR, 0x0025, "ADCH" }, { CPU_ALL_AVR, 0x0026, "ADCSR" }, { CPU_ALL_AVR, 0x0027, "ADMUX" }, { CPU_ALL_AVR, 0x0028, "ACSR" }, { CPU_ALL_AVR, 0x0029, "UBRR" }, { CPU_ALL_AVR, 0x002A, "UCR" }, { CPU_ALL_AVR, 0x002B, "USR" }, { CPU_ALL_AVR, 0x002C, "UDR" }, { CPU_ALL_AVR, 0x002D, "SPCR" }, { CPU_ALL_AVR, 0x002E, "SPSR" }, { CPU_ALL_AVR, 0x002F, "SPDR" }, { CPU_ALL_AVR, 0x0030, "PIND" }, { CPU_ALL_AVR, 0x0031, "DDRD" }, { CPU_ALL_AVR, 0x0032, "PORTD" }, { CPU_ALL_AVR, 0x0033, "PINC" }, { CPU_ALL_AVR, 0x0034, "DDRC" }, { CPU_ALL_AVR, 0x0035, "PORTC" }, { CPU_ALL_AVR, 0x0036, "PINB" }, { CPU_ALL_AVR, 0x0037, "DDRB" }, { CPU_ALL_AVR, 0x0038, "PORTB" }, { CPU_ALL_AVR, 0x0039, "PINA" }, { CPU_ALL_AVR, 0x003A, "DDRA" }, { CPU_ALL_AVR, 0x003B, "PORTA" }, { CPU_ALL_AVR, 0x003C, "EECR" }, { CPU_ALL_AVR, 0x003D, "EEDR" }, { CPU_ALL_AVR, 0x003E, "EEARL" }, { CPU_ALL_AVR, 0x003E, "EEARH" }, { CPU_ALL_AVR, 0x0041, "WDTCR" }, { CPU_ALL_AVR, 0x0042, "ASSR" }, { CPU_ALL_AVR, 0x0043, "OCR2" }, { CPU_ALL_AVR, 0x0044, "TCNT2" }, { CPU_ALL_AVR, 0x0045, "TCCR2" }, { CPU_ALL_AVR, 0x0046, "ICR1L" }, { CPU_ALL_AVR, 0x0047, "ICR1H" }, { CPU_ALL_AVR, 0x0048, "OCR1BL" }, { CPU_ALL_AVR, 0x0049, "OCR1BH" }, { CPU_ALL_AVR, 0x004A, "OCR1AL" }, { CPU_ALL_AVR, 0x004B, "OCR1AH" }, { CPU_ALL_AVR, 0x004C, "TCNT1L" }, { CPU_ALL_AVR, 0x004D, "TCNT1H" }, { CPU_ALL_AVR, 0x004E, "TCCR1B" }, { CPU_ALL_AVR, 0x004F, "TCCR1A" }, { CPU_ALL_AVR, 0x0052, "TCNT0" }, { CPU_ALL_AVR, 0x0053, "TCCR0" }, { CPU_ALL_AVR, 0x0054, "MCUSR" }, { CPU_ALL_AVR, 0x0055, "MCUCR" }, { CPU_ALL_AVR, 0x0058, "TIFR" }, { CPU_ALL_AVR, 0x0059, "TIMSK" }, { CPU_ALL_AVR, 0x005A, "GIFR" }, { CPU_ALL_AVR, 0x005B, "GIMSK" }, { CPU_ALL_AVR, 0x005D, "SPL" }, { CPU_ALL_AVR, 0x005E, "SPH" }, { CPU_ALL_AVR, 0x005F, "SREG" }, {0, 0, NULL} }; /* * Base type of AVR microcontrollers */ cl_avr::cl_avr(class cl_sim *asim): cl_uc(asim) { type= (struct cpu_entry *)malloc(sizeof(struct cpu_entry)); type->type= CPU_AVR; sleep_executed= 0; } int cl_avr::init(void) { cl_uc::init(); /* Memories now exist */ int i; for (i= 0; sfr_tabl[i].name != NULL; i++) { if (type->type & sfr_tabl[i].cpu_type) { class cl_var *v; vars->add(v= new cl_var(chars(sfr_tabl[i].name), ram, sfr_tabl[i].addr, "")); v->init(); } } return(0); } char * cl_avr::id_string(void) { return((char*)"unspecified AVR"); } /* * Making elements of the controller */ void cl_avr::mk_hw_elements(void) { class cl_hw *h; cl_uc::mk_hw_elements(); add_hw(h= new cl_port(this)); h->init(); } void cl_avr::make_memories(void) { class cl_address_space *as; rom= as= new cl_address_space("rom"/*MEM_ROM_ID*/, 0, 0x10000, 16); as->init(); address_spaces->add(as); ram= as= new cl_address_space(MEM_IRAM_ID, 0, 0x10000, 8); as->init(); address_spaces->add(as); class cl_address_decoder *ad; class cl_memory_chip *chip; chip= new cl_memory_chip("rom_chip", 0x10000, 16); chip->init(); memchips->add(chip); ad= new cl_address_decoder(as= rom/*address_space(MEM_ROM_ID)*/, chip, 0, 0xffff, 0); ad->init(); as->decoders->add(ad); ad->activate(0); chip= new cl_memory_chip("iram_chip", 0x80, 8); chip->init(); memchips->add(chip); ad= new cl_address_decoder(as= ram/*address_space(MEM_IRAM_ID)*/, chip, 0, 0x7f, 0); ad->init(); as->decoders->add(ad); ad->activate(0); } /* * Help command interpreter */ struct dis_entry * cl_avr::dis_tbl(void) { return(disass_avr); } char * cl_avr::disass(t_addr addr, const char *sep) { char work[256], temp[20]; char *buf, *p, *t, *s; const char *b; uint code, data= 0; int i; p= work; code= rom/*get_mem*/->get(/*MEM_ROM_ID,*/ addr); i= 0; while ((code & dis_tbl()[i].mask) != dis_tbl()[i].code && dis_tbl()[i].mnemonic) i++; if (dis_tbl()[i].mnemonic == NULL) { buf= (char*)malloc(30); strcpy(buf, "UNKNOWN/INVALID"); return(buf); } b= dis_tbl()[i].mnemonic; while (*b) { if (*b == '%') { b++; switch (*(b++)) { case 'd': // Rd .... ...d dddd .... 0<=d<=31 if (!/*get*/addr_name(data= (code&0x01f0)>>4, /*sfr_tbl()*/ram, temp)) sprintf(temp, "r%d", data); break; case 'D': // Rd .... .... dddd .... 16<=d<=31 if (!/*get*/addr_name(data= 16+((code&0xf0)>>4), /*sfr_tbl()*/ram, temp)) sprintf(temp, "r%d", data); break; case 'K': // K .... KKKK .... KKKK 0<=K<=255 sprintf(temp, "%d", ((code&0xf00)>>4)|(code&0xf)); break; case 'r': // Rr .... ..r. .... rrrr 0<=r<=31 if (!/*get*/addr_name(data= ((code&0x0200)>>5)|(code&0x000f), /*sfr_tbl()*/ram, temp)) sprintf(temp, "r%d", data); break; case '2': // Rdl .... .... ..dd .... dl= {24,26,28,30} if (!/*get*/addr_name(data= 24+(2*((code&0x0030)>>4)), /*sfr_tbl()*/ram, temp)) sprintf(temp, "r%d", data); break; case '6': // K .... .... KK.. KKKK 0<=K<=63 sprintf(temp, "%d", ((code&0xc0)>>2)|(code&0xf)); break; case 's': // s .... .... .sss .... 0<=s<=7 sprintf(temp, "%d", (code&0x70)>>4); break; case 'b': // b .... .... .... .bbb 0<=b<=7 sprintf(temp, "%d", code&0x7); break; case 'k': // k .... ..kk kkkk k... -64<=k<=+63 { int k= (code&0x3f8)>>3; if (code&0x200) k|= -128; sprintf(temp, "0x%06x", k+1+(signed int)addr); break; } case 'A': // k .... ...k kkkk ...k 0<=k<=64K // kkkk kkkk kkkk kkkk 0<=k<=4M sprintf(temp, "0x%06x", (((code&0x1f0)>>3)|(code&1))*0x10000+ (uint)rom->get/*_mem*/(/*MEM_ROM_ID,*/ addr+1)); break; case 'P': // P .... .... pppp p... 0<=P<=31 data= (code&0xf8)>>3; if (!/*get*/addr_name(data+0x20, /*sfr_tbl()*/ram, temp)) sprintf(temp, "%d", data); break; case 'p': // P .... .PP. .... PPPP 0<=P<=63 data= ((code&0x600)>>5)|(code&0xf); if (!/*get*/addr_name(data+0x20, /*sfr_tbl()*/ram, temp)) sprintf(temp, "%d", data); break; case 'q': // q ..q. qq.. .... .qqq 0<=q<=63 sprintf(temp, "%d", ((code&0x2000)>>8)|((code&0xc00)>>7)|(code&7)); break; case 'R': // k SRAM address on second word 0<=k<=65535 sprintf(temp, "0x%06x", (uint)rom->get/*_mem*/(/*MEM_ROM_ID,*/ addr+1)); break; case 'a': // k .... kkkk kkkk kkkk -2k<=k<=2k { int k= code&0xfff; if (code&0x800) k|= -4096; sprintf(temp, "0x%06x", (int)rom->validate_address(k+1+(signed int)addr)); break; } default: strcpy(temp, "?"); break; } t= temp; while (*t) *(p++)= *(t++); } else *(p++)= *(b++); } *p= '\0'; p= strchr(work, ' '); if (!p) { buf= strdup(work); return(buf); } if (sep == NULL) buf= (char *)malloc(6+strlen(p)+1); else buf= (char *)malloc((p-work)+strlen(sep)+strlen(p)+1); for (p= work, s= buf; *p != ' '; p++, s++) *s= *p; p++; *s= '\0'; if (sep == NULL) { while (strlen(buf) < 6) strcat(buf, " "); } else strcat(buf, sep); strcat(buf, p); return(buf); } void cl_avr::print_regs(class cl_console_base *con) { uchar data, sreg= ram->get(SREG); uint x, y, z; ram->dump(0, 31, 16, con/*->get_fout()*/); con->dd_color("answer"); con->dd_printf("ITHSVNZC SREG= 0x%02x %3d %c\n", sreg, sreg, isprint(sreg)?sreg:'.'); con->dd_printf("%c%c%c%c%c%c%c%c ", (sreg&BIT_I)?'1':'0', (sreg&BIT_T)?'1':'0', (sreg&BIT_H)?'1':'0', (sreg&BIT_S)?'1':'0', (sreg&BIT_V)?'1':'0', (sreg&BIT_N)?'1':'0', (sreg&BIT_Z)?'1':'0', (sreg&BIT_C)?'1':'0'); con->dd_printf("SP = 0x%06x\n", ram->get(SPH)*256+ram->get(SPL)); x= ram->get(XH)*256 + ram->get(XL); data= ram->get(x); con->dd_printf("X= 0x%04x [X]= 0x%02x %3d %c ", x, data, data, isprint(data)?data:'.'); y= ram->get(YH)*256 + ram->get(YL); data= ram->get(y); con->dd_printf("Y= 0x%04x [Y]= 0x%02x %3d %c ", y, data, data, isprint(data)?data:'.'); z= ram->get(ZH)*256 + ram->get(ZL); data= ram->get(z); con->dd_printf("Z= 0x%04x [Z]= 0x%02x %3d %c\n", z, data, data, isprint(data)?data:'.'); print_disass(PC, con); } /* * Execution */ int cl_avr::exec_inst(void) { t_mem code; instPC= PC; if (fetch(&code)) return(resBREAKPOINT); tick(1); switch (code) { case 0x9419: return(eijmp(code)); case 0x9519: return(eicall(code)); case 0x9508: case 0x9528: case 0x9548: case 0x9568: return(ret(code)); case 0x9518: case 0x9538: case 0x9558: case 0x9578: return(reti(code)); case 0x95c8: return(lpm(code)); case 0x95d8: return(elpm(code)); // in some devices equal to lpm case 0x95e8: return(spm(code)); case 0x95f8: return(espm(code)); case 0x9408: return(sec(code)); case 0x9488: return(clc(code)); case 0x9428: return(sen(code)); case 0x94a8: return(cln(code)); case 0x9418: return(sez(code)); case 0x9498: return(clz(code)); case 0x9478: return(sei(code)); case 0x94f8: return(cli(code)); case 0x9448: return(ses(code)); case 0x94c8: return(cls(code)); case 0x9438: return(sev(code)); case 0x94b8: return(clv(code)); case 0x9468: return(set(code)); case 0x94e8: return(clt(code)); case 0x9458: return(seh(code)); case 0x94d8: return(clh(code)); case 0x0000: return(nop(code)); case 0x9588: case 0x9598: return(sleep(code)); case 0x95a8: case 0x95b8: return(wdr(code)); } switch (code & 0xf000) { case 0x3000: return(cpi_Rd_K(code)); case 0x4000: return(sbci_Rd_K(code)); case 0x5000: return(subi_Rd_K(code)); case 0x6000: return(ori_Rd_K(code)); case 0x7000: return(andi_Rd_K(code)); case 0xc000: return(rjmp_k(code)); case 0xd000: return(rcall_k(code)); case 0xe000: return(ldi_Rd_K(code)); } switch (code & 0xf000) { case 0x0000: { // 0x0... switch (code & 0xfc00) { case 0x0000: { switch (code & 0xff00) { case 0x0100: return(movw_Rd_Rr(code)); case 0x0200: return(muls_Rd_Rr(code)); case 0x0300: { switch (code & 0xff88) { case 0x0300: return(mulsu_Rd_Rr(code)); case 0x0308: return(fmul_Rd_Rr(code)); case 0x0380: return(fmuls_Rd_Rr(code)); case 0x0388: return(fmulsu_Rd_Rr(code)); } break; } break; } break; } case 0x0400: return(cpc_Rd_Rr(code)); case 0x0800: return(sbc_Rd_Rr(code)); case 0x0c00: return(add_Rd_Rr(code)); } break; } case 0x1000: { // 0x1... switch (code & 0xfc00) { case 0x1000: return(cpse_Rd_Rr(code)); case 0x1400: return(cp_Rd_Rr(code)); case 0x1800: return(sub_Rd_Rr(code)); case 0x1c00: return(adc_Rd_Rr(code)); } break; } case 0x2000: { // 0x2... switch (code & 0xfc00) { case 0x2000: return(and_Rd_Rr(code)); case 0x2400: return(eor_Rd_Rr(code)); case 0x2800: return(or_Rd_Rr(code)); case 0x2c00: return(mov_Rd_Rr(code)); } break; } case 0x8000: { // 0x8... switch (code &0xf208) { case 0x8000: return(ldd_Rd_Z_q(code)); case 0x8008: return(ldd_Rd_Y_q(code)); case 0x8200: return(std_Z_q_Rr(code)); case 0x8208: return(std_Y_q_Rr(code)); } break; } case 0x9000: { // 0x9... if ((code & 0xff0f) == 0x9509) return(icall(code)); if ((code & 0xff0f) == 0x9409) return(ijmp(code)); if ((code & 0xff00) == 0x9600) return(adiw_Rdl_K(code)); if ((code & 0xff00) == 0x9700) return(sbiw_Rdl_K(code)); switch (code & 0xfc00) { case 0x9000: { switch (code & 0xfe0f) { case 0x9000: return(lds_Rd_k(code)); case 0x9001: return(ld_Rd_ZS(code)); case 0x9002: return(ld_Rd_SZ(code)); case 0x9004: return(lpm_Rd_Z(code)); case 0x9005: return(lpm_Rd_ZS(code)); case 0x9006: return(elpm_Rd_Z(code)); case 0x9007: return(elpm_Rd_ZS(code)); case 0x9009: return(ld_Rd_YS(code)); case 0x900a: return(ld_Rd_SY(code)); case 0x900c: return(ld_Rd_X(code)); case 0x900d: return(ld_Rd_XS(code)); case 0x900e: return(ld_Rd_SX(code)); case 0x900f: return(pop_Rd(code)); case 0x9200: return(sts_k_Rr(code)); case 0x9201: return(st_ZS_Rr(code)); case 0x9202: return(st_SZ_Rr(code)); case 0x9209: return(st_YS_Rr(code)); case 0x920a: return(st_SY_Rr(code)); case 0x920c: return(st_X_Rr(code)); case 0x920d: return(st_XS_Rr(code)); case 0x920e: return(st_SX_Rr(code)); case 0x920f: return(push_Rr(code)); } break; } case 0x9400: { switch (code & 0xfe0f) { case 0x9400: return(com_Rd(code)); case 0x9401: return(neg_Rd(code)); case 0x9402: return(swap_Rd(code)); case 0x9403: return(inc_Rd(code)); case 0x9405: return(asr_Rd(code)); case 0x9406: return(lsr_Rd(code)); case 0x9407: return(ror_Rd(code)); case 0x940a: return(dec_Rd(code)); case 0x940c: case 0x940d: return(jmp_k(code)); case 0x940e: case 0x940f: return(call_k(code)); } break; } case 0x9800: { switch (code & 0xff00) { case 0x9800: return(cbi_A_b(code)); case 0x9900: return(sbic_P_b(code)); case 0x9a00: return(sbi_A_b(code)); case 0x9b00: return(sbis_P_b(code)); } break; } case 0x9c00: return(mul_Rd_Rr(code)); } break; } case 0xa000: { // 0xa... switch (code &0xf208) { case 0xa000: return(ldd_Rd_Z_q(code)); case 0xa008: return(ldd_Rd_Y_q(code)); case 0xa200: return(std_Z_q_Rr(code)); case 0xa208: return(std_Y_q_Rr(code)); } break; } case 0xb000: { // 0xb... switch (code & 0xf800) { case 0xb000: return(in_Rd_A(code)); case 0xb800: return(out_A_Rr(code)); } break; } case 0xe000: { // 0xe... switch (code & 0xff0f) { case 0xef0f: return(ser_Rd(code)); } break; } case 0xf000: { // 0xf... switch (code & 0xfc00) { case 0xf000: return(brbs_s_k(code)); case 0xf400: return(brbc_s_k(code)); case 0xf800: case 0xfc00: { switch (code & 0xfe08) { case 0xf800: return(bld_Rd_b(code)); case 0xfa00: return(bst_Rd_b(code)); case 0xfc00: case 0xfc08: return(sbrc_Rr_b(code)); case 0xfe00: case 0xfe08: return(sbrs_Rr_b(code)); } break; } } break; } } /*if (PC) PC--; else PC= get_mem_size(MEM_ROM_ID)-1;*/ class cl_error_unknown_code *e= new cl_error_unknown_code(this); error(e); return(resGO); PC= rom->inc_address(PC, -1); //tick(-clock_per_cycle()); sim->stop(resINV_INST); return(resINV_INST); } /* */ int cl_avr::push_data(t_mem data) { t_addr sp; t_mem spl, sph; spl= ram->read(SPL); sph= ram->read(SPH); sp= 0xffff & (256*sph + spl); data= ram->write(sp, data); vc.wr++; sp= 0xffff & (sp-1); spl= sp & 0xff; sph= (sp>>8) & 0xff; ram->write(SPL, spl); ram->write(SPH, sph); return(resGO); } int cl_avr::push_addr(t_addr addr) { t_addr sp; t_mem spl, sph, al, ah; spl= ram->read(SPL); sph= ram->read(SPH); sp= 0xffff & (256*sph + spl); al= addr & 0xff; ah= (addr>>8) & 0xff; ram->write(sp, ah); sp= 0xffff & (sp-1); ram->write(sp, al); vc.wr+= 2; sp= 0xffff & (sp-1); spl= sp & 0xff; sph= (sp>>8) & 0xff; ram->write(SPL, spl); ram->write(SPH, sph); return(resGO); } int cl_avr::pop_data(t_mem *data) { t_addr sp; t_mem spl, sph; spl= ram->read(SPL); sph= ram->read(SPH); sp= 256*sph + spl; sp= 0xffff & (sp+1); *data= ram->read(sp); vc.rd++; spl= sp & 0xff; sph= (sp>>8) & 0xff; ram->write(SPL, spl); ram->write(SPH, sph); return(resGO); } int cl_avr::pop_addr(t_addr *addr) { t_addr sp; t_mem spl, sph, al, ah; spl= ram->read(SPL); sph= ram->read(SPH); sp= 256*sph + spl; sp= 0xffff & (sp+1); al= ram->read(sp); sp= 0xffff & (sp+1); ah= ram->read(sp); vc.rd+= 2; *addr= ah*256 + al; spl= sp & 0xff; sph= (sp>>8) & 0xff; ram->write(SPL, spl); ram->write(SPH, sph); return(resGO); } /* * Set Z, N, V, S bits of SREG after logic instructions and some others */ void cl_avr::set_zn0s(t_mem data) { t_mem sreg= ram->get(SREG) & ~BIT_V; data= data&0xff; if (!data) sreg|= BIT_Z; else sreg&= ~BIT_Z; if (data & 0x80) sreg|= (BIT_N|BIT_S); else sreg&= ~(BIT_N|BIT_S); ram->set(SREG, sreg); } /* End of avr.src/avr.cc */