目次

ROMエミュレータ設計開発

 単体のROMにプログラムを格納するには、ROMライターが
 必要になるので、何度もプログラムを修正するやり方は
 時間がかかり過ぎます。

 ROMエミュレータを作って、ROMの消去とライトの時間を
 なくしていきます。

 回路図は、以下。



 プログラムの内容を変更するには、シリアルインタフェースを
 利用して、内蔵EEPROMの値を変えます。

 シリアルインタフェースを使うので、1文字コマンドを定義しておきます。

 1文字コマンドを定義したので、より細かく仕様を決めます。

 ヘルプ

  ヘルプは、1文字コマンドと簡単な説明を表示します。

 1バイトのデータ設定

  1バイトのデータを設定するには、アドレスとデータが必要です。

  アドレスは16バイトでよいので、16進数で0からFと1けたで
  指定します。データは16進数2けたで指定します。

  (例) EA12{enter}

  データを設定したなら、逆アセンブルして表示します。

 16バイトのデータ表示

  16バイトのデータ表示は、1文字eを入力したならば
  一気に表示します。同時に逆アセンブルしておきます。

  (例) e{enter}
             30= MOV A,0
             01= ADD A,1
             40= MOV B,A
             90= OUT B
             f1= JMP 1

 3コマンドを実現するインタプリタは、次のようになります。

    if ( uflag == ON ) {
      /* clear flag */
      uflag = OFF ;
      /* new line */
      crlf();
      /* get command */
      cmd = *(sbuf+0) ;
      /* help */
      if ( cmd == '?' ) { show_help() ; }
      /* store data */
      if ( cmd == 'E' ) {
        /* get address */
        xadr = get_hex( *(sbuf+1) );
        /* get data */
        xdat = get_hex( *(sbuf+2) );
        xdat <<= 4 ;
        xdat |= get_hex( *(sbuf+3) );
        /* store */
        *(xrom+xadr) = xdat ;
        store_eeprom( xadr , xdat );
        /* show */
        show_code( xdat );
      }
      /* show store data */
      if ( cmd == 'e' ) {
        for ( xadr = 0 ; xadr < 16 ; xadr++ ) {
          /* get code */
          xdat = load_eeprom( xadr ) ;
          /* store address */
          *(xmsg+0) = get_asc(xadr >> 4) ;
          *(xmsg+1) = get_asc(xadr & MASK0F) ;
          /* store machine code */
          *(xmsg+3) = get_asc(xdat >> 4) ;
          *(xmsg+4) = get_asc(xdat & MASK0F) ;
          /* show */
          rs_puts( (UBYTE *)xmsg );
          show_code( xdat );
        }
      }
    }

 ROMのアドレスを与えられたときの動作は、アドレスを
 読込んでひとつ前と異なれば、指定アドレスの内容を
 出力します。

      /* get address */
      eadr = PIND ;
      eadr >>= 2 ;
      eadr &= MASK0F ;
      /* judge */
      if ( eadr != padr ) {
        /* update address */
        padr = eadr ;
        /* get data and impress */
        PORTB = *(xrom+padr);
      }

 EEPROMからのデータを取得すると時間がかかるので
 同一内容を配列に取出しておきます。

 通信プロトコルは、以下とします。

 ソースコードにまとめます。

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>

#define OFF 0
#define ON  OFF+1

#define MASKFF 0xff
#define MASK0F 0x0f

#define FOSC   5000000
#define BAUD   9600
#define MYUBRR (FOSC/16/BAUD)-1

typedef unsigned char  UBYTE ;
typedef unsigned short UWORD ;

volatile UBYTE uflag ;
volatile UBYTE cmd   ;
volatile UBYTE xadr  ;
volatile UBYTE xdat  ;
volatile UBYTE sbuf[8];
volatile UBYTE sindex ;
volatile UBYTE xrom[16] ;
volatile UBYTE eadr ;
volatile UBYTE padr ;
volatile UBYTE xmsg[8];

const prog_char msg_h[] PROGMEM = "? help" ;
const prog_char msg_le[] PROGMEM = "E set" ;
const prog_char msg_se[] PROGMEM = "e show" ;
const prog_char msg_c0[] PROGMEM = "ADD A," ;
const prog_char msg_c1[] PROGMEM = "MOV A,B" ;
const prog_char msg_c2[] PROGMEM = "IN A" ;
const prog_char msg_c3[] PROGMEM = "MOV A," ;
const prog_char msg_c4[] PROGMEM = "MOV B,A" ;
const prog_char msg_c5[] PROGMEM = "ADD B," ;
const prog_char msg_c6[] PROGMEM = "IN B" ;
const prog_char msg_c7[] PROGMEM = "MOV B," ;
const prog_char msg_c9[] PROGMEM = "OUT B" ;
const prog_char msg_c11[] PROGMEM = "OUT " ;
const prog_char msg_c14[] PROGMEM = "JNC " ;
const prog_char msg_c15[] PROGMEM = "JMP " ;

/*--------------------------------*/
/* Insert user functions protoype */
/*--------------------------------*/
void  user_initialize(void);
UBYTE get_hex(UBYTE x);
UBYTE get_asc(UBYTE x);
void  store_eeprom(UBYTE xa,UBYTE xd);
UBYTE load_eeprom(UBYTE xa);
void  show_help(void);
void  rs_putchar(UBYTE x);
void  rs_puts(UBYTE *x);
void  crlf(void);
void  show_code(UBYTE x);
UBYTE is_no_operand(UBYTE x);
void  show_omsg(void);

/*------*/
/* main */
/*------*/
int main(void)
{
  /* initialize port and variables */
  user_initialize();
  /* enable interrupt */
  sei();
  /* opening message */
  show_omsg();
  /* endless loop */
  while ( ON ) {
    if ( uflag == ON ) {
      /* clear flag */
      uflag = OFF ;
      /* new line */
      crlf();
      /* get command */
      cmd = *(sbuf+0) ;
      /* help */
      if ( cmd == '?' ) { show_help() ; }
      /* store data */
      if ( cmd == 'E' ) {
        /* get address */
        xadr = get_hex( *(sbuf+1) );
        /* get data */
        xdat = get_hex( *(sbuf+2) );
        xdat <<= 4 ;
        xdat |= get_hex( *(sbuf+3) );
        /* store */
        *(xrom+xadr) = xdat ;
        store_eeprom( xadr , xdat );
        /* show */
        show_code( xdat );
      }
      /* show store data */
      if ( cmd == 'e' ) {
        for ( xadr = 0 ; xadr < 16 ; xadr++ ) {
          /* get code */
          xdat = load_eeprom( xadr ) ;
          /* store address */
          *(xmsg+0) = get_asc(xadr >> 4) ;
          *(xmsg+1) = get_asc(xadr & MASK0F) ;
          /* store machine code */
          *(xmsg+3) = get_asc(xdat >> 4) ;
          *(xmsg+4) = get_asc(xdat & MASK0F) ;
          /* show */
          rs_puts( (UBYTE *)xmsg );
          show_code( xdat );
        }
      }
    }
    /* ROM handling */
    {
      /* get address */
      eadr = PIND ;
      eadr >>= 2 ;
      eadr &= MASK0F ;
      /* judge */
      if ( eadr != padr ) {
        /* update address */
        padr = eadr ;
        /* get data and impress */
        PORTB = *(xrom+padr);
      }
    }
  }
  /* dummy */
  return 0 ;
}

/*-----------------------*/
/* Insert user functions */
/*-----------------------*/
void user_initialize(void)
{
  UWORD tmp ;
  /* PORT B */
  PORTB = 0b00000000 ; /* 00000000 */
  DDRB  = 0b11111111 ; /* oooooooo */
  /* PORT D */
  PORTD = 0b00111101 ; /* 00111101 */
  DDRD  = 0b11000010 ; /* ooiiiioi */
  /* set uart */
  {
    /* set Baud Rate Registers */
    tmp = MYUBRR ;
    UBRRH = (tmp >> 8) & MASKFF ;
    UBRRL = tmp & MASKFF;
    /* Enable receive interrupt , receive module and transmit module */
    UCSRB = (1 << RXCIE) | (1 << RXEN) | (1 << TXEN) ;
  }
  /* clear flag */
  uflag = OFF ;
  /* */
  sindex = 0 ;
  eadr = 0 ;
  padr = 0 ;
  /* message */
  *(xmsg+2) = ':' ;
  *(xmsg+5) = '=' ;
  *(xmsg+6) = ' ' ; 
  *(xmsg+7) = '\0' ;
  /* get previous code */
  for ( xadr = 0 ; xadr < 16 ; xadr++ ) {
    *(xrom+xadr) = load_eeprom( xadr ) ;
  }
  PORTB = *(xrom+0) ;
}

UBYTE get_hex(UBYTE x)
{
  UBYTE result ;
  /* default */
  result = 0 ;
  /* judge */
  if ( '0' <= x && x <= '9' ) { result = x - '0' ; }
  if ( 'A' <= x && x <= 'F' ) { result = x - 'A' + 10 ; }
  if ( 'a' <= x && x <= 'f' ) { result = x - 'a' + 10 ; }

  return result ;
}

UBYTE get_asc(UBYTE x)
{
  UBYTE result ;
  /* default */
  result = '0' ;
  /* judge */
  if (  0 <= x && x <=  9 ) { result = x + '0' ; }
  if ( 10 <= x && x <= 15 ) { result = x - 10 + 'A' ; }

  return result ;
}

void  store_eeprom(UBYTE xa,UBYTE xd)
{
  UBYTE adr ;
  adr = xa ^ 0x7f ;
  eeprom_write_byte( adr , xd );
}

UBYTE load_eeprom(UBYTE xa)
{
  UBYTE adr ;
  adr = xa ^ 0x7f ;
  return eeprom_read_byte( adr );
}

void  show_help(void)
{
  char  msg[16];
  strcpy_P(msg,msg_h) ; rs_puts((UBYTE *)msg); crlf();
  strcpy_P(msg,msg_le); rs_puts((UBYTE *)msg); crlf();
  strcpy_P(msg,msg_se); rs_puts((UBYTE *)msg); crlf();
}

void  rs_putchar(UBYTE x)
{
  while ( !(UCSRA & (1 << UDRE)) ) {}
  UDR = x ;
}

void  rs_puts(UBYTE *x)
{
  while ( *x != '\0' ) {
    rs_putchar( *x );
    x++ ;
  }
}

void  crlf(void)
{
  rs_putchar('\r');
  rs_putchar('\n');
}

void  show_code(UBYTE x)
{
  UBYTE xh ;
  UBYTE xl ;
  char  msg[8];
  /* clear */
  for ( xh = 0 ; xh < 8 ; xh++ ) { *(msg+0) = 0 ; }
  /* separate */
  xh = (x >> 4) & MASK0F ;
  xl = get_asc(x & MASK0F) ;
  /* have operand */
  if ( is_no_operand(xh) == OFF ) {
    /* MOV A,Im */
    if ( xh ==  3 ) {
      strcpy_P(msg,msg_c3) ;
      rs_puts((UBYTE *)msg);
    }
    /* ADD A,Im */
    if ( xh ==  0 ) {
      strcpy_P(msg,msg_c0) ;
      rs_puts((UBYTE *)msg);
    }
    /* MOV B,Im */
    if ( xh ==  7 ) {
      strcpy_P(msg,msg_c7) ;
      rs_puts((UBYTE *)msg);
    }
    /* ADD B,Im */
    if ( xh ==  5 ) {
      strcpy_P(msg,msg_c5) ;
      rs_puts((UBYTE *)msg);
    }
    /* OUT Im */
    if ( xh == 11 ) {
      strcpy_P(msg,msg_c11) ;
      rs_puts((UBYTE *)msg);
    }
    /* JMP Im */
    if ( xh == 15 ) {
      strcpy_P(msg,msg_c15) ;
      rs_puts((UBYTE *)msg);
    }
    /* JNC Im */
    if ( xh == 14 ) {
      strcpy_P(msg,msg_c14) ;
      rs_puts((UBYTE *)msg);
    }
    rs_putchar( xl );
  } else {
    /* MOV A,B */
    if ( xh ==  1 ) {
      strcpy_P(msg,msg_c1) ;
      rs_puts((UBYTE *)msg);
    }
    /* IN A */
    if ( xh ==  2 ) {
      strcpy_P(msg,msg_c2) ;
      rs_puts((UBYTE *)msg);
    }
    /* MOV B,A */
    if ( xh ==  4 ) {
      strcpy_P(msg,msg_c4) ;
      rs_puts((UBYTE *)msg);
    }
    /* IN B */
    if ( xh ==  6 ) {
      strcpy_P(msg,msg_c6) ;
      rs_puts((UBYTE *)msg);
    }
    /* OUT B */
    if ( xh ==  9 ) {
      strcpy_P(msg,msg_c9) ;
      rs_puts((UBYTE *)msg);
    }
  }
  /* new line */
  crlf();
}

UBYTE is_no_operand(UBYTE x)
{
  UBYTE result ;
  /* default */
  result = OFF ;
  /* judge */
  if ( x == 1 ) { result = ON ; }
  if ( x == 2 ) { result = ON ; }
  if ( x == 4 ) { result = ON ; }
  if ( x == 6 ) { result = ON ; }
  if ( x == 9 ) { result = ON ; }
  
  return result ;
}

void  show_omsg(void)
{
  rs_puts((UBYTE *)"Hello !");
  crlf();
}

/* UART receive interrupt */
ISR(USART_RX_vect)
{
  volatile UBYTE ch ;
  /* get 1 charactoer */
  ch = UDR ;
  /* store */
  *(sbuf+sindex) = ch ;
  /* increment */
  sindex++ ;
  /* judge */
  if ( ch == '\r' ) {
    sindex = 0 ;
    uflag  = ON ;
  }
}

 ATTiny2313を利用した場合、ほぼフラッシュROMの90%近く
 まで使っています。

 操作は、次のようにします。

 ROMの内容を確認



 機械語入力



 再度ROMの内容を確認



 最大プログラムサイズは16バイトなので、ハンドアセンブルでも
 16進数コードにできますが、ROMエミュレータがコンピュータに
 接続されるので、スクリプトでアセンブルを楽にします。

 ニモニックから16進数に変換するアセンブラを
 AWKで作成しておきます。

BEGIN {
  printf("TD4 assembler\n")
  printf(" adr  data   ------------------------\n")
}
{
  # get code
  instructionx = tolower($1)
  oprandx      = tolower($2)
  # split
  idx = index(oprandx,",")
  if ( idx == 0 ) {
    oprand0 = oprandx
  } else {
    oprand0 = substr(oprandx,idx-1,1)
    oprand1 = substr(oprandx,idx+1,1)
  }
  # conversion
  opcode = 15
  oprand = 15
  # add
  if ( instructionx == "add" ) {
    oprand = oprand1
    if ( oprand0 == "a" ) { opcode = 0 }
    if ( oprand0 == "b" ) { opcode = 5 }
  }
  # mov
  if ( instructionx == "mov" ) {
    oprand = oprand1
    if ( oprand0 == "a" ) {
      opcode = 3
      oprand = oprand1
      if ( oprand1 == "b" ) {
        opcode = 1
        oprand = 0
      }
    }
    if ( oprand0 == "b" ) {
      opcode = 7
      oprand = oprand1
      if ( oprand1 == "a" ) {
        opcode = 4
        oprand = 0
      }
    }
  }
  # in
  if ( instructionx == "in" ) {
    oprand = 0
    if ( oprand0 == "a" ) { opcode = 2 }
    if ( oprand0 == "b" ) { opcode = 6 }
  }
  # out
  if ( instructionx == "out" ) {
    oprand = 0
    opcode = 11
    if ( oprand0 == "b" ) {
      opcode = 9 
    }
  }
  # JNC
  if ( instructionx == "jnc" ) {
    opcode = 14
    oprand = oprand0
  }
  # JMP
  if ( instructionx == "jmp" ) {
    opcode = 15
    oprand = oprand0
  }
  # generate code
  result = opcode * 16 + oprand
  # show
  printf(" 0x%02X 0x%02X => %s\n",NR-1,result,$0)
}
END {
  printf("---------------------------------------\n")
}

 CUIで動かすので、テキストファイルにニモニックを
 入れておき、アセンブルした結果をI/Oリダイレクト
 で保存します。

c:\>gawk -f td4asm.awk test.txt > tout.txt

 I/Oリダイレクトでテキストファイルにすると以下となります。

TD4 assembler
 adr  data   ------------------------
 0x00 0x30 => MOV A,0
 0x01 0x01 => ADD A,1
 0x02 0x40 => MOV B,A
 0x03 0x90 => OUT B
 0x04 0xF1 => JMP 1
---------------------------------------

 16進数の2けたで、左にアドレス、右に機械語を
 おいてあるので、さらにAWKのスクリプトで入力
 するコマンドを生成することもできます。


目次

inserted by FC2 system