ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ
struc   SampleRec
sname           db      16h dup (?)
length          dw      ?
volume          dw      ?
repeat          dw      ?
replen          dw      ?
ends    SampleRec

struc   Header
songname        db      20 dup (?)
samples         db      31*size SampleRec dup (?)
songlen         db      ?
restart         db      ?
sequences       db      128 dup (?)
mk              dd      ?
ends    Header

ModHeader       dw      0       ;; Segment of MOD Header
ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ

SigCopy         db      'PS16ŝ'
PS16            PS16Header <>
HeaderSeg       dw      0

ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ
; MOD_Open - MOD conversion routine.  Assumes you know the file is a .MOD.
;   In : CX:DX - Input MOD filename.
;        BX:SI - Output PS16 filename.
;        AX    - Memory segment(s) to do conversion (at least 64k).
ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ
ModIn           dw      0       ;; MOD input handle.
ModOut          dw      0       ;; PS16 output handle.
ModTOD          dw      0       ;; Top of data.
proc            MOD_Convert near
                push    ds es
                mov     [cs:ModTOD],ax
                ; ħħħħħ Open/create files.
                mov     ds,cx           ;; Open input MOD.
                mov     ax,3D00h
                int     21h
                jb      @@Exit
                mov     [cs:ModIn],ax
                mov     ds,bx           ;; Create output PS16.
                mov     dx,si
                mov     cx,0
                mov     ah,3Ch
                int     21h
                mov     [cs:ModOut],ax

                ; ħħħħħ Read and convert module.
                mov     ax,cs
                mov     ds,ax
                mov     es,ax
                call    MOD_ReadConvert

                ; ħħħħħ Close files.
                mov     ah,3Eh          ;; Close module
                mov     bx,[cs:ModIn]
                int     21h
                mov     ah,3Eh          ;; Close PS16
                mov     bx,[cs:ModOut]
                int     21h
@@Exit:         pop     es ds
                ret
endp            MOD_Convert

ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ
; MOD_ReadConvert - Conversion routine.  Must be called by MOD_Open.
ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ
proc            MOD_ReadConvert near
                ; ħħħħħ Read module header.
                mov     ax,[cs:ModTOD]
                mov     [cs:HeaderSeg],ax
                add     [cs:ModTOD],size Header/16+1
                mov     ds,ax
                mov     es,ax
                mov     ax,3F00h
                mov     bx,[cs:ModIn]
                xor     dx,dx
                mov     cx,size Header
                int     21h

                ; ħħħħħ Test for 15 or 31 instrument module.
                cmp     [Word ds:Header.mk],'.M'
                jnz     @@CheckFLT4
                cmp     [Word ds:2+Header.mk],'.K'
                jz      @@31Ins
@@CheckFLT4:    cmp     [Word ds:Header.mk],'LF'
                jnz     @@15Ins
                cmp     [Word ds:2+Header.mk],'4T'
                jz      @@31Ins

@@15Ins:        ; ħħħħħ Do 15 instrument conversion to 31 instrument.
                mov     si,20+15*30                ;; Copy orders, etc.
                mov     di,(offset (Header).songlen)
                mov     cx,134
                rep     movsb
                mov     di,offset Header+20+15*30  ;; Clear out 16-31 ins.
                mov     cx,16*30
                mov     al,0
                rep     stosb
                mov     ax,4200h                   ;; Reposition file ptr.
                mov     bx,[cs:ModIn]
                mov     cx,0
                mov     dx,258h
                int     21h

@@31Ins:        ; ħħħħħ Convert 31 instrument header to PS16.
                call    MOD_ConvertHeader

                ; ħħħħħ Convert all patterns to PS16 format.
                call    MOD_LoadSavePatterns

                ; ħħħħħ Convert all samples to PS16 format.
                call    MOD_LoadSaveSamples

                ; ħħħħħ Convert all instrument names to PS16 format.
                call    MOD_ConvertComments

                ; ħħħħħ Write final header out.
                mov     ax,4200h
                mov     bx,[cs:ModOut]
                mov     cx,0
                mov     dx,0
                int     21h
                mov     ax,cs
                mov     ds,ax
                mov     cx,size PS16Header
                mov     dx,offset PS16
                mov     ah,40h
                int     21h

                ret
