|
13 | 13 | #include "net/net.h"
|
14 | 14 | #include "hw/irq.h"
|
15 | 15 | #include "hw/net/smc91c111.h"
|
| 16 | +#include "hw/registerfields.h" |
16 | 17 | #include "hw/qdev-properties.h"
|
17 | 18 | #include "qapi/error.h"
|
18 | 19 | #include "qemu/log.h"
|
@@ -126,6 +127,13 @@ static const VMStateDescription vmstate_smc91c111 = {
|
126 | 127 | #define RS_TOOSHORT 0x0400
|
127 | 128 | #define RS_MULTICAST 0x0001
|
128 | 129 |
|
| 130 | +FIELD(PTR, PTR, 0, 11) |
| 131 | +FIELD(PTR, NOT_EMPTY, 11, 1) |
| 132 | +FIELD(PTR, RESERVED, 12, 1) |
| 133 | +FIELD(PTR, READ, 13, 1) |
| 134 | +FIELD(PTR, AUTOINCR, 14, 1) |
| 135 | +FIELD(PTR, RCV, 15, 1) |
| 136 | + |
129 | 137 | static inline bool packetnum_valid(int packet_num)
|
130 | 138 | {
|
131 | 139 | return packet_num >= 0 && packet_num < NUM_PACKETS;
|
@@ -372,6 +380,49 @@ static void smc91c111_reset(DeviceState *dev)
|
372 | 380 | #define SET_LOW(name, val) s->name = (s->name & 0xff00) | val
|
373 | 381 | #define SET_HIGH(name, val) s->name = (s->name & 0xff) | (val << 8)
|
374 | 382 |
|
| 383 | +/* |
| 384 | + * The pointer register's pointer is an 11 bit value (so it exactly |
| 385 | + * indexes a 2048-byte data frame). Add the specified offset to it, |
| 386 | + * wrapping around at the 2048 byte mark, and return the resulting |
| 387 | + * wrapped value. There are flag bits in the top part of the register, |
| 388 | + * but we can ignore them here as the mask will mask them out. |
| 389 | + */ |
| 390 | +static int ptr_reg_add(smc91c111_state *s, int offset) |
| 391 | +{ |
| 392 | + return (s->ptr + offset) & R_PTR_PTR_MASK; |
| 393 | +} |
| 394 | + |
| 395 | +/* |
| 396 | + * For an access to the Data Register at @offset, return the |
| 397 | + * required offset into the packet's data frame. This will |
| 398 | + * perform the pointer register autoincrement if required, and |
| 399 | + * guarantees to return an in-bounds offset. |
| 400 | + */ |
| 401 | +static int data_reg_ptr(smc91c111_state *s, int offset) |
| 402 | +{ |
| 403 | + int p; |
| 404 | + |
| 405 | + if (s->ptr & R_PTR_AUTOINCR_MASK) { |
| 406 | + /* |
| 407 | + * Autoincrement: use the current pointer value, and |
| 408 | + * increment the pointer register's pointer field. |
| 409 | + */ |
| 410 | + p = FIELD_EX32(s->ptr, PTR, PTR); |
| 411 | + s->ptr = FIELD_DP32(s->ptr, PTR, PTR, ptr_reg_add(s, 1)); |
| 412 | + } else { |
| 413 | + /* |
| 414 | + * No autoincrement: register offset determines which |
| 415 | + * byte we're addressing. Setting the pointer to the top |
| 416 | + * of the data buffer and then using the pointer wrapping |
| 417 | + * to read the bottom byte of the buffer is not something |
| 418 | + * sensible guest software will do, but the datasheet |
| 419 | + * doesn't say what the behaviour is, so we don't forbid it. |
| 420 | + */ |
| 421 | + p = ptr_reg_add(s, offset & 3); |
| 422 | + } |
| 423 | + return p; |
| 424 | +} |
| 425 | + |
375 | 426 | static void smc91c111_writeb(void *opaque, hwaddr offset,
|
376 | 427 | uint32_t value)
|
377 | 428 | {
|
@@ -518,12 +569,7 @@ static void smc91c111_writeb(void *opaque, hwaddr offset,
|
518 | 569 | n);
|
519 | 570 | return;
|
520 | 571 | }
|
521 |
| - p = s->ptr & 0x07ff; |
522 |
| - if (s->ptr & 0x4000) { |
523 |
| - s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x7ff); |
524 |
| - } else { |
525 |
| - p += (offset & 3); |
526 |
| - } |
| 572 | + p = data_reg_ptr(s, offset); |
527 | 573 | s->data[n][p] = value;
|
528 | 574 | }
|
529 | 575 | return;
|
@@ -673,12 +719,7 @@ static uint32_t smc91c111_readb(void *opaque, hwaddr offset)
|
673 | 719 | n);
|
674 | 720 | return 0;
|
675 | 721 | }
|
676 |
| - p = s->ptr & 0x07ff; |
677 |
| - if (s->ptr & 0x4000) { |
678 |
| - s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x07ff); |
679 |
| - } else { |
680 |
| - p += (offset & 3); |
681 |
| - } |
| 722 | + p = data_reg_ptr(s, offset); |
682 | 723 | return s->data[n][p];
|
683 | 724 | }
|
684 | 725 | case 12: /* Interrupt status. */
|
|
0 commit comments