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
+
+
+
+ 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,