static uint8_t lsi_reg_readb(LSIState *s, int offset)
{
    uint8_t ret;

#define CASE_GET_REG24(name, addr) \
    case addr: ret = s->name & 0xff; break; \
    case addr + 1: ret = (s->name >> 8) & 0xff; break; \
    case addr + 2: ret = (s->name >> 16) & 0xff; break;

#define CASE_GET_REG32(name, addr) \
    case addr: ret = s->name & 0xff; break; \
    case addr + 1: ret = (s->name >> 8) & 0xff; break; \
    case addr + 2: ret = (s->name >> 16) & 0xff; break; \
    case addr + 3: ret = (s->name >> 24) & 0xff; break;

    switch (offset) {
    case 0x00: /* SCNTL0 */
        ret = s->scntl0;
        break;
    case 0x01: /* SCNTL1 */
        ret = s->scntl1;
        break;
    case 0x02: /* SCNTL2 */
        ret = s->scntl2;
        break;
    case 0x03: /* SCNTL3 */
        ret = s->scntl3;
        break;
    case 0x04: /* SCID */
        ret = s->scid;
        break;
    case 0x05: /* SXFER */
        ret = s->sxfer;
        break;
    case 0x06: /* SDID */
        ret = s->sdid;
        break;
    case 0x07: /* GPREG0 */
        ret = 0x7f;
        break;
    case 0x08: /* Revision ID */
        ret = 0x00;
        break;
    case 0x09: /* SOCL */
        ret = s->socl;
        break;
    case 0xa: /* SSID */
        ret = s->ssid;
        break;
    case 0xb: /* SBCL */
        ret = s->sbcl;
        break;
    case 0xc: /* DSTAT */
        ret = s->dstat | LSI_DSTAT_DFE;
        if ((s->istat0 & LSI_ISTAT0_INTF) == 0)
            s->dstat = 0;
        lsi_update_irq(s);
        break;
    case 0x0d: /* SSTAT0 */
        ret = s->sstat0;
        break;
    case 0x0e: /* SSTAT1 */
        ret = s->sstat1;
        break;
    case 0x0f: /* SSTAT2 */
        ret = s->scntl1 & LSI_SCNTL1_CON ? 0 : 2;
        break;
    CASE_GET_REG32(dsa, 0x10)
    case 0x14: /* ISTAT0 */
        ret = s->istat0;
        break;
    case 0x15: /* ISTAT1 */
        ret = s->istat1;
        break;
    case 0x16: /* MBOX0 */
        ret = s->mbox0;
        break;
    case 0x17: /* MBOX1 */
        ret = s->mbox1;
        break;
    case 0x18: /* CTEST0 */
        ret = 0xff;
        break;
    case 0x19: /* CTEST1 */
        ret = 0;
        break;
    case 0x1a: /* CTEST2 */
        ret = s->ctest2 | LSI_CTEST2_DACK | LSI_CTEST2_CM;
        if (s->istat0 & LSI_ISTAT0_SIGP) {
            s->istat0 &= ~LSI_ISTAT0_SIGP;
            ret |= LSI_CTEST2_SIGP;
        }
        break;
    case 0x1b: /* CTEST3 */
        ret = s->ctest3;
        break;
    CASE_GET_REG32(temp, 0x1c)
    case 0x20: /* DFIFO */
        ret = s->dfifo;
        break;
    case 0x21: /* CTEST4 */
        ret = s->ctest4;
        break;
    case 0x22: /* CTEST5 */
        ret = s->ctest5;
        break;
    case 0x23: /* CTEST6 */
        ret = 0;
        break;
    CASE_GET_REG24(dbc, 0x24)
    case 0x27: /* DCMD */
        ret = s->dcmd;
        break;
    CASE_GET_REG32(dnad, 0x28)
    CASE_GET_REG32(dsp, 0x2c)
    CASE_GET_REG32(dsps, 0x30)
    CASE_GET_REG32(scratch[0], 0x34)
    case 0x38: /* DMODE */
        ret = s->dmode;
        break;
    case 0x39: /* DIEN */
        ret = s->dien;
        break;
    case 0x3a: /* SBR */
        ret = s->sbr;
        break;
    case 0x3b: /* DCNTL */
        ret = s->dcntl;
        break;
    /* ADDER Output (Debug of relative jump address) */
    CASE_GET_REG32(adder, 0x3c)
    case 0x40: /* SIEN0 */
        ret = s->sien0;
        break;
    case 0x41: /* SIEN1 */
        ret = s->sien1;
        break;
    case 0x42: /* SIST0 */
        ret = s->sist0;
        s->sist0 = 0;
        lsi_update_irq(s);
        break;
    case 0x43: /* SIST1 */
        ret = s->sist1;
        s->sist1 = 0;
        lsi_update_irq(s);
        break;
    case 0x46: /* MACNTL */
        ret = 0x0f;
        break;
    case 0x47: /* GPCNTL0 */
        ret = 0x0f;
        break;
    case 0x48: /* STIME0 */
        ret = s->stime0;
        break;
    case 0x4a: /* RESPID0 */
        ret = s->respid0;
        break;
    case 0x4b: /* RESPID1 */
        ret = s->respid1;
        break;
    case 0x4d: /* STEST1 */
        ret = s->stest1;
        break;
    case 0x4e: /* STEST2 */
        ret = s->stest2;
        break;
    case 0x4f: /* STEST3 */
        ret = s->stest3;
        break;
    case 0x50: /* SIDL */
        /* This is needed by the linux drivers.  We currently only update it
           during the MSG IN phase.  */
        ret = s->sidl;
        break;
    case 0x52: /* STEST4 */
        ret = 0xe0;
        break;
    case 0x56: /* CCNTL0 */
        ret = s->ccntl0;
        break;
    case 0x57: /* CCNTL1 */
        ret = s->ccntl1;
        break;
    case 0x58: /* SBDL */
        /* Some drivers peek at the data bus during the MSG IN phase.  */
        if ((s->sstat1 & PHASE_MASK) == PHASE_MI) {
            assert(s->msg_len > 0);
            return s->msg[0];
        }
        ret = 0;
        break;
    case 0x59: /* SBDL high */
        ret = 0;
        break;
    CASE_GET_REG32(mmrs, 0xa0)
    CASE_GET_REG32(mmws, 0xa4)
    CASE_GET_REG32(sfs, 0xa8)
    CASE_GET_REG32(drs, 0xac)
    CASE_GET_REG32(sbms, 0xb0)
    CASE_GET_REG32(dbms, 0xb4)
    CASE_GET_REG32(dnad64, 0xb8)
    CASE_GET_REG32(pmjad1, 0xc0)
    CASE_GET_REG32(pmjad2, 0xc4)
    CASE_GET_REG32(rbc, 0xc8)
    CASE_GET_REG32(ua, 0xcc)
    CASE_GET_REG32(ia, 0xd4)
    CASE_GET_REG32(sbc, 0xd8)
    CASE_GET_REG32(csbc, 0xdc)
    case 0x5c ... 0x9f:
    {
        int n;
        int shift;
        n = (offset - 0x58) >> 2;
        shift = (offset & 3) * 8;
        ret = (s->scratch[n] >> shift) & 0xff;
        break;
    }
    default:
    {
        qemu_log_mask(LOG_GUEST_ERROR,
                      "lsi_scsi: invalid read from reg %s %x\n",
                      offset < ARRAY_SIZE(names) ? names[offset] : "???",
                      offset);
        ret = 0xff;
        break;
    }
    }
#undef CASE_GET_REG24
#undef CASE_GET_REG32

    trace_lsi_reg_read(offset < ARRAY_SIZE(names) ? names[offset] : "???",
                       offset, ret);

    return ret;
}