;+ ; ; Program to pack/unpack RK05 images from Wilson Labs RK05-to-Zip bridge boxes. ; ; By John Wilson (no relation!). ; ; Copyright (C) 1998 by D Bit. All rights reserved. ; This program may be freely copied or modified as long as this copyright ; notice appears intact in both the source code, and the banner that's clearly ; visible every time the program begins execution. ; ; 11/04/1998 JMBW Created. ; 11/14/1998 JMBW Inserts as well as extracts. ; ; Usage: RK05WL +/-n zip rk05 ; ; +/- + means insert (into Zip image), - means extract ; n decimal partition number (0-23) ; zip input file (96 MB raw Zip image) ; rk05 output file (2.5 MB RK05 image) ; ; Zip disk format (reverse engineered 11/04/1998 by John Wilson): ; ; Each RK05 track (12 sectors) is stored in 8 KB of space on the Zip disk. ; ; For some reason head 1 is stored before head 0, so to get contiguous data ; you have to reverse the order of each pair of 8 KB tracks. ; ; RK05 volumes start at 4 MB address multiples, so there are 24 of them in a 96 ; MB Zip disk ("Zip 100" refers to marketing MBs, not real MBs). ; ; Sectors start at offsets (into the 8 KB track) that are multiples of 0290h. ; RK05s are hard-sectored so the sector preamble begins at these fixed offsets, ; there's no need to search for them. ; ; Each sector contains the decoded head data for that sector, with the bits ; serialized left-to-right (i.e. backwards!) within each byte. The data are ; as described in the RK11D documentation: ; ; 13. words ;preamble, approximately 13 words of zeros ; ;(N.B. the first byte always seems to be ; ;non-zero on Wilson Labs disks, no idea whose ; ;fault that is but skip a few bytes to be safe) ; 1 bit ;sync bit, the first 1 after the preamble ; 1 word ;header word, contains cylinder number (only, ; ;no head or sector) shifted left 5 bits ; 256. words ;sector data ; 1 word ;checksum, sum of the 256 16-bit data words ; 1 word ;postamble, all zeros ; (junk until the end of the 0290h sector data) ; ; So to find a sector, you compute a starting disk address like this: ; addr = (volume * 4 MB) + (cyl * 16 KB) + ((NOT head) * 8 KB) + (sec * 0290h) ; ; Add a little to addr to skip the non-zero junk at the beginning of the ; preamble (I skip 8 words just to be safe), then search for the first non-zero ; bit. Immediately following that bit is the 16-bit header word and the sector ; data/checksum/postamble. ; ;- .radix 8 ; locals ;(requires TASM!) ; lf= 12 cr= 15 ; secsiz= 0290h ;size of sector dump on disk ; fatal macro txt ;fatal error local a,b call err1 a db b,txt b= $-a-1 endm ; code segment assume cs:code org 100h start: cld ;DF=0 ; parse partition # from command line mov si,80h ;point at JCL lodsb ;get length cbw ;AH=0 mov cx,ax ;copy call getw ;get +n or -n jc @@5 mov al,[bx] ;get + or - inc bx dec dx ;(count it) jnz @@1 ;number is stuck to + or - push ax ;save call getw ;parse number pop ax ;[restore] jc @@5 @@1: cmp al,'+' ;inserting? je @@2 cmp al,'-' ;extracting? jne @@5 ;no @@2: mov ds:func,al ;save xor di,di ;init partition # @@3: mov al,[bx] ;get a char inc bx sub al,'0' ;convert to binary if digit cmp al,9d ;digit? ja @@5 ;no, invalid cbw ;AH=0 xchg ax,di ;save, get old number push dx ;save mov dx,10d ;multiplier mul dx ;make space for new digit pop dx ;restore add di,ax ;add it in dec dx ;loop through all chars jnz @@3 mov ds:part,di ;save ; get Zip image filename call getw ;get it jc @@5 xchg bx,dx ;switch ptr, length add bx,dx ;point past end xor al,al ;load 0 xchg al,[bx] ;mark end, save old char push ax ;save mov ax,3D00h ;func=open /RONLY cmp byte ptr ds:func,'+' ;inserting? jne @@4 inc ax ;func=open /WONLY @@4: int 21h ;open the file jc @@6 mov ds:ziphnd,ax ;save handle pop ax ;catch terminating char mov [bx],al ;restore ; get RK05 image filename call getw ;get it jnc @@8 @@5: mov dx,offset usage ;point at string mov ah,09h ;func=print int 21h int 20h ;exit @@6: fatal '?Zip image open error' @@7: fatal '?RK05 image open error' @@8: xchg bx,dx ;switch ptr, length add bx,dx ;point past end mov byte ptr [bx],0 ;mark end mov ax,3D00h ;func=open /RONLY cmp byte ptr ds:func,'-' ;extracting? jne @@9 mov ah,3Ch ;func=create xor cx,cx ;mode=default @@9: int 21h ;open/create the file jc @@7 mov ds:rkhnd,ax ;save handle ; seek to beginning of partition mov ax,ds:part ;get it mov cl,6 ;total left shift = 22 bits sal ax,cl mov cx,ax ;CX:DX = offset from BOF xor dx,dx mov bx,ds:ziphnd ;handle mov ax,4200h ;func=seek int 21h jnc @@10 ;success fatal '?Seek error' @@10: cmp byte ptr ds:func,'-' ;extract? je read ;yes, go read RK05 data out of Zip image ;jmp short write ;write RK05 data into Zip image ; write: ; write disk into Zip image mov word ptr ds:header,0 ;init header @@1: ; read a cylinder in mov dx,offset fmtbuf ;point at buf mov cx,12d*512d*2 ;size of data mov bx,ds:rkhnd ;handle mov ah,3Fh ;func=read int 21h jc @@2 ;error cmp ax,cx ;read all? je @@3 @@2: fatal '?Read error' @@3: ; initialize cyl data to 0 mov di,offset cylbuf ;point at cyl data xor ax,ax ;load 0 mov cx,16384d/2 ;word count rep stosw ;init to all zeros ; pack cyl mov dx,offset fmtbuf ;point at formated buf mov di,offset cylbuf+8192d ;side 0 comes *second* call pactrk ;pack track 0 mov di,offset cylbuf ;point at side 1 call pactrk ;pack track 1 ; write a cylinder out mov dx,offset cylbuf ;point at buf mov cx,8192d*2 ;length of sector data mov bx,ds:ziphnd ;handle mov ah,40h ;func=write int 21h jc @@4 ;error cmp ax,cx ;got it all? je @@5 ;yes @@4: fatal '?Write error' @@5: add word ptr ds:header,40 ;bump to next cyl cmp word ptr ds:header,200d*40 ;done all? ;(use 202d*40 if including maint tracks) jb @@1 ;loop if not jmp short exit ;done ; read: ; read disk from Zip image mov word ptr ds:header,0 ;init header @@1: ; read a cylinder in mov dx,offset cylbuf ;point at buf mov cx,8192d*2 ;length of sector data mov bx,ds:ziphnd ;handle mov ah,3Fh ;func=read int 21h jc @@2 ;error cmp ax,cx ;got it all? je @@3 ;yes @@2: fatal '?Read error' @@3: mov di,offset fmtbuf ;point at output buf mov dx,offset cylbuf+8192d ;side 0 comes *second* call unptrk ;unpack track 0 mov dx,offset cylbuf ;point at side 1 call unptrk ;unpack track 1 ; write a cylinder out mov dx,offset fmtbuf ;point at buf mov cx,12d*512d*2 ;size of data mov bx,ds:rkhnd ;handle mov ah,40h ;func=write int 21h jc @@5 ;error cmp ax,cx ;wrote all? je @@6 @@5: fatal '?Write error' @@6: add word ptr ds:header,40 ;bump to next cyl cmp word ptr ds:header,200d*40 ;done all? ;(use 202d*40 if including maint tracks) jb @@1 ;loop if not exit: mov bx,ds:ziphnd ;get Zip handle mov ah,3Eh ;func=close int 21h mov bx,ds:rkhnd ;RK05 handle mov ah,3Eh ;func=close int 21h int 20h ;+ ; ; Pack a track. ; ; es:di raw output buf ; ds:dx starting addr of track data ; ;- pactrk: mov cx,12d ;# sectors per track @@1: mov si,dx ;point at sector push cx ;save push dx push di call pacsec ;pack a sector pop di ;restore pop dx pop cx add dx,512d ;bump to next sector add di,secsiz loop @@1 ;do whole track ret ;+ ; ; Pack a sector of data. ; ; ds:si formated input data ; es:di raw head data (MSB first) ; ; Sector contents: ; ~13. words of zero (officially -- first byte is NZ in Wilson Labs disks) ; one sync bit (1, not padded out to a word) ; header word (containing cylinder number shifted left 5 bits) ; 256. data words ; 1 checksum word ; ;- pacsec: xor cx,cx ;nothing written yet xor ax,ax ;all zeros mov dx,14d*16d+1 ;write 14 words +1 bit of zeros ;(normally 13 words, but this seems to be what ;the Wilson Labs box gets most often) call putbit mov ax,1 ;write 1 bit mov dx,ax call putbit mov ax,ds:header ;get header mov dx,16d ;16 bits call putbit mov dx,256d ;# words to get mov ds:chksum,0 ;clear out checksum @@1: push dx ;save lodsw ;get a data word add ds:chksum,ax ;add it up mov dx,16d ;write 16 bits call putbit pop dx ;restore count dec dx ;done all? jnz @@1 ;loop if not mov ax,ds:chksum ;get checksum mov dx,16d ;write 16 bits call putbit xor ax,ax ;load 0 mov dx,8d ;write 8 zeros to flush out CH call putbit ;(postamble + rest of sec already cleared) ret ;+ ; ; Put DX bits from AX ; ; ch current byte ; cl # bits in current byte ; di buffer ptr ; ;- putbit: shr ax,1 ;get a bit rcl ch,1 ;into CH inc cx ;count it cmp cl,8d ;filled the byte? je @@1 dec dx ;done all? jnz putbit ;loop if not ret @@1: mov [di],ch ;save inc di xor cx,cx ;load 0 dec dx ;done all? jnz putbit ;loop if not ret ;+ ; ; Unpack a track. ; ; es:di output buf ; ds:dx starting addr of raw track data ; ;- unptrk: mov cx,12d ;# sectors per track @@1: mov si,dx ;point at sector push cx ;save push dx call unpsec ;unpack a sector pop dx ;restore pop cx add dx,secsiz ;bump to next sector loop @@1 ;do whole track ret ;+ ; ; Unpack a sector of data. ; ; ds:si raw head data (MSB first) ; es:di formated output data ; ; Sector contents: ; ~13. words of zero (officially -- first byte is NZ in Wilson Labs disks) ; one sync bit (1, not padded out to a word) ; header word (containing cylinder number shifted left 5 bits) ; 256. data words ; 1 checksum word ; ;- unpsec: add si,8d*2 ;skip 8 words (to get past non-zero junk) mov dx,10d*16d ;should find sync in 5 more words, allow 10. xor ax,ax ;init buf xor cx,cx ;nothing read yet @@1: push dx ;save mov dx,1 ;fetch 1 bit call getbit pop dx ;restore test ax,ax ;sync bit? jnz @@2 ;yes dec dx ;keep looking jnz @@1 ; sync bit isn't where it should be, don't get lost looking for it fatal '?Header not found' @@2: mov dx,16d ;found sync bit, get header word call getbit cmp ax,ds:header ;is it what we expect? jne @@4 mov dx,256d+1 ;# words to get including checksum mov ds:chksum,0 ;clear out checksum @@3: push dx ;save mov dx,16d ;get a data word call getbit stosw ;save it add ds:chksum,ax ;add it up pop dx ;restore count dec dx ;done all? jnz @@3 ;loop if not sub di,2 ;back up off checksum add ax,ax ;checksum *2 (since we counted it in CHKSUM) cmp ax,ds:chksum ;match? jne @@5 ;no ret ;happy return @@4: fatal '?Header error' @@5: fatal '?Checksum error' ;+ ; ; Get DX bits into AX ; ; ch current byte ; cl # bits left in current byte (zeros shifted in to replace ones we took) ; si buffer ptr ; ;- getbit: jcxz @@2 ;nothing left @@1: shl ch,1 ;get a bit rcr ax,1 ;into AX dec dx ;count it jz @@3 ;done all dec cl ;no, any left in byte? jnz @@1 ;yes, get next bit @@2: mov ch,[si] ;fetch next byte inc si mov cl,8d ;reinit bit count jmp short @@1 @@3: dec cl ;count final bit ret ;+ ; ; Parse a word off the command line. ; ; si cmd line ptr ; cx # chars left ; ; On return, SI/CX are updated, and: ; ; bx begn of word ; dx length ; ; Or CF=1 if nothing left on line. ; ;- getw: jcxz @@2 ;EOL already @@1: mov bx,si ;in case word starts here lodsb ;get a char cmp al,' ' ;blank or ctrl? ja @@4 ;no loop @@1 ;loop @@2: stc ;no luck ret @@3: lodsb ;get a char @@4: cmp al,' ' ;blank or ctrl? jbe @@6 ;yes, end of word cmp al,'a' ;lower case? jb @@5 cmp al,'z' ;hm? ja @@5 and al,not 40 ;yes, convert mov [si-1],al ;put back @@5: loop @@3 ;loop inc si ;compensate for next inst @@6: dec si ;unget mov dx,si ;calc length sub dx,bx ;CF=0 ret ;+ ; ; Handle fatal error. ; ;- err1: pop si ;catch R.A. call err2 ;print it mov si,offset crlf ;point at call err2 mov ax,4C01h ;func=punt int 21h ; err2: lodsb ;get length of string cbw ;AH=0 mov dx,si ;point at string mov cx,ax ;length mov bx,0002h ;STDERR mov ah,40h ;func=write int 21h ret ; crlf db 2,cr,lf ; as counted string ; usage db 'RK05WL Zip disk RK05 image insert/extract program',cr,lf db 'Copyright (C) 1998 by D Bit. All rights reserved.',cr,lf db 'This program may be freely copied.',cr,lf db cr,lf db 'Usage: RK05WL +/- vol zip rk05',cr,lf db cr,lf db '+/- function: + means insert (into Zip image to RK05)' db cr,lf db ' - means extract (from Zip image into RK05)' db cr,lf db 'vol volume number as a decimal number, 0-23',cr,lf db 'zip name of 96 MB Zip disk image file',cr,lf db 'rk05 name of 2.5 MB RK05 disk image file',cr,lf db cr,lf db '$' ; func db 1 dup(?) ;function to perform, +=insert, -=extract part dw 1 dup(?) ;partition # ziphnd dw 1 dup(?) ;Zip disk image file handle rkhnd dw 1 dup(?) ;RK05 disk image file handle chksum dw 1 dup(?) ;sector checksum header dw 1 dup(?) ;header word for this track (must mask out b4) ; cylbuf db 8192d*2 dup(?) ;raw cylinder input data fmtbuf db 12d*512d*2 dup(?) ;formated data buf dw 1 dup(?) ;(space for checksum word on last sector) ; code ends end start