/* Memory mapped I/O routines for spim. Copyright (C) 1992 by Scott Kempf (scottk@cs.wisc.edu). SPIM 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 1, or (at your option) any later version. SPIM 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. You should have received a copy of the GNU General Public License along with GNU CC; see the file COPYING. If not, write to James Goodman, Computer Sciences Department, University of Wisconsin--Madison, 1210 West Dayton Street, Madison, WI 53706, USA or to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include "spim.h" #include "inst.h" #include "mem.h" #include "reg.h" #ifndef NOMEMIO #include #define READY 0x00000001 #define INTER 0x00000002 struct key_info { mem_word stat; mem_word buf; struct timeval time; int count; }; struct key_info key1 = {0, 0, {0, 0}, 0}; struct key_info key2 = {0, 0, {0, 0}, 0}; struct disp_info { mem_word stat; struct timeval time; int count; }; struct disp_info disp1 = {READY, {0, 0}, 0}; struct disp_info disp2 = {READY, {0, 0}, 0}; struct clock_info { mem_word stat; struct timeval time; }; struct clock_info clocktick = {READY, {0, 0}}; struct timezone zone = {0, 0}; #define USECS_DELAY 40000 #define KEY_COUNT 4000 #define CLOCK_TIME 1000000 #define KEY1_BUF_ADDR (mem_addr) 0xffff0004 #define KEY1_STAT_ADDR (mem_addr) 0xffff0000 #define KEY1_INTERUPT 1 #define DISP1_BUF_ADDR (mem_addr) 0xffff000c #define DISP1_STAT_ADDR (mem_addr) 0xffff0008 #define DISP1_INTERUPT 2 #define KEY2_BUF_ADDR (mem_addr) 0xffff0014 #define KEY2_STAT_ADDR (mem_addr) 0xffff0010 #define KEY2_INTERUPT 3 #define DISP2_BUF_ADDR (mem_addr) 0xffff001c #define DISP2_STAT_ADDR (mem_addr) 0xffff0018 #define DISP2_INTERUPT 4 #define CLOCK_ADDR (mem_addr) 0xffff0020 #define CLOCK_INTERUPT 0 #define TIMEDIF(t1, t2) (((t1).tv_sec - (t2).tv_sec) * 1000000 \ + ((t1).tv_usec - (t2).tv_usec)) void handle_IO_write (mem_addr addr, mem_word value, int size) { size = size; /* prevents silly lint errors, optimizer will remove */ switch (addr) { case DISP1_BUF_ADDR: if (disp1.stat & READY) { disp1.stat &= ~READY; gettimeofday(&disp1.time, &zone); sprintf(mess_buff, "%c", value & 0x7f); write_output(console_out, mess_buff); } break; case DISP2_BUF_ADDR: if (disp2.stat & READY) { disp2.stat &= ~READY; gettimeofday(&disp2.time, &zone); sprintf(mess_buff, "%c", value & 0x7f); write_output(terminal_out, mess_buff); } break; case KEY1_BUF_ADDR: /* key1.stat = 0; */ break; case KEY2_BUF_ADDR: /* key2.stat = 0; */ break; case KEY1_STAT_ADDR: value &= INTER; key1.stat = (key1.stat & ~INTER) | value; break; case KEY2_STAT_ADDR: value &= INTER; key2.stat = (key2.stat & ~INTER) | value; break; case DISP1_STAT_ADDR: value &= INTER; disp1.stat = (disp1.stat & ~INTER) | value; break; case DISP2_STAT_ADDR: value &= INTER; disp2.stat = (disp2.stat & ~INTER) | value; break; } } void handle_IO_read(mem_addr addr, mem_word *dest, int size) { mem_word data; switch (addr) { case KEY1_BUF_ADDR: data = key1.buf; key1.stat &= ~READY; break; case KEY1_STAT_ADDR: data = key1.stat; break; case DISP1_BUF_ADDR: data = 0; break; case DISP1_STAT_ADDR: /* disp1.stat &= ~INTER; */ data = disp1.stat; break; case KEY2_BUF_ADDR: data = key2.buf; key2.stat &= ~READY; break; case KEY2_STAT_ADDR: data = key2.stat; break; case DISP2_BUF_ADDR: data = 0; break; case DISP2_STAT_ADDR: /* disp2.stat &= ~INTER; */ data = disp2.stat; break; case CLOCK_ADDR: data = clocktick.stat; clocktick.stat &= ~READY; break; default: data = 0; break; } switch (size) { case 4: *dest = data; break; case 2: *dest = (*dest & 0xffff0000) | (data & 0xffff); break; case 1: *dest = (*dest & 0xffffff00) | (data & 0xff); break; } } void Update_IO() { struct timeval time; gettimeofday(&time, &zone); if (disp1.time.tv_sec) if (TIMEDIF(time, disp1.time) > USECS_DELAY) { disp1.stat |= READY; disp1.time.tv_sec = 0; } if (disp2.time.tv_sec) if (TIMEDIF(time, disp2.time) > USECS_DELAY) { disp2.stat |= READY; disp2.time.tv_sec = 0; } if (key1.time.tv_sec) { key1.count++; if ((TIMEDIF(time, key1.time) > USECS_DELAY) && (key1.count >= KEY_COUNT)) { key1.time.tv_sec = 0; key1.count = 0; } } if (key2.time.tv_sec) { key2.count++; if ((TIMEDIF(time, key2.time) > USECS_DELAY) && (key2.count >= KEY_COUNT)) { key2.time.tv_sec = 0; key2.count = 0; } } if (!(key1.stat & READY) || (key1.time.tv_sec == 0)) { int tmp = read_input_maybe(0); if (tmp != -1) { key1.buf = tmp; key1.stat |= READY; gettimeofday(&key1.time, &zone); } } if (!(key2.stat & READY) || (key2.time.tv_sec == 0)) { int tmp = read_input_maybe(1); if (tmp != -1) { key2.buf = tmp; key2.stat |= READY; gettimeofday(&key2.time, &zone); } } if ((clocktick.time.tv_sec == 0) || (TIMEDIF(time, clocktick.time) > CLOCK_TIME)){ clocktick.stat |= READY; gettimeofday(&clocktick.time, &zone); } Cause &= 0xffff03ff; /* Clear IP bits 0-4 */ if ((key1.stat & READY) && (key1.stat & INTER)) /* Set IP bits as needed */ Cause |= (1 << (KEY1_INTERUPT + 10)); if ((disp1.stat & READY) && (disp1.stat & INTER)) Cause |= (1 << (DISP1_INTERUPT + 10)); if ((key2.stat & READY) && (key2.stat & INTER)) Cause |= (1 << (KEY2_INTERUPT + 10)); if ((disp2.stat & READY) && (disp2.stat & INTER)) Cause |= (1 << (DISP2_INTERUPT + 10)); if ((clocktick.stat & READY) && (clocktick.stat & INTER)) Cause |= (1 << (CLOCK_INTERUPT + 10)); } #else /* Provide some error messages that should never be seen, since memio can't be set if NOMEMIO is defined. */ void Update_IO() {fatal_error("Spim not compiled for memory mapped I/O\n");} void handle_IO_write (mem_addr addr, mem_word value, int size) {fatal_error("Spim not compiled for memory mapped I/O\n");} void handle_IO_read(mem_addr addr, mem_word *dest, int size) {fatal_error("Spim not compiled for memory mapped I/O\n");} #endif /* NOMEMIO */