aboutsummaryrefslogtreecommitdiff
path: root/src/usart.ts
blob: 276207e464664eaf14f2c3d98919bdf51f8366f6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import { CPU } from './cpu';
import { avrInterrupt } from './interrupt';
import { u8 } from './types';

export interface USARTConfig {
  rxCompleteInterrupt: u8;
  dataRegisterEmptyInterrupt: u8;
  txCompleteInterrupt: u8;

  UCSRA: u8;
  UCSRB: u8;
  UCSRC: u8;
  UBRRL: u8;
  UBRRH: u8;
  UDR: u8;
}

export const usart0Config: USARTConfig = {
  rxCompleteInterrupt: 0x24,
  dataRegisterEmptyInterrupt: 0x26,
  txCompleteInterrupt: 0x28,
  UCSRA: 0xc0,
  UCSRB: 0xc1,
  UCSRC: 0xc2,
  UBRRL: 0xc4,
  UBRRH: 0xc5,
  UDR: 0xc6
};

export type USARTTransmitCallback = (value: u8) => void;

// Register bits
const UCSRA_RXC = 0x80; // USART Receive Complete
const UCSRA_TXC = 0x40; // USART Transmit Complete
const UCSRA_UDRE = 0x20; // USART Data Register Empty
const UCSRA_FE = 0x10; // Frame Error
const UCSRA_DOR = 0x8; // Data OverRun
const UCSRA_UPE = 0x4; // USART Parity Error
const UCSRA_U2X = 0x2; // Double the USART Transmission Speed
const UCSRA_MPCM = 0x1; // Multi-processor Communication Mode
const UCSRB_RXCIE = 0x80; // RX Complete Interrupt Enable
const UCSRB_TXCIE = 0x40; // TX Complete Interrupt Enable
const UCSRB_UDRIE = 0x20; // USART Data Register Empty Interrupt Enable
const UCSRB_RXEN = 0x10; // Receiver Enable
const UCSRB_TXEN = 0x8; // Transmitter Enable
const UCSRB_UCSZ2 = 0x4; // Character Size 2
const UCSRB_RXB8 = 0x2; // Receive Data Bit 8
const UCSRB_TXB8 = 0x1; // Transmit Data Bit 8
const UCSRC_UMSEL1 = 0x80; // USART Mode Select 1
const UCSRC_UMSEL0 = 0x40; // USART Mode Select 0
const UCSRC_UPM1 = 0x20; // Parity Mode 1
const UCSRC_UPM0 = 0x10; // Parity Mode 0
const UCSRC_USBS = 0x8; // Stop Bit Select
const UCSRC_UCSZ1 = 0x4; // Character Size 1
const UCSRC_UCSZ0 = 0x2; // Character Size 0
const UCSRC_UCPOL = 0x1; // Clock Polarity

export class AVRUSART {
  public onByteTransmit: USARTTransmitCallback | null = null;

  constructor(private cpu: CPU, private config: USARTConfig, private freqMHz: number) {
    this.cpu.writeHooks[config.UCSRA] = (value, oldValue) => {
      this.cpu.data[config.UCSRA] = value | UCSRA_UDRE | UCSRA_TXC;
      return true;
    };
    this.cpu.writeHooks[config.UCSRB] = (value, oldValue) => {
      if (value & UCSRB_TXEN && !(oldValue & UCSRB_TXEN)) {
        // Enabling the transmission - mark UDR as empty
        this.cpu.data[config.UCSRA] |= UCSRA_UDRE;
      }
    };
    this.cpu.writeHooks[config.UDR] = (value) => {
      if (this.onByteTransmit) {
        this.onByteTransmit(value);
      }
      this.cpu.data[config.UCSRA] |= UCSRA_UDRE | UCSRA_TXC;
    };
  }

  tick() {
    if (this.cpu.interruptsEnabled) {
      const ucsra = this.cpu.data[this.config.UCSRA];
      const ucsrb = this.cpu.data[this.config.UCSRB];
      if (ucsra & UCSRA_UDRE && ucsrb & UCSRB_UDRIE) {
        avrInterrupt(this.cpu, this.config.dataRegisterEmptyInterrupt);
        this.cpu.data[this.config.UCSRA] &= ~UCSRA_UDRE;
      }
      if (ucsrb & UCSRA_TXC && ucsrb & UCSRB_TXCIE) {
        avrInterrupt(this.cpu, this.config.txCompleteInterrupt);
        this.cpu.data[this.config.UCSRA] &= ~UCSRA_TXC;
      }
    }
  }

  get baudRate() {
    const UBRR = (this.cpu.data[this.config.UBRRH] << 8) | this.cpu.data[this.config.UBRRL];
    const multiplier = this.cpu.data[this.config.UCSRA] & UCSRA_U2X ? 8 : 16;
    return Math.floor(this.freqMHz / (multiplier * (1 + UBRR)));
  }

  get bitsPerChar() {
    const ucsz =
      ((this.cpu.data[this.config.UCSRA] & (UCSRC_UCSZ1 | UCSRC_UCSZ0)) >> 1) |
      (this.cpu.data[this.config.UCSRB] & UCSRB_UCSZ2);
    switch (ucsz) {
      case 0:
        return 5;
      case 1:
        return 6;
      case 2:
        return 7;
      case 3:
        return 8;
      default: // 4..6 are reserved
      case 7:
        return 9;
    }
  }
}