/* RT-11 Adapter Package for CP/M Rev. 1.0 -- July 1980 Rev. 1.1 -- March 1981 consisting of adding a valid system date word to all files placed on the RT-11 disk and putting the volume ID on a disk when the directory is initialized. This will keep RT-11 versions later than V02C from choking. copyright (c) 1980, William C. Colley, III This group of functions implements enough of RT-11 to allow the rest of the package to work. The functions are built and named as per the RT-11 Software Support Manual for version 2C of RT-11. */ #include "RT11.H" /* Routine to look up an RT-11 file. The routine accepts a file name as an array of int and returns 0 if the file was not found, 1 otherwise. The length of the file can be extracted thru dir_pointer, and the starting block of the file appears in file_start. */ lookup(file_name) int *file_name; { usrcom(); return dleet(file_name); } /* Routine to rename an RT-11 file. Pass the old and new names. The routine returns 0 if the file doesn't exist, 1 otherwise. */ rename(old_file,new_file) int *old_file, *new_file; { char i; usrcom(); if (!dleet(old_file)) return 0; for (i = 0; i < 3; i++) _putword(dir_pointer,i+1,new_file[i]); *dir_pointer = 0; *(dir_pointer + 1) = TENTAT; clocom(new_file); return 1; } /* Routine to delete an RT-11 file. Pass the routine the file name as an array of int. If the file does not exist, the routine returns 0, else it returns 1. */ delete(file_name) int *file_name; { usrcom(); if (!dleet(file_name)) return 0; *dir_pointer = 0; *(dir_pointer + 1) = EMPTY; consol(0); segrw(WRITE,current_segment); return 1; } /* Routine to build a tentative entry in the directory. Routine requires a file name in an array of int, and the size of the file required. The routine returns 0 if the entry is not possible, or the first block of the file if it is. If the entry fails, the appropriate diagnostic is printed. */ enter(file_name,size) int *file_name, size; { char *save_pntr, i; unsigned ret_value; usrcom(); do { retry: consol(0); while (entry(EMPTY)) { if (size <= _getword(dir_pointer,4)) { save_pntr = dir_pointer; ret_value = file_start; while (entry(EMPTY)) incr1(); if (dir_pointer > &directory.entries[1000] - directory.extra_bytes) { ret_value = current_segment; if (extend()) { blkchk(ret_value); goto retry; } puts("\nError -- directory full\n"); return 0; } dir_pointer = save_pntr; _expand(dir_pointer); *dir_pointer++ = 0; *dir_pointer++ = TENTAT; for (i = 0; i < 3; i++) { _putword(dir_pointer,i,file_name[i]); } _putword(dir_pointer,3,size); _putword(dir_pointer,5,sysdate); incr1(); _putword(dir_pointer,3, _getword(dir_pointer,3) - size); segrw(WRITE,current_segment); return ret_value; } incr1(); } } while (nxblk()); puts("\nError -- no large enough gaps\n"); return 0; } /* Routine to extend an RT-11 directory by splitting a directory segment. The routine returns 0 if no directory segment is available, 1 otherwise. */ extend() { struct dirseg temp_seg; int t, newseg; emt_375(READ,0,2,temp_seg); if (temp_seg.highest_segment >= temp_seg.total_segments) return 0; newseg = temp_seg.highest_segment; temp_seg.highest_segment++; emt_375(WRITE,0,2,temp_seg); movmem(directory,temp_seg,1024); blkchk(current_segment); for (t = (1024 - 10) / (14 + directory.extra_bytes); t > 0; t--) incr1(); *dir_pointer = 0; *(dir_pointer + 1) = EMPTY; temp_seg.next_segment = directory.next_segment; directory.next_segment = newseg; segrw(WRITE,current_segment); temp_seg.first_block = file_start; movmem(temp_seg,directory,10); movmem(temp_seg.entries + (dir_pointer - directory.entries), directory.entries, directory.entries + 1024 - dir_pointer); segrw(WRITE,newseg); current_segment = newseg; blkchk(newseg); return 1; } /* Routine to close an RT-11 file. The file name comes over in radix 50 in the array file_name. The routine returns 0 if no file by that name was around to close, 1 otherwise. */ klose(file_name) int *file_name; { char i; blkchk(1); do { while(entry(TENTAT)) { for (i = 0; i < 3; i++) { if (_getword(dir_pointer,i+1) != file_name[i]) break; } if (i == 3) { clocom(file_name); return 1; } incr1(); } } while (nxblk()); return 0; } /* Routine that continues close and rename operations. */ clocom(file_name) int *file_name; { char *tdptr; int tseg; tdptr = dir_pointer; tseg = current_segment; if (dleet(file_name)) { *dir_pointer = 0; *(dir_pointer + 1) = EMPTY; if (tseg != current_segment) { consol(0); segrw(WRITE,current_segment); } } blkchk(tseg); dir_pointer = tdptr; *tdptr++ = 0; *tdptr = PERM; consol(0); segrw(WRITE,current_segment); } /* Routine to get an RT-11 file name from the console. The name is packed into the int array file_name in radix 50. The routine returns 0 if the file name is not legal, 1 otherwise. */ getfd(file_name) int *file_name; { char c, i, j, name[20], *pntr; pntr = gets(name); file_name[0] = file_name[1] = file_name[2] = 0; for (i = 0; i < 2; i++) { for (j = 0; j < 3; j++) { c = *pntr; if (c == '.' || c == '\0') c = ' '; else pntr++; if ((c = ator50(c)) == 255) return 0; file_name[i] = file_name[i] * 050 + c; } } if (*pntr == '.') pntr++; for (i = 0; i < 3; i++) { c = *pntr; if (c == '\0') c = ' '; else pntr++; if ((c = ator50(c)) == 255) return 0; file_name[2] = file_name[2] * 050 + c; } return 1; } /* Routine to set up an RT-11 file I/O operation. */ usrcom() { current_segment = 0; blkchk(1); } /* Routine to scan the directory for a file of a specified name. The routine is passed the file name in radix 50 as an array of int. The routine returns 0 if the file is not found, 1 otherwise. */ dleet(file_name) int *file_name; { char i; blkchk(1); do { while(entry(PERM)) { for (i = 0; i < 3; i++) { if (_getword(dir_pointer,i+1) != file_name[i]) break; } if (i == 3) return 1; incr1(); } } while (nxblk()); return 0; } /* Routine to get the next directory segment into core. The routine returns 0 if no next segment exists, 1 otherwise. */ nxblk() { if (directory.next_segment == 0) return 0; blkchk(directory.next_segment); return 1; } /* Routine to find out if the requested directory segment is in core. If it isn't, the segment is read in. */ blkchk(segment) int segment; { if (segment != current_segment) { current_segment = segment; segrw(READ,segment); } dir_pointer = directory.entries; file_start = directory.first_block; } /* Function to read/write a directory segment. Parameters passed are the directory segment desired, and a read/write flag as per emt_375. */ segrw(read_write,segment) int segment; char read_write; { emt_375(read_write,segment * 2 + 4,2,directory); } /* Routine to find a specified file type in a directory segment. The routine starts from the current value of dir_pointer, and returns either 0 if the type isn't found or 1 if it is. */ entry(type) char type; { char t; while (1) { if ((t = *(dir_pointer + 1)) == type) return 1; if (t == ENDSEG) return 0; incr1(); } } /* Routine to increment the directory pointer to the next entry in the directory. */ incr1() { file_start += _getword(dir_pointer,4); dir_pointer += 14 + directory.extra_bytes; } /* Routine to compress the stray tentatives and empties out of a directory segment. The file name file_name gives a tentative file that is to be exempt from the compression (i. e., there is only one active channel). If no files are to be exempt, pass 0 for file_name. */ consol(file_name) int *file_name; { char i, *next; dir_pointer = directory.entries; while (entry(TENTAT)) { if (file_name != 0) { for (i = 0; i < 3; i++) { if (_getword(dir_pointer,i+1) != file_name[i]) break; } } else i = 0; if (i != 3) { *dir_pointer = 0; *(dir_pointer + 1) = EMPTY; } incr1(); } dir_pointer = directory.entries; while (entry(EMPTY)) { next = dir_pointer + 14 + directory.extra_bytes; if (*(next + 1) == EMPTY) { _putword(dir_pointer,4,_getword(next,4) + _getword(dir_pointer,4)); _squeez(next); continue; } if (_getword(dir_pointer,4) == 0) { if (*(dir_pointer - 13 - directory.extra_bytes) == PERM) { _squeez(dir_pointer); continue; } } incr1(); } blkchk(current_segment); } /* This routine emulates the block read/write EMT call (#375). You pass the routine a starting block number, a block count, and an appropriate core buffer. The routine reads or writes the blocks and returns. A read is done if the read/write flag has value READ, and a write is done if the read/write flag is WRITE. Each block is 512 bytes. */ emt_375(read_write,start_block,block_count,core_buffer) int start_block, block_count; char read_write, *core_buffer; { char i; while (--block_count >= 0) { for (i = 0; i < 4; i++) { bios(SEL_DSK,1); bdos(SET_DMA,core_buffer); _find(start_block,i); bios(read_write,0); bdos(SET_DMA,DMA_ADDR); bios(SEL_DSK,0); core_buffer += 128; } start_block++; } } /* Internal function to map an RT-11 block number and sector number inside the block into a physical sector number. The sectors within a block are numbered from 0 to 3. */ _find(block,sector) int block; char sector; { int r, s, t; r = (block - 6) * 2 + (sector >> 1); sector &= 001; t = (r + 12) / 13; r = (r + 12) % 13; s = t * 6 + r * 4 + (r > 6 ? 1 : 0); if (sector) s += (r == 6 ? 3 : 2); t++; bios(SET_TRK,t); bios(SET_SEC,s % 26 + 1); } /* Internal routine to extract a word from a core buffer that is an array of char. The array is passed, as is the word number desired. The function returns the word. */ _getword(buffer,word_number) unsigned word_number; char *buffer; { buffer += word_number << 1; return *buffer++ + (*buffer << 8); } /* Internal routine to place a word into the core buffer that is an array of char. The array is passed, as is the word number desired, and the word itself. */ _putword(buffer,word_number,word) unsigned word_number, word; char *buffer; { buffer += word_number << 1; *buffer++ = word & 0xff; *buffer = word >> 8; } /* Internal routine to compress an entry out of a directory segment. The address of the entry is passed. */ _squeez(entry) char *entry; { char *next; next = entry + 14 + directory.extra_bytes; movmem(next,entry,&directory.entries[1014] - next); } /* Internal routine to splice an entry into a directory segment. The address of the disired entry is passed. */ _expand(entry) char *entry; { char *next; next = entry + 14 + directory.extra_bytes; movmem(entry,next,&directory.entries[1014] - next); } /* Routine to map a character into its radix 50 representation. The function returns the rad 50 version or 255 if no rad 50 version exists. */ ator50(ascii) char ascii; { switch(ascii) { case ' ': return 0; case '$': return 033; case '.': return 034; } ascii = toupper(ascii); if ((ascii -= '0') <= 9) return ascii + 036; if ((ascii -= ('A' - '0')) <= ('Z' - 'A')) return ascii + 1; return 255; }