3 #include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
12 #include <hw/vga/vga.h>
13 #include <hw/pci/pci.h>
14 #include <hw/dos/dos.h>
15 #include <hw/8254/8254.h> /* 8254 timer */
16 #include <hw/8259/8259.h> /* 8259 PIC interrupts */
17 #include <hw/vga/vgagui.h>
18 #include <hw/vga/vgatty.h>
19 #include <hw/ide/idelib.h>
38 static void do_cdrom_drive_read_test(struct ide_controller *ide,unsigned char which,unsigned char continuous) {
39 uint16_t drq_log[((unsigned long)sizeof(cdrom_sector))/2048UL];
40 unsigned long sector = 16; /* read the ISO 9660 table of contents */
41 unsigned long tlen = 2048; /* one sector */
42 unsigned long tlen_sect = 1;
43 unsigned char user_esc = 0;
44 struct vga_msg_box vgabox;
45 unsigned int drq_log_ent;
46 unsigned int cleared=0;
47 uint8_t buf[12] = {0};
51 sector = prompt_cdrom_sector_number();
54 tlen_sect = prompt_cdrom_sector_count();
55 if (tlen_sect == 0UL || tlen_sect == ~0UL)
57 if (tlen_sect > ((unsigned long)sizeof(cdrom_sector) / 2048UL))
58 tlen_sect = ((unsigned long)sizeof(cdrom_sector) / 2048UL);
59 if (tlen_sect > ((65536UL/2048UL)-1UL)) /* don't try ATAPI requests 64KB or larger, the size field is 16-bit wide */
60 tlen_sect = ((65536UL/2048UL)-1UL);
61 tlen = tlen_sect * 2048UL;
63 again: /* jump point: send execution back here for another sector */
64 if (do_ide_controller_user_wait_busy_controller(ide) != 0 || do_ide_controller_user_wait_drive_ready(ide) < 0)
66 idelib_controller_ack_irq(ide); /* <- make sure to ack IRQ */
67 if (idelib_controller_atapi_prepare_packet_command(ide,/*xfer=to host no DMA*/0x04,/*byte count=*/tlen) < 0) /* fill out taskfile with command */
69 if (idelib_controller_apply_taskfile(ide,0xBE/*base_io+1-5&7*/,IDELIB_TASKFILE_LBA48_UPDATE/*clear LBA48*/) < 0) /* also writes command */
72 /* NTS: Despite OSDev ATAPI advice, IRQ doesn't seem to fire at this stage, we must poll wait */
73 if (do_ide_controller_user_wait_busy_controller(ide) != 0 || do_ide_controller_user_wait_drive_ready(ide) < 0)
76 idelib_controller_update_atapi_state(ide);
77 if (!(ide->last_status&1)) { /* if no error, read result from count register */
78 do_warn_if_atapi_not_in_command_state(ide); /* sector count register should signal we're in the command stage */
80 do_construct_atapi_scsi_mmc_read(buf,sector,tlen_sect,cdrom_read_mode);
81 idelib_controller_reset_irq_counter(ide); /* IRQ will fire after command completion */
82 idelib_controller_atapi_write_command(ide,buf,12); /* write 12-byte ATAPI command data */
83 if (ide->flags.io_irq_enable) { /* NOW we wait for the IRQ */
84 if (do_ide_controller_user_wait_irq(ide,1) < 0)
86 idelib_controller_ack_irq(ide); /* <- or else it won't fire again */
89 if (do_ide_controller_user_wait_busy_controller(ide) != 0 || do_ide_controller_user_wait_drive_ready(ide) < 0)
92 if (!idelib_controller_is_error(ide)) { /* OK. success. now read the data */
93 unsigned int ret_len = 0,drq_len,ey;
95 /* NTS: I hate to break it to newbie IDE programmers, but reading back the sector isn't
96 * quite the simple "read N bytes" from the drive. In reality, you wait for the drive
97 * to signal DRQ, and then read back the length of data it has available for you to
98 * read by, then you read that amount, and if more data is due, then you wait for
99 * another IRQ and DRQ signal.
101 * In most cases, the DRQ returned by the drive is the same length you passed in,
102 * but NOT ALWAYS. Many cheap laptop drives for example will only return "2048"
103 * because they don't have a lot of buffer, and many DVD-ROM drives like to vary
104 * the DRQ size per transfer for whatever reason, whether "dynamically" according
105 * to CD-ROM spin speed or based on whatever data it's managed to read and buffer
108 * On the positive side, it means that on an error, the transfer can abort early if
111 memset(cdrom_sector,0,tlen);
112 while (ret_len < tlen) {
113 if (idelib_controller_is_error(ide)) {
114 vga_msg_box_create(&vgabox,"Error",0,0);
115 wait_for_enter_or_escape();
116 vga_msg_box_destroy(&vgabox);
120 idelib_controller_update_atapi_state(ide); /* having completed the command, read ATAPI state again */
121 idelib_controller_update_atapi_drq(ide); /* also need to read back the DRQ (data) length the drive has chosen */
122 if (idelib_controller_atapi_complete_state(ide)) { /* if suddenly in complete state, exit out */
123 do_warn_if_atapi_not_in_data_input_state(ide); /* sector count register should signal we're in the completed stage (command/data=0 input/output=1) */
127 if (do_ide_controller_user_wait_drive_drq(ide) < 0) {
131 idelib_controller_update_atapi_state(ide); /* having completed the command, read ATAPI state again */
132 idelib_controller_update_atapi_drq(ide); /* also need to read back the DRQ (data) length the drive has chosen */
133 do_warn_if_atapi_not_in_data_input_state(ide); /* sector count register should signal we're in the completed stage (command/data=0 input/output=1) */
135 assert(drq_log_ent < (sizeof(drq_log)/sizeof(drq_log[0])));
136 drq_len = idelib_controller_read_atapi_drq(ide);
137 drq_log[drq_log_ent++] = drq_len;
138 if (drq_len < 2048UL || (drq_len % 2048UL) != 0UL || (drq_len+ret_len) > tlen) {
139 /* we're asking for one sector (2048) bytes, the drive should return that, if not, something's wrong.
140 * even cheap POS drives in old laptops will at least always return 2048! */
141 sprintf(tmp,"Warning: ATAPI device returned unexpected DRQ=%u (%u+%u = %u)",
142 drq_len,ret_len,tlen);
143 vga_msg_box_create(&vgabox,tmp,0,0);
144 wait_for_enter_or_escape();
145 vga_msg_box_destroy(&vgabox);
150 idelib_read_pio_general(cdrom_sector+ret_len,drq_len,ide,IDELIB_PIO_WIDTH_DEFAULT);
151 if (ide->flags.io_irq_enable) { /* NOW we wait for another IRQ (completion) */
152 if (do_ide_controller_user_wait_irq(ide,1) < 0) {
156 idelib_controller_ack_irq(ide); /* <- or else it won't fire again */
159 if (do_ide_controller_user_wait_busy_controller(ide) != 0 || do_ide_controller_user_wait_drive_ready(ide) < 0) {
166 idelib_controller_update_atapi_state(ide); /* having completed the command, read ATAPI state again */
167 do_warn_if_atapi_not_in_complete_state(ide); /* sector count register should signal we're in the completed stage (command/data=1 input/output=1) */
169 /* ---- draw contents on the screen ---- */
170 vga_write_color(0x0E);
177 vga_write("Contents of CD-ROM sector ");
178 sprintf(tmp,"%lu-%lu",sector,sector+tlen_sect-1UL); vga_write(tmp);
179 sprintf(tmp,"(%lu) bytes",(unsigned long)tlen); vga_write(tmp);
182 vga_moveto(0,3+16+1);
183 sprintf(tmp,"%u/%lu in %u DRQ transfers: ",ret_len,tlen,drq_log_ent);
185 for (x=0;x < drq_log_ent;x++) {
186 int len = sprintf(tmp,"%u ",drq_log[x]);
187 if ((vga_pos_x+len) > vga_width) vga_write("\n ");
190 while (vga_pos_y <= ey) vga_write(" ");
193 vga_write_color(0x08);
197 for (x=0;x < 16;x++) {
198 sprintf(tmp,"+%X ",x);
202 vga_moveto(5+(16*3)+1,2);
203 for (x=0;x < 16;x++) {
208 for (i=0;i < (tlen/256UL);i++) { /* 16x16x8 = 2^(4+4+3) = 2^11 = 2048 */
209 for (y=0;y < 16;y++) {
211 vga_write_color(0x08);
212 sprintf(tmp,"%04X ",(i*256)+(y*16));
216 for (y=0;y < 16;y++) {
218 vga_write_color(0x0F);
219 for (x=0;x < 16;x++) {
220 sprintf(tmp,"%02X ",cdrom_sector[(i*256)+(y*16)+x]);
224 vga_moveto(5+(16*3)+1,y+3);
225 vga_write_color(0x0E);
226 for (x=0;x < 16;x++) {
227 vga_writec(sanitizechar(cdrom_sector[(i*256)+(y*16)+x]));
231 if (continuous && !user_esc) {
234 if (c == 0) c = getch() << 8;
239 if ((c=wait_for_enter_or_escape()) == 27)
240 break; /* allow user to exit early by hitting ESC */
244 if (c != 27 && !user_esc) {
245 /* if the user hit ENTER, then read another sector and display that too */
251 common_ide_success_or_error_vga_msg_box(ide,&vgabox);
252 wait_for_enter_or_escape();
253 vga_msg_box_destroy(&vgabox);
255 idelib_controller_update_atapi_state(ide); /* having completed the command, read ATAPI state again */
256 do_warn_if_atapi_not_in_complete_state(ide); /* sector count register should signal we're in the completed stage (command/data=1 input/output=1) */
260 common_ide_success_or_error_vga_msg_box(ide,&vgabox);
261 wait_for_enter_or_escape();
262 vga_msg_box_destroy(&vgabox);
266 static const char *drive_cdrom_reading_menustrings[] = {
267 "Show IDE register taskfile", /* 0 */
268 "Read CD-ROM data sectors",
269 "Read CD-ROM data continuously"
272 void do_drive_cdrom_reading(struct ide_controller *ide,unsigned char which) {
273 struct menuboxbounds mbox;
281 /* UI element vars */
282 menuboxbounds_set_def_list(&mbox,/*ofsx=*/4,/*ofsy=*/7,/*cols=*/1);
283 menuboxbounds_set_item_strings_arraylen(&mbox,drive_cdrom_reading_menustrings);
285 /* most of the commands assume a ready controller. if it's stuck,
286 * we'd rather the user have a visual indication that it's stuck that way */
287 c = do_ide_controller_user_wait_busy_controller(ide);
290 /* select the drive we want */
291 idelib_controller_drive_select(ide,which,/*head*/0,IDELIB_DRIVE_SELECT_MODE_CHS);
293 /* in case the IDE controller is busy for that time */
294 c = do_ide_controller_user_wait_busy_controller(ide);
297 /* read back: did the drive select take effect? if not, it might not be there. another common sign is the head/drive select reads back 0xFF */
298 c = do_ide_controller_drive_check_select(ide,which);
301 /* it might be a CD-ROM drive, which in some cases might not raise the Drive Ready bit */
302 do_ide_controller_atapi_device_check_post_host_reset(ide);
304 /* wait for the drive to indicate readiness */
305 /* NTS: If the drive never becomes ready even despite our reset hacks, there's a strong
306 * possibility that the device doesn't exist. This can happen for example if there
307 * is a master attached but no slave. */
308 c = do_ide_controller_user_wait_drive_ready(ide);
311 /* for completeness, clear pending IRQ */
312 idelib_controller_ack_irq(ide);
320 for (y=0;y < vga_height;y++) {
321 for (x=0;x < vga_width;x++) {
322 *vga++ = 0x1E00 + 177;
328 vga_write_color(0x1F);
329 vga_write(" IDE controller ");
330 sprintf(tmp,"@%X",ide->base_io);
332 if (ide->alt_io != 0) {
333 sprintf(tmp," alt %X",ide->alt_io);
337 sprintf(tmp," IRQ %d",ide->irq);
340 vga_write(which ? " Slave" : " Master");
341 while (vga_pos_x < vga_width && vga_pos_x != 0) vga_writec(' ');
343 vga_write_color(0xC);
344 vga_write("WARNING: This code talks directly to your hard disk controller.");
345 while (vga_pos_x < vga_width && vga_pos_x != 0) vga_writec(' ');
346 vga_write_color(0xC);
347 vga_write(" If you value the data on your hard drive do not run this program.");
348 while (vga_pos_x < vga_width && vga_pos_x != 0) vga_writec(' ');
354 vga_moveto(mbox.ofsx,mbox.ofsy - 2);
355 vga_write_color((select == -1) ? 0x70 : 0x0F);
356 vga_write("Back to IDE drive main menu");
357 while (vga_pos_x < (mbox.width+mbox.ofsx) && vga_pos_x != 0) vga_writec(' ');
359 menuboxbound_redraw(&mbox,select);
363 if (c == 0) c = getch() << 8;
373 case 0: /* show IDE register taskfile */
374 do_common_show_ide_taskfile(ide,which);
375 redraw = backredraw = 1;
377 case 1: /*Read sectors*/
378 case 2: /*Read sectors continuously*/
379 do_cdrom_drive_read_test(ide,which,/*continuous=*/(select==2));
380 redraw = backredraw = 1;
384 else if (c == 0x4800) {
386 select = mbox.item_max;
390 else if (c == 0x4B00) { /* left */
393 else if (c == 0x4D00) { /* right */
396 else if (c == 0x5000) {
397 if (++select > mbox.item_max)