endp            MOD_ReadConvert

ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ
; MOD_ConvertHeader - Converts the module header to a PS16 header.  Called
;                     from MOD_ReadConvert.
;   In : DS - set to segment of module header.
ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ
proc            MOD_ConvertHeader near
                pusha
                push    ds

                ; ħħħħħ Clear out PS16 Header Data.
                mov     ax,cs
                mov     es,ax
                mov     di,offset PS16
                mov     al,0
                mov     cx,size PS16Header
                rep     stosb

                ; ħħħħħ Copy Sig and Name to PS16 Header.
                mov     di,offset PS16
                mov     ax,[Word cs:SigCopy]
                stosw
                mov     ax,[Word cs:SigCopy+2]
                stosw
                mov     al,[Byte cs:SigCopy+4]
                stosb
                mov     si,(offset (Header).songname)
                mov     di,offset PS16.SongName
                mov     cx,20
                rep     movsb
                mov     al,26           ;; ^Z terminate it.
                stosb

                ; ħħħħħ Copy Song Length and Sequences to PS16Header.
                mov     al,[ds:Header.songlen]
                mov     [es:PS16.SongLen],al
                mov     si,(offset (Header).sequences)
                mov     di,offset PS16.Sequences
                mov     cx,128
                xor     ax,ax
@@SetBlock:     mov     ah,al
                jmp     @@BotLoop
@@SearchLoop:   lodsb
                stosb
                cmp     al,ah
                jg      @@SetBlock
@@BotLoop:      loop    @@SearchLoop
                mov     al,ah
                inc     al
                mov     [es:PS16.numpatterns],al

                ; ħħħħħ Copy Samples to PS16Header doing any appropriate
                ; ħħħħħ fixups as necessary.
                mov     bp,31           ;; 31 instruments.
                mov     si,(offset (Header).samples)+(offset (SampleRec).length)
                mov     di,offset PS16.Samples
@@FlipLoop:     mov     ax,[ds:si+4]    ;; Get repeat start.
                xchg    ah,al           ;; Amiga swap.
                mov     cx,ax           ;; Save.
                mov     bx,[ds:si+6]    ;; Get repeat length.
                xchg    bh,bl           ;; Amiga swap.
                add     ax,bx           ;; Add onto repeat.
                mov     dx,[ds:si]      ;; Get the length.
                xchg    dh,dl           ;; Amiga swap.
                cmp     ax,dx           ;; If the repeat+replen > length...
                jbe     @@OKRepeat
                shr     cx,1            ;; Then divide the repeat by 2.
@@OKRepeat:     shl     dx,1            ;; Multiply the length by 2.
                mov     [Word es:di+PS16Sample.length],dx  ;; Store it.
                mov     ax,[ds:si+2]    ;; Get the volume and finetune and
                xchg    ah,al           ;; swap.
                mov     [Word es:di+PS16Sample.volume],ax  ;; Store.
                shl     bx,1            ;; Multiply the rep len by 2.
                cmp     bx,2            ;; If the repeat is <= 2,
                ja      @@StoreIt
                mov     bx,0            ;; then there isn't one.
@@StoreIt:
                mov     [Word es:di+PS16Sample.replen],bx  ;; Store it.
                shl     cx,1            ;; Multiply the repeat by 2.
                mov     [Word es:di+PS16Sample.repeat],cx  ;; Store it.
                mov     [Word es:di+PS16Sample.c2freq],8448 ;; C-2 Frequency.
@@OKSample:     add     si,size SampleRec ;; Go to next MOD sample.
                add     di,size PS16Sample ;; Go to next PS16 sample.
                dec     bp              ;; T minus bp samples.
                jne     @@FlipLoop      ;; Continue if not done.

                ; ħħħħħ Write PS16 Header to file.
                mov     bx,[cs:ModOut]
                mov     ax,cs
                mov     ds,ax
                mov     cx,size PS16Header
                mov     dx,offset PS16
                mov     ah,40h
                int     21h

                pop     ds
                popa
                ret
endp            MOD_ConvertHeader

ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ
; MOD_LoadSavePatterns - Converts MOD patterns to PS16 patterns and saves
;                        them to disk.
ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ
ModPatSeg       dw      0
ModBuf3         dw      0
LastLine        db      0FFh
proc            MOD_LoadSavePatterns near
                ; ħħħħħ Allocate buffers.
                mov     ax,[cs:ModTOD]
                mov     [cs:ModPatSeg],ax       ;; 1k MOD pattern buffer.
                add     ax,1024/16
                mov     [cs:ModBuf3],ax         ;; 3K PS16 pattern buffer.
                mov     es,ax

                mov     [cs:LastLine],0FFh

                ; ħħħħħ Start loop.
                mov     cx,0

@@BlockReadLoop:; ħħħħħ Read in a pattern.
                push    cx
                mov     cx,1024
                mov     bx,[cs:ModIn]
                mov     ax,[cs:ModPatSeg]
                mov     ds,ax
                xor     dx,dx
                mov     ax,3F00h                ; Load in the block.
                int     21h

                ; ħħħħħ Set up for compression.
                mov     ax,[cs:ModBuf3] ; Clear pattern of garbage.
                mov     es,ax
                mov     di,0
                mov     cx,3072/2
                mov     ax,0
                rep     stosw
                mov     di,0
                mov     ax,0            ; Size of pattern so far.
                stosw
                mov     al,64           ; Number of lines in pattern.
                stosb

                mov     cx,0                    ; CX - Number of Channels Done.
@@ChannelLoop:  ; ħħħħħ Compress this baby!
                push    cx              ; Push Number of Channels Done.
                shl     cx,2            ; MOD channel offset - Channel*4.
                mov     si,cx
                mov     cx,0            ; Current line counter (0-63)
@@LineLoop:     push    cx              
                cmp     [Word ds:si],0  ; Is there anything at all to
                jne     @@ItsThere      ;   compress?
                cmp     [Word ds:si+2],0
                je      @@NothingSkip   ; No!  Skip compressing this line.
@@ItsThere:     xor     dh,dh
                mov     dl,cl
                dec     dl
                cmp     [cs:LastLine],dl
                jne     @@WholeLineStore
                mov     dh,10000000b
                jmp     @@SkipLineStore
@@WholeLineStore:
                mov     al,cl           ; Store down the current line.
                stosb
@@SkipLineStore:mov     [cs:LastLine],cl
                call    MOD_NoteMatcher ; Find a matching note.
                mov     bl,[ds:si]      ; Get the hi byte of instrument.
                and     bl,0F0h
                shl     bl,2            ; Shift it so it will fit in our note.
                or      al,bl           ; Put 'em together.
                or      al,dh           ; Put on whether line is continuous.
                stosb                   ;   ...and store it down.
                mov     ax,[ds:si+2]    ; Also store the lo byte of ins.
                stosw                   ;   and the special effect and data.
@@NothingSkip:  add     si,16           ; Go to next MOD pattern line.
                pop     cx              ; Pop off the line counter.
                inc     cx
                cmp     cx,64           ; Are we done?
                jne     @@LineLoop
                mov     al,-1           ; Store a -1 to signify End of Track.
                stosb
                pop     cx              ; Pop off the Channel Counter.
                inc     cx              ; Since MODs are only four channels,
                cmp     cx,4            ;   we only have four channels to
                jnz     @@ChannelLoop   ;   do.

                ; ħħħħħ Store channels 4-16 as -1's.
                mov     cx,4            ; Channel Counter.
@@CLoop:        mov     al,-1
                stosb
                inc     cx
                cmp     cx,16
                jnz     @@CLoop

                ; ħħħħħ Store the new PS16 pattern size in position 0.
                mov     bx,di           ; Save size.
                mov     cx,bx           ; Modify it to 16-byte bound.
                and     cx,0Fh          ; Are lower 4 bits set to anything?
                or      cx,cx
                je      @@OKSize
                and     bx,0FFF0h
                add     bx,16
@@OKSize:       add     [Word cs:PS16.totalPatternSize],bx
                adc     [Word cs:2+PS16.totalPatternSize],0
                mov     di,0
                mov     cx,bx           ; Size for save.
                mov     ax,bx
                stosw

                ; ħħħħħ Write the PS16 pattern to disk.
                mov     ax,[cs:ModBuf3]
                mov     ds,ax
                mov     dx,0
                mov     bx,[cs:ModOut]
                mov     ah,40h
                int     21h

                ; ħħħħħ Are we done yet?
                pop     cx
                inc     cx
                cmp     cl,[cs:PS16.numpatterns]
                jnz     @@BlockReadLoop

                ret
endp            MOD_LoadSavePatterns

ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ
; MOD_NoteMatcher - Tries to come up with an equivalent note position as per
;                   the MOD_Match table.  For instance, 1712 is 1, 56 is 60
;                   and 0 is 0.
;   In : DS:SI - MOD note.
;   Out: AX    - Note found (0-61)
ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ
MOD_Match       dw      1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,912
		dw	856,808,762,720,678,640,604,570,538,508,480,453
		dw	428,404,381,360,339,320,302,285,269,254,240,226
		dw	214,202,190,180,170,160,151,143,135,127,120,113
		dw	107,101,95,90,85,80,75,71,67,63,60,56,0
proc            MOD_NoteMatcher near
                mov     ax,[ds:si]      ; Get the note.
                xchg    ah,al           ; Amiga swap.
                and     ax,0FFFh        ; We want only the note.
                or      ax,ax           ; Is there one?
                je      @@Done          ;   NOPE!!!
                mov     bx,offset MOD_Match
                mov     cx,0            ; Note counter.
@@NoteMatcher:  cmp     ax,[cs:bx]      ; Do we have a close match?
                jae     @@Found         ;   YES!
                add     bx,2            ; Kick our pointer up by a word.
                inc     cx              ; Increment the note counter.
                cmp     cx,61           ; Are we out of notes?
		jnz	@@NoteMatcher
                mov     ax,0            ; If we made it here, then the note
                ret                     ; in question was WAY out of range.
@@Found:        mov     ax,cx           ; Return the result.
		inc	ax
@@Done:         ret
endp            MOD_NoteMatcher

ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ
; MOD_LoadSaveSamples - Loads and saves the new PS16 samples to disk.
ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ
proc            MOD_LoadSaveSamples near
                mov     cx,31
                mov     bx,offset PS16.Samples
@@DoSamples:    push    cx bx

                ; ħħħħħ Is this a sample?
                mov     cx,[Word cs:bx+PS16Sample.length]
                or      cx,cx
                je      @@Bottom

                ; ħħħħħ Load sample.
                mov     ds,[cs:ModTOD]
                mov     dx,0
                mov     bx,[cs:ModIn]
                mov     ax,3F00h
                int     21h

                ; ħħħħħ Convert sample.
                mov     es,[cs:ModTOD]
                xor     si,si
                xor     di,di
                call    MOD_ConvertSample

                ; ħħħħħ Save sample.
                mov     ds,[cs:ModTOD]
                mov     dx,0
                mov     bx,[cs:ModOut]
                mov     ah,40h
                int     21h

@@Bottom:       pop     bx
                add     bx,size PS16Sample
                pop     cx
                loop    @@DoSamples
                ret
endp            MOD_LoadSaveSamples

ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ
; MOD_ConvertSample - Converts sample to new format for better compression.
;   In : DS:SI - Pointer to input sample
;        ES:DI - Pointer to output sample
;        CX    - Number of bytes to do.
ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ
proc            MOD_ConvertSample
                push    cx
                jcxz    @@20            ; Abort if nothing to do.
                mov     ah, 0
@@10:           lodsb
                mov     bl, al
                sub     al, ah
                stosb
                mov     ah, bl
                loop    @@10
@@20:           pop     cx
                ret
endp            MOD_ConvertSample

ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ
; MOD_ConvertComments - Converts instrument names to comments.
;   In : DS:SI - Pointer to input sample
;        ES:DI - Pointer to output sample
;        CX    - Number of bytes to do.
ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ
InsText         db      'INST',22,31
proc            MOD_ConvertComments
                mov     ax,4201h        ; Get current position.
                mov     bx,[cs:ModOut]
                xor     cx,cx
                xor     dx,dx
                int     21h
                mov     [Word PS16.commentofs],ax       ; Store comment ofs.
                mov     [Word 2+PS16.commentofs],dx
                mov     ax,cs           ; Write INST text.
                mov     ds,ax
                mov     dx,offset InsText
                mov     cx,6
                mov     ah,40h
                int     21h
                mov     ds,[cs:HeaderSeg]
                mov     si,(offset (Header).samples)+(offset (SampleRec).sname)
                mov     cx,31
@@CopyIns:      push    cx
                mov     dx,si           ; Write sample name.
                mov     cx,22
                mov     ah,40h
                int     21h
                add     si,size SampleRec
                pop     cx
                loop    @@CopyIns
                ret
endp            MOD_ConvertComments
