/* ===== CRU.C ===== Communications Register Unit routines */ #define __CRU__ #include #include "v9t9_common.h" #include "cru.h" #include "9901.h" #define _L LOG_CRU | LOG_INFO /************************************************************/ /* In order to most efficiently handle CRU requests, we no longer support multi-bit CRU operations. */ typedef struct crudevicelist { u32 addr; /* base address */ crufunc *handler; struct crudevicelist *next; } crudevicelist; static crudevicelist *cruwritedevices, *crureaddevices; void cruinit(void) { cruwritedevices = crureaddevices = NULL; } /***************************************************/ static void crudevicedump(void) { struct crudevicelist *ptr; logger(_L | 0, "crudevicedump:"); logger(_L | 0, "\tREAD:\n"); ptr = crureaddevices; while (ptr) { logger(_L | 0, "\t\tdevice = >%04X\n\n", ptr->addr); ptr = ptr->next; } logger(_L | 0, "\tWRITE:"); ptr = cruwritedevices; while (ptr) { logger(_L | 0, "\t\tdevice = >%04X\n\n", ptr->addr); ptr = ptr->next; } } /* Insert an address into the CRU device list. Sorts entries by address. Returns 1 if success, or 0 if failure. */ int cruadddevice(int rw, u32 addr, u32 bits, crufunc * handler) { crudevicelist **head, *ptr, *tmp; crudevicelist *new; if (addr >= 0x2000) { logger(_L | LOG_ERROR | LOG_USER, "cruadddevice: address 0x%x is invalid\n", addr); return 0; } if (bits > 1) { logger(_L | LOG_ERROR | LOG_USER, "cruadddevice: only single-bit ranges supported (0x%04X, %d)\n", addr, bits); return 0; } if (rw == CRU_READ) head = &crureaddevices; else if (rw == CRU_WRITE) head = &cruwritedevices; else { logger(_L | LOG_ERROR | LOG_USER, "cruadddevice: invalid 'rw' flag (%d) passed\n", rw); return 0; } new = (struct crudevicelist *) xmalloc(sizeof *new); new->addr = addr; new->handler = handler; new->next = NULL; if (*head) { ptr = *head; tmp = NULL; while (ptr && addr >= ptr->addr) { tmp = ptr; ptr = ptr->next; } if (ptr && addr == ptr->addr) { logger(_L | LOG_ERROR | LOG_USER, "cruadddevice: overlapping I/O (0x%x)\n", ptr->addr); return 0; } if (tmp) { new->next = tmp->next; tmp->next = new; } else { new->next = ptr; *head = new; } } else *head = new; if (log_level(LOG_CRU) >= 2) crudevicedump(); return 1; } int crudeldevice(int rw, u32 addr, u32 bits, crufunc * handler) { crudevicelist **head, *ptr, *tmp; if (addr >= 0x2000) { logger(_L | LOG_ERROR | LOG_USER, "crudeldevice: address 0x%x is invalid\n", addr); return 0; } if (bits > 1) { logger(_L | LOG_ERROR | LOG_USER, "crudeldevice: only single-bit ranges supported (0x%04X, %d)\n", addr, bits); return 0; } if (rw == CRU_READ) head = &crureaddevices; else if (rw == CRU_WRITE) head = &cruwritedevices; else { logger(_L | LOG_ERROR | LOG_USER, "crudeldevice: invalid 'rw' flag (%d) passed\n", rw); return 0; } while (*head) { tmp = NULL; if ((*head)->addr == addr && handler == (*head)->handler) { ptr = *head; *head = (*head)->next; xfree(ptr); return 1; } head = &(*head)->next; } logger(_L | LOG_ERROR | LOG_USER, "crudeldevice: device not found (0x%x)\n", addr); return 0; } /***************************************************/ void cruwrite(u32 addr, u32 val, u32 num) { crudevicelist *ptr = cruwritedevices; addr &= 0x1fff; logger(_L | L_2, "CRU write: >%04X[%d], %04X\n", addr, num, val & 0xffff); if (addr >= 0x30) { setclockmode9901(0); } while (ptr && num) { if (ptr->addr > addr) { /* if we've already passed a handler for addr, shift out the lost bits */ int lost = (ptr->addr - addr) / 2; if (lost > num) lost = num; val >>= lost; num -= lost; addr = ptr->addr; logger(_L | L_2, "cruwrite: skipping bits, range is now %04X[%d]\n", addr, num); } if (addr == ptr->addr && num) { int used; u32 mask; logger(_L | L_2, "cruwrite: handling %04X[%d] with %04X\n", addr, num, ptr->addr); used = 1; mask = ~(~0 << used); (ptr->handler) (addr, val & mask, used); num -= used; addr += used * 2; val >>= used; } ptr = ptr->next; } } /* Routines write 1. Shift answer left into output. */ #include "keyboard.h" u32 cruread(u32 addr, u32 num) { crudevicelist *ptr = crureaddevices; u32 orgaddr = addr; u32 val = 0; //(num >= 8) ? 0xff00 : 0xffff; addr &= 0x1fff; orgaddr = addr; logger(_L | L_2, "CRU read: >%04X[%d] = \n", addr, num); if (addr >= 0x30) { setclockmode9901(0); } while (ptr && num) { /* if we've already passed a handler for addr, shift out the lost bits */ if (ptr->addr > addr) { int lost = (ptr->addr - addr) / 2; if (lost > num) lost = num; num -= lost; addr = ptr->addr; logger(_L | L_2, "cruread: skipping bits, range is now %04X[%d]\n", addr, num); } if (addr == ptr->addr && num) { int used; u32 mask, bits, shift; logger(_L | L_2, "cruread: handling %04X[%d] with %04X\n", addr, num, ptr->addr); used = 1; mask = ~((~0) << used); shift = (addr - orgaddr) / 2; bits = ((ptr->handler) (addr, val, used) & mask); val = (val & ~(mask << shift)) | (bits << shift); num -= used; addr += used * 2; } ptr = ptr->next; } if (orgaddr == 0x6 && 0xff != (val & 0xff)) logger(_L | L_2, "keyrow: %2X/%04X\n", crukeyboardmap[crukeyboardcol], val & 0xffff); return val & 0xffff; }