/* * AVR emulator * Ports * * Copyright (C) 2007 Michael Buesch * * This program 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. * * This program 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. */ #include "ports.h" #include "memory.h" #include "util.h" #include #include #include #include enum port_voltage_level { PORT_VOLT_TRISTATE = 0, PORT_VOLT_HIGH, PORT_VOLT_LOW, }; struct port { /* Spinlock for this port. */ pthread_spinlock_t lock; /* Data direction of the individual port bits. * Bit-set means output. */ uint8_t ddr; /* PORTX register status. */ uint8_t port; /* Voltage level status on the physical pin. */ enum port_voltage_level levels[8]; }; static struct port portb; static struct port portc; static struct port portd; static void port_read(struct port *port, uint16_t mem_offset) { pthread_spin_lock(&port->lock); memory.ram[mem_offset] = port->port; pthread_spin_lock(&port->lock); } static void port_write(struct port *port, uint8_t new_val) { pthread_spin_lock(&port->lock); port->port = new_val; //TODO pthread_spin_unlock(&port->lock); } static void ddr_read(struct port *port, uint16_t mem_offset) { pthread_spin_lock(&port->lock); memory.ram[mem_offset] = port->ddr; pthread_spin_unlock(&port->lock); } static void ddr_write(struct port *port, uint8_t new_val) { pthread_spin_lock(&port->lock); port->ddr = new_val; pthread_spin_unlock(&port->lock); } static void pin_read(struct port *port, uint16_t mem_offset) { int i; uint8_t mask, data = 0; pthread_spin_lock(&port->lock); for (i = 0; i < 8; i++) { mask = (1 << i); if (unlikely(port->ddr & mask)) { /* This is an output pin. */ if (port->port & mask) data |= mask; } else { /* This is an input pin. */ switch (port->levels[i]) { case PORT_VOLT_TRISTATE: if (port->port & mask) data |= mask; break; case PORT_VOLT_HIGH: data |= mask; break; case PORT_VOLT_LOW: break; } } } memory.ram[mem_offset] = data; pthread_spin_lock(&port->lock); } static void io_portb_write(uint16_t mem_offset, uint8_t old_val, uint8_t new_val) { port_write(&portb, new_val); } static void io_portc_write(uint16_t mem_offset, uint8_t old_val, uint8_t new_val) { port_write(&portc, new_val); } static void io_portd_write(uint16_t mem_offset, uint8_t old_val, uint8_t new_val) { port_write(&portd, new_val); } static void io_ddrb_write(uint16_t mem_offset, uint8_t old_val, uint8_t new_val) { ddr_write(&portb, new_val); } static void io_ddrc_write(uint16_t mem_offset, uint8_t old_val, uint8_t new_val) { ddr_write(&portc, new_val); } static void io_ddrd_write(uint16_t mem_offset, uint8_t old_val, uint8_t new_val) { ddr_write(&portd, new_val); } static void io_portb_read(uint16_t mem_offset) { port_read(&portb, mem_offset); } static void io_portc_read(uint16_t mem_offset) { port_read(&portc, mem_offset); } static void io_portd_read(uint16_t mem_offset) { port_read(&portd, mem_offset); } static void io_pinb_read(uint16_t mem_offset) { pin_read(&portb, mem_offset); } static void io_pinc_read(uint16_t mem_offset) { pin_read(&portc, mem_offset); } static void io_pind_read(uint16_t mem_offset) { pin_read(&portd, mem_offset); } static void io_ddrb_read(uint16_t mem_offset) { ddr_read(&portb, mem_offset); } static void io_ddrc_read(uint16_t mem_offset) { ddr_read(&portc, mem_offset); } static void io_ddrd_read(uint16_t mem_offset) { ddr_read(&portd, mem_offset); } static int port_init(struct port *port) { int err; memset(port, 0, sizeof(struct port)); err = pthread_spin_init(&port->lock, 0); if (err) { fprintf(stderr, "Ports: Failed to init spinlock\n"); return err; } return 0; } static void port_cleanup(struct port *port) { pthread_spin_destroy(&port->lock); } int avrdev_ports_init(void) { int err; err = port_init(&portb); if (err) goto error; err = port_init(&portc); if (err) goto err_cleanup_portb; err = port_init(&portd); if (err) goto err_cleanup_portc; err = 0; err |= register_io_write_handler(io_portb_write, IO_PORTB); err |= register_io_write_handler(io_portc_write, IO_PORTC); err |= register_io_write_handler(io_portd_write, IO_PORTD); err |= register_io_write_handler(io_ddrb_write, IO_DDRB); err |= register_io_write_handler(io_ddrc_write, IO_DDRC); err |= register_io_write_handler(io_ddrd_write, IO_DDRD); err |= register_io_read_handler(io_portb_read, IO_PORTB); err |= register_io_read_handler(io_portc_read, IO_PORTC); err |= register_io_read_handler(io_portd_read, IO_PORTD); err |= register_io_read_handler(io_pinb_read, IO_PINB); err |= register_io_read_handler(io_pinc_read, IO_PINC); err |= register_io_read_handler(io_pind_read, IO_PIND); err |= register_io_read_handler(io_ddrb_read, IO_DDRB); err |= register_io_read_handler(io_ddrc_read, IO_DDRC); err |= register_io_read_handler(io_ddrd_read, IO_DDRD); if (err) { err = -ENOMEM; fprintf(stderr, "Failed to register port handlers.\n"); goto err_cleanup_portd; } return 0; err_cleanup_portd: port_cleanup(&portd); err_cleanup_portc: port_cleanup(&portc); err_cleanup_portb: port_cleanup(&portb); error: return err; } void avrdev_ports_exit(void) {//FIXME somebody must call me port_cleanup(&portd); port_cleanup(&portc); port_cleanup(&portb); }