diff --git a/src/Makefile.am b/src/Makefile.am index cffac57..7e0ab63 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -39,7 +39,7 @@ keyboard_amstrad.c keyboard_at.c keyboard_olim24.c keyboard_pcjr.c keyboard_xt.c mca.c mcr.c mem.c mem_bios.c mfm_at.c mfm_xebec.c model.c mouse.c mouse_msystems.c mouse_ps2.c mouse_serial.c mvp3.c \ neat.c nmi.c nvr.c olivetti_m24.c opti495.c paths.c pc.c pc87306.c pci.c pic.c piix.c pit.c ppi.c ps1.c ps2.c ps2_mca.c \ ps2_nvr.c nvr_tc8521.c pzx.c rom.c rtc.c rtc_tc8521.c scamp.c scat.c scsi.c scsi_53c400.c scsi_aha1540.c scsi_cd.c scsi_hd.c \ -scsi_zip.c serial.c sio.c sis496.c sl82c460.c sound.c sound_ad1848.c sound_adlib.c sound_adlibgold.c sound_audiopci.c \ +scsi_ibm.c scsi_zip.c serial.c sio.c sis496.c sl82c460.c sound.c sound_ad1848.c sound_adlib.c sound_adlibgold.c sound_audiopci.c \ sound_cms.c sound_emu8k.c sound_gus.c sound_mpu401_uart.c sound_opl.c sound_pas16.c sound_ps1.c sound_pssj.c \ sound_sb.c sound_sb_dsp.c sound_sn76489.c sound_speaker.c sound_ssi2001.c sound_wss.c sound_ym7128.c soundopenal.c \ sst39sf010.c tandy_eeprom.c tandy_rom.c t1000.c t3100e.c timer.c um8669f.c um8881f.c vid_ati_eeprom.c vid_ati_mach64.c \ diff --git a/src/Makefile.mingw-wx-sdl2 b/src/Makefile.mingw-wx-sdl2 index cd7d7cd..b022645 100644 --- a/src/Makefile.mingw-wx-sdl2 +++ b/src/Makefile.mingw-wx-sdl2 @@ -25,7 +25,7 @@ OBJ = 386.o 386_common.o 386_dynarec.o 386_dynarec_ops.o 808x.o 82091aa.o acc203 mem.o mem_bios.o mfm_at.o mfm_xebec.o model.o mouse.o mouse_msystems.o mouse_ps2.o mouse_serial.o \ mvp3.o neat.o nmi.o nvr.o nvr_tc8521.o olivetti_m24.o opti495.o paths.o pc.o pc87306.o pci.o pic.o \ piix.o pit.o ppi.o ps1.o ps2.o ps2_mca.o ps2_nvr.o pzx.o rom.o rtc.o rtc_tc8521.o scamp.o scat.o scsi.o \ - scsi_53c400.o scsi_aha1540.o scsi_cd.o scsi_hd.o scsi_zip.o serial.o sio.o sis496.o sl82c460.o \ + scsi_53c400.o scsi_aha1540.o scsi_cd.o scsi_hd.o scsi_ibm.o scsi_zip.o serial.o sio.o sis496.o sl82c460.o \ sound.o sound_ad1848.o sound_adlib.o sound_adlibgold.o sound_audiopci.o sound_cms.o sound_dbopl.o \ sound_emu8k.o sound_gus.o sound_mpu401_uart.o sound_opl.o sound_pas16.o sound_ps1.o sound_pssj.o \ sound_resid.o sound_sb.o sound_sb_dsp.o sound_sn76489.o sound_speaker.o sound_ssi2001.o sound_wss.o \ diff --git a/src/Makefile.mingw-wx-sdl2-network b/src/Makefile.mingw-wx-sdl2-network index bafc186..18962a0 100644 --- a/src/Makefile.mingw-wx-sdl2-network +++ b/src/Makefile.mingw-wx-sdl2-network @@ -25,7 +25,7 @@ OBJ = 386.o 386_common.o 386_dynarec.o 386_dynarec_ops.o 808x.o 82091aa.o acc203 mem.o mem_bios.o mfm_at.o mfm_xebec.o model.o mouse.o mouse_msystems.o mouse_ps2.o mouse_serial.o \ mvp3.o neat.o nmi.o nvr.o nvr_tc8521.o olivetti_m24.o opti495.o paths.o pc.o pc87306.o pci.o pic.o \ piix.o pit.o ppi.o ps1.o ps2.o ps2_mca.o ps2_nvr.o pzx.o rom.o rtc.o rtc_tc8521.o scamp.o scat.o scsi.o \ - scsi_53c400.o scsi_aha1540.o scsi_cd.o scsi_hd.o scsi_zip.o serial.o sio.o sis496.o sl82c460.o \ + scsi_53c400.o scsi_aha1540.o scsi_cd.o scsi_hd.o scsi_ibm.o scsi_zip.o serial.o sio.o sis496.o sl82c460.o \ sound.o sound_ad1848.o sound_adlib.o sound_adlibgold.o sound_audiopci.o sound_cms.o sound_dbopl.o \ sound_emu8k.o sound_gus.o sound_mpu401_uart.o sound_opl.o sound_pas16.o sound_ps1.o sound_pssj.o \ sound_resid.o sound_sb.o sound_sb_dsp.o sound_sn76489.o sound_speaker.o sound_ssi2001.o sound_wss.o \ diff --git a/src/hdd.c b/src/hdd.c index f2a9fd7..00b246d 100644 --- a/src/hdd.c +++ b/src/hdd.c @@ -9,6 +9,7 @@ #include "mfm_xebec.h" #include "scsi_53c400.h" #include "scsi_aha1540.h" +#include "scsi_ibm.h" #include "xtide.h" char hdd_controller_name[16]; @@ -39,6 +40,7 @@ static struct {"[IDE] XTIDE (PS/1)", "xtide_ps1", &xtide_ps1_device, 0, 1, 0}, {"[SCSI] Adaptec AHA-1542C", "aha1542c", &scsi_aha1542c_device, 0, 0, 1}, {"[SCSI] BusLogic BT-545S", "bt545s", &scsi_bt545s_device, 0, 0, 1}, + {"[SCSI] IBM SCSI Adapter with Cache", "ibmscsi_mca",&scsi_ibm_device, 0, 0, 1}, {"[SCSI] Longshine LCS-6821N", "lcs6821n", &scsi_lcs6821n_device, 0, 0, 1}, {"[SCSI] Rancho RT1000B", "rt1000b", &scsi_rt1000b_device, 0, 0, 1}, {"[SCSI] Trantor T130B", "t130b", &scsi_t130b_device, 0, 0, 1}, diff --git a/src/scsi_ibm.c b/src/scsi_ibm.c new file mode 100644 index 0000000..708c24e --- /dev/null +++ b/src/scsi_ibm.c @@ -0,0 +1,1278 @@ +#include +#include +#include "ibm.h" + +#include "device.h" +#include "dma.h" +#include "io.h" +#include "mca.h" +#include "mem.h" +#include "pic.h" +#include "rom.h" +#include "scsi.h" +#include "timer.h" + +#include "scsi_ibm.h" + +#define SCSI_TIME (20) +#define POLL_TIME (TIMER_USEC * 10) +#define MAX_BYTES_TRANSFERRED_PER_POLL 50 + +extern char ide_fn[4][512]; + +typedef enum +{ + SCB_STATE_IDLE, + SCB_STATE_START, + SCB_STATE_WAIT, + SCB_STATE_COMPLETE +} sbc_state_t; + +typedef enum +{ + SCSI_STATE_IDLE, + SCSI_STATE_SELECT, + SCSI_STATE_SELECT_FAILED, + SCSI_STATE_SEND_COMMAND, + SCSI_STATE_NEXT_PHASE, + SCSI_STATE_END_PHASE, + SCSI_STATE_READ_DATA, + SCSI_STATE_WRITE_DATA, + SCSI_STATE_READ_STATUS, + SCSI_STATE_READ_MESSAGE +} scsi_state_t; + +typedef struct scb_t +{ + uint16_t command; + uint16_t enable; + uint32_t lba_addr; + uint32_t sys_buf_addr; + uint32_t sys_buf_byte_count; + uint32_t term_status_block_addr; + uint32_t scb_chain_addr; + uint16_t block_count; + uint16_t block_length; +} scb_t; + +typedef struct scsi_ibm_t +{ + rom_t bios_rom; + + uint8_t pos_regs[8]; + + uint8_t basic_ctrl; + uint32_t command; + + uint8_t attention; + uint8_t attention_pending; + int attention_waiting; + + uint8_t cir[4]; + uint8_t cir_pending[4]; + + uint8_t irq_status; + + uint32_t scb_addr; + + uint8_t status; + + int in_invalid; + int in_reset; + sbc_state_t scb_state; + + scb_t scb; + int scb_id; + + int cmd_status; + int cir_status; + + uint8_t pacing; + + uint8_t buf[0x600]; + + uint8_t int_buffer[512]; + + struct + { + int phys_id; + int lun_id; + } dev_id[16]; + + struct + { + int idx, len; + uint8_t data[256]; + + int data_idx, data_len; + uint32_t data_pointer; + + uint8_t last_status; + + int bytes_transferred; + + int target_id; + } cdb; + + int irq_requests[16]; + + int cmd_timer; + pc_timer_t callback_timer; + + scsi_state_t scsi_state; + scsi_bus_t bus; +} scsi_ibm_t; + +#define CTRL_RESET (1 << 7) +#define CTRL_DMA_ENA (1 << 1) +#define CTRL_IRQ_ENA (1 << 0) + +#define STATUS_CMD_FULL (1 << 3) +#define STATUS_CMD_EMPTY (1 << 2) +#define STATUS_IRQ (1 << 1) +#define STATUS_BUSY (1 << 0) + +#define ENABLE_PT (1 << 12) + +#define CMD_MASK 0xff3f +#define CMD_ASSIGN 0x040e +#define CMD_DEVICE_INQUIRY 0x1c0b +#define CMD_DMA_PACING_CONTROL 0x040d +#define CMD_FEATURE_CONTROL 0x040c +#define CMD_GET_POS_INFO 0x1c0a +#define CMD_INVALID_412 0x0412 +#define CMD_GET_COMPLETE_STATUS 0x1c07 +#define CMD_READ_DATA 0x1c01 +#define CMD_READ_DEVICE_CAPACITY 0x1c09 +#define CMD_REQUEST_SENSE 0x1c08 +#define CMD_RESET 0x0400 +#define CMD_SEND_OTHER_SCSI 0x241f +#define CMD_UNKNOWN_1c10 0x1c10 +#define CMD_UNKNOWN_1c11 0x1c11 +#define CMD_WRITE_DATA 0x1c02 +#define CMD_VERIFY 0x1c03 + +#define IRQ_TYPE_NONE 0x0 +#define IRQ_TYPE_SCB_COMPLETE 0x1 +#define IRQ_TYPE_SCB_COMPLETE_RETRY 0x5 +#define IRQ_TYPE_ADAPTER_HW_FAILURE 0x7 +#define IRQ_TYPE_IMM_CMD_COMPLETE 0xa +#define IRQ_TYPE_COMMAND_FAIL 0xc +#define IRQ_TYPE_COMMAND_ERROR 0xe +#define IRQ_TYPE_SW_SEQ_ERROR 0xf +#define IRQ_TYPE_RESET_COMPLETE 0x10 + +static void scsi_rethink_irqs(scsi_ibm_t *scsi) +{ + int irq_pending = 0; + int c; + +/* pclog("scsi_rethink_irqs: basic_ctrl=%02x status=%02x\n", scsi->basic_ctrl, scsi->irq_status); + pclog(" "); + for (c = 0; c < 16; c++) + pclog(" %02x", scsi->irq_requests[c]); + pclog("\n");*/ + + if (!scsi->irq_status) + { + for (c = 0; c < 16; c++) + { + if (scsi->irq_requests[c] != IRQ_TYPE_NONE) + { +// pclog(" Found IRQ on %i\n", c); + /*Found IRQ*/ + scsi->irq_status = c | (scsi->irq_requests[c] << 4); + scsi->status |= STATUS_IRQ; + irq_pending = 1; + break; + } + } + } + else + irq_pending = 1; + + if (scsi->basic_ctrl & CTRL_IRQ_ENA) + { + if (irq_pending) + { + picintlevel(1 << 14); +// pclog(" SCSI assert INT\n"); + } + else + { + /*No IRQs pending, clear IRQ state*/ + scsi->irq_status = 0; + scsi->status &= ~STATUS_IRQ; + picintc(1 << 14); +// pclog(" SCSI clear INT - no IRQ\n"); + } + } + else + { + picintc(1 << 14); +// pclog(" SCSI clear INT - IRQs disabled\n"); + } +} + +static inline void scsi_ibm_set_irq(scsi_ibm_t *scsi, int id, int type) +{ +// pclog("scsi_ibm_set_irq id=%i type=%x %02x\n", id, type, scsi->irq_status); + scsi->irq_requests[id] = type; + if (!scsi->irq_status) /*Don't change IRQ status if one is currently being processed*/ + scsi_rethink_irqs(scsi); +} +static inline void scsi_clear_irq(scsi_ibm_t *scsi, int id) +{ + scsi->irq_requests[id] = IRQ_TYPE_NONE; + scsi_rethink_irqs(scsi); +} + +static uint8_t scsi_read(uint16_t port, void *p) +{ + scsi_ibm_t *scsi = (scsi_ibm_t *)p; + uint8_t temp = 0xff; + + switch (port & 7) + { + case 0: case 1: case 2: case 3: /*Command Interface Register*/ + temp = scsi->cir_pending[port & 3]; + break; + + case 4: /*Attention Register*/ + temp = scsi->attention_pending; + break; + case 5: /*Basic Control Register*/ + temp = scsi->basic_ctrl; + break; + case 6: /*IRQ status*/ + temp = scsi->irq_status; + break; + case 7: /*Status*/ + temp = scsi->status; + if (scsi->cir_status == 0) + temp |= STATUS_CMD_EMPTY; + if (scsi->cir_status == 3) + temp |= STATUS_CMD_FULL; + break; + } + +// pclog("scsi_read: port=%04x val=%02x %04x(%05x):%04x %i %02x\n", port, temp,CS,cs,cpu_state.pc, 0/*scsi->callback*/, BH); + return temp; +} + +static void scsi_write(uint16_t port, uint8_t val, void *p) +{ + scsi_ibm_t *scsi = (scsi_ibm_t *)p; + +// pclog("scsi_write: port=%04x val=%02x %04x:%04x\n", port, val,CS,cpu_state.pc); + + switch (port & 7) + { + case 0: case 1: case 2: case 3: /*Command Interface Register*/ + scsi->cir_pending[port & 3] = val; + if (port & 2) + scsi->cir_status |= 2; + else + scsi->cir_status |= 1; + break; + + case 4: /*Attention Register*/ + scsi->attention_pending = val; + scsi->attention_waiting = 2; + scsi->status |= STATUS_BUSY; + break; + + case 5: /*Basic Control Register*/ + if ((scsi->basic_ctrl & CTRL_RESET) && !(val & CTRL_RESET)) + { +// pclog("SCSI reset\n"); + scsi->in_reset = 1; + scsi->cmd_timer = SCSI_TIME*2; + scsi->status |= STATUS_BUSY; + } + scsi->basic_ctrl = val; + scsi_rethink_irqs(scsi); + break; + +// default: +// fatal("scsi_write port=%04x val=%02x\n", port, val); + } +} + +static uint16_t scsi_readw(uint16_t port, void *p) +{ + scsi_ibm_t *scsi = (scsi_ibm_t *)p; + uint16_t temp = 0xffff; + + switch (port & 7) + { + case 0: /*Command Interface Register*/ + temp = scsi->cir_pending[0] | (scsi->cir_pending[1] << 8); + break; + case 2: /*Command Interface Register*/ + temp = scsi->cir_pending[2] | (scsi->cir_pending[3] << 8); + break; + +// default: +// fatal("scsi_readw port=%04x\n", port); + } + +// pclog("scsi_readw: port=%04x val=%04x\n", port, temp); + return temp; +} + +static void scsi_writew(uint16_t port, uint16_t val, void *p) +{ + scsi_ibm_t *scsi = (scsi_ibm_t *)p; + + switch (port & 7) + { + case 0: /*Command Interface Register*/ + scsi->cir_pending[0] = val & 0xff; + scsi->cir_pending[1] = val >> 8; + scsi->cir_status |= 1; + break; + case 2: /*Command Interface Register*/ + scsi->cir_pending[2] = val & 0xff; + scsi->cir_pending[3] = val >> 8; + scsi->cir_status |= 2; + break; + +// default: +// fatal("scsi_writew port=%04x val=%04x\n", port, val); + } + +// pclog("scsi_writew: port=%04x val=%04x\n", port, val); +} + +static void sg_fetch_next(scsi_ibm_t *scsi) +{ + if (scsi->scb.sys_buf_byte_count > 0) + { + scsi->cdb.data_idx = 0; + scsi->cdb.data_pointer = mem_readl_phys(scsi->scb.sys_buf_addr); + scsi->cdb.data_len = mem_readl_phys(scsi->scb.sys_buf_addr + 4); +// pclog("SG list: from %08x addr=%08x len=%08x\n", scsi->scb.sys_buf_addr, scsi->cdb.data_pointer, scsi->cdb.data_len); + scsi->scb.sys_buf_addr += 8; + scsi->scb.sys_buf_byte_count -= 8; + } +} + +static void process_ibm_cmd(void *p) +{ + scsi_ibm_t *scsi = (scsi_ibm_t *)p; + scb_t *scb = &scsi->scb; + int c; + int old_scb_state; + +// pclog("scsi_callback: in_reset=%i command=%x\n", scsi->in_reset, scsi->command); + if (scsi->in_reset) + { + scsi->status &= ~STATUS_BUSY; + + scsi->irq_status = 0; + for (c = 0; c < 16; c++) + scsi_clear_irq(scsi, c); + if (scsi->in_reset == 1) + { + scsi->basic_ctrl |= CTRL_IRQ_ENA; + scsi_ibm_set_irq(scsi, 0xf, IRQ_TYPE_RESET_COMPLETE); + } + else + scsi_ibm_set_irq(scsi, 0xf, IRQ_TYPE_RESET_COMPLETE); + + /*Reset device mappings*/ + for (c = 0; c < 7; c++) + { + scsi->dev_id[c].phys_id = c; + scsi->dev_id[c].lun_id = 0; + } + for (; c < 15; c++) + scsi->dev_id[c].phys_id = -1; + + scsi->in_reset = 0; +// pclog("scsi reset complete\n"); + return; + } + if (scsi->in_invalid) + { + scsi_ibm_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_COMMAND_ERROR); + scsi->in_invalid = 0; + return; + } + do + { + old_scb_state = scsi->scb_state; + +// pclog("scb_state=%i\n", scsi->scb_state); + switch (scsi->scb_state) + { + case SCB_STATE_IDLE: + break; + + case SCB_STATE_START: + if (scsi->dev_id[scsi->scb_id].phys_id == -1) + { + scsi_ibm_set_irq(scsi, scsi->scb_id, IRQ_TYPE_COMMAND_FAIL); + scsi->scb_state = SCB_STATE_IDLE; + mem_writew_phys(scb->term_status_block_addr + 0x7*2, (0xe << 8) | 0); + mem_writew_phys(scb->term_status_block_addr + 0x8*2, (0xa << 8) | 0); + break; + } + + scb->command = mem_readw_phys(scsi->scb_addr); + scb->enable = mem_readw_phys(scsi->scb_addr + 0x02); + scb->lba_addr = mem_readl_phys(scsi->scb_addr + 0x04); + scb->sys_buf_addr = mem_readl_phys(scsi->scb_addr + 0x08); + scb->sys_buf_byte_count = mem_readl_phys(scsi->scb_addr + 0x0c); + scb->term_status_block_addr = mem_readl_phys(scsi->scb_addr + 0x10); + scb->scb_chain_addr = mem_readl_phys(scsi->scb_addr + 0x14); + scb->block_count = mem_readw_phys(scsi->scb_addr + 0x18); + scb->block_length = mem_readw_phys(scsi->scb_addr + 0x1a); + +/* pclog("SCB : \n" + " Command = %04x\n" + " Enable = %04x\n" + " LBA addr = %08x\n" + " System buffer addr = %08x\n" + " System buffer byte count = %08x\n" + " Terminate status block addr = %08x\n" + " SCB chain address = %08x\n" + " Block count = %04x\n" + " Block length = %04x\n", + scb->command, scb->enable, scb->lba_addr, + scb->sys_buf_addr, scb->sys_buf_byte_count, + scb->term_status_block_addr, scb->scb_chain_addr, + scb->block_count, scb->block_length);*/ + + if (scb->enable & ENABLE_PT) + sg_fetch_next(scsi); + else + { + scsi->cdb.data_idx = 0; + scsi->cdb.data_pointer = scb->sys_buf_addr; + scsi->cdb.data_len = scb->sys_buf_byte_count; + } + + switch (scb->command & CMD_MASK) + { + case CMD_GET_COMPLETE_STATUS: + mem_writew_phys(scb->sys_buf_addr + 0x0*2, 0x201); /*SCB status*/ + mem_writew_phys(scb->sys_buf_addr + 0x1*2, 0); /*Retry counts*/ + mem_writel_phys(scb->sys_buf_addr + 0x2*2, 0); /*Residual byte count*/ + mem_writel_phys(scb->sys_buf_addr + 0x4*2, 0); /*Scatter/gather list element address*/ + mem_writew_phys(scb->sys_buf_addr + 0x6*2, 0xc); /*Device dependent status length*/ + mem_writew_phys(scb->sys_buf_addr + 0x7*2, scsi->cmd_status << 8); + mem_writew_phys(scb->sys_buf_addr + 0x8*2, 0); /*Error*/ + mem_writew_phys(scb->sys_buf_addr + 0x9*2, 0); /*Reserved*/ + mem_writew_phys(scb->sys_buf_addr + 0xa*2, 0); /*Cache information status*/ + mem_writel_phys(scb->sys_buf_addr + 0xb*2, scsi->scb_addr); + scsi->scb_state = SCB_STATE_COMPLETE; + break; + + case CMD_UNKNOWN_1c10: + for (c = 0; c < 0x600 && c < scb->sys_buf_byte_count; c++) + scsi->buf[c] = mem_readb_phys(scb->sys_buf_addr + c); + scsi->scb_state = SCB_STATE_COMPLETE; + break; + case CMD_UNKNOWN_1c11: + for (c = 0; c < 0x600 && c < scb->sys_buf_byte_count; c++) + mem_writeb_phys(scb->sys_buf_addr + c, scsi->buf[c]); + scsi->scb_state = SCB_STATE_COMPLETE; + break; + + case CMD_GET_POS_INFO: + mem_writew_phys(scb->sys_buf_addr + 0x0*2, 0x8eff); + mem_writew_phys(scb->sys_buf_addr + 0x1*2, scsi->pos_regs[3] | (scsi->pos_regs[2] << 8)); + mem_writew_phys(scb->sys_buf_addr + 0x2*2, 0x0e | (scsi->pos_regs[4] << 8)); + mem_writew_phys(scb->sys_buf_addr + 0x3*2, 1 << 12); + mem_writew_phys(scb->sys_buf_addr + 0x4*2, (7 << 8) | 8); + mem_writew_phys(scb->sys_buf_addr + 0x5*2, (16 << 8) | scsi->pacing); + mem_writew_phys(scb->sys_buf_addr + 0x6*2, (30 << 8) | 1); + mem_writew_phys(scb->sys_buf_addr + 0x7*2, 0); + mem_writew_phys(scb->sys_buf_addr + 0x8*2, 0); + scsi->scb_state = SCB_STATE_COMPLETE; + break; + + case CMD_DEVICE_INQUIRY: + scsi->cdb.data[0] = SCSI_INQUIRY; + scsi->cdb.data[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/ + scsi->cdb.data[2] = 0; /*Page code*/ + scsi->cdb.data[3] = 0; + scsi->cdb.data[4] = scb->sys_buf_byte_count; /*Allocation length*/ + scsi->cdb.data[5] = 0; /*Control*/ + scsi->cdb.idx = 0; + scsi->cdb.len = 6; + scsi->cdb.target_id = scsi->dev_id[scsi->scb_id].phys_id; + scsi->cdb.data_pointer = scb->sys_buf_addr; + scsi->cdb.data_idx = 0; + scsi->cdb.data_len = scb->sys_buf_byte_count; + scsi->scsi_state = SCSI_STATE_SELECT; + scsi->scb_state = SCB_STATE_WAIT; + return; + + case CMD_SEND_OTHER_SCSI: +// pclog("SEND_OTHER_SCSI:"); + for (c = 0; c < 12; c++) + { + scsi->cdb.data[c] = mem_readb_phys(scsi->scb_addr + 0x18 + c); +// pclog(" %02x", scsi->cdb.data[c]); + } +// pclog("\n"); + scsi->cdb.data[1] = (scsi->cdb.data[1] & 0x1f) | (scsi->dev_id[scsi->scb_id].lun_id << 5); /*Patch correct LUN into command*/ + scsi->cdb.idx = 0; + scsi->cdb.len = (scb->lba_addr & 0xff) ? (scb->lba_addr & 0xff) : 6; +// pclog("SEND_OTHER_SCSI: %04x\n", scb->lba_addr & 0xffff); + scsi->cdb.target_id = scsi->dev_id[scsi->scb_id].phys_id; + scsi->scsi_state = SCSI_STATE_SELECT; + scsi->scb_state = SCB_STATE_WAIT; + return; + + case CMD_READ_DEVICE_CAPACITY: + scsi->cdb.data[0] = SCSI_READ_CAPACITY_10; + scsi->cdb.data[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/ + scsi->cdb.data[2] = 0; /*LBA*/ + scsi->cdb.data[3] = 0; + scsi->cdb.data[4] = 0; + scsi->cdb.data[5] = 0; + scsi->cdb.data[6] = 0; /*Reserved*/ + scsi->cdb.data[7] = 0; + scsi->cdb.data[8] = 0; + scsi->cdb.data[9] = 0; /*Control*/ + scsi->cdb.idx = 0; + scsi->cdb.len = 10; + scsi->cdb.target_id = scsi->dev_id[scsi->scb_id].phys_id; + scsi->scsi_state = SCSI_STATE_SELECT; + scsi->scb_state = SCB_STATE_WAIT; + return; + + case CMD_READ_DATA: + scsi->cdb.data[0] = SCSI_READ_10; + scsi->cdb.data[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/ + scsi->cdb.data[2] = (scb->lba_addr >> 24) & 0xff; /*LBA*/ + scsi->cdb.data[3] = (scb->lba_addr >> 16) & 0xff; + scsi->cdb.data[4] = (scb->lba_addr >> 8) & 0xff; + scsi->cdb.data[5] = scb->lba_addr & 0xff; + scsi->cdb.data[6] = 0; /*Reserved*/ + scsi->cdb.data[7] = (scb->block_count >> 8) & 0xff; + scsi->cdb.data[8] = scb->block_count & 0xff; + scsi->cdb.data[9] = 0; /*Control*/ + scsi->cdb.idx = 0; + scsi->cdb.len = 10; + scsi->cdb.target_id = scsi->dev_id[scsi->scb_id].phys_id; + scsi->scsi_state = SCSI_STATE_SELECT; + scsi->scb_state = SCB_STATE_WAIT; + return; + + case CMD_REQUEST_SENSE: + scsi->cdb.data[0] = SCSI_REQUEST_SENSE; + scsi->cdb.data[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/ + scsi->cdb.data[2] = 0; + scsi->cdb.data[3] = 0; + scsi->cdb.data[4] = scb->block_count & 0xff; /*Allocation length*/ + scsi->cdb.data[5] = 0; + scsi->cdb.idx = 0; + scsi->cdb.len = 6; + scsi->cdb.target_id = scsi->dev_id[scsi->scb_id].phys_id; + scsi->scsi_state = SCSI_STATE_SELECT; + scsi->scb_state = SCB_STATE_WAIT; + return; + + case CMD_WRITE_DATA: + scsi->cdb.data[0] = SCSI_WRITE_10; + scsi->cdb.data[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/ + scsi->cdb.data[2] = (scb->lba_addr >> 24) & 0xff; /*LBA*/ + scsi->cdb.data[3] = (scb->lba_addr >> 16) & 0xff; + scsi->cdb.data[4] = (scb->lba_addr >> 8) & 0xff; + scsi->cdb.data[5] = scb->lba_addr & 0xff; + scsi->cdb.data[6] = 0; /*Reserved*/ + scsi->cdb.data[7] = (scb->block_count >> 8) & 0xff; + scsi->cdb.data[8] = scb->block_count & 0xff; + scsi->cdb.data[9] = 0; /*Control*/ + scsi->cdb.idx = 0; + scsi->cdb.len = 10; + scsi->cdb.target_id = scsi->dev_id[scsi->scb_id].phys_id; + scsi->scsi_state = SCSI_STATE_SELECT; + scsi->scb_state = SCB_STATE_WAIT; + return; + + case CMD_VERIFY: + scsi->cdb.data[0] = SCSI_VERIFY_10; + scsi->cdb.data[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/ + scsi->cdb.data[2] = (scb->lba_addr >> 24) & 0xff; /*LBA*/ + scsi->cdb.data[3] = (scb->lba_addr >> 16) & 0xff; + scsi->cdb.data[4] = (scb->lba_addr >> 8) & 0xff; + scsi->cdb.data[5] = scb->lba_addr & 0xff; + scsi->cdb.data[6] = 0; /*Reserved*/ + scsi->cdb.data[7] = (scb->block_count >> 8) & 0xff; + scsi->cdb.data[8] = scb->block_count & 0xff; + scsi->cdb.data[9] = 0; /*Control*/ + scsi->cdb.idx = 0; + scsi->cdb.len = 10; + scsi->cdb.target_id = scsi->dev_id[scsi->scb_id].phys_id; + scsi->cdb.data_len = 0; + scsi->scsi_state = SCSI_STATE_SELECT; + scsi->scb_state = SCB_STATE_WAIT; + return; + +#ifndef RELEASE_BUILD + default: + fatal("SCB command %04x\n", scb->command); +#endif + } + break; + + case SCB_STATE_WAIT: + if (scsi->scsi_state == SCSI_STATE_IDLE) + { + if (scsi->cdb.last_status == STATUS_GOOD) + scsi->scb_state = SCB_STATE_COMPLETE; + else if (scsi->cdb.last_status == STATUS_CHECK_CONDITION) + { + scsi_ibm_set_irq(scsi, scsi->scb_id, IRQ_TYPE_COMMAND_FAIL); + scsi->scb_state = SCB_STATE_IDLE; + mem_writew_phys(scb->term_status_block_addr + 0x7*2, (0xc << 8) | 2); + mem_writew_phys(scb->term_status_block_addr + 0x8*2, 0x20); + mem_writew_phys(scb->term_status_block_addr + 0xb*2, scsi->scb_addr & 0xffff); + mem_writew_phys(scb->term_status_block_addr + 0xc*2, scsi->scb_addr >> 16); + } +#ifndef RELEASE_BUILD + else + fatal("SCB unknown result %02x\n", scsi->cdb.last_status); +#endif + } + else if (scsi->scsi_state == SCSI_STATE_SELECT_FAILED) + { +// pclog("SCB_STATE_WAIT SELECT_FAILED\n"); + scsi_ibm_set_irq(scsi, scsi->scb_id, IRQ_TYPE_COMMAND_FAIL); + scsi->scb_state = SCB_STATE_IDLE; + mem_writew_phys(scb->term_status_block_addr + 0x7*2, (0xc << 8) | 2); + mem_writew_phys(scb->term_status_block_addr + 0x8*2, 0x10); + } +#ifndef RELEASE_BUILD + else + fatal("SCB_STATE_WAIT other %i\n", scsi->scsi_state); +#endif + break; + + case SCB_STATE_COMPLETE: + if (scb->enable & 1) + { + scsi->scb_state = SCB_STATE_START; + scsi->scb_addr = scb->scb_chain_addr; +// pclog("Next SCB - %08x\n", scsi->scb_addr); + } + else + { + scsi_ibm_set_irq(scsi, scsi->scb_id, IRQ_TYPE_SCB_COMPLETE); + scsi->scb_state = SCB_STATE_IDLE; + } + break; + } + } while (scsi->scb_state != old_scb_state); +} + +static void process_immediate_command(scsi_ibm_t *scsi) +{ + switch (scsi->command & CMD_MASK) + { + case CMD_ASSIGN: +// pclog("Assign: adapter dev=%x scsi ID=%i LUN=%i\n", (scsi->command >> 16) & 0xf, (scsi->command >> 20) & 7, (scsi->command >> 24) & 7); + if (((scsi->command >> 16) & 0xf) == 0xf && ((scsi->command >> 20) & 7) == 7) /*Device 15 always adapter*/ + scsi_ibm_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE); + else if (((scsi->command >> 16) & 0xf) == 0xf) /*Can not re-assign device 15 (always adapter)*/ + scsi_ibm_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_COMMAND_FAIL); + else if (scsi->command & (1 << 23)) + { + scsi->dev_id[(scsi->command >> 16) & 0xf].phys_id = -1; + scsi_ibm_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE); + } + else if (((scsi->command >> 20) & 7) == 7) /*Can not assign adapter*/ + scsi_ibm_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_COMMAND_FAIL); + else + { + scsi->dev_id[(scsi->command >> 16) & 0xf].phys_id = (scsi->command >> 20) & 7; + scsi->dev_id[(scsi->command >> 16) & 0xf].lun_id = (scsi->command >> 24) & 7; + scsi_ibm_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE); + } + break; + + case CMD_DMA_PACING_CONTROL: + scsi->pacing = scsi->cir[2]; +// pclog("Pacing control: %i\n", scsi->pacing); + scsi_ibm_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE); + break; + + case CMD_FEATURE_CONTROL: +// pclog("Feature control: timeout=%is d-rate=%i\n", (scsi->command >> 16) & 0x1fff, scsi->command >> 29); + scsi_ibm_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE); + break; + + case CMD_INVALID_412: + scsi_ibm_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE); + break; + + case CMD_RESET: + if ((scsi->attention & 0xf) == 0xf) /*Adapter reset*/ + { + scsi_bus_reset(&scsi->bus); + scsi->scb_state = SCB_STATE_IDLE; + } + else /*Device reset*/ + { + } + scsi_ibm_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE); + break; + + default: + fatal("scsi_callback: Bad command %02x\n", scsi->command); + } +} + +static int wait_for_bus(scsi_bus_t *bus, int state, int req_needed) +{ + int c; + + for (c = 0; c < 20; c++) + { + int bus_state = scsi_bus_read(bus); + + if ((bus_state & (BUS_IO | BUS_CD | BUS_MSG)) == state && (bus_state & BUS_BSY)) + { + if (!req_needed || (bus_state & BUS_REQ)) + return 1; + } + } + + return 0; +} + +static void process_scsi(scsi_ibm_t *scsi) +{ + int c; + int bytes_transferred = 0; + + switch (scsi->scsi_state) + { + case SCSI_STATE_IDLE: + break; + + case SCSI_STATE_SELECT: + scsi->cdb.last_status = 0; +// pclog("Select target ID %i\n", scsi->cdb.target_id); + scsi_bus_update(&scsi->bus, BUS_SEL | BUS_SETDATA(1 << scsi->cdb.target_id)); + if (!(scsi_bus_read(&scsi->bus) & BUS_BSY) || scsi->cdb.target_id > 6) + { +// pclog("STATE_SCSI_SELECT failed to select target %i\n", scsi->cdb.target_id); + scsi->scsi_state = SCSI_STATE_SELECT_FAILED; + if (!scsi->cmd_timer) + scsi->cmd_timer = 1; + break; + } + + scsi_bus_update(&scsi->bus, 0); + if (!(scsi_bus_read(&scsi->bus) & BUS_BSY)) + { +// pclog("STATE_SCSI_SELECT failed to select target %i 2\n", scsi->cdb.target_id); + scsi->scsi_state = SCSI_STATE_SELECT_FAILED; + if (!scsi->cmd_timer) + scsi->cmd_timer = 1; + break; + } + + /*Device should now be selected*/ + if (!wait_for_bus(&scsi->bus, BUS_CD, 1)) + { +// pclog("Device failed to request command\n"); + scsi->scsi_state = SCSI_STATE_SELECT_FAILED; + if (!scsi->cmd_timer) + scsi->cmd_timer = 1; + break; + } + + scsi->scsi_state = SCSI_STATE_SEND_COMMAND; + break; + + case SCSI_STATE_SELECT_FAILED: + break; + + case SCSI_STATE_SEND_COMMAND: + while (scsi->cdb.idx < scsi->cdb.len && bytes_transferred < MAX_BYTES_TRANSFERRED_PER_POLL) + { + int bus_out; + + for (c = 0; c < 20; c++) + { + int bus_state = scsi_bus_read(&scsi->bus); + +#ifndef RELEASE_BUILD + if (!(bus_state & BUS_BSY)) + fatal("SEND_COMMAND - dropped BSY\n"); + if ((bus_state & (BUS_IO | BUS_CD | BUS_MSG)) != BUS_CD) + fatal("SEND_COMMAND - bus phase incorrect\n"); +#endif + if (bus_state & BUS_REQ) + break; + } + if (c == 20) + { +// pclog("SEND_COMMAND timed out\n"); + break; + } + + bus_out = BUS_SETDATA(scsi->cdb.data[scsi->cdb.idx]); +// pclog(" Command send %02x %i\n", scsi->cdb.data[scsi->cdb.idx], scsi->cdb.len); + scsi->cdb.idx++; + bytes_transferred++; + + scsi_bus_update(&scsi->bus, bus_out | BUS_ACK); + scsi_bus_update(&scsi->bus, bus_out & ~BUS_ACK); + } + if (scsi->cdb.idx == scsi->cdb.len) + scsi->scsi_state = SCSI_STATE_NEXT_PHASE; + break; + + case SCSI_STATE_NEXT_PHASE: + /*Wait for SCSI command to move to next phase*/ + for (c = 0; c < 20; c++) + { + int bus_state = scsi_bus_read(&scsi->bus); +#ifndef RELEASE_BUILD + if (!(bus_state & BUS_BSY)) + fatal("NEXT_PHASE - dropped BSY waiting\n"); +#endif + if (bus_state & BUS_REQ) + { + int bus_out; +// pclog("SCSI next phase - %x\n", bus_state); + switch (bus_state & (BUS_IO | BUS_CD | BUS_MSG)) + { + case 0: +// pclog("Move to write data\n"); + scsi->scsi_state = SCSI_STATE_WRITE_DATA; + break; + + case BUS_IO: +// pclog("Move to read data\n"); + scsi->scsi_state = SCSI_STATE_READ_DATA; + break; + + case (BUS_CD | BUS_IO): +// pclog("Move to read status\n"); + scsi->scsi_state = SCSI_STATE_READ_STATUS; + break; + + case (BUS_CD | BUS_IO | BUS_MSG): +// pclog("Move to read message\n"); + scsi->scsi_state = SCSI_STATE_READ_MESSAGE; + break; + + case BUS_CD: + bus_out = BUS_SETDATA(0); + + scsi_bus_update(&scsi->bus, bus_out | BUS_ACK); + scsi_bus_update(&scsi->bus, bus_out & ~BUS_ACK); + break; + +#ifndef RELEASE_BUILD + default: + fatal(" Bad new phase %x\n", bus_state); +#endif + } + break; + } + } + break; + + case SCSI_STATE_END_PHASE: + /*Wait for SCSI command to move to next phase*/ + for (c = 0; c < 20; c++) + { + int bus_state = scsi_bus_read(&scsi->bus); + + if (!(bus_state & BUS_BSY)) + { +// pclog("END_PHASE - dropped BSY waiting\n"); +// if (scsi->ccb.req_sense_len == CCB_SENSE_NONE) +// fatal("No sense data\n"); + scsi->scsi_state = SCSI_STATE_IDLE; +// pclog("End of command\n"); + if (!scsi->cmd_timer) + scsi->cmd_timer = 1; + break; + } +#ifndef RELEASE_BUILD + if (bus_state & BUS_REQ) + fatal("END_PHASE - unexpected REQ\n"); +#endif + } + break; + + case SCSI_STATE_READ_DATA: + while (scsi->scsi_state == SCSI_STATE_READ_DATA && bytes_transferred < MAX_BYTES_TRANSFERRED_PER_POLL) + { + int d; + + for (d = 0; d < 20; d++) + { + int bus_state = scsi_bus_read(&scsi->bus); +#ifndef RELEASE_BUILD + if (!(bus_state & BUS_BSY)) + fatal("READ_DATA - dropped BSY waiting\n"); +#endif + if ((bus_state & (BUS_IO | BUS_CD | BUS_MSG)) != BUS_IO) + { +// pclog("READ_DATA - changed phase\n"); + scsi->scsi_state = SCSI_STATE_NEXT_PHASE; + break; + } + + if (bus_state & BUS_REQ) + { + uint8_t data = BUS_GETDATA(bus_state); + int bus_out = 0; + + if (scsi->cdb.data_idx < scsi->cdb.data_len) + { + if (scsi->cdb.data_pointer == -1) + scsi->int_buffer[scsi->cdb.data_idx] = data; + else + mem_writeb_phys(scsi->cdb.data_pointer + scsi->cdb.data_idx, data); + scsi->cdb.data_idx++; + + if ((scsi->scb.enable & ENABLE_PT) && scsi->cdb.data_idx == scsi->cdb.data_len) + sg_fetch_next(scsi); + } + scsi->cdb.bytes_transferred++; + +// pclog("Read data %02x %i %06x\n", data, scsi->cdb.data_idx, scsi->cdb.data_pointer + scsi->cdb.data_idx); + scsi_bus_update(&scsi->bus, bus_out | BUS_ACK); + scsi_bus_update(&scsi->bus, bus_out & ~BUS_ACK); + break; + } + } + + bytes_transferred++; + } + break; + + case SCSI_STATE_WRITE_DATA: + while (scsi->scsi_state == SCSI_STATE_WRITE_DATA && bytes_transferred < MAX_BYTES_TRANSFERRED_PER_POLL) + { + int d; + + for (d = 0; d < 20; d++) + { + int bus_state = scsi_bus_read(&scsi->bus); +#ifndef RELEASE_BUILD + if (!(bus_state & BUS_BSY)) + fatal("WRITE_DATA - dropped BSY waiting\n"); +#endif + if ((bus_state & (BUS_IO | BUS_CD | BUS_MSG)) != 0) + { +// pclog("WRITE_DATA - changed phase\n"); + scsi->scsi_state = SCSI_STATE_NEXT_PHASE; + break; + } + + if (bus_state & BUS_REQ) + { + uint8_t data;// = BUS_GETDATA(bus_state); + int bus_out; + + if (scsi->cdb.data_idx < scsi->cdb.data_len) + { + if (scsi->cdb.data_pointer == -1) + data = scsi->int_buffer[scsi->cdb.data_idx]; + else + data = mem_readb_phys(scsi->cdb.data_pointer + scsi->cdb.data_idx); + + scsi->cdb.data_idx++; + if ((scsi->scb.enable & ENABLE_PT) && scsi->cdb.data_idx == scsi->cdb.data_len) + sg_fetch_next(scsi); + } + else + data = 0xff; + scsi->cdb.bytes_transferred++; + +// pclog("Write data %02x %i\n", data, scsi->cdb.data_idx); + bus_out = BUS_SETDATA(data); + scsi_bus_update(&scsi->bus, bus_out | BUS_ACK); + scsi_bus_update(&scsi->bus, bus_out & ~BUS_ACK); + break; + } + } + + bytes_transferred++; + } +// if (scsi->cdb.data_idx == scsi->cdb.data_len) +// scsi->scsi_state = SCSI_STATE_NEXT_PHASE; + break; + + case SCSI_STATE_READ_STATUS: + for (c = 0; c < 20; c++) + { + int bus_state = scsi_bus_read(&scsi->bus); +#ifndef RELEASE_BUILD + if (!(bus_state & BUS_BSY)) + fatal("READ_STATUS - dropped BSY waiting\n"); +#endif + if ((bus_state & (BUS_IO | BUS_CD | BUS_MSG)) != (BUS_CD | BUS_IO)) + { +// pclog("READ_STATUS - changed phase\n"); + scsi->scsi_state = SCSI_STATE_NEXT_PHASE; + break; + } + + if (bus_state & BUS_REQ) + { + uint8_t status = BUS_GETDATA(bus_state); + int bus_out = 0; + +// pclog("Read status %02x\n", status); + scsi->cdb.last_status = status; + + scsi_bus_update(&scsi->bus, bus_out | BUS_ACK); + scsi_bus_update(&scsi->bus, bus_out & ~BUS_ACK); + + scsi->scsi_state = SCSI_STATE_NEXT_PHASE; + break; + } + } + break; + + case SCSI_STATE_READ_MESSAGE: + for (c = 0; c < 20; c++) + { + int bus_state = scsi_bus_read(&scsi->bus); +#ifndef RELEASE_BUILD + if (!(bus_state & BUS_BSY)) + fatal("READ_MESSAGE - dropped BSY waiting\n"); +#endif + if ((bus_state & (BUS_IO | BUS_CD | BUS_MSG)) != (BUS_CD | BUS_IO | BUS_MSG)) + { +// pclog("READ_MESSAGE - changed phase\n"); + scsi->scsi_state = SCSI_STATE_NEXT_PHASE; + break; + } + + if (bus_state & BUS_REQ) + { + uint8_t msg = BUS_GETDATA(bus_state); + int bus_out = 0; + +// pclog("Read message %02x\n", msg); + scsi_bus_update(&scsi->bus, bus_out | BUS_ACK); + scsi_bus_update(&scsi->bus, bus_out & ~BUS_ACK); + + switch (msg) + { + case MSG_COMMAND_COMPLETE: + scsi->scsi_state = SCSI_STATE_END_PHASE; + break; +#ifndef RELEASE_BUILD + default: + fatal("READ_MESSAGE - unknown message %02x\n", msg); +#endif + } + break; + } + } + break; + +#ifndef RELEASE_BUILD + default: + fatal("Unknown SCSI_state %d\n", scsi->scsi_state); +#endif + } +} + +static void scsi_callback(void *p) +{ + scsi_ibm_t *scsi = (scsi_ibm_t *)p; + + timer_advance_u64(&scsi->callback_timer, POLL_TIME); + +// pclog("poll %i\n", scsi->cmd_state); + if (scsi->cmd_timer) + { + scsi->cmd_timer--; + if (!scsi->cmd_timer) + process_ibm_cmd(scsi); + } + if (scsi->attention_waiting && + (scsi->scb_state == SCB_STATE_IDLE || (scsi->attention_pending & 0xf0) == 0xe0)) + { + scsi->attention_waiting--; + if (!scsi->attention_waiting) + { +// pclog("Process attention request\n"); + scsi->attention = scsi->attention_pending; + scsi->status &= ~STATUS_BUSY; + scsi->cir[0] = scsi->cir_pending[0]; + scsi->cir[1] = scsi->cir_pending[1]; + scsi->cir[2] = scsi->cir_pending[2]; + scsi->cir[3] = scsi->cir_pending[3]; + scsi->cir_status = 0; + + switch (scsi->attention >> 4) + { + case 0x1: /*Immediate command*/ + scsi->cmd_status = 0xa; + scsi->command = scsi->cir[0] | (scsi->cir[1] << 8) | (scsi->cir[2] << 16) | (scsi->cir[3] << 24); + switch (scsi->command & CMD_MASK) + { + case CMD_ASSIGN: + case CMD_DMA_PACING_CONTROL: + case CMD_FEATURE_CONTROL: + case CMD_INVALID_412: + case CMD_RESET: + process_immediate_command(scsi); + break; + +#ifndef RELEASE_BUILD + default: + fatal("Unknown immediate command %08x\n", scsi->command); +#endif + } + break; + + case 0x3: case 0x4: case 0xf: /*Start SCB*/ + scsi->cmd_status = 0x1; + scsi->scb_addr = scsi->cir[0] | (scsi->cir[1] << 8) | (scsi->cir[2] << 16) | (scsi->cir[3] << 24); + scsi->scb_state = SCB_STATE_START; + scsi->scb_id = scsi->attention & 0xf; + scsi->cmd_timer = SCSI_TIME*2; +// pclog("Start SCB at %08x\n", scsi->scb_addr); + break; + + case 5: /*Invalid*/ + case 0xa: /*Invalid*/ + scsi->in_invalid = 1; + scsi->cmd_timer = SCSI_TIME*2; + break; + + case 0xe: /*EOI*/ + scsi->irq_status = 0; + scsi_clear_irq(scsi, scsi->attention & 0xf); + break; +#ifndef RELEASE_BUILD + default: + fatal("Attention Register 5 %02x\n", scsi->attention); +#endif + } + } + } + process_scsi(scsi); +} + +static uint8_t scsi_ibm_mca_read(int port, void *p) +{ + scsi_ibm_t *scsi = (scsi_ibm_t *)p; + +// pclog("scsi_mca_read: port=%04x %02x %04x:%04x\n", port, scsi->pos_regs[port & 7], CS,cpu_state.pc); + + return scsi->pos_regs[port & 7]; +} + +static void scsi_ibm_mca_write(int port, uint8_t val, void *p) +{ + scsi_ibm_t *scsi = (scsi_ibm_t *)p; + + if (port < 0x102) + return; + +// pclog("scsi_mca_write: port=%04x val=%02x %04x:%04x\n", port, val, CS, cpu_state.pc); + + io_removehandler((((scsi->pos_regs[2] >> 1) & 7) * 8) + 0x3540, 0x0008, scsi_read, scsi_readw, NULL, scsi_write, scsi_writew, NULL, scsi); + mem_mapping_disable(&scsi->bios_rom.mapping); + + scsi->pos_regs[port & 7] = val; + + if (scsi->pos_regs[2] & 1) + { +// pclog(" scsi io = %04x\n", (((scsi->pos_regs[2] >> 1) & 7) * 8) + 0x3540); + io_sethandler((((scsi->pos_regs[2] >> 1) & 7) * 8) + 0x3540, 0x0008, scsi_read, scsi_readw, NULL, scsi_write, scsi_writew, NULL, scsi); + if ((scsi->pos_regs[2] & 0xf0) != 0xf0) + { + mem_mapping_enable(&scsi->bios_rom.mapping); + mem_mapping_set_addr(&scsi->bios_rom.mapping, ((scsi->pos_regs[2] >> 4) * 0x2000) + 0xc0000, 0x8000); +// pclog("SCSI BIOS ROM at %05x %02x\n", ((scsi->pos_regs[2] >> 4) * 0x2000) + 0xc0000, scsi->pos_regs[2]); + } + } +} + +static void scsi_ibm_reset(void *p) +{ + scsi_ibm_t *scsi = (scsi_ibm_t *)p; + +// pclog("scsi_ibm_reset\n"); + scsi->in_reset = 2; + scsi->cmd_timer = SCSI_TIME * 50; + scsi->status = STATUS_BUSY; + scsi->scsi_state = SCSI_STATE_IDLE; + scsi->scb_state = SCB_STATE_IDLE; + scsi->in_invalid = 0; + scsi->attention_waiting = 0; + scsi->basic_ctrl = 0; + scsi_bus_reset(&scsi->bus); +} + +static void *scsi_ibm_init() +{ + int c; + scsi_ibm_t *scsi = malloc(sizeof(scsi_ibm_t)); + memset(scsi, 0, sizeof(scsi_ibm_t)); + + rom_init_interleaved(&scsi->bios_rom, "92F2244.U68", "92F2245.U69", 0xc8000, 0x8000, 0x7fff, 0x4000, MEM_MAPPING_EXTERNAL); + mem_mapping_disable(&scsi->bios_rom.mapping); + + timer_add(&scsi->callback_timer, scsi_callback, scsi, 1); + + mca_add(scsi_ibm_mca_read, scsi_ibm_mca_write, scsi_ibm_reset, scsi); + + scsi->pos_regs[0] = 0xff; + scsi->pos_regs[1] = 0x8e; + + scsi->in_reset = 2; + scsi->cmd_timer = SCSI_TIME * 50; + scsi->status = STATUS_BUSY; + + scsi_bus_init(&scsi->bus); + +/* for (c = 0; c < 7; c++) + { + scsi->dev_id[c].phys_id = c; + scsi->dev_id[c].lun_id = 0; + }*/ + for (c = 0; c < 15; c++) + scsi->dev_id[c].phys_id = -1; + scsi->dev_id[15].phys_id = 7; + + return scsi; +} + +static void scsi_ibm_close(void *p) +{ + scsi_ibm_t *scsi = (scsi_ibm_t *)p; + + free(scsi); +} + +static int scsi_ibm_available() +{ + return rom_present("92F2244.U68") && rom_present("92F2245.U69"); +} + +device_t scsi_ibm_device = +{ + "IBM SCSI Adapter with Cache (MCA)", + DEVICE_MCA, + scsi_ibm_init, + scsi_ibm_close, + scsi_ibm_available, + NULL, + NULL, + NULL, + NULL +}; diff --git a/src/scsi_ibm.h b/src/scsi_ibm.h new file mode 100644 index 0000000..b528486 --- /dev/null +++ b/src/scsi_ibm.h @@ -0,0 +1 @@ +extern device_t scsi_ibm_device;