diff --git a/src/ide_atapi.c b/src/ide_atapi.c index 78a57bd..1760a43 100644 --- a/src/ide_atapi.c +++ b/src/ide_atapi.c @@ -438,7 +438,7 @@ void atapi_process_packet(atapi_device_t *atapi_dev) else { atapi_dev->state = ATAPI_STATE_NEXT_PHASE; - idecallback[atapi_dev->board] = 6*IDE_TIME; + idecallback[atapi_dev->board] = 1*IDE_TIME; } } else @@ -490,7 +490,7 @@ void atapi_process_packet(atapi_device_t *atapi_dev) } atapi_dev->state = ATAPI_STATE_NEXT_PHASE; - idecallback[atapi_dev->board] = 6 * IDE_TIME; + idecallback[atapi_dev->board] = 1 * IDE_TIME; } break; diff --git a/src/pc.c b/src/pc.c index 71fd9f4..67b99a0 100644 --- a/src/pc.c +++ b/src/pc.c @@ -35,6 +35,7 @@ #include "plat-keyboard.h" #include "plat-midi.h" #include "plat-mouse.h" +#include "scsi_cd.h" #include "scsi_zip.h" #include "serial.h" #include "sound.h" @@ -781,6 +782,8 @@ void loadconfig(char *fn) fdd_set_type(1, config_get_int(CFG_MACHINE, NULL, "drive_b_type", 7)); bpb_disable = config_get_int(CFG_MACHINE, NULL, "bpb_disable", 0); + cd_speed = config_get_int(CFG_MACHINE, NULL, "cd_speed", 24); + joystick_type = config_get_int(CFG_MACHINE, NULL, "joystick_type", 0); mouse_type = config_get_int(CFG_MACHINE, NULL, "mouse_type", 0); @@ -926,6 +929,8 @@ void saveconfig(char *fn) config_set_int(CFG_MACHINE, NULL, "drive_b_type", fdd_get_type(1)); config_set_int(CFG_MACHINE, NULL, "bpb_disable", bpb_disable); + config_set_int(CFG_MACHINE, NULL, "cd_speed", cd_speed); + config_set_int(CFG_MACHINE, NULL, "joystick_type", joystick_type); config_set_int(CFG_MACHINE, NULL, "mouse_type", mouse_type); diff --git a/src/pc.xrc b/src/pc.xrc index bc38ddc..06ac8a1 100644 --- a/src/pc.xrc +++ b/src/pc.xrc @@ -1127,6 +1127,41 @@ 5 0,0 + + + wxALL | wxALIGN_CENTER_VERTICAL + 5 + + 80,-1 + + -1 + + + + + wxEXPAND + 5 + + wxHORIZONTAL + + + wxALL + 5 + + + 350,-1 + + + + + + + + + wxEXPAND + 5 + 0,0 + diff --git a/src/scsi_cd.c b/src/scsi_cd.c index d782d78..33b11ad 100644 --- a/src/scsi_cd.c +++ b/src/scsi_cd.c @@ -7,8 +7,10 @@ #include "ide_atapi.h" #include "scsi.h" #include "scsi_cd.h" +#include "timer.h" #define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define ABS(x) ((x) > 0 ? (x) : -(x)) #define BUFFER_SIZE (256*1024) @@ -20,7 +22,8 @@ enum CMD_POS_IDLE = 0, CMD_POS_WAIT, CMD_POS_START_SECTOR, - CMD_POS_TRANSFER + CMD_POS_TRANSFER, + CMD_POS_COMPLETE }; /* ATAPI Commands */ @@ -162,7 +165,10 @@ typedef struct scsi_cd_data_t int blocks; - int cmd_pos; + int cmd_pos, new_cmd_pos; + int callback; + int wait_time; + scsi_bus_t *bus; int addr, len; int sector_pos; @@ -197,8 +203,115 @@ typedef struct scsi_cd_data_t atapi_device_t *atapi_dev; int pio_mode, mdma_mode; + + int short_seek, long_seek; + int max_speed, cur_speed; } scsi_cd_data_t; +static scsi_cd_data_t *cd_data = NULL; + +/*Note - all transfer speeds currently assume CAV behaviour. Drives above about 8x + should use CLV instead, but this is not currently supported.*/ +static struct +{ + int speed; + int short_seek; + int long_seek; +} cd_speeds[] = +{ + {1, 240, 1446}, + {2, 160, 1000}, + {3, 150, 900}, + {4, 112, 675}, + {6, 112, 675}, + {8, 112, 675}, + {12, 75, 400}, + {16, 58, 350}, + {20, 50, 300}, + {24, 45, 270}, + {32, 45, 270}, + {40, 50, 300}, + {44, 50, 300}, + {48, 50, 300}, + {52, 45, 270}, + {72, 45, 270}, +}; + +int cd_speed; + +int cd_get_speed(int i) +{ + return cd_speeds[i].speed; +} + +void cd_set_speed(int speed) +{ + if (cd_data) + { + int c = 0; + + while (1) + { + if (cd_speeds[c].speed == speed) + break; + if (cd_speeds[c].speed >= MAX_CD_SPEED) + break; + + c++; + } + + cd_data->max_speed = speed; + cd_data->cur_speed = speed; + cd_data->short_seek = cd_speeds[c].short_seek; + cd_data->long_seek = cd_speeds[c].long_seek; + } +} + +static void scsi_cd_callback(void *p) +{ + scsi_cd_data_t *data = p; + + if (data->cmd_pos == CMD_POS_WAIT) + { + data->wait_time--; + if (!data->wait_time) + { + data->callback = 0; + data->cmd_pos = data->new_cmd_pos; + scsi_bus_kick(data->bus); + } + else + data->callback = 1000 * TIMER_USEC; + } + else + data->callback = 0; +} + +//#define SECTOR_TIME 13333 /*Time between sectors on 1x drive*/ +#define SECTOR_TIME 12000 /*Adjusted from above value to account for transfer time on current SCSI/ATAPI code*/ + +/*The seek model is currently very basic. Seeks of less than MIN_SEEK sectors are treated as + immediate. Seeks between MIN_SEEK and MAX_SEEK sectors scale linearly between short_seek and + long seek. + + This really shouldn't be a linear scale, but this is a good enough approximation for now.*/ +#define MIN_SEEK 2000 +#define MAX_SEEK 333000 + +static int get_seek_time(scsi_cd_data_t *data, uint32_t start, uint32_t dest) +{ + uint32_t diff = ABS((int)(start - dest)); + + if (diff < MIN_SEEK) + return 0; + if (diff > MAX_SEEK) + diff = MAX_SEEK; + + diff -= MIN_SEEK; + + return data->short_seek + ((data->long_seek * diff) / (MAX_SEEK - MIN_SEEK)); +} + static void scsi_add_data(scsi_cd_data_t *data, uint8_t val) { data->data_in[data->data_pos_write++] = val; @@ -278,11 +391,17 @@ static void *scsi_cd_init(scsi_bus_t *bus, int id) scsi_cd_data_t *data = malloc(sizeof(scsi_cd_data_t)); memset(data, 0, sizeof(scsi_cd_data_t)); + data->bus = bus; data->id = id; page_flags[GPMODE_CDROM_AUDIO_PAGE] &= 0xFD; /* Clear changed flag for CDROM AUDIO mode page. */ memset(mode_pages_in[GPMODE_CDROM_AUDIO_PAGE], 0, 256); /* Clear the page itself. */ page_flags[GPMODE_CDROM_AUDIO_PAGE] &= ~PAGE_CHANGED; + + timer_add(scsi_cd_callback, &data->callback, &data->callback, data); + + cd_data = data; + cd_set_speed(cd_speed); return data; } @@ -301,6 +420,7 @@ static void scsi_cd_close(void *p) { scsi_cd_data_t *data = p; + cd_data = NULL; free(data); } @@ -387,12 +507,12 @@ static uint32_t ide_atapi_mode_sense(scsi_cd_data_t *data, uint32_t pos, uint8_t buf[pos++] = 0; /* Some other stuff not supported */ buf[pos++] = 0; /* Some other stuff not supported (lock state + eject) */ buf[pos++] = 0; /* Some other stuff not supported */ - buf[pos++] = (uint8_t) (CDROM_SPEED >> 8); - buf[pos++] = (uint8_t) CDROM_SPEED; /* Maximum speed */ + buf[pos++] = (uint8_t) ((data->max_speed * 176) >> 8); + buf[pos++] = (uint8_t) (data->max_speed * 176); /* Maximum speed */ buf[pos++] = 0; buf[pos++] = 2; /* Number of audio levels - on and off only */ buf[pos++] = 0; buf[pos++] = 0; /* Buffer size - none */ - buf[pos++] = (uint8_t) (CDROM_SPEED >> 8); - buf[pos++] = (uint8_t) CDROM_SPEED; /* Current speed */ + buf[pos++] = (uint8_t) ((data->cur_speed * 176) >> 8); + buf[pos++] = (uint8_t) (data->cur_speed * 176); /* Current speed */ buf[pos++] = 0; /* Reserved */ buf[pos++] = 0; /* Drive digital format */ buf[pos++] = 0; /* Reserved */ @@ -506,6 +626,23 @@ static int scsi_cd_command(uint8_t *cdb, void *p) return SCSI_PHASE_STATUS; case SCSI_REZERO_UNIT: + if (data->cmd_pos == CMD_POS_IDLE) + { + uint32_t old_pos = data->cdpos; + int seek_time; + + data->cdpos = 0; + + seek_time = get_seek_time(data, old_pos, data->cdpos); + if (seek_time) + { + data->cmd_pos = CMD_POS_WAIT; + data->new_cmd_pos = CMD_POS_COMPLETE; + data->wait_time = seek_time; + data->callback = 1000 * TIMER_USEC; + return SCSI_PHASE_COMMAND; + } + } data->cmd_pos = CMD_POS_IDLE; return SCSI_PHASE_STATUS; @@ -553,10 +690,32 @@ static int scsi_cd_command(uint8_t *cdb, void *p) return SCSI_PHASE_DATA_IN; case SCSI_SEEK_6: + if (data->cmd_pos == CMD_POS_IDLE) + { + uint32_t old_pos = data->cdpos; + int seek_time; + + data->cdpos = (((uint32_t)cdb[1] & 0x1f) << 16) | ((uint32_t)cdb[2] << 8) | (uint32_t)cdb[3]; + + seek_time = get_seek_time(data, old_pos, data->cdpos); + if (seek_time) + { + data->cmd_pos = CMD_POS_WAIT; + data->new_cmd_pos = CMD_POS_COMPLETE; + data->wait_time = seek_time; + data->callback = 1000 * TIMER_USEC; + return SCSI_PHASE_COMMAND; + } + } data->cmd_pos = CMD_POS_IDLE; return SCSI_PHASE_STATUS; case GPCMD_SET_SPEED: + data->cur_speed = (cdb[3] | (cdb[2] << 8)) / 176; + if (data->cur_speed < 1) + data->cur_speed = 1; + else if (data->cur_speed > data->max_speed) + data->cur_speed = data->max_speed; data->cmd_pos = CMD_POS_IDLE; return SCSI_PHASE_STATUS; @@ -625,21 +784,60 @@ static int scsi_cd_command(uint8_t *cdb, void *p) if (data->cmd_pos == CMD_POS_IDLE) { + uint32_t old_pos = data->cdpos; + int seek_time; + data->cdlen = (cdb[6] << 16) | (cdb[7] << 8) | cdb[8]; data->cdpos = (cdb[2] << 24) | (cdb[3] << 16) | (cdb[4] << 8) | cdb[5]; - data->cmd_pos = CMD_POS_TRANSFER; + data->cmd_pos = CMD_POS_START_SECTOR; data->bytes_expected = data->cdlen * ((rcdmode == 0x10) ? 2048 : 2352); - if (rcdmode == 0x10) - atapi_set_transfer_granularity(data->atapi_dev, 2048); - else - atapi_set_transfer_granularity(data->atapi_dev, 2352); + if (data->is_atapi) + { + if (rcdmode == 0x10) + atapi_set_transfer_granularity(data->atapi_dev, 2048); + else + atapi_set_transfer_granularity(data->atapi_dev, 2352); + } + + seek_time = get_seek_time(data, old_pos, data->cdpos); + if (seek_time) + { + data->cmd_pos = CMD_POS_WAIT; + data->new_cmd_pos = CMD_POS_START_SECTOR; + data->wait_time = seek_time; + data->callback = 1000 * TIMER_USEC; + return SCSI_PHASE_COMMAND; + } } + if (data->cmd_pos == CMD_POS_WAIT) + return SCSI_PHASE_COMMAND; if (!data->cdlen) { data->cmd_pos = CMD_POS_IDLE; return SCSI_PHASE_STATUS; } + if (data->cmd_pos == CMD_POS_START_SECTOR) + { + uint64_t time = (((uint64_t)SECTOR_TIME * (uint64_t)TIMER_USEC) / data->cur_speed) * (uint64_t)data->cdlen; + + data->cmd_pos = CMD_POS_WAIT; + data->new_cmd_pos = CMD_POS_TRANSFER; + + if (time > 0x7fffffffull) + { + data->wait_time = (int)(time / ((uint64_t)TIMER_USEC * 1000ull)); + data->callback = (int)(time % ((uint64_t)TIMER_USEC * 1000ull));; + } + else + { + data->callback = time; + data->wait_time = 1; + } + if (data->callback == 0) + data->callback = 1; + return SCSI_PHASE_COMMAND; + } if (rcdmode == 0x10) { @@ -683,6 +881,9 @@ static int scsi_cd_command(uint8_t *cdb, void *p) // readcdmode = 0; if (data->cmd_pos == CMD_POS_IDLE) { + uint32_t old_pos = data->cdpos; + int seek_time; + if (cdb[0] == GPCMD_READ_6) { data->cdlen = cdb[4]; @@ -700,16 +901,51 @@ static int scsi_cd_command(uint8_t *cdb, void *p) } // pclog("cdpos=%08x cdlen=%08x\n", data->cdpos, data->cdlen); - data->cmd_pos = CMD_POS_TRANSFER; + data->cmd_pos = CMD_POS_START_SECTOR; data->bytes_expected = data->cdlen * 2048; - atapi_set_transfer_granularity(data->atapi_dev, 2048); - } + if (data->is_atapi) + atapi_set_transfer_granularity(data->atapi_dev, 2048); + + seek_time = get_seek_time(data, old_pos, data->cdpos); + if (seek_time) + { + data->cmd_pos = CMD_POS_WAIT; + data->new_cmd_pos = CMD_POS_START_SECTOR; + data->wait_time = seek_time; + data->callback = 1000 * TIMER_USEC; + return SCSI_PHASE_COMMAND; + } + } + if (data->cmd_pos == CMD_POS_WAIT) + return SCSI_PHASE_COMMAND; if (!data->cdlen) { data->cmd_pos = CMD_POS_IDLE; return SCSI_PHASE_STATUS; } + if (data->cmd_pos == CMD_POS_START_SECTOR) + { + uint64_t time = (((uint64_t)SECTOR_TIME * (uint64_t)TIMER_USEC) / data->cur_speed) * (uint64_t)data->cdlen; + + data->cmd_pos = CMD_POS_WAIT; + data->new_cmd_pos = CMD_POS_TRANSFER; + + if (time > 0x7fffffffull) + { + data->wait_time = (int)(time / ((uint64_t)TIMER_USEC * 1000ull)); + data->callback = (int)(time % ((uint64_t)TIMER_USEC * 1000ull));; + } + else + { + data->callback = time; + data->wait_time = 1; + } + if (data->callback == 0) + data->callback = 1; + return SCSI_PHASE_COMMAND; + } + readflash_set(READFLASH_HDC, data->id); c = MIN(data->cdlen, MAX_NR_SECTORS); if (atapi->readsector(data->data_in, data->cdpos, c)) @@ -1066,8 +1302,25 @@ atapi_out: return SCSI_PHASE_STATUS; case GPCMD_SEEK: - pos = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5]; - atapi->seek(pos); + if (data->cmd_pos == CMD_POS_IDLE) + { + uint32_t old_pos = data->cdpos; + int seek_time; + + data->cdpos = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5]; + + atapi->seek(data->cdpos); + + seek_time = get_seek_time(data, old_pos, data->cdpos); + if (seek_time) + { + data->cmd_pos = CMD_POS_WAIT; + data->new_cmd_pos = CMD_POS_COMPLETE; + data->wait_time = seek_time; + data->callback = 1000 * TIMER_USEC; + return SCSI_PHASE_COMMAND; + } + } data->cmd_pos = CMD_POS_IDLE; return SCSI_PHASE_STATUS; diff --git a/src/scsi_cd.h b/src/scsi_cd.h index 77f81fb..dc86826 100644 --- a/src/scsi_cd.h +++ b/src/scsi_cd.h @@ -1 +1,7 @@ extern scsi_device_t scsi_cd; + +#define MAX_CD_SPEED 72 +int cd_get_speed(int i); +void cd_set_speed(int speed); + +extern int cd_speed; diff --git a/src/wx-config.c b/src/wx-config.c index fbe9bc9..618363d 100644 --- a/src/wx-config.c +++ b/src/wx-config.c @@ -15,6 +15,7 @@ #include "mem.h" #include "nethandler.h" #include "nvr.h" +#include "scsi_cd.h" #include "sound.h" #include "video.h" #include "vid_voodoo.h" @@ -325,6 +326,30 @@ static void recalc_hdd_list(void* hdlg, int model, int use_selected_hdd, int for } } +static void recalc_cd_list(void *hdlg, int cur_speed) +{ + void *h = wx_getdlgitem(hdlg, WX_ID("IDC_COMBO_CDSPEED")); + int c = 0; + + wx_sendmessage(h, WX_CB_RESETCONTENT, 0, 0); + while (1) + { + char s[8]; + int speed; + + speed = cd_get_speed(c); + sprintf(s, "%iX", speed); + wx_sendmessage(h, WX_CB_ADDSTRING, 0, (LONG_PARAM)s); + + if (speed == cur_speed) + wx_sendmessage(h, WX_CB_SETCURSEL, c, 0); + + c++; + if (speed >= MAX_CD_SPEED) + break; + } +} + #ifdef USE_NETWORKING static void recalc_net_list(void *hdlg, int model) { @@ -548,6 +573,10 @@ int config_dlgsave(void* hdlg) cpu_waitstates = wx_sendmessage(h, WX_CB_GETCURSEL, 0, 0); cpu_update_waitstates(); + h = wx_getdlgitem(hdlg, WX_ID("IDC_COMBO_CDSPEED")); + cd_speed = cd_get_speed(wx_sendmessage(h, WX_CB_GETCURSEL, 0, 0)); + cd_set_speed(cd_speed); + if (has_been_inited) saveconfig(NULL); @@ -823,6 +852,8 @@ int config_dlgproc(void* hdlg, int message, INT_PARAM wParam, LONG_PARAM lParam) else wx_sendmessage(h, WX_CB_SETCURSEL, 0, 0); } + + recalc_cd_list(hdlg, cd_speed); #ifdef USE_NETWORKING recalc_net_list(hdlg, romstomodel[romset]); diff --git a/src/wx-resources.cpp b/src/wx-resources.cpp index 76e94d7..13ad3f9 100644 --- a/src/wx-resources.cpp +++ b/src/wx-resources.cpp @@ -1784,7 +1784,7 @@ static unsigned char xml_res_file_25[] = { 87,93,129,205,251,40,255,176,89,15,134,167,154,149,127,4,24,0,227,17,114, 29,252,169,144,138,0,0,0,0,73,69,78,68,174,66,96,130}; -static size_t xml_res_size_26 = 136071; +static size_t xml_res_size_26 = 137689; static unsigned char xml_res_file_26[] = { 60,63,120,109,108,32,118,101,114,115,105,111,110,61,34,49,46,48,34,32,101, 110,99,111,100,105,110,103,61,34,85,84,70,45,56,34,63,62,10,60,33,45,45, @@ -3821,6 +3821,78 @@ static unsigned char xml_res_file_26[] = { 32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,115,105,122,101,62,48,44,48, 60,47,115,105,122,101,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,111,98,106,101,99,116, +32,99,108,97,115,115,61,34,115,105,122,101,114,105,116,101,109,34,62,10, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +60,111,112,116,105,111,110,62,48,60,47,111,112,116,105,111,110,62,10,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60, +102,108,97,103,62,119,120,65,76,76,32,124,32,119,120,65,76,73,71,78,95, +67,69,78,84,69,82,95,86,69,82,84,73,67,65,76,60,47,102,108,97,103,62,10, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +60,98,111,114,100,101,114,62,53,60,47,98,111,114,100,101,114,62,10,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,111, +98,106,101,99,116,32,99,108,97,115,115,61,34,119,120,83,116,97,116,105, +99,84,101,120,116,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,60,115,105,122,101,62,56,48,44,45,49,60, +47,115,105,122,101,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,60,108,97,98,101,108,62,67,68,45,82,79,77, +58,60,47,108,97,98,101,108,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,60,119,114,97,112,62,45,49,60,47, +119,114,97,112,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101,99, +116,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,60,111,98,106,101,99,116,32,99,108,97,115,115,61,34,115,105,122,101, +114,105,116,101,109,34,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,60,111,112,116,105,111,110,62,49,60,47,111, +112,116,105,111,110,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,60,102,108,97,103,62,119,120,69,88,80,65,78, +68,60,47,102,108,97,103,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,60,98,111,114,100,101,114,62,53,60,47,98, +111,114,100,101,114,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32,99,108,97,115,115, +61,34,119,120,66,111,120,83,105,122,101,114,34,62,10,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,111,114, +105,101,110,116,62,119,120,72,79,82,73,90,79,78,84,65,76,60,47,111,114, +105,101,110,116,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32,99,108,97,115,115, +61,34,115,105,122,101,114,105,116,101,109,34,62,10,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,111,112, +116,105,111,110,62,48,60,47,111,112,116,105,111,110,62,10,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60, +102,108,97,103,62,119,120,65,76,76,60,47,102,108,97,103,62,10,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,60,98,111,114,100,101,114,62,53,60,47,98,111,114,100,101,114,62,10,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,60,111,98,106,101,99,116,32,99,108,97,115,115,61,34,119,120,67, +111,109,98,111,66,111,120,34,32,110,97,109,101,61,34,73,68,67,95,67,79, +77,66,79,95,67,68,83,80,69,69,68,34,62,10,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,115,116, +121,108,101,62,119,120,67,66,95,68,82,79,80,68,79,87,78,124,119,120,67, +66,95,82,69,65,68,79,78,76,89,60,47,115,116,121,108,101,62,10,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,60,115,105,122,101,62,51,53,48,44,45,49,60,47,115,105,122,101, +62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,60,118,97,108,117,101,47,62,10,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +60,99,111,110,116,101,110,116,47,62,10,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101, +99,116,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101, +99,116,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,60,111,98,106,101,99,116,32,99,108,97, +115,115,61,34,115,112,97,99,101,114,34,62,10,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,111,112,116,105,111,110, +62,49,60,47,111,112,116,105,111,110,62,10,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,102,108,97,103,62,119,120, +69,88,80,65,78,68,60,47,102,108,97,103,62,10,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,98,111,114,100,101,114, +62,53,60,47,98,111,114,100,101,114,62,10,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,115,105,122,101,62,48,44,48, +60,47,115,105,122,101,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62,10,32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106,101,99,116,62, 10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,60,47,111,98,106, 101,99,116,62,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,