______________________________________________________________________________ | SECTION 1 PART A SUBPART 1 | CD Programming Part 2 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Part 2: Getting System Information and Using Request Headers/Command Blocks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Welcome back! Well, well, well! Another issue has passed and you are still interested in programming your CD-ROM drive. Last time we stopped after we got the MSCDEX Version Number using interrupt &H2F with AX=&H150C. That's how we reference all these interrupt functions. Now it's time to expand our repertoire of MSCDEX interrupt calls, especially getting more information about the computer system. Sure, most of the time a CD-ROM drive is located on D:, but what if someone has it on E:, F: or even Z:. One way would be to just prompt the user for his/her CD-ROM drive letter, but we can actually determine the number of the first drive via another interrupt function. You might wonder what a drive number is, but it's a fairly simple concept. Since computers are number based all of these functions will reference drives by numbers, drive A: is 0, drive B: is 1, C: is 2, etc. No sweat! The call uses interrupt &H2F with AX=&H1500 and returns the number of the first CD-ROM drive in register CX. The BASIC code looks like this. ' Get first CD-ROM drive number inregsx.ax = &H1500 ' Referencing the CD function inregsx.cx = &H0 ' Clear register CX CALL interruptx(&H2F, inregsx, outregsx) ' Call interrupt &H2F drv% = outregsx.cx ' Store drive number in drv% Wow, it doesn't get any easier than this. Actually the entire CD-ROM thing is very simple, once you find the right documentation. How else would you know the values for the registers? Since I feel we are on a roll here, let's make another one of these interrupt calls. The next one will check whether the drive referenced by the number in drv% si supported by MSCDEX. This is done using AX=&H150B and BX containing the drive number. The interrupt returns a value of &HADAD in register BX if the drive is supported, so this is another fairly simple matter of plugging the right values in the correct registers. ' CD-ROM Drive Check inregsx.ax = &H150B ' Again referencing the CD function inregsx.bx = &H0 ' Clear BX inregsx.cx = drv% ' Set CX equal to the first drive CALL interruptx(&H2F, inregsx, outregsx) ' Call interrupt &H2F ' Check the value of the BX register after the interrupt call IF outregsx.bx <> &HADAD THEN PRINT "Drive "; CHR$(drv% + 65); ": is not supported by MSCDEX!" END END IF NOTE: The program will only use the first CD-ROM drive, since it decreases the size of the code tremendously. If you would like to use another drive, then just set drv% equal to its drive number at this point. Now that we have covered some of the most useful simple interrupt functions, it's time to move on to bigger and better things. Whenever using any hardware one should always RESET it before use, so that's what we'll do next. Hold on a second, though, this is not as easy as it sounds. All the remaining interrupt functions that we'll cover in the guide will use AX=&H1510. According to the documentation, this function is used to send a `Device Driver Request.' It will need the following register values to work: AX = &H1510 CX = CD-ROM drive number (In our case the value in drv%) ES = Segment of CD-ROM device driver request header BX = Offset of CD-ROM device driver request header Yikes! What is a request header? A request header contains values that the interrupt function needs in order to know what it is doing and how it is supposed to do it. That's about as simple as I can make it! Usually, these request headers are stored in arrays, so ES:BX points to the address in memory of the array containing the header. The request header for the RESET function will be an array that is 26 bytes long, so we will use this line: ' Reset Drive DIM rh(25) AS STRING * 1 '25 + 1 * 1 byte = 26 bytes Since the request header will also access it's own control block of length 1, we better invoke the following line. A control block is almost exactly like a request header, so this next line should make sense: DIM cb(0) AS STRING * 1 'STRING * 1 is one byte long All right, now we plug those critical values into our arrays. As you can see, there are many unused offsets in the array, but they should not concern us. Explaining all the fields in the array would be over-kill at this point, so I'll just give you the code and what it means in remarks. This should clear some things up and make the entire thing easy to read. So ... Let's get down to it! ' Creating the control block for the RESET function cb(0) = CHR$(2) 'IOCTL Output control block code: 2 is RESET ' Now we write the information for the request header rh(0) = CHR$(26) 'Length of request header in bytes rh(2) = CHR$(12) 'Request header command code field: 12 is IOCTL Output rh(14) = CHR$(lbyte%(VARPTR(cb(0)))) 'Lowbyte of offset for control block rh(15) = CHR$(hbyte%(VARPTR(cb(0)))) 'Highbyte of offset for control block rh(16) = CHR$(lbyte%(VARSEG(cb(0)))) 'Lowbyte of segment for control block rh(17) = CHR$(hbyte%(VARSEG(cb(0)))) 'Highbyte of segment for control block rh(18) = CHR$(0) 'Number of bytes to transfer;Highbyte rh(19) = CHR$(1) 'Number of bytes to transfer;Lowbyte ' Time to call interrupt &H2F inregsx.ax = &H1510 inregsx.es = VARSEG(rh(0)) 'Segment of request header inregsx.bx = VARPTR(rh(0)) 'Offset of request header inregsx.cx = drv% 'Drive number CALL interruptx(&H2F, inregsx, outregsx) Wew! This is all I can get into this part of the guide, otherwise Peter is probably going to club me really, really hard! Next time I'll explain all this request header stuff once more, since that's what we are going to keep on using. _Just for fun_: Replace the 2 in the IOCTL Output command code field with a 0! -------------------------------------------------------- * EDITOR'S NOTE: * This article was originally printed in Peter Cooper's BASIX Fanzine, * Issue #6 from August 1996.