diff --git a/src/machine/machine_atmega.go b/src/machine/machine_atmega.go index aeb32d09a7..c6a194ef39 100644 --- a/src/machine/machine_atmega.go +++ b/src/machine/machine_atmega.go @@ -5,11 +5,95 @@ package machine import ( "device/avr" + "errors" "runtime/interrupt" "runtime/volatile" "unsafe" ) +// EEPROM on ATMega +type EEPROM struct { +} + +var EEPROM0 = EEPROM{} + +// setAddress sets the address for a given read or write into the MCUs EEARH/L register. +func (e EEPROM) setAddress(addr int16) error { + if addr < 0 || addr > int16(e.Size()) { + return errors.New("address provided is out of bounds") + } + + avr.EEARH.Set(uint8(addr >> 8)) + avr.EEARL.Set(uint8(addr & 0xFF)) + + return nil +} + +// WriteAt writes len(data) bytes in the EEPROMs at the provided offset. +func (e EEPROM) WriteAt(data []byte, off int64) (int, error) { + written := 0 + for i, value := range data { + if err := e.WriteByteAt(value, off+int64(i)); err != nil { + return written, err + } + written++ + } + return written, nil +} + +// WriteByteAt performs the logic to writes a byte into the EEPROM at the given address. +func (e EEPROM) WriteByteAt(value byte, addr int64) error { + for avr.EECR.HasBits(avr.EECR_EEPE) { + } + + if err := e.setAddress(int16(addr)); err != nil { + return err + } + + avr.EEDR.Set(value) + + avr.EECR.SetBits(avr.EECR_EEMPE) + avr.EECR.SetBits(avr.EECR_EEPE) + + return nil +} + +// ReadAt reads exactly len(buf) into buf at the offset. It will return the amount of bytes copied or an error if one exists. +// The buffer cannot be empty, and an an error is thrown if fewer bytes are read than the size of the buffer. +func (e EEPROM) ReadAt(buf []byte, off int64) (int, error) { + if len(buf) == 0 { + return 0, nil + } + + read := 0 + for i := 0; i < len(buf); i++ { + val, err := e.ReadByteAt(off + int64(i)) + if err != nil { + return read, err + } + + buf[i] = val + + read++ + } + + return len(buf), nil +} + +// ReadByteAt reads and returns the byte at the specified address. An error is returned if there is a failure to read. +func (e EEPROM) ReadByteAt(addr int64) (byte, error) { + for avr.EECR.HasBits(avr.EECR_EEPE) { + } + + if err := e.setAddress(int16(addr)); err != nil { + return byte(0), err + } + + avr.EECR.SetBits(avr.EECR_EERE) + + return avr.EEDR.Get(), nil +} + // I2C on AVR. type I2C struct { } diff --git a/src/machine/machine_atmega1280.go b/src/machine/machine_atmega1280.go index ba9c1462b9..d0c80493fd 100644 --- a/src/machine/machine_atmega1280.go +++ b/src/machine/machine_atmega1280.go @@ -11,6 +11,11 @@ import ( const irq_USART0_RX = avr.IRQ_USART0_RX +// Size returns the size of the EEPROM for this machine. +func (e EEPROM) Size() int64 { + return 4096 +} + const ( portA Pin = iota * 8 portB diff --git a/src/machine/machine_atmega1284p.go b/src/machine/machine_atmega1284p.go index 633a4e9c8e..8b0f3dedb4 100644 --- a/src/machine/machine_atmega1284p.go +++ b/src/machine/machine_atmega1284p.go @@ -10,6 +10,11 @@ import ( const irq_USART0_RX = avr.IRQ_USART0_RX +// Size returns the size of the EEPROM for this machine. +func (e EEPROM) Size() int64 { + return 4096 +} + // Return the current CPU frequency in hertz. func CPUFrequency() uint32 { return 20000000 diff --git a/src/machine/machine_atmega2560.go b/src/machine/machine_atmega2560.go index c3a1ca1aba..079ab4ad6d 100644 --- a/src/machine/machine_atmega2560.go +++ b/src/machine/machine_atmega2560.go @@ -13,6 +13,11 @@ const irq_USART1_RX = avr.IRQ_USART1_RX const irq_USART2_RX = avr.IRQ_USART2_RX const irq_USART3_RX = avr.IRQ_USART3_RX +// Size returns the size of the EEPROM for this machine. +func (e EEPROM) Size() int64 { + return 4096 +} + const ( portA Pin = iota * 8 portB diff --git a/src/machine/machine_atmega328p.go b/src/machine/machine_atmega328p.go index a7d5d0c20f..1eb6678bda 100644 --- a/src/machine/machine_atmega328p.go +++ b/src/machine/machine_atmega328p.go @@ -11,6 +11,11 @@ import ( const irq_USART0_RX = avr.IRQ_USART_RX +// Size returns the size of the EEPROM for this machine. +func (e EEPROM) Size() int64 { + return 1024 +} + // getPortMask returns the PORTx register and mask for the pin. func (p Pin) getPortMask() (*volatile.Register8, uint8) { switch { diff --git a/src/machine/machine_atmega328pb.go b/src/machine/machine_atmega328pb.go index afae4798d1..b0e030f2a5 100644 --- a/src/machine/machine_atmega328pb.go +++ b/src/machine/machine_atmega328pb.go @@ -10,6 +10,11 @@ import ( const irq_USART0_RX = avr.IRQ_USART0_RX +// Size returns the size of the EEPROM for this machine. +func (e EEPROM) Size() int64 { + return 1024 +} + // getPortMask returns the PORTx register and mask for the pin. func (p Pin) getPortMask() (*volatile.Register8, uint8) { switch {