#include #include #include #include #include #define N 1000 //************************************************************************ //** Cable specifications: ** //** ** //** Pinouts: ** //** IBM Parallel Port Atari SIO name ** //** ** //** 1 -----------------> 7 command/ ** //** 14 <---------------- 5 dataout ** //** 16 -----------------> 10 vcc/rdy ** //** 17 -----------------> 3 datain ** //** 24 ----------------- 4 gnd ** //** 25 ----------------- 6 gnd ** //** ** //** ** //** Connector Specs: ** //** IBM Parallel Port DB25M 25 pin male d-shell ** //** (view as if you were plugging the connector into your face) ** //** 1 2 3 4 5 6 7 8 9 10 11 12 13 ** //** 14 15 16 17 18 19 20 21 22 23 24 25 ** //** ** //** ** //** ** //** Atari SIO Molex 13 pin ** //** (view as if you were plugging the connector into your face) ** //** ** //** 1 3 5 7 9 11 13 ** //** 2 4 6 8 10 12 ** //** ** //** ** //** Notes: ** //** (1) Keep the wire length less than 3 feet. ** //** ** //************************************************************************ //*****************This is the structure of the xf551 configuration record. //This is here for information only, the struct is not used in this program. struct DRVCONFIG { char DRVTRC; // number of tracks (0x28) int DRVSTP; // drive step rate (0) char DRVSEC; // number of sectors on a track (0x12, 0x1a) char DRVSID; // number of disk sides (0=single 1=double) char DRVDEN; // drive density 0 = sd, 4 = dd int DRVBYT; // bytes per sector 0x80 = 128, 0x100 = 256 char DRVSEL; // drive address (d1: = 1) char DRVSER; // drive serial rate (0x41 = 19200bps) int DRVMSC; // ??? char CHKSUM; // checksum for above data }; //********Below are the configuration records for the various drive types //single sided single density char d810[13] = {0x28,00,00,0x12,0x00,0x00,0x00,80,0x01,0x41,00,00,0xfc}; //single sided enhanced density char d1050[13] ={0x28,00,00,0x1a,0x00,0x04,0x00,80,0x01,0x41,00,00,0x09}; //single sided double density char xf551[13] ={0x28,00,00,0x12,0x00,0x04,0x01,00,0x01,0x41,00,00,0x82}; char data[512]; unsigned data_valid, data_xmit; //************************************************************************ //** calibration(freq) ** //** This routine estimates the number of loop counts ** //** in 25uS. This count is used to synchronize this ** //** program with the sio data rate expected by the ** //** atari disk drive. ** //** ** //** Inputs: ** //** freq - The atari sio data rate (ideally ~19034 bits per ** //** second) however, 18350bps seems to be more reliable. ** //** Outputs: ** //** none. ** //** ** //** Routines called: ** //** none. ** //** ** //** History: ** //** 07/11/94 Created M.S.M. ** //** ** //** ** //************************************************************************ void calibrate(double freq) { double tim, rate = (0.5/freq); // get 1/2 the period double clock_period = 1.0/1193180.0; unsigned temp, inc= 0x80; // start off with msb set data_valid = 0x80; long t; outportb(97,1); // enable timer 2 //*******use successive approximation method to estimate the loop count for (int sa=0;sa<8;sa++) { t = 0L; for (int i=0;irate) data_valid -= inc; // if the loop time is too inc = inc>>1; // long, set current bit to zero. data_valid |= inc; // try next lower bit // for debug // printf("tim = %lg, data_valid = %x\n", tim, data_valid); } //Account for the instruction just before and just after the timing loop data_valid = unsigned(double(data_valid)*0.98); printf("\ncount error = %lg nS loop count = %d\n", (tim-rate)*1e9, data_valid); if (data_valid <75) puts( "The cpu clock is probably too slow for this program to work properly." ); } //************************************************************************ //** get_bytes(data,len,timeout) ** //** This routine expects to receive len bytes of data ** //** over the atari SIO. ** //** The received data is stored in char near *data. ** //** If the data is not received before timeout, then ** //** the data buffer is filled with 0xff. ** //** ** //** Inputs: ** //** data - A near pointer to the data buffer memory. ** //** len - The nember of bytes to be received, including ** //** the checksum byte. ** //** timeout - The routine waits timeout*8mS for a response. ** //** Outputs: ** //** data - The data read from the serial bus is stored ** //** in this memory space. The first two bytes ** //** are the acknowledge and complete bytes. ** //** This is so that the calling routine can ** //** can determine if the transfer was successful. ** //** ** //** Routines called: ** //** none. ** //** ** //** History: ** //** 07/11/94 Created M.S.M. ** //** ** //** Notes: ** //** 1. The timing in this routine is critical. Changes ** //** should be made with extreme care. ** //** ** //** 2. The numbers in parenthesis are the 286 clock cycles ** //** per instruction. ** //************************************************************************ void get_bytes(char near *data, int len, int timeout) { // data - is the data buffer // len - is the number of bytes to send // timeout - is multiples of 8mS asm { cli // I need exact timing (2) mov dx,37ah // parallel control port (4) mov di,data // get offset of data (21) mov bx,len // initialize data counter (21) mov si,0 // set up time out timer (4) mov cx,timeout // intialize timeout counter (21) } Data_Len_Loop: asm { dec si // dec timeout LSW counter (3) jne Wait_For_Data // LSW timeout loop (4,16) dec cx // dec timeout MSW counter (3) je P_DV_1 // jmp for time out error (4,16) } Wait_For_Data: asm { in al,dx // read PIO control port (8) test al,8 // isolate dataout bit (4) je Data_Len_Loop // wait for start bit (4,16) mov cl,0 // cl holds received data (4) mov ch,9d // data + start bit (4) } P_DV_1: asm mov ax,data_valid // start 26uS delay (21) DV_1: asm { dec ax // (4) clock delay jne DV_1 // (4,16) total delay = 20*delay_valid shr cl,1 // get ready for next bit (2) in al,dx // read start bit (8) shl al,4 // make msb (11) and al,80h // isolate dataout bit (4) or cl,al // add bit to cl (3) mov ax,data_valid // start 26uS delay (21) } DV_2: asm { dec ax // (4) clock delay jne DV_2 // (4,16) total delay = 20*delay_valid dec ch // 10 bits (3) jne P_DV_1 // read next bit (4,16) not cl // invert bits (3) mov ds:[di],cl // save data (20) inc di // data++ (3) mov ax,data_valid // sync up to next bit (21) } DV_3: asm { dec ax // ~25uS counter (3) jne DV_3 // 25uS conunter loop (4,16) dec bx // len-- (3) mov si,0 // setup timeout counter (4) mov cx,timeout // (21) jne Data_Len_Loop // loop back to get more bytes (4,16) sti // enable interrupts // maximum time interrupts disabled <200ms } } //************************************************************************ //** send_data(data,len) ** //** This routine sends len bytes of data over the atari SIO.** //** The data is sent from the buffer pointed to by 'data'. ** //** The command line is not toggled. ** //** ** //** Inputs: ** //** data - A near pointer to the data buffer memory. ** //** len - The nember of bytes to be sent, including ** //** the checksum byte. The checksum byte must ** //** be computed by the caller. ** //** Outputs: ** //** data - The data read from the serial bus is stored ** //** in this memory space. These two bytes ** //** are the 'acknowledge' and 'complete' bytes. ** //** This is so that the calling routine can ** //** can determine if the transfer was successful. ** //** ** //** Routines called: ** //** none. ** //** ** //** History: ** //** 07/11/94 Created M.S.M. ** //** ** //** Notes: ** //** 1. The timing in this routine is critical. Changes ** //** should be made with extreme care. ** //** ** //** 2. The numbers in parenthesis are the 286 clock cycles ** //** per instruction. ** //************************************************************************ void send_data(char *data, int len) { asm { cli // I need exact timing (2) mov dx,37ah // parallel control port (4) mov di,data // get offset of data (21) mov si,len // number of byte to send (21) xor bh,bh // clear bh (3) } Data_Len_Loop: asm { mov bl,[di] // get send data from memory (21) not bl // invert the data (3) stc // set start bit (2) rcl bx,2 // align data with proper pin (7) mov cx,10d // data + start and stop bits (4) } Read_Next_Bit: asm { mov ax,data_valid // 26uS delay (21) } DV_1: asm { dec ax // (4) clock delay jne DV_1 // (4,16) jmp to finish delay mov al,bl // get data (3) and al,2 // get data bit (4) or al,4 // rdy set (4) out dx,al // send it out (10) shr bx,1 // get next bit (2) mov ax,data_xmit // 26uS delay } DV_2: asm { dec ax // (4) clock delay jne DV_2 // (4,16) jmp to finish delay dec cx // 10 bits (3) jne Read_Next_Bit // read next bit (4,16) mov ax,data_valid // 26uS delay (21) } DV_3: asm { dec ax // (4) clock delay jne DV_3 // (4,16) jmp to finish delay inc di // data++ dec si // len-- (3) jne Data_Len_Loop // get bytes (4,16) sti // enable interrupts // maximum time interrupts disabled <200ms } get_bytes(data,2,5); } //************************************************************************ //** send_frame(data) ** //** This routine sends 5 bytes of data over the atari SIO. ** //** The data is sent from the buffer pointed to by 'data'. ** //** The command line is toggled. ** //** --- ** //** Inputs: ** //** data - A near pointer to the data buffer memory. ** //** ** //** Outputs: ** //** data - The data read from the serial bus is stored ** //** in this memory space. These two bytes ** //** are the 'acknowledge' and 'complete' bytes. ** //** This is so that the calling routine can ** //** can determine if the transfer was successful. ** //** ** //** Routines called: ** //** none. ** //** ** //** History: ** //** 07/11/94 Created M.S.M. ** //** ** //** Notes: ** //** 1. The timing in this routine is critical. Changes ** //** should be made with extreme care. ** //** ** //** 2. The numbers in parenthesis are the 286 clock cycles ** //** per instruction. ** //************************************************************************ void send_frame(char *data) { asm { cli // I need exact timing (2) mov dx,37ah // parallel control port (4) mov di,data // get offset of data (21) mov si,5 // number of byte to send (21) xor bh,bh // clear bh (3) } Data_Len_Loop: asm { mov bl,[di] // get send data from memory (21) not bl // invert the data (3) stc // set start bit (2) rcl bx,2 // align data with proper pin (7) mov cx,10d // data + start and stop bits (4) } Read_Next_Bit: asm { mov ax,data_valid // 26uS delay (21) } DV_1: asm { dec ax // (4) clock delay jne DV_1 // (4,16) jmp to finish delay mov al,bl // get data (3) and al,2 // get data bit (4) or al,5 // rdy and command set (4) out dx,al // send it out (10) shr bx,1 // get next bit (2) mov ax,data_xmit // 26uS delay } DV_2: asm { dec ax // (4) clock delay jne DV_2 // (4,16) jmp to finish delay dec cx // 10 bits (3) jne Read_Next_Bit // read next bit (4,16) mov ax,data_valid // 26uS delay (21) } DV_3: asm { dec ax // (4) clock delay jne DV_3 // (4,16) jmp to finish delay inc di // data++ dec si // len-- (3) jne Data_Len_Loop // get bytes (4,16) sti // enable interrupts // maximum time interrupts disabled <200ms } } //************************************************************************ //** frame_getdata(drive,command,aux1,aux2,timeout,data,len) ** //** This routine sends a command frame over the atari SIO ** //** and expects to 'len' bytes of receive data back. ** //** ** //** Inputs: ** //** drive - Atari disk drive # 1 through 8. (usually 1) ** //** command - One of the atari SIO commands: ** //** Read 0x52 ** //** Write 0x57 ** //** Status 0x53 ** //** Put 0x50 ** //** Format 0x21 ** //** etc. ** //** aux1, aux2 - Same as SIO: value depends on command sent ** //** timeout - Wait for response 'timeout'*8mS ** //** data - A near pointer to the data buffer memory. ** //** len - The nember of bytes to be received, including ** //** the checksum byte. ** //** ** //** Outputs: ** //** data - The data read from the serial bus is stored ** //** in this memory space. The first two bytes ** //** are the 'acknowledge' and 'complete' bytes. ** //** This is so that the calling routine can ** //** can determine if the transfer was successful. ** //** ** //** Routines called: ** //** send_frame() ** //** get_bytes() ** //** ** //** History: ** //** 07/11/94 Created M.S.M. ** //** ** //************************************************************************ int frame_getdata(char drive, char command, char aux1, char aux2, int timeout, char *data, int len) { int check = 0; outportb(0x37a,4); data[0] = 0x30+drive; // drive 1 data[1] = command; // command data[2] = aux1; // aux1 data[3] = aux2; // aux2 for (int i=0;i<4;i++) { check += data[i]; if (check>255) check -= 255; } data[4] = check; // puts("sending command frame"); outportb(0x37a,0x05); asm {mov ax,200;} loop1: asm {dec ax; jne loop1} send_frame(data); // send command frame asm {mov ax,100;} loop2: asm {dec ax; jne loop2} outportb(0x37a,0x04); get_bytes(data,len+2,timeout); // for (i=0;i255) check -= 255; } data[4] = check; // puts("sending command frame"); outportb(0x37a,0x05); asm {mov ax,200;} loop1: asm {dec ax; jne loop1} send_frame(data); // send command frame asm {mov ax,100;} loop2: asm {dec ax; jne loop2} outportb(0x37a,0x04); get_bytes(data,1,timeout); send_data(buf,lenbuf); // printf("%x %x\n",data[0],data[1]); return (data[0]==0x41); } //************************************************************************ //** Main routine to copy an atari disk to the IBM harddrive. ** //** ** //** ** //** ** //** ** //************************************************************************ void main(int argc, char **argv) { //**************Check for required command line inputs************* if (argc<2) { puts("**This program copy and entire atari disk to an ibm image file.**"); puts("usage:siocopy out_filename < -s, -e -d, or -2> "); puts("-s single sided single density"); puts("-e single sided enhanced density"); puts("-d double sided double density"); puts("If sio rate not given, 19200 bits per second is used"); exit(0); } // genreal purpose variables unsigned i, check; // conatins the sio data rate double siorate; //******* The data rate is adjustable to account for slightly ******** //******* different data rates on different atari disk drives.******** // get sio rate from command line or use default if (argc>3) siorate = _atold(argv[3]); else siorate = 19200.0; // Estimate the number of loop counts in 1/2 the period of the sio // data rate. printf("--- calibrating SIO data rate to %lg hz---\n", siorate); calibrate(siorate); data_xmit = data_valid; // used for debug outportb(0x37a,4); // initialize PIO lines // Try to read the disk drive status if (frame_getdata(1,0x53,0,0,10,data, 5)==0) // get four byte of status { // plus one checksum byte puts("Disk drive not responding: be sure the drive is connected and turned on."); exit(0); } // open IBM file FILE *fout = fopen(argv[1],"wb"); // Determine the atari disk drive type int num_sect = 720, num_bytes = 128, read = 'R'; puts("reading atari disk drive #1"); // if no drive specification is given, assume SSSD if (argc>2) { argv[2]++; switch(*argv[2]) { // The user thinks it is an enhanced density disk. Test it to // be sure. case 'e': case 'E': // Try to configure the disk drive if (frame_senddata(1,0x4f,0,0,40,data,d1050,13)==0) { // disk is not configurable, maybe its a true 1050. // Use the 1050 read command (I think this is correct). read = 0x24; num_sect = 1040; break; } // Read sector 1 from the disk frame_getdata(1,'R',1,0,40,data,129); delay(500); // wait for rest of data if 256 byte/sector //Read confiuration record frame_getdata(1,0x4e,0,0,40,data,13); // Did it set up properly? if (data[7]!=0x04) { puts("Not an enhanced density disk."); num_sect = 720; } else num_sect = 1040; break; // Double density double sided case. //************************************************************* //*** This does not work. I do not know the proper format **** //*** for the double density case. **** //************************************************************* case 'd': case 'D': if (frame_senddata(1,0x4f,0,0,40,data,xf551,13)==0) {puts("This disk drive is not configurable."); exit(0);} //Read sector 1 to test data frame_getdata(1,'R',1,0,40,data,129); delay(500); // wait for rest of data if 256 byte/sector // get configuration record. frame_getdata(1,0x4e,0,0,40,data,13); // for (i=0;i<13;i++) printf("%x ",data[i+2]); // puts(""); // Check if we have 256 byte sectors if (data[8] == 1) num_bytes = 256; else { puts("not double density"); num_bytes = 128; } // Check if we have a two sided disk drive if (data[6] == 1) num_sect = 1040; else { puts("not double sided"); num_sect = (data[7]==0)?720:1040; } break; // Single sided single density case (atari 810) case 's': case 'S': num_sect = 720; frame_senddata(1,0x4f,0,0,10,data,d810,13); break; default: puts("Drive type must be one of the following: <-s, -e, or -d>"); exit(0); } } //read all the sectors for (int sect=1;sect<=num_sect;sect++) { printf("reading sector %d\r", sect); if (frame_getdata(1,read,sect&0xff,sect>>8,2,data,num_bytes+1)==0) puts("sector not read"); // verify checksum check = 0; for (int k=0;k255) check -=255; } if (check!=data[num_bytes+2]) {printf("checksum error at sector %d.\n",sect); puts("Bad disk, copy protected disk or wrong disk type"); exit(0); } // write the atari disk data to the ibm file fwrite(&data[2],num_bytes,1,fout); } fclose(fout); }