/***
  ダイナミックデザイン課題 2進数を理解するための計算機
  足し算・引き算・掛け算・割り算の結果を随時表示
  ボタン4つ同時押しで計算モード切り替え
  
  CPU: AT90S8535 4MHz(内蔵)
  Compiler: WinAVR20050214(AVR-GCC3.4.3)
  Date: 2005/11/28
  Author: Sho Hashimoto
  WebSite: http://web.sfc.keio.ac.jp/~t03792sh/
  ***/

#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>

#define TRUE 1
#define FALSE 0
#define NULL '\0'
#define sbi(PORT,BIT) PORT|=_BV(BIT) // PORTの指定BITに1をセット
#define cbi(PORT,BIT) PORT&=~_BV(BIT) // PORTの指定BITをクリア

/** 動作設定 **/
#define FOSC 4000000 // 4MHz

/** I/O設定 **/
#define LED_SET() sbi(PORTA,PA4) // LED点灯
#define LED_CLR() cbi(PORTA,PA4) // LED消灯

/* PORT設定 */
void port_init(void){
  DDRB = 0xff; // 1段目 全出力
  DDRC = 0xff; // 2段目 全出力
  DDRA = 0b11110000; // PA0-PA3 ボタン入力
  DDRD = 0xff; // 計算結果 全出力
}

/** グローバル変数 **/
char num1,num2,result,mode; // 数1、数2、合計結果、計算モード
#define NUM1_INC 1 // 数1増加
#define NUM1_DEC 2 // 数1減少
#define NUM2_INC 3
#define NUM2_DEC 4
#define MODE_CHANGE 5 // 状態変化

#define MODE_PLS 1 // 足す
#define MODE_MIN 2 // 引く
#define MODE_MUL 3 // 掛ける
#define MODE_DIV 4 // 割る

/* No Operation */
void nop(unsigned int count){
  int i;
  for(i = 0; i < count*100; i++){
  }
}

/* ボタンの状態を取得 */
char btnState(void){
  if(bit_is_set(PINA,PA0) &&
     bit_is_set(PINA,PA1) &&
     bit_is_set(PINA,PA2) &&
     bit_is_set(PINA,PA3)) return MODE_CHANGE; // 同時押しの時状態変化
  if(bit_is_set(PINA,PA0)) return NUM1_DEC; // 数1増加
  if(bit_is_set(PINA,PA1)) return NUM2_DEC; // 数1減少
  if(bit_is_set(PINA,PA2)) return NUM1_INC; // 数2増加
  if(bit_is_set(PINA,PA3)) return NUM2_INC; // 数2減少
  else return 0; // 何も無し
}

int main(void){
  port_init(); // PORT設定

  LED_SET(); // 起動 点滅
  nop(30);
  LED_CLR();
  nop(30);
  LED_SET();
  
  num1 = 1; // 初期状態
  num2 = 1;
  mode = MODE_PLS; // 初期状態 足し算モード
  char state, pstate; // ボタン状態、前の状態
  for(;;){
    if(pstate != (state = btnState())){ // ボタン状態取得 前の状態と違う時
      switch(state){ // ボタン状態により計算
      case NUM1_INC: // 数1増加
        if(num1 < 0b11111) num1++;
        break;
      case NUM1_DEC: // 数1減少
        if(num1 > 0) num1--;
        break;
      case NUM2_INC: // 数2増加
        if(num2 < 0b11111) num2++;
        break;
      case NUM2_DEC: // 数2減少
        if(num2 > 0) num2--;
        break;
      case MODE_CHANGE: // 状態変化
        if(mode < MODE_DIV) mode++; // 次のモード
        else mode = MODE_PLS;
        break;
      }
    }
    
    PORTB = num1; // 1段目表示
    PORTC = num2; // 2段目表示
    
    switch(mode){ // 計算モードに合わせて計算
    case MODE_PLS: // 足し算
      result = num1+num2;
      break;
    case MODE_MIN: // 引き算
      result = num1-num2;
      break;
    case MODE_MUL: // 掛け算
      result = num1*num2;
      break;
    case MODE_DIV: // 割り算
      result = num1/num2;
      break;
    }
    
    PORTD = result; // 結果表示
    
    pstate = state; // 状態保存
  }
}

