` DISK STRUKTURE As we all know disks contain tracks,data on the disk is stored on those tracks. There are 80 (official) cylinders on the disk, each cylinder containing two tracks (upper and lower). When we number things as 0-79, we really talk about cylinders, but usualy this numbering is called track numbering. The system numbers tracks 0-159 and numbering starts from lower side, where is track 0, track 1 is really cylinder 0 upper side. Data on these tracks is usually MFM-decoded. Each track contains 11 sectors of 512 bytes of data. Each sector has a header, which tells us track number, sector number and some additional data. Contents of normal DOS-format track is; ...(gap)...(track)...(gap)... (gap) is normally 0-bytes decoded into MFM. 00 -> AAAA (track) contains 11 sectors which are: (00)(00)(sync)(sync)(header)(fill)(headerchecksum)(datachecksum)(data) (00) is zero byte (in MFM AAAA) (sync) is (A1)-bytes converted to MFM and clock pulse is dropped so that result is 4489, which is standard syncword. Any data never converts into this pattern (in MFM) (header) is sector header. More later. (fill) is 16 bytes of zeros. These are intended for use by dos, but dos doesn't use them. (headerchecksum) is checksum of header info. Checksum is calculated using exclusive-or and contains only databits. (datachecksum) is same for data. (data) is 512-byte data block. Sector header consists from; (format)(track)(sector)(length) (format) is FF for normal format (track) is track number (sector) is sector number (length) is count of sectors before gap (end of track) This stuff is converted into MFM before writing and is is done as follows: (sector header) is converted as one longword (fill) is converted as one 16-byte block (checksums) are converted as longword (data) is converted as one 512-byte block MFM conversion is in the following principle; Take two data bits and add one clock bit between them. Clock bit is 1, if both data bits are 0 otherwise 0. so 01001110 goes to; C0C1C0C0C1C1C1C0 -> 0001001001010100 Each byte converts into a word. As streching bytes is quite hard to do on the Amiga, data is first split into two halves; odd and even. First even bits are converted and then odd ones. So in Amiga: 01001110 -> 0011 1010 even odd C0C0C1C1C1C0C1C0 -> 0010010101000100 This lead into faster operation, because extracting odd and even halves can be done by logical ops. Even_half = ((data & 0xAAAA) >> 1) Odd_half = (data & 0x5555) And values for clock bits are: clock_bit = not( previous_bit or next_bit ) So Result = ( ~( (Half>>1) | (Half<<1) ) ) | Half Conversion of one block (in Amiga style) can be done with following code: ; Input: ; a0 - address of data to be converted ; a1 - address of destination block ; d0 - length of block (in bytes) ; d1 - continuation flag (is end of previous block ok) ; ; Result: ; decoded data in destination buffer ; checksum in d0 ; d2,d3,d4 also used _encodeblock: movem.l d2-d4,-(sp) ; save working regs clr.l d4 ; initialize checksum move.l d1,-(sp) ; flag is saved move.l d0,d1 ; copy length into index move.l a1,-(sp) ; save start of dst buffer asr.l #1,d1 ; create word count bra 2$ ; Copy loop first 1$: move.w (a0)+,d2 ; pick word from src move.w d2,0(a1,d0) ; put odd bits into latter half ... asr.w #1,d2 ; ... and even bits ... move.w d2,(a1)+ ; ... into first half 2$: dbra d1,1$ ; done ; Now convert from dst to dst one word at time move.l (sp)+,a1 ; restore dst address move.l (sp)+,d1 ; pick flag beq 4$ ; no continuation move.l -2(a1),d1 ; or previous block before this one bra 4$ ; last word is picked from there ; conversion loop as each word of source ; converts into longword we convert length count of words 3$: move.w (a1),d1 ; take first word to convert and.l #$55555555,d1 ; pick bits to convert move.l d1,d2 ; copy longword in reg ... move.l d1,d3 ; ... twice into work regs ... asr.l #1,d2 ; other is shifted right add.l d3,d3 ; and other left or.l d3,d2 ; combine these not.l d2 ; and complement or.l #$AAAAAAAA,d1 ; turn all clocks up and.l d2,d1 ; turn some of them down move.w d1,(a1)+ ; put resulting word into buffer eor.w d1,d4 ; update checksum swap d4 ; swap checksum swap d1 ; push last word into upper half 4$: dbra d0,3$ ; and here we go for next word swap d4 ; take checksum move.l d4,d0 ; put into result reg and.l #$55555555,d0 ; and drop clock bits movem.l (sp)+,d2-d4 ; restore regs rts ; and retrun into caller In real life this is done with the blitter, which may very well be slower if faster processors than normal are used. We need four blits for each block and for tiny blocks this is slower. But for large blocks, situaion is something else because blitter ops are effectively parallel with processor. Decoding a block is even easier and can be done with one blit. Very quick, except for single longword (as header). Decoding in principle is; data = (odd_word & 0x5555) | ((even_word & 0x5555)<<1) And can be done with following code ; long decodeblock( char * from, char * to, int len ) ; a0 a1 d0 _decodeblock: movem.l d2-d4,-(sp) ; save working regs clr.l d4 ; init checksum move.l d0,d1 ; save src legth for indexing asr.l #2,d0 ; src bytecount to dst wordcount bra 2$ 1$: move.l (a0)+,d2 ; get even_word and.l #$55555555,d2 ; drop clockbits eor.l d2,d4 ; update checksum add.l d2,d2 ; shift left move.l -4(a0,d1),d3 ; get odd_word and.l #$55555555,d3 ; drop clocks eor.l d3,d4 ; update checksum or.l d3,d2 ; combine words move.l d2,(a1)+ ; save results 2$: dbra d0,1$ ; count words add.l d1,a0 ; update pointer move.l d4,d0 ; put checksum in result reg movem.l (sp)+,d2-d4 ; restore working regs rts ; go back where you belong ; Usually disks are decoded as MFM and standard sync word is used. There is no actual reason for this, but it is so. {2{A O T H E R T O P I C S {1 Length of a normal track is; 2 (zeros) 2 (syncs) 4 (header) 16 (zeros) 4 (header checksum) 4 (data checksum) +512 (data) ---- 544 bytes (sector) * 11 (sector count) ---- 5984 bytes (normally) * 2 ( factor from MFM-decode) ----- 11968 bytes in MFM As each track is circular, between track end and track start there is gap, which is usually about 670 bytes. Normal variations to normal track format are; S I N G L E S E C T O R Track contains only one sector. Length of sector is about 11968, so it must be correctly found in order to copy it. N O N E M P T Y G A P Normally the gap is empty, but somebody may store some id data on it. L O N G T R A C K More data than normally is put into the track. Length of data is more than 11968 + gap. It is (oficially) impossible write these kind of disks on the Amiga (without extra hardware), but the Amiga is able to read them. Some other variations also exist and more are to come. With visual mode, it is possible to look at a track closely and analyze its contents (in your head). Normal track usually looks as follows: (end of track)(gap)(track)(gap) (start of track) In order to copy this correctly, end of write should be positioned on the latter gap. As we read little more than two track lengths into the buffer, we ensure that track is in buffer at least once. What we need to find is the position where the write originally terminated and use the same position. Sounds simple, but for normal track do the following; At the beginning, you are positioned at the last sync on the buffer. You know the thing here: Position and alignment. Search the syncs towards the start, until the alignment changes. This usually means that you have just passed the gap. Move back to the previous sync (alignment changes back) and put an end of write about 100 bytes before sync. Sometimes alignment stays the same for all syncs. This means that the length of physical track happens to be an even multiple of 8 (not too likely). You must recognize the gap from the destination between two consecutive syncs. Track syncs are about 1088 bytes from each other and a longer distance means a gap. This all applies to normal tracks, but usually you don't need to copy normal tracks with visual mode. On some disks, when dirty gaps are used, you should recognize gap and decide how to copy it correctly. Also, if index sync is needed, you should usually use visual mode. Always write a whole track (writing less than about 12500 bytes may leave some old contents visible). A track is circular, don't forget it. When writing more than about 12500 bytes, the last written stuff overwrites the first written data. This is usually wanted, because we normally write: (pregap)(track)(postgap) where pregap is partly overwritten with postgap. We don't exactly know how much, because of variations in drive speeds etc.