diff --git a/core/core_api.c b/core/core_api.c index 2c60fae..ce9fcdf 100644 --- a/core/core_api.c +++ b/core/core_api.c @@ -56,7 +56,8 @@ void shoebill_stop() pthread_join(shoe.via_thread_pid, NULL); pthread_mutex_destroy(&shoe.via_clock_thread_lock); - unstop_cpu_thread(); // wake up the CPU thread if it was STOPPED + // wake up the CPU thread if it was STOPPED + unstop_cpu_thread(); pthread_join(shoe.cpu_thread_pid, NULL); pthread_mutex_destroy(&shoe.cpu_thread_lock); @@ -68,6 +69,11 @@ void shoebill_stop() shoe.running = 0; + // Destroy all the nubus cards + for (i=0; i<15; i++) + if (shoe.slots[i].destroy_func) + shoe.slots[i].destroy_func(i); + // Close all the SCSI disk images for (i=0; i<8; i++) { if (shoe.scsi_devices[i].f) @@ -165,7 +171,7 @@ struct __attribute__ ((__packed__)) kernel_info { // A series of DrvQEl (drive queue elements) follow this structure }; -/* Inside Macintosh: Files 2-85 throughtfully provides this information +/* Inside Macintosh: Files 2-85 thoughtfully provides this information * on the secret internal flags: * * The File Manager also maintains four flag bytes preceding each drive queue element. @@ -548,6 +554,7 @@ uint32_t shoebill_install_video_card(shoebill_config_t *config, uint8_t slotnum, shoe.slots[slotnum].connected = 1; shoe.slots[slotnum].read_func = nubus_video_read_func; shoe.slots[slotnum].write_func = nubus_video_write_func; + shoe.slots[slotnum].destroy_func = NULL; shoe.slots[slotnum].interrupts_enabled = 1; nubus_video_init(ctx, slotnum, width, height, scanline_width); return 1; @@ -570,6 +577,7 @@ uint32_t shoebill_install_tfb_card(shoebill_config_t *config, uint8_t slotnum) shoe.slots[slotnum].connected = 1; shoe.slots[slotnum].read_func = nubus_tfb_read_func; shoe.slots[slotnum].write_func = nubus_tfb_write_func; + shoe.slots[slotnum].destroy_func = NULL; shoe.slots[slotnum].interrupts_enabled = 1; nubus_tfb_init(ctx, slotnum); return 1; @@ -591,6 +599,7 @@ uint32_t shoebill_install_ethernet_card(shoebill_config_t *config, uint8_t slotn shoe.slots[slotnum].connected = 1; shoe.slots[slotnum].read_func = nubus_ethernet_read_func; shoe.slots[slotnum].write_func = nubus_ethernet_write_func; + shoe.slots[slotnum].destroy_func = nubus_ethernet_destroy_func; shoe.slots[slotnum].interrupts_enabled = 1; nubus_ethernet_init(ctx, slotnum, ethernet_addr); return 1; @@ -1013,6 +1022,8 @@ void slog(const char *fmt, ...) va_start(args, fmt); vprintf(fmt, args); va_end(args); + + fflush(stdout); } diff --git a/core/cpu.c b/core/cpu.c index 32b63f2..e78decc 100644 --- a/core/cpu.c +++ b/core/cpu.c @@ -69,7 +69,7 @@ static void inst_chk2_cmp2 (void) { } static void inst_illegal (void) { - assert(!"illegal: error: not implemented\n"); + throw_illegal_instruction(); } static void inst_move16 (void) { @@ -81,7 +81,19 @@ static void inst_rtm (void) { } static void inst_tas (void) { - assert(!"tas: error: not implemented\n"); + ~decompose(shoe.op, 0100 1010 11 MMMMMM); + + call_ea_read(M, 1); + const uint8_t byte = shoe.dat; + + set_sr_c(0); + set_sr_v(0); + set_sr_z(byte == 0); + set_sr_n(byte >> 7); + + shoe.dat = byte | 0x80; + + call_ea_write(M, 1); } static void inst_trapcc (void) { @@ -550,15 +562,11 @@ static void inst_exg (void) { shoe.a[x] = shoe.a[y]; shoe.a[y] = tmp; } - else if (p == ~b(10001)) { // addr/reg mode + else /* if (p == ~b(10001)) */ { // addr/reg mode const uint32_t tmp = shoe.d[x]; shoe.d[x] = shoe.a[y]; shoe.a[y] = tmp; } - else { - // I'm not sure whether decode.c will map opcodes with other modes here - assert(!"inst_exg: bad op mode"); - } } static void inst_stop (void) { @@ -1071,16 +1079,8 @@ static void inst_long_div (void) { static void inst_addq (void) { ~decompose(shoe.op, 0101 ddd 0 ss MMMMMM); const uint8_t dat = d + ((!d)<<3); - if ((s==0) && ((M>>3) == 1)) { - // There's a very subtle distinciton in the M68000 Family Programmer's Reference Manual, - // "Only word and long operations can be used with address registers" with subq, but - // "Word and long operations are also allowed on the address registers" with addq. - // Hmm... - // What it actually means is "If size==byte, illegal instruction. If size==word -> pretend size is long." - throw_illegal_instruction(); - return ; - } - else if ((M>>3) == 1) { // size is always long if using addr register, CCodes aren't set. + + if ((M>>3) == 1) { // size is always long if using addr register, CCodes aren't set. shoe.a[M&7] += dat; return ; } @@ -1103,11 +1103,7 @@ static void inst_subq (void) { ~decompose(shoe.op, 0101 ddd 1 ss MMMMMM); const uint8_t dat = d + ((!d)<<3); - if ((s==0) && ((M>>3) == 1)) { // Reject byte-size for addr registers - throw_illegal_instruction(); - return ; - } - else if ((M>>3) == 1) { // Use long-size for addr registers + if ((M>>3) == 1) { // Use long-size for addr registers shoe.a[M&7] -= dat; return ; } @@ -1314,12 +1310,6 @@ static void inst_moves (void) { ~decompose(shoe.op, 0000 1110 ss MMMMMM); ~decompose(ext, a rrr d 00000000000); - if ((M>>3) < 2) { - // Dn and An addressing modes not supported - throw_illegal_instruction(); - return ; - } - const uint8_t fc = d ? shoe.dfc : shoe.sfc; const uint8_t sz = 1<register - if (~bmatch(M, xx100xxx)) { // predecrement isn't allowed for mem->reg - throw_illegal_instruction(); - return ; - } if (~bmatch(M, xx011xxx)) // if postincrement, shoe.dat = shoe.a[M&7]; // hand-parse this address mode else @@ -2101,10 +2082,6 @@ static void inst_movem (void) { shoe.a[M&7] = shoe.dat; } else { // register->memory - if (~bmatch(M, xx011xxx)) { // postincrement isn't allowed for reg->mem - throw_illegal_instruction(); - return ; - } uint32_t addr; uint16_t newmask = mask; // if predecrement-mode, bit-reversed mask. Regular mask otherwise. uint16_t numbits = 0; // the number of set bits in mask @@ -2220,30 +2197,54 @@ static void inst_movep (void) { switch (m) { case 0: { // word, mem->reg uint16_t val = lget(addr, 1); + if (shoe.abort) return; + val = (val << 8) | lget(addr + 2, 1); + if (shoe.abort) return; + set_d(d, val, 2); break; } case 1: { // long, mem->reg uint32_t val = lget(addr, 1); + if (shoe.abort) return; + val = (val << 8) | lget(addr + 2, 1); + if (shoe.abort) return; + val = (val << 8) | lget(addr + 4, 1); + if (shoe.abort) return; + val = (val << 8) | lget(addr + 6, 1); + if (shoe.abort) return; + shoe.d[d] = val; break; } case 2: { // word, reg->mem const uint16_t val = shoe.d[d]; lset(addr + 0, 1, (val >> 8) & 0xff); + if (shoe.abort) return; + lset(addr + 2, 1, (val >> 0) & 0xff); + if (shoe.abort) return; + break; } case 3: { // long, reg->mem const uint32_t val = shoe.d[d]; lset(addr + 0, 1, (val >> 24) & 0xff); + if (shoe.abort) return; + lset(addr + 2, 1, (val >> 16) & 0xff); + if (shoe.abort) return; + lset(addr + 4, 1, (val >> 8) & 0xff); + if (shoe.abort) return; + lset(addr + 6, 1, (val >> 0) & 0xff); + if (shoe.abort) return; + break; } } @@ -2723,9 +2724,7 @@ static void inst_ext (void) { uint32_t val = (int8_t)get_d(r, 1); set_d(r, val, 4); break; - } default: - throw_illegal_instruction(); - return ; + } } set_sr_v(0); set_sr_c(0); @@ -2971,7 +2970,7 @@ void cpu_step() if (shoe.pc & 1) { // throw_address_error(shoe.pc, 0); // I'm leaving this assert in here for now because it almost always indicates a bug in the emulator when it fires - assert(!"What do I do here?"); + assert(!"odd PC address (probably a bug)"); return ; } diff --git a/core/dis.c b/core/dis.c index 785e087..6e45214 100644 --- a/core/dis.c +++ b/core/dis.c @@ -1104,11 +1104,16 @@ void dis_pea() { } void dis_nbcd() { - sprintf(dis.str, "nbcd???"); + ~decompose(dis_op, 0100 1000 00 MMMMMM); + sprintf(dis.str, "nbcd %s", decode_ea_rw(M, 1)); } void dis_sbcd() { - sprintf(dis.str, "sbcd???"); + ~decompose(dis_op, 1000 yyy 10000 r xxx); + if (r) + sprintf(dis.str, "sbcd d%u,d%u", x, y); + else + sprintf(dis.str, "sbcd -(a%u),-(a%u)", x, y); } void dis_pack() { @@ -1130,7 +1135,8 @@ void dis_divs() { } void dis_bkpt() { - sprintf(dis.str, "bkpt???"); + ~decompose(dis_op, 0100 1000 0100 1 vvv); + sprintf(dis.str, "bkpt %u", v); } void dis_swap() { @@ -1411,23 +1417,38 @@ void dis_move16 () { } void dis_rtm () { - sprintf(dis.str, "rtm???"); + ~decompose(dis_op, 0000 0110 1100 d rrr); + sprintf(dis.str, "rtm %c%u", "da"[d], r); } void dis_tas () { - sprintf(dis.str, "tas???"); + ~decompose(dis_op, 1000 rrr 011 MMMMMM); + sprintf(dis.str, "tas.b %s", decode_ea_rw(M, 1)); } void dis_trapcc() { - sprintf(dis.str, "trapcc???"); + ~decompose(dis_op, 0101 cccc 11111 ooo); + uint32_t data; + switch (c) { + case 2: + data = dis_next_word(); + sprintf(dis.str, "trapcc.w 0x%04x", data); + break; + case 3: + data = dis_next_word(); + data = (data << 16) | dis_next_word(); + sprintf(dis.str, "trapcc.w 0x%08x", data); + break; + case 4: + sprintf(dis.str, "trapcc"); + break; + } } void dis_trapv() { - sprintf(dis.str, "trapv???"); + sprintf(dis.str, "trapv"); } - - void dis_mc68851_decode() { ~decompose(dis_op, 1111 000 a b c MMMMMM); diff --git a/core/ethernet.c b/core/ethernet.c index 22ac07f..31b383e 100644 --- a/core/ethernet.c +++ b/core/ethernet.c @@ -28,6 +28,12 @@ #include #include "shoebill.h" +#include +#include +#include +#include + + #include "ethernet_rom/rom.c" static uint32_t compute_nubus_crc(uint8_t *rom, uint32_t len) @@ -136,10 +142,326 @@ enum ether_dcr_masks { dcr_ft1 = 1<<6, // fifo threshhold select (1) }; +// receive status register +enum ether_rsr_masks { + rsr_prx = 1<<0, // packet received intact + rsr_crc = 1<<1, // crc error + rsr_fae = 1<<2, // frame alignment error + rsr_fo = 1<<3, // fifo overrun + rsr_mpa = 1<<4, // missed packet + rsr_phy = 1<<5, // physical/multicast address (0->phys, 1->multi) + rsr_dis = 1<<6, // received disabled + rsr_dfr = 1<<7, // deferring +}; + +static void _nubus_interrupt(uint8_t slotnum) +{ + shoe.via[1].rega_input &= 0x3f & ~(1 << (slotnum - 9)); + via_raise_interrupt(2, IFR_CA1); +} + +static void _clear_nubus_interrupt(uint8_t slotnum) +{ + shoe.via[1].rega_input |= (1 << (slotnum - 9)); +} + +/* + * How many recv buffers (256-byte buffers) does the + * given number of bytes require? + */ +#define eth_recv_required_bufs(a) ({ \ + const uint32_t sz = (a); \ + (sz >> 8) + ((sz & 0xff) != 0); \ +}) + +/* + * The number of 256-byte buffers available for writing + * in the receive buffer (between ctx->curr and ctx->bnry) + */ +#define eth_recv_free_bufs() ({ \ + const uint8_t boundary = (ctx->bnry >= ctx->pstop) ? ctx->pstart : ctx->bnry; \ + const uint8_t curr = (ctx->curr >= ctx->pstop) ? ctx->pstart : ctx->curr; \ + const uint8_t total_bufs = ctx->pstop - ctx->pstart; \ + uint8_t f; \ + if (curr == boundary) \ + f = 0; /* This shouldn't happen */ \ + else if (curr > boundary) \ + f = (ctx->pstop - curr) + (boundary- ctx->pstart) - 1; \ + else \ + f = boundary - curr - 1; \ + f; \ +}) + + +void *_ethernet_receiver_thread(void *arg) +{ + const uint8_t multicast_addr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + shoebill_card_ethernet_t *ctx = (shoebill_card_ethernet_t*)arg; + uint8_t *buf = malloc(4096); + assert(buf); + + // While nubus_ethernet_destroy() hasn't been called + while (!ctx->teardown) { + struct timeval tv; + fd_set fdset; + int ret; + uint32_t i; + + FD_ZERO(&fdset); + FD_SET(ctx->tap_fd, &fdset); + + tv.tv_sec = 0; + tv.tv_usec = 100000; + + ret = select(ctx->tap_fd + 1, &fdset, NULL, NULL, &tv); + assert(ret != -1); + + if (FD_ISSET(ctx->tap_fd, &fdset)) { + FD_CLR(ctx->tap_fd, &fdset); + + /* + * Read in the next packet, leaving space for the 4 byte + * header + */ + int actual_packet_length = read(ctx->tap_fd, buf + 4, 4092); + + slog("ethernet: received packet bnry=%x curr=%x pstart=%x pstop=%x cr=%x ret=%d frame=0x%02x%02x\n", + ctx->bnry, ctx->curr, ctx->pstart, ctx->pstop, ctx->cr, actual_packet_length, + buf[0x10], buf[0x11]); + + /* + * If it's a bogus packet length, reject it + * (what's the actual minimum allowable packet length?) + */ + if (actual_packet_length <= 12) { + slog("ethernet: too small len\n"); + continue; + } + + /* I'm sure A/UX can't handle > 2kb packets */ + if (actual_packet_length > 2048) { + slog("ethernet: too high len\n"); + continue; + } + + /* If it's neither multicast nor addressed to us, reject it */ + if ((memcmp(buf + 4, ctx->ethernet_addr, 6) != 0) && + (memcmp(buf + 4, multicast_addr, 6) != 0)) { + slog("ethernet: bad address\n"); + continue; + } + + /* A/UX seems to expect a minimum packet length (60 bytes??) */ + if (actual_packet_length < 60) + actual_packet_length = 60; + + /* The number of bytes to write + the 4 byte header */ + const uint32_t received_bytes = actual_packet_length + 4; + + pthread_mutex_lock(&ctx->lock); + + /* + * If the card isn't initialized yet, just drop the packet + */ + if (ctx->cr & cr_stp) { + slog("ethernet: uninit\n"); + pthread_mutex_unlock(&ctx->lock); + continue; + } + + /* + * If the receive-register state is bogus, just drop the + * packet + */ + if ((ctx->pstop <= ctx->pstart) || + (ctx->curr < ctx->pstart) || + (ctx->bnry < ctx->pstart) || + (ctx->pstop > 0x40) || + (ctx->pstart == 0)) { + // This shouldn't happen if the card is initialized + assert(!"ethernet: receive register state is bogus"); + pthread_mutex_unlock(&ctx->lock); + continue; + } + + slog("ethernet: success, req=%u free=%u\n", eth_recv_required_bufs(received_bytes), eth_recv_free_bufs()); + + /* + * If there isn't enough buffer space to store the packet, + * block until ctx->bnry is modified. + */ + const uint8_t required_bufs = eth_recv_required_bufs(received_bytes); + while (eth_recv_free_bufs() < required_bufs) { + pthread_mutex_unlock(&ctx->lock); + + if (ctx->teardown) + goto bail; + + printf("ethernet: sleeping\n"); + usleep(50); // FIXME: use a cond variable here + pthread_mutex_lock(&ctx->lock); + } + + /* Roll around ctx->curr if necessary */ + if (ctx->curr >= ctx->pstop) + ctx->curr = ctx->pstart; + + const uint8_t orig_curr = ctx->curr; + + /* Copy the packet to card RAM */ + for (i = 0; i < required_bufs; i++) { + assert(ctx->curr != ctx->bnry); // this can't happen if we did our math right earlier + + uint8_t *ptr = &ctx->ram[ctx->curr * 256]; + memcpy(ptr, &buf[i * 256], 256); + + ctx->curr++; + if (ctx->curr >= ctx->pstop) + ctx->curr = ctx->pstart; + } + assert(ctx->curr != ctx->bnry); // this can't happen if we did our math right earlier + + /* The packet was received intact */ + ctx->rsr = rsr_prx; + + /* Fill in the 4 byte packet header */ + ctx->ram[orig_curr * 256 + 0] = ctx->rsr; + ctx->ram[orig_curr * 256 + 1] = ctx->curr; + ctx->ram[orig_curr * 256 + 2] = received_bytes & 0xff; // low byte + ctx->ram[orig_curr * 256 + 3] = (received_bytes >> 8) & 0xff; // high byte + + /* If the prx interrupt is enabled, interrupt */ + if (ctx->imr & imr_pxre) { + ctx->isr |= isr_prx; + _nubus_interrupt(ctx->slotnum); + } + + slog("ethernet: received packet (len=%d)\n", ret); + + pthread_mutex_unlock(&ctx->lock); + } + } + +bail: + + free(buf); + + return NULL; +} + +/* +_Bool sent_arp_response = 0; + +const uint8_t arp_response[60] = { + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, // router's MAC address + 0x22, 0x33, 0x55, 0x77, 0xbb, 0xdd, // card MAC address + 0x08, 0x06, // ARP frame + 0x00, 0x01, // Ethernet + 0x08, 0x00, // IP + 0x06, // MAC size + 0x04, // IP size + 0x00, 0x02, // reply + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, // router's MAC address + 192, 168, 2, 1, // router IP address + 0x22, 0x33, 0x55, 0x77, 0xbb, 0xdd, // card MAC address + 192, 168, 2, 100, // card IP address + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // padding +}; + +void _test_write_packet(shoebill_card_ethernet_t *ctx) +{ + slog("ethernet: writing packet to curr=0x%02x\n", ctx->curr); + + uint8_t *ptr = &ctx->ram[ctx->curr * 256]; + + // The packet was received intact + ctx->rsr = rsr_prx; + + // The next packet address is the next 256 byte chunk + ctx->curr += 1; + + ptr[0] = ctx->rsr; + ptr[1] = ctx->curr; // next packet ptr (8 bit) + ptr[2] = 60; // low byte of the packet size + ptr[3] = 0; // high byte of the packet size + memcpy(ptr + 4, arp_response, 60); // the packet + + if (ctx->imr & imr_pxre) + ctx->isr |= isr_prx; + + _nubus_interrupt(ctx->slotnum); +} +*/ + +void *_ethernet_sender_thread(void *arg) +{ + shoebill_card_ethernet_t *ctx = (shoebill_card_ethernet_t*)arg; + + slog("ethernet: ethernet_sender_thread starts...\n"); + + // While nubus_ethernet_destroy() hasn't been called + while (!ctx->teardown) { + struct timeval now; + struct timespec later; + int ret; + + // Wait on the condition variable, with a timeout of 100ms + // slog("ethernet: locking cond mutex...\n"); + pthread_mutex_lock(&ctx->sender_cond_mutex); + // slog("ethernet: locked cond mutex...\n"); + gettimeofday(&now, NULL); + later.tv_sec = now.tv_sec; + later.tv_nsec = (now.tv_usec * 1000) + (1000000000 / 10); + if (later.tv_nsec >= 1000000000) { + later.tv_nsec -= 1000000000; + later.tv_sec++; + } + + // slog("ethernet: waiting on cond...\n"); + pthread_cond_timedwait(&ctx->sender_cond, + &ctx->sender_cond_mutex, + &later); + assert(pthread_mutex_unlock(&ctx->sender_cond_mutex) == 0); + + // Only proceed if there's a packet ready to send + if (!ctx->send_ready) + continue; + + slog("ethernet: sender thread wakes up...\n"); + + ctx->send_ready = 0; + + // --- Send the packet here --- + assert(ctx->tbcr <= 2048); // sanity check the packet len + assert(ctx->tbcr >= 42); + + ret = write(ctx->tap_fd, ctx->ram, ctx->tbcr); + if (ret != ctx->tbcr) { + slog("ethernet: write() returned %d, not %d errno=%d\n", ret, ctx->tbcr, errno); + } + + // Lock the ethernet context (we're going to manipulate the ethernet registers) + pthread_mutex_lock(&ctx->lock); + + // indicate that the packet has been sent + ctx->cr &= ~cr_txp; // clear the command register txp bit + ctx->isr |= isr_ptx; // interrupt status: packet transmitted with no errors + + // the "packet transmitted" interrupt really should be enabled + if (ctx->imr & imr_ptxe) { + _nubus_interrupt(ctx->slotnum); + slog("ethernet: sender: sending interrupt to slot %u\n", ctx->slotnum); + } + + assert(pthread_mutex_unlock(&ctx->lock) == 0); + } + + return NULL; +} + void nubus_ethernet_init(void *_ctx, uint8_t slotnum, uint8_t ethernet_addr[6]) { shoebill_card_ethernet_t *ctx = (shoebill_card_ethernet_t*)_ctx; - memset(ctx, 0, sizeof(shoebill_card_ethernet_t)); memcpy(ctx->rom, _ethernet_rom, 4096); @@ -148,6 +470,15 @@ void nubus_ethernet_init(void *_ctx, uint8_t slotnum, uint8_t ethernet_addr[6]) ctx->rom[6] = 0x00; ctx->rom[7] = 0x00; + ctx->slotnum = slotnum; // so the threads know which slot this is + + pthread_mutex_init(&ctx->lock, NULL); + pthread_cond_init(&ctx->sender_cond, NULL); + pthread_mutex_init(&ctx->sender_cond_mutex, NULL); + + pthread_create(&ctx->sender_pid, NULL, _ethernet_sender_thread, ctx); + pthread_create(&ctx->receiver_pid, NULL, _ethernet_receiver_thread, ctx); + /* * The first 8 bytes contain the MAC address * and aren't part of the CRC @@ -156,6 +487,25 @@ void nubus_ethernet_init(void *_ctx, uint8_t slotnum, uint8_t ethernet_addr[6]) ctx->cr |= cr_stp; // "STP powers up high" ctx->isr |= isr_rst; // I presume ISR's RST powers up high too + + /* Platform-specific tap code */ + ctx->tap_fd = open("/dev/tap0", O_RDWR); + assert(ctx->tap_fd >= 0); +} + +void nubus_ethernet_destroy_func(uint8_t slotnum) +{ + shoebill_card_ethernet_t *ctx = (shoebill_card_ethernet_t*)shoe.slots[slotnum].ctx; + + ctx->teardown = 1; + pthread_join(ctx->sender_pid, NULL); + pthread_join(ctx->receiver_pid, NULL); + + pthread_mutex_destroy(&ctx->lock); + pthread_mutex_destroy(&ctx->sender_cond_mutex); + pthread_cond_destroy(&ctx->sender_cond); + + close(ctx->tap_fd); } uint32_t nubus_ethernet_read_func(const uint32_t rawaddr, @@ -165,16 +515,18 @@ uint32_t nubus_ethernet_read_func(const uint32_t rawaddr, shoebill_card_ethernet_t *ctx = (shoebill_card_ethernet_t*)shoe.slots[slotnum].ctx; uint32_t result = 0; + pthread_mutex_lock(&ctx->lock); + switch ((rawaddr >> 16) & 0xf) { case 0xd: { // ram - const uint16_t addr = rawaddr & 0xfff; + const uint16_t addr = rawaddr & 0x3fff; uint8_t *ram = ctx->ram; if (size == 1) result = ram[addr]; else if (size == 2) { result = ram[addr] << 8; - result |= ram[(addr+1) & 0xfff]; + result |= ram[(addr+1) & 0x3fff]; } else assert(!"read: bogus size"); @@ -224,6 +576,15 @@ uint32_t nubus_ethernet_read_func(const uint32_t rawaddr, case 7: // isr (interrupt status register) result = ctx->isr; + + // test test test + // if we're reading isr_ptx for the first time, + // send a test packet (but never again) + /*if ((result & isr_ptx) && (!sent_arp_response)) { + sent_arp_response = 1; + _test_write_packet(ctx); + }*/ + goto done; case 8: // crda0 (current remote DMA address 0) @@ -241,6 +602,7 @@ uint32_t nubus_ethernet_read_func(const uint32_t rawaddr, goto done; case 12: // rsr (receive status register) + result = ctx->rsr; goto done; case 13: // cntr0 (tally counter 0 (frame alignment errors)) @@ -302,6 +664,8 @@ uint32_t nubus_ethernet_read_func(const uint32_t rawaddr, done: + pthread_mutex_unlock(&ctx->lock); + slog("result = 0x%x\n", result); // slog("ethernet: reading 0x%x sz=%u from addr 0x%x\n", result, size, rawaddr); @@ -316,19 +680,19 @@ void nubus_ethernet_write_func(const uint32_t rawaddr, { shoebill_card_ethernet_t *ctx = (shoebill_card_ethernet_t*)shoe.slots[slotnum].ctx; uint32_t i; - // slog("ethernet: writing 0x%x sz=%u to addr 0x%x\n", data, size, rawaddr); + + pthread_mutex_lock(&ctx->lock); switch ((rawaddr >> 16) & 0xf) { case 0xd: { // ram - const uint16_t addr = rawaddr & 0xfff; + const uint16_t addr = rawaddr & 0x3fff; uint8_t *ram = ctx->ram; - if (size == 1) ram[addr] = data; else if (size == 2) { ram[addr] = data >> 8; - ram[(addr+1) & 0xfff] = data & 0xff; + ram[(addr+1) & 0x3fff] = data & 0xff; } else assert(!"write: bogus size"); @@ -342,18 +706,33 @@ void nubus_ethernet_write_func(const uint32_t rawaddr, const uint8_t reg = 15 ^ ((rawaddr >> 2) & 15); assert(size == 1); + { + const char *name = "???"; + if (ETHPAGE() == 0) name = eth_w0_reg_names[reg]; + else if (ETHPAGE() == 1) name = eth_1_reg_names[reg]; + slog("ethernet: writing 0x%02x to register %u (%s) (rawaddr=0x%x) pc=0x%x\n", data, reg, name, rawaddr, shoe.pc); + } + if (reg == 0) { // command register (exists in all pages) + + // If we're setting TXP, wake up the sender thread + if (((ctx->cr & cr_txp) == 0) && + ((data & cr_txp) != 0)) { + ctx->send_ready = 1; + assert(pthread_mutex_lock(&ctx->sender_cond_mutex) == 0); + assert(pthread_cond_signal(&ctx->sender_cond) == 0); + assert(pthread_mutex_unlock(&ctx->sender_cond_mutex) == 0); + } + + // if we're setting STA, clear isr_rst + if (data & cr_sta) + ctx->isr &= ~isr_rst; + + // FIXME: if we're setting STP, then we probably need to set isr_rst + ctx->cr = data; goto done; } else if (ETHPAGE() == 0) { // page 0 - - { - const char *name = "???"; - if (ETHPAGE() == 0) name = eth_w0_reg_names[reg]; - else if (ETHPAGE() == 1) name = eth_1_reg_names[reg]; - slog("ethernet: writing 0x%02x to register %u (%s) (rawaddr=0x%x) pc=0x%x\n", data, reg, name, rawaddr, shoe.pc); - } - switch (reg) { default: assert(!"never get here"); @@ -383,10 +762,31 @@ void nubus_ethernet_write_func(const uint32_t rawaddr, ctx->tbcr = (ctx->tbcr & 0x00ff) | (data<<8); goto done; - case 7: // isr (interrupt status) - ctx->isr = data; + case 7: { // isr (interrupt status) + // writing 1's clears the bits in the ISR + uint8_t mask = data & 0x7f; // but not the RST bit + ctx->isr &= ~mask; + + /* + * If there are packets yet to be processed, + * then continue to assert the isr_prx bit + */ + uint8_t inc_boundary = ctx->bnry + 1; + if (inc_boundary >= ctx->pstop) + inc_boundary = ctx->pstart; + if (ctx->curr != inc_boundary) + ctx->isr |= isr_prx; + + /* + * If prx and ptx are no longer asserted, + * then we may clear the nubus interrupt. + */ + if (((ctx->isr & (isr_prx | isr_ptx)) == 0) && + ((ctx->cr & cr_stp) == 0)) + _clear_nubus_interrupt(slotnum); + goto done; - + } case 8: // rsar0 (remote start address 0) goto done; @@ -412,6 +812,7 @@ void nubus_ethernet_write_func(const uint32_t rawaddr, goto done; case 15: // imr (interrupt mask) + ctx->imr = data & 0x7f; goto done; } } else if (ETHPAGE() == 1) { // page 1 @@ -453,6 +854,7 @@ void nubus_ethernet_write_func(const uint32_t rawaddr, done: + pthread_mutex_unlock(&ctx->lock); return; } diff --git a/core/shoebill.h b/core/shoebill.h index 61b7b50..87ea031 100644 --- a/core/shoebill.h +++ b/core/shoebill.h @@ -596,17 +596,39 @@ typedef struct { } shoebill_card_tfb_t; typedef struct { - uint8_t rom[4096]; - uint8_t ram[4096]; + // Card ROM (4kb) + uint8_t rom[0x1000]; + + // Card RAM (16kb buffer, apparently) + uint8_t ram[0x4000]; + + // Card MAC address uint8_t ethernet_addr[6]; + // Card slot number + uint8_t slotnum; + + // -- thread state -- + uint8_t recv_buf[4096], send_buf[4096]; + uint16_t recv_len, send_len; + _Bool teardown, send_ready; + + pthread_t sender_pid, receiver_pid; + pthread_mutex_t lock, sender_cond_mutex; + pthread_cond_t sender_cond; + + // -- registers -- + uint8_t cr; // command register, all pages, read/write // Page 0 registers uint8_t isr; // interrupt status register, read/write + uint8_t imr; // interrupt mask register, write + uint8_t dcr; // data configuration register (write) uint8_t tcr; // transmit configuration register (write) uint8_t rcr; // receive configuration register (write) + uint8_t pstart; // receive buffer start pointer (write) uint8_t pstop; // receive buffer boundary (write) uint8_t bnry; // a different kind of receive buffer boundary (read/write) @@ -614,12 +636,16 @@ typedef struct { uint8_t tpsr; // transmit page start pointer (write) uint16_t tbcr; // transmit buffer count register (write) + uint8_t rsr; // receive status register (read) + // Page 1 registers (read/write) uint8_t mar[8]; // multicast address uint8_t par[6]; // physical address uint8_t curr; // current page + + int tap_fd; } shoebill_card_ethernet_t; typedef enum { @@ -632,6 +658,7 @@ typedef enum { typedef struct { uint32_t (*read_func)(uint32_t, uint32_t, uint8_t); void (*write_func)(uint32_t, uint32_t, uint32_t, uint8_t); + void (*destroy_func)(uint8_t); uint8_t slotnum; _Bool connected; @@ -1054,6 +1081,7 @@ shoebill_video_frame_info_t nubus_video_get_frame(shoebill_card_video_t *ctx, void nubus_ethernet_init(void *_ctx, uint8_t slotnum, uint8_t ethernet_addr[6]); uint32_t nubus_ethernet_read_func(uint32_t, uint32_t, uint8_t); void nubus_ethernet_write_func(uint32_t, uint32_t, uint32_t, uint8_t); +void nubus_ethernet_destroy_func(uint8_t); // Sound (Apple Sound Chip) void sound_dma_write_raw(uint16_t addr, uint8_t sz, uint32_t data); diff --git a/core/sound.c b/core/sound.c index 2b60ebe..872bb8c 100644 --- a/core/sound.c +++ b/core/sound.c @@ -1,3 +1,28 @@ +/* + * Copyright (c) 2014, Peter Rutenbar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + #include #include "../core/shoebill.h" diff --git a/debugger/debugger.c b/debugger/debugger.c index 80b3142..dcf5433 100644 --- a/debugger/debugger.c +++ b/debugger/debugger.c @@ -845,7 +845,7 @@ int main (int argc, char **argv) config.rom_path = "../priv/macii.rom"; - config.scsi_devices[0].path = "../priv/aux3.0.1.img"; + config.scsi_devices[0].path = "../priv/root3.img"; //config.scsi_devices[1].path = "../priv/marathon.img"; /*dbg_state.ring_len = 256 * 1024 * 1024; @@ -864,6 +864,9 @@ int main (int argc, char **argv) 640, // 1024, 480); // 768, + uint8_t ethernet_addr[6] = {0x22, 0x33, 0x55, 0x77, 0xbb, 0xdd}; + shoebill_install_ethernet_card(&config, 13, ethernet_addr); + // Start the VIA timer thread shoebill_start(); diff --git a/gui/Shoebill/shoeApplication.m b/gui/Shoebill/shoeApplication.m index 17d43a0..617aa23 100644 --- a/gui/Shoebill/shoeApplication.m +++ b/gui/Shoebill/shoeApplication.m @@ -373,7 +373,7 @@ void pram_callback (void *param, const uint8_t addr, const uint8_t byte) [self createScreenWindow:9 height:height width:width]; - uint8_t ethernet_addr[6] = {0x22, 0x33, 0x55, 0x77, 0xbb, 0xdd}; + uint8_t ethernet_addr[6] = {0x00, 0x24, 0x7e, 0x14, 0xd7, 0xff}; shoebill_install_ethernet_card(&config, 13, ethernet_addr); shoebill_start();