mirror of
https://github.com/pruten/shoebill.git
synced 2025-01-20 03:29:58 +00:00
Mostly-working ethernet and various changes
This may break the linux/windows builds - I haven't tried to compile on those platforms yet. - Ethernet is more or less working with a hardcoded /dev/tun0 network interface and hardcoded MAC address, though there's no automatic ifconfig/route configuration yet. - Instructions TAS and ILLEGAL are implemented now - Fixed some bugs in MOVEP - Implemented some other instruction disassemblers - Other little changes
This commit is contained in:
parent
4af4262993
commit
b29c69453e
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
89
core/cpu.c
89
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<<s;
|
||||
|
||||
@ -1452,11 +1442,6 @@ static void inst_negx (void) {
|
||||
|
||||
const uint8_t x = (sr_x() != 0);
|
||||
|
||||
if (s == 3) { // is it possible for the decoder to send s==3 here?
|
||||
throw_illegal_instruction();
|
||||
return ;
|
||||
}
|
||||
|
||||
const uint8_t sz = 1<<s;
|
||||
call_ea_read(M, sz);
|
||||
|
||||
@ -2055,10 +2040,6 @@ static void inst_movem (void) {
|
||||
uint32_t i;
|
||||
|
||||
if (d) { // memory->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 ;
|
||||
}
|
||||
|
||||
|
39
core/dis.c
39
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);
|
||||
|
||||
|
438
core/ethernet.c
438
core/ethernet.c
@ -28,6 +28,12 @@
|
||||
#include <string.h>
|
||||
#include "shoebill.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
25
core/sound.c
25
core/sound.c
@ -1,3 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Peter Rutenbar <pruten@gmail.com>
|
||||
* 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 <assert.h>
|
||||
#include "../core/shoebill.h"
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user