program KCLOAD;
{$D KC-Load 0.XX (02/02)}
{$R-,S-,I-,Q-,W-,K+,G+,A+}
{$C MOVEABLE PRELOAD PERMANENT}
{$R KCLOAD.RES}
{Wünschenswerte Erweiterungen:
* OK: Auto-Detect von KC, KC-Turbo, Z1013 und BASICODE
* Hochpass-Filter: nö!
* Variable Sampleraten 11, 22, 44 kS/s: nö!
* Vollautomatik (Einlesen und Abspeichern hintereinanderweg)
* OK: grüne Aussteuerungs-Anzeige
* Motorschaltspannungs-Unterstützung (KCMOTOR.DRV)
* OK: Unverfrühte Statusanzeige bei SAVE
* Mehr als nur 2 Puffer
}
uses
WinTypes, WinProcs, Win31, MMSystem, CommDlg, ShellApi,
WUtils;
const
RegPath='KCEMU\KCLOAD';
HelpFileName='KCLOAD.HLP';
UserDllFilter='*.KCL';
WaveFileName='KCLOAD.WAV';
type
AModus=(NONE,LOAD,SAVE);
ACoding=(AUTO,KC,MPM,HS,Z1013,BASICODE);
AFilter=(NON,KCC,DUM,SSS,Z80,BAC);
var
hWav: HWaveIn; {für Ein- und Ausgabe (erfolgt nie gleichzeitig)}
Waves: array[0..1] of THandle; {später mehr für bessere "Flüssigkeit"}
Modus: AModus; {0=frei, 1=LOADing, 2=SAVEteimas}
{Gespeicherte Setup-Daten (Registry), MÜSSEN HINTEREINANDER LIEGEN BLEIBEN}
Retries: Integer; {Anzahl Wiederholungen beim Lesen}
Coding: ACoding; {Modus KC normal, Turbo MPM, Turbo h#s, Z1013}
Filter: AFilter; {FilterIndex bei Datei-Auswahl-Dialog}
WaveVol: ShortInt; {Lautstärke-Multiplikator (log.) bei Ausgabe}
AutoSave: Boolean; {Schalter für Automatisches Speichern}
WaveInDev, WaveOutDev: Integer;
{Nummer des Ein- bzw. Ausgabegerätes:
-2=KCLOAD.WAV, -1=Wave-Mapper, 0=erste Soundkarte usw.}
Installed: Boolean; {zum automatischen Sichern von Einstellungen}
BytesToWrite: LongInt;
BufPtr: LongInt;
AppName: array[0..31] of Char;
CurWaveHdr: PWaveHdr; {ZeigerZeiger auf momentan zu prozessierende Daten}
ByteIndex: Word; {Index für momentanes Sample in CurWaveHdr}
Task: THandle; {Handle der MMTASK.TSK ("Windows-3.1-Thread")}
LibInst: THandle; {Geladene KCL-Bibliothek}
Back: HBrush; {Hintergrundpinsel für Statusfenster (Blau)}
Green: HBrush; {Pinsel für Aussteuer-Anzeige (Dunkelgrün)}
Font: HFont; {Schrift für Statuszeile}
StatusChars: Integer; {Anzahl sichtbarer Zeichen in Statuszeile}
ckRIFF,ck: TMmCkInfo; {RIFF-Merker zum (Lesen und) Schreiben}
const
WAVBLK=$1000; {je größer, desto weniger Aussetzer, aber asynchroner}
MyWav: TPCMWaveFormat=(
wf:(
wFormatTag: WAVE_Format_PCM;
nChannels: 1;
nSamplesPerSec: 22050;
nAvgBytesPerSec: 22050;
nBlockAlign: 1);
wBitsPerSample: 8);
WM_ContinueInit=WM_User+10;
WM_EndProcess=WM_User+11;
WM_SetStatus=WM_User+12;
WM_ReportWaveError=WM_User+13;
type
TBlock=array[0..127] of Byte;
TCallProc=procedure; {Formatabhängige Lese/Schreibfunktion}
var
MainWnd: HWnd; {Global ist besser im Zugriff!}
hBuffer: THandle; {Speicherabbild der Datei, Wachstum durch Verdopplung}
BufContent: AFilter; {vom Leseprogramm gesetzter Puffer-Inhalt}
Amp: ShortInt; {Momentanwert (also Elongation!)}
WavePeak: Integer; {Maximale Amplitude eines Wave-Blocks beim Lesen}
StatusBuf: TS31; {Statuszeilen-Text}
{*************************************}
{** Prozeduren zur Wave-Ein/Ausgabe **}
{*************************************}
procedure FindPeak(P:PChar; len:Word);
begin
WavePeak:=0;
while len<>0 do begin
WavePeak:=max(WavePeak,abs(ShortInt(Byte(P^)-$80)));
Inc(P);
Dec(len);
end;
InvalidateRect(GetDlgItem(MainWnd,19),nil,false);
end;
function GetSize:LongInt;
begin
case Modus of
LOAD: GetSize:=BufPtr;
SAVE: GetSize:=BytesToWrite-BufPtr;
end;
end;
function GetData(P:PChar; Size:Integer):Integer;
var
Buf: PChar;
begin
Buf:=GlobalLock(hBuffer);
IncHPL(Buf,BufPtr);
if Size>BytesToWrite-BufPtr then Size:=BytesToWrite-BufPtr;
hmemcpy(P,Buf,Size);
Inc(BufPtr,Size);
GetData:=Size;
GlobalUnlock(hBuffer);
end;
procedure EndProcess1(OK:Boolean); forward;
procedure AddData(P:PChar; Size:Integer);
var
Buf: PChar;
begin
if BufPtr+Size>GlobalSize(hBuffer) then begin
hBuffer:=GlobalReAlloc(hBuffer,GlobalSize(hBuffer) shl 1,0);
if hBuffer=0 then EndProcess1(false); {sollte nie passieren}
end;
Buf:=GlobalLock(hBuffer);
IncHPL(Buf,BufPtr);
hmemcpy(Buf,P,Size);
Inc(BufPtr,Size);
GlobalUnlock(hBuffer);
end;
procedure ProcessBlock;
{Kontext: MMTASK.TSK}
var
Msg: TMsg;
begin
with CurWaveHdr^ do case Modus of
LOAD: begin
if WaveInDev=-2 then begin
LongRec(dwBytesRecorded).Lo:=
mmioRead(hWav,lpData,LongRec(dwBufferLength).Lo);
if LongRec(dwBytesRecorded).Lo=0 then EndProcess1(false);
PeekMessage(Msg,0,0,0,PM_Remove); {Windows zum Zug kommen lassen}
if Msg.message=WM_Quit then EndProcess1(false);
end else begin
WaveInAddBuffer(hWav,CurWaveHdr,sizeof(TWaveHdr)); {leeren Puffer hinein}
if not GetMessage(Msg,0,0,0) then EndProcess1(false);
{gibt Rechenzeit frei}
{$IFOPT D+} if Msg.message<>MM_WIM_Data then asm int 3 end; {$ENDIF}
CurWaveHdr:=Pointer(Msg.lParam); {Neuer Block, gefüllt mit Daten, steht bereit}
end;
FindPeak(CurWaveHdr^.lpData,CurWaveHdr^.dwBytesRecorded);
end;
SAVE: begin
if WaveOutDev=-2 then begin
if mmioWrite(hWav,CurWaveHdr^.lpData,
LongRec(CurWaveHdr^.dwBufferLength).Lo)<>
LongRec(CurWaveHdr^.dwBufferLength).Lo
then EndProcess1(false);
PeekMessage(Msg,0,0,0,PM_Remove); {Windows zum Zug kommen lassen}
if Msg.message=WM_Quit then EndProcess1(false);
end else begin
WaveOutWrite(hWav,CurWaveHdr,sizeof(TWaveHdr)); {vollen Puffer hinein}
if not GetMessage(Msg,0,0,0) then EndProcess1(false);
{gibt Rechenzeit frei}
{$IFOPT D+} if Msg.message<>MM_WOM_Done then asm int 3 end; {$ENDIF}
CurWaveHdr:=Pointer(Msg.lParam); {Geleerter Puffer}
{Jetzt erst Status setzen:}
if PChar(CurWaveHdr)[sizeof(TWaveHdr)+WAVBLK]<>#0 then begin
lstrcpy(StatusBuf,PChar(CurWaveHdr)+sizeof(TWaveHdr)+WAVBLK);
{Nicht doppelt in Warteschlange stellen (z.B. langsamer Rechner)}
if not PeekMessage(Msg,MainWnd,WM_SetStatus,WM_SetStatus,
PM_NoYield or PM_NoRemove)
then PostMessage(MainWnd,WM_SetStatus,0,0);
PChar(CurWaveHdr)[sizeof(TWaveHdr)+WAVBLK]:=#0;
end;
end;
end;
end;
ByteIndex:=0; {Lese- oder Schreibzeiger}
end;
function MakeWaveBlock(var M:THandle):PWaveHdr;
{Erzeugt einen Speicherblock, bestehend aus WaveHdr und
einem direkt darauf folgenden Datenpuffer.
Benutzt globale Variablen hWav, Modus, WaveInDev und WaveOutDev
wegen der notwendigen "Präparation" dieser Puffer.
Beim Speichern "hängt" an diesem Puffer auch noch Platz für
einen Status-String, damit dieser erst bei Wave-Ausgabe und nicht
schon zu seiner Vorbereitung erscheint.}
var
WH: PWaveHdr;
begin
M:=GlobalAlloc(GHND or GMEM_Share,sizeof(TWaveHdr)+WAVBLK+
IfThenElse(Modus=SAVE,32,0));
if M=0 then EndProcess1(false);
WH:=GlobalLock(M);
WH^.lpData:=PChar(WH)+sizeof(TWaveHdr);
WH^.dwBufferLength:=WAVBLK;
case Modus of
LOAD: if WaveInDev>-2 then WaveInPrepareHeader(hWav,WH,sizeof(TWaveHdr));
SAVE: if WaveOutDev>-2 then WaveOutPrepareHeader(hWav,WH,sizeof(TWaveHdr));
end;
MakeWaveBlock:=WH;
end;
procedure FreeWaveBlock(var M:THandle);
{Gegenteil von oben}
var
WH: PWaveHdr;
begin
if M=0 then exit; {Nichts tun, falls gar nicht angefordert}
GlobalUnlock(M); {der Block war die ganze Zeit gelockt!}
if hWav<>0 then begin
WH:=GlobalLock(M); {Pointer beschaffen}
case Modus of
LOAD: if WaveInDev>-2 then WaveInUnprepareHeader(hWav,WH,sizeof(TWaveHdr));
SAVE: if WaveOutDev>-2 then WaveOutUnprepareHeader(hWav,WH,sizeof(TWaveHdr));
end;
GlobalUnlock(M);
end;
M:=GlobalFree(M);
end;
procedure EndProcess1(OK:Boolean);
{Kontext: MMTASK.TSK, Funktion beendet Task}
begin
case Modus of
LOAD: begin {Lesen vom Band oder von der WAV-Datei}
if WaveInDev<>-2 then begin
WaveInReset(hWav); {Restliche (leere) Puffer "ausspucken"}
WaveInClose(hWav); {Gerät schließen}
end else mmioClose(hWav,0);
FindPeak(nil,0); {Anzeige rücksetzen (lassen)}
end;
SAVE: begin {Schreiben aufs Band oder in die WAV-Datei}
if OK and (ByteIndex<>0) then begin
LongRec(CurWaveHdr^.dwBufferLength).Lo:=ByteIndex;
ProcessBlock;
end;
if WaveOutDev<>-2 then begin
if OK then begin
while WaveOutClose(hWav)<>0 {=WAVERR_StillPlaying}
do MMTaskBlock(Task);
end else begin
WaveOutReset(hWav); {im Abbruch-Fall}
WaveOutClose(hWav);
end;
hWav:=0;
end else begin
if OK then begin {WAV-Datei patchen lassen}
if mmioAscend(hWav,@ck,0)<>0 then OK:=false
else if mmioAscend(hWav,@ckRIFF,0)<>0 then OK:=false;
end;
if mmioClose(hWav,0)<>0 then OK:=false;
if not OK then mmioOpen(WaveFileName,nil,MMIO_Delete);
end;
end;
end{case};
FreeWaveBlock(Waves[0]);
FreeWaveBlock(Waves[1]);
PostMessage(MainWnd,WM_EndProcess,Word(OK),0);
halt;
end;
function memcmpw(var p1, p2; vlen: Word):Boolean; assembler;
{Speicher-Vergleich, wortweise}
asm push ds
lds si,[p2]
les di,[p1]
mov cx,vlen
mov al,FALSE
cld
rep cmpsw {[SI]-[DI]}
pop ds
jnz @@e
inc al {TRUE}
@@e:
end;
procedure WaveInTask(CallProc:TCallProc);far;
{Kontext: MMTASK.TSK, Funktion beendet Task und kehrt nicht zurück}
label
err,errfile;
var
I: Integer;
ThisWav: TPCMWaveFormat;
begin
asm mov ax,seg @data; mov ds,ax; {$IFOPT D+} int 3 {$ENDIF} end;
if WaveInDev=-2 then begin
I:=mmsyserr_InvalHandle; {5}
hWav:=mmioOpen(WaveFileName,nil,
MMIO_Read or MMIO_AllocBuf or MMIO_DenyNone);
if hWav=0 then goto err;
I:=mmsyserr_NotSupported; {8}
if mmioDescend(hWav,@ckRIFF,nil,0)<>0 then goto errfile;
if ckRIFF.ckID<>FOURCC_RIFF then goto errfile;
if ckRIFF.fccType<>$45564157 then goto errfile; {'WAVE'}
ck.ckID:=$20746D66; {'fmt '}
if mmioDescend(hWav,@ck,@ckRIFF,MMIO_FindChunk)<>0 then goto errfile;
if ck.ckSize<sizeof(TPCMWaveFormat) then goto errfile;
if mmioRead(hWav,PChar(@ThisWav),sizeof(TPCMWaveFormat))
<>sizeof(TPCMWaveFormat) then goto errfile;
if mmioAscend(hWav,@ck,0)<>0 then goto errfile;
if not memcmpw(ThisWav,MyWav,sizeof(TPCMWaveFormat) div 2)
then goto errfile;
ck.ckID:=$61746164; {'data'}
if mmioDescend(hWav,@ck,@ckRIFF,MMIO_FindChunk)<>0 then goto errfile;
CurWaveHdr:=MakeWaveBlock(Waves[0]);
end else begin
I:=Integer(WaveInOpen(@hWav,Word(WaveInDev),@MyWav.wf,
MMGetCurrentTask,0,Callback_Task));
if I<>0 then goto err;
MMTaskBlock(Task); {MM_WIM_Open entfernen}
WaveInAddbuffer(hWav,MakeWaveBlock(Waves[0]),sizeof(TWaveHdr));
CurWaveHdr:=MakeWaveBlock(Waves[1]);
ByteIndex:=$FFFF;
WaveInStart(hWav);
end;
CallProc;
EndProcess1(true);
errfile:
mmioClose(hWav,0);
err:
PostMessage(MainWnd,WM_ReportWaveError,I,0);
halt; {Prozess MMTASK.TSK beenden (nicht KCLOAD)}
end;
procedure WaveOutTask(CallProc:TCallProc);far;
{Kontext: MMTASK.TSK, Funktion beendet Task und kehrt nicht zurück}
label
err,errfile;
var
I: Integer;
begin
asm mov ax,seg @data; mov ds,ax; {$IFOPT D+} int 3 {$ENDIF} end;
if WaveOutDev=-2 then begin
I:=mmsyserr_InvalHandle; {5}
hWav:=mmioOpen(WaveFileName,nil,
MMIO_Write or MMIO_AllocBuf or MMIO_Create or MMIO_DenyNone);
if hWav=0 then goto err;
I:=mmsyserr_NotSupported; {8 - besser: cant_write}
ckRIFF.fccType:=$45564157; {'WAVE'}
if mmioCreateChunk(hWav,@ckRIFF,MMIO_CreateRIFF)<>0 then goto errfile;
ck.ckID:=$20746D66; {'fmt '}
ck.ckSize:=sizeof(TPCMWaveFormat);
if mmioCreateChunk(hWav,@ck,0)<>0 then goto errfile;
if mmioWrite(hWav,PChar(@MyWav),sizeof(TPCMWaveFormat))
<>sizeof(TPCMWaveFormat) then goto errfile;
if mmioAscend(hWav,@ck,0)<>0 then goto errfile;
ck.ckID:=$61746164; {'data'}
if mmioCreateChunk(hWav,@ck,0)<>0 then goto errfile;
end else begin
I:=Integer(WaveOutOpen(@hWav,Word(WaveOutDev),@MyWav.wf,
MMGetCurrentTask,0,Callback_Task));
if I<>0 then goto err;
MMTaskBlock(Task); {MM_WOM_Open entfernen}
PostAppMessage(MMGetCurrentTask,MM_WOM_Done,hWav,
LongInt(MakeWaveBlock(Waves[1])));
end;
CurWaveHdr:=MakeWaveBlock(Waves[0]);
ByteIndex:=0;
CallProc;
EndProcess1(true);
errfile:
mmioClose(hWav,0);
mmioOpen(WaveFileName,nil,MMIO_Delete);
err:
PostMessage(MainWnd,WM_ReportWaveError,I,0);
halt;
end;
{**********************************************}
{** Formatabhängiges Lesen: Anzeige-Routinen **}
{**********************************************}
function LoadStr31(D,S:PChar):Integer;
begin
if PtrRec(S).sel=0
then LoadStr31:=LoadString(Seg(HInstance),LongRec(S).lo,D,32)
else begin
lstrcpyn(D,S,32);
LoadStr31:=lstrlen(D);
end;
end;
function SetStat_LOAD(S:PChar):Integer;
begin
SetStat_LOAD:=LoadStr31(StatusBuf,S);
PostMessage(MainWnd,WM_SetStatus,0,0);
end;
function SetStat_SAVE(S:PChar):Integer;
begin
SetStat_SAVE:=LoadStr31(PChar(CurWaveHdr)+sizeof(TWaveHdr)+WAVBLK,S);
end;
function Char_KC2ANSI:Char; assembler;
{Zeichen-Bereich begrenzen, 6 Zeichencodes (vornehmlich Umlaute) austauschen.
Liefert außerdem Flags vom Vergleich des Zeichens mit ' '!}
asm and al,7Fh
cmp al,5Ch
mov ah,7Ch {Pipe-Symbol}
je @@2
cmp al,5Dh
mov ah,0ACh {Negations-Zeichen}
je @@2
cmp al,7Bh
mov ah,0E4h {ä}
je @@2
cmp al,7Ch
mov ah,0F6h {ö}
je @@2
cmp al,7Dh
mov ah,0FCh {ü}
je @@2
cmp al,7Eh
mov ah,0DFh {ß}
jne @@e
@@2: xchg ah,al
@@e: cmp al,' '
ja @@1
mov al,' '
@@1:
end;
function GetFileNameKC(S,buf:PChar):integer;assembler;
{Dateiname von Pufferzeiger buf (hier: 11 Bytes, mit Leerzeichen aufgefüllt)
nach S (als ASCIIZ 8.3) extrahieren,
bei den typischen BASIC-Dateinamen wird die vorn liegende Extension
nach hinten gebracht.
Leerzeichen und ungültige Zeichen werden NOCH NICHT aus dem String entfernt,
das muß ein nachfolgendes Programm (je nach Vorhandensein einer LFN-API) tun.
Hat der Name keine Erweiterung, gibt's auch keinen Punkt.
Liefert Anzahl der Zeichen in S}
asm cld
push ds
les di,[S]
lds si,[Buf]
mov dx,di
xor bx,bx
mov cx,8 {8 Zeichen}
lodsb {1. Zeichen}
test al,al {Endung am Anfang?}
jns @@1
mov bx,11 {Kennung}
add si,2 {mit dem 4. Zeichen loslegen}
@@l1: lodsb
@@1: call Char_KC2ANSI
stosb {Alle (auch verbotene W31) in Puffer}
jbe @@2
mov dx,di {Vorrücken bei nicht-leeren Zeichen}
@@2: loop @@l1
mov di,dx
mov al,'.'
stosb {jetzt kommt die Extension!}
sub si,bx {Korrektur, wenn Extension vorn war!}
mov cx,3
@@l2: lodsb
call Char_KC2ANSI
stosb
jbe @@3
mov dx,di {Vorrücken bei nicht-leeren Zeichen}
@@3: loop @@l2
mov di,dx
xor al,al
stosb {Terminierende Null}
xchg dx,ax
sub ax,word ptr [S]
@@e: pop ds
end;
function GetFileNameZ80(S,buf:PChar):integer; assembler;
{Extrahiert Z1013-Headersave-Dateiname - nachlaufende Leerzeichen werden
abgeschnitten. Liefert Anzahl der Zeichen in S}
asm cld
push ds
les di,[S]
lds si,[buf]
mov cx,16 {16 Zeichen können's sein!}
add si,cx
mov dx,di {Merker für letztes Nicht-Leerzeichen}
@@l: lodsb
cmp al,' '
ja @@1
mov al,' '
@@1: stosb
jbe @@2
mov dx,di {"Ein nicht-leeres Zeichen" merken}
@@2: loop @@l
mov di,dx
xor al,al
stosb
xchg dx,ax
sub ax,word ptr [S] {Anzahl Zeichen liefern}
pop ds
end;
{-Bin-Laden-von-Kassette-----------------------------------------------------}
procedure ReadB;
{Liest das nächste Sample als vzb. 8-Bit-Wert zur globalen Variable "Amp"}
{Kontext: MMTASK.TSK}
begin
with CurWaveHdr^ do begin
if ByteIndex>=LongRec(dwBytesRecorded).Lo
then ProcessBlock; {Nächsten Block einlesen}
end; {CurWaveHdr geändert, WITH beenden!!!}
with CurWaveHdr^ do begin
Amp:=Integer(lpData[ByteIndex])-$80;
Inc(ByteIndex);
end;
end;
function ReadSwing1:Integer;
{Liest Samples bis zum nächsten Nulldurchgang und liefert die Sample-Anzahl,
somit die Zeit für eine halbe Schwingung}
{Kontext: MMTASK.TSK}
var
W: Integer;
begin
W:=0;
if Amp<0
then repeat ReadB; Inc(W); until Amp>=0
else repeat ReadB; Inc(W); until Amp<0;
ReadSwing1:=W;
end;
function ReadSwing2: Integer;
{Liefert die Zeit in Samples für eine ganze Schwingung}
{Kontext: MMTASK.TSK}
begin
ReadSwing2:=ReadSwing1+ReadSwing1;
end;
{**************************************}
{** Formatabhängiges Lesen: Routinen **}
{**************************************}
type
TBL=record {Blocklese-Struktur}
expect: Word; {Erwartete Blocknummer}
readbl: Word; {Gelesene Blocknummer}
showch: Char; {Anzeige-Zeichen}
ok: Boolean; {Block OK oder nicht}
retry: Integer; {Wiederholungs-Zähler (aufwärts!)}
spos1: Integer; {String-Position: ! stehenlassen}
spos2: Integer; {String-Position: ? stehenlassen}
t: PChar; {String-Template}
tl: Integer; {Ausgabe-Länge pro String-Template}
s: TS31; {String-Puffer}
end;
procedure InitBL(var BL:TBL; t:PChar; tl:Integer);
{BL-Struktur initialisieren}
begin
FillChar(BL,sizeof(BL),0);
BL.t:=t;
BL.tl:=tl;
FillChar(BL.s,31,' ');
end;
procedure HandleBL(var BL:TBL);
{Gelesenen Block behandeln; der Aufrufer muss die Elemente
expect, readbl und ok geeignet setzen!}
begin
with BL do begin
spos2:=tl; {zunächst Platz lassen}
if readbl=expect then begin
if ok then begin
showch:='>';
end else begin
showch:='?';
Inc(retry);
if (retry>=Retries) or (WaveInDev=-2) then begin
ok:=true;
showch:='!';
end;
end;
if ok then begin
spos2:=0;
retry:=0;
end;
end else begin
showch:='*';
end;
wvsprintf(s+spos1+spos2,t,readbl);
if (showch='!') and (spos1<StatusChars-2*tl)
then Inc(spos1,tl); {(einige) Ausrufezeichen-Blöcke stehen lassen}
SetStat_LOAD(s);
end;
end;
procedure CatHex(S:PChar; W:Word);
{Hängt eine vierstellige Hexzahl an S an, solange S, mit 31 Zeichen
angenommen, noch nicht voll ist.}
var
I:Integer;
begin
I:=lstrlen(S);
if I>31-5 then exit; {kein Platz: Zahl weglassen!}
wvsprintf(S+I,' %04X',W);
end;
function HandleMPMHeader(buf:TBlock):LongInt;
{Zeigt den KC85-Header in der Statuszeile und berechnet die Dateilänge}
function IsBASICProg: Boolean;
var
B: Byte;
begin
IsBASICProg:=false;
B:=buf[0];
if (B-$D3) and not 4 <>0 then exit;
if B<>buf[1] then exit;
if B<>buf[2] then exit;
IsBASICProg:=true;
end;
function IsBASICField: Boolean;
var
B: Byte;
begin
IsBASICField:=false;
B:=buf[0];
if (B-$D3) and not 4 >3 then exit; {SSS,TTT,UUU, WWW,XXX,YYY +80h}
if B<>buf[1] then exit;
if B<>buf[2] then exit;
IsBASICField:=true;
end;
function IsMCProg: Boolean;
begin
IsMCProg:=buf[16] in [2..10];
end;
var
s:TS31;
begin
HandleMPMHeader:=MaxLongInt;
GetFileNameKC(s,PChar(@buf));
if IsBASICProg then begin
HandleMPMHeader:=PWord(@buf[11])^+LongInt(14);
CatHex(s,PWord(@buf[11])^);
BufContent:=SSS; {als BASIC speichern lassen}
end else if IsBASICField then begin
BufContent:=SSS; {als BASIC speichern lassen}
end else if IsMCProg then begin
HandleMPMHeader:=PWord(@buf[19])^-PWord(@buf[17])^+LongInt(128);
BufContent:=KCC; {als MC speichern lassen}
CatHex(s,PWord(@buf[17])^);
CatHex(s,PWord(@buf[19])^);
if buf[16]>2 then CatHex(s,PWord(@buf[21])^);
end else if PLongInt(PChar(@buf)+12)^=0 then BufContent:=KCC
else BufContent:=SSS; {wahrscheinlich! - aber ziemlich sicher}
SetStat_LOAD(s); {müsste normalerweise in eine andere Statuszeile!}
end;
{KC-Format:
Die Angaben im KC-Handbuch und (z.Z.) KCEMU-Hilfe sind FALSCH!
Ursache: Die Mühlhäuser Programmierer haben die Latenzzeit der
Interruptserviceroutine unterschlagen.
Z.B. Vorton: TC=$2F=47->1179Hz(848µs), es sind aber ca. 1060Hz(943µs).
In dieser Zeit wird der Timer 2x per ISR neu programmiert, 943-848=95µs,
pro Aufruf also 47.5µs->83 Takte.
Der IM2 verbraucht 19 Takte, in der ISR vergehen 11+7+11+19+11 Takte,
macht zusammen 78 Takte. Da die Warteschleife aus einer Schleife
mit 19+4+12 Takten besteht, ist die mittlere Latenz 5 Takte
(wegen mittlerer Befehlsausführungszeit von 11 Takten). Kommt genau hin!
(Dummerweise wird die CTC rückgesetzt, eigentlich Schwachsinn!)
0-Bit: Vollschwingung 1950 Hz (11 Samples, 7-16)
1-Bit: Vollschwingung 1050 Hz (21 Samples, 17-26)
Trennzeichen: Vollschw. 557 Hz (39 Samples, 27-50)
Byte: Bit0, Bit1, ... Bit7, Trennzeichen (TZ)
Block: Vorton (>160 1-Bits), TZ, Byte (BlockNr), 128 Bytes, Byte (Summe)
Datei: Vorblock (Nr. 0 oder 1), n Datenblöcke, Schlussblock (Nr. FF)
Vorblock: 11 Byte Name, 117 Byte verschieden verwendet oder Datenbytes
Schlussblock: Kann Daten enthalten
Idiotien:
* Keine vernünftige Definition des Vorblock-Inhaltes, verschiedene
Auslegung für BASIC oder MC, unsichere Block-Nr.
* Block-Nr. FF kann, muss aber nicht Schlussblock sein; Auslegung
verschieden: VERIFY und LOAD machen Schluss, BASIC lädt weiter
und interessiert sich mitnichten für den FF-Block
(dieser enthält keine Daten und wird gar nicht gelesen)
KC87-BASIC generiert keinen FF-Block
Dateien >31K haben einen FF-Block mittendrin
* Schwer behebbare Differenzen zwischen Kassetten- und Disketten-
Aufzeichnung (das mache man erst mal dem Normalverbraucher klar...)
}
procedure ReadDataKCC; far;
{Kontext: MMTASK.TSK}
function Bytein:Byte;
var
I,W:Integer;
B:Byte;
begin
for I:=0 to 7 do begin
w:=ReadSwing2;
asm shr [B],1 end;
if w>=17 then asm or [B],80h end; {kompakter als "B:=B or $80"}
end;
ReadSwing2;
Bytein:=B;
end;
function ReadBlock(var BlkNr:Byte; var Buffer:TBlock; Vorton:Integer):Boolean;
label step1,step2;
var
I,W:Integer;
B,Sum:Byte;
begin
ReadBlock:=false;
{Schritt 1: Vorton erkennen und aufsynchronisieren}
step1:
I:=Vorton;
{Vorton:=16; {Wiederholungen mit kürzerem Einlauf?}
repeat
W:=ReadSwing2;
if W<17 then goto step1;
if W>26 then goto step1;
Dec(I);
until I=0;
{Schritt 2: 1. Trennzeichen holen}
step2:
repeat
W:=ReadSwing1;
if W>25 then goto step1; {Aussetzer im Vorton}
if W<8 then goto step1; {Störnadel im Vorton}
until W>13;
Inc(W,ReadSwing1);
if W<27 then goto step2;
if W>50 then goto step2; {2. halbes Trennzeichen muß folgen!}
{Schritt 3: Bytes lesen}
BLKNr:=ByteIn;
Sum:=0;
for I:=0 to 127 do begin
B:=ByteIn;
Inc(Sum,b);
Buffer[I]:=b;
end;
If Sum=ByteIn then ReadBlock:=true;
end;
label nochmal;
var
BL:TBL;
buf: TBlock;
L:LongInt;
begin
SetStat_LOAD(PChar(17)); {'KC85,KC87'}
InitBL(BL,' %02X%c',4);
repeat
BL.ok:=ReadBlock(Byte(BL.readbl),buf,300);
if BL.readbl in [0,1]
then BL.expect:=BL.readbl
else BL.expect:=$FFFF; {"falscher Block" erzwingen}
HandleBL(BL);
until BL.ok;
AddData(PChar(@Buf),sizeof(buf));
L:=HandleMPMHeader(buf);
repeat
Inc(Byte(BL.expect));
nochmal:
BL.ok:=ReadBlock(Byte(BL.readbl),buf,16);
if (BL.readbl=$FF) {Schlussblock: gekommen?}
and (BL.ok) {und in Ordnung?}
and (BL.expect<>$FF) {und nicht erwartet?}
and (L=MaxLongInt) {Datenmenge: unbekannt?}
then begin
BL.expect:=$FF; {Sei erwarteter Block}
L:=0; {Datenvolumen erfüllt}
end;
HandleBL(BL);
if not BL.ok then goto nochmal;
AddData(PChar(@Buf),sizeof(buf));
until GetSize>=L;
end;
{MPM-Turbo-Format:
Vorton: Vollwelle 2200 Hz (10 Samples)
Trennz: Vollwelle 900 Hz (24 Samples)
0-Bit: Halbwelle 2200 Hz ( 5 Samples, 2..8)
1-Bit: Halbwelle 900 Hz (12 Samples, 9..15)
Byte: Bit0, Bit1, ... Bit7
Block: Vorton(70h..1000h) - Trennzeichen - 130 Bytes
weiter wie KC-Format
In grauer Vorgeschichte habe ich MPM-Turbo umgeschrieben, damit es
kürzer werde und habe die Geschichte mit der Interrupt-Latenz
übersehen, so dass verschiedene Ergebnisse zu Tage kommen!
Original Latenz: (8+12)/4+19+4+7+11+4+11 = 61 Takte (Jitter: 11)
Meine Latenz: (19+4+12)/6+19+11+7+11+19+11 = 84 Takte (Jitter: 18)
0-Bit: ZK 15h->336 Takte (Halbwelle), 2200 Hz vs. 2100 Hz
1-Bit: ZK=39h->912 Takte (Halbwelle), 900 Hz vs. 880 Hz
Vorton = viele 0-Bits, Trennzeichen = 2 1-Bits
Zum Glück bleiben die Frequenzen noch detektierbar...
}
procedure ReadDataMPMTurbo; far;
{Kontext: MMTASK.TSK}
function Bytein:Byte; assembler;
asm
mov ah,8
@@l: push ax
call ReadSwing1
cmp ax,9
cmc
pop ax
rcr al,1
dec ah
jnz @@l
end;
function ReadBlock(var BlkNr:Byte; var Buffer:TBlock; Vorton:Integer):Boolean;
label Try;
var
I,W:Integer;
B,Sum:Byte;
begin
ReadBlock:=false;
{Schritt 1: Vorton erkennen und aufsynchronisieren}
Try:
for I:=1 to Vorton do begin
W:=ReadSwing1;
if W>=10 then begin
I:=0;
continue;
end;
end;
{Schritt 2: 1. Trennzeichen holen}
for I:=1 to 2 do begin
W:=ReadSwing1;
if W<10 then begin
if I=2 then Goto Try else I:=0;
continue;
end;
end;
{Schritt 3: Bytes lesen}
BLKNr:=ByteIn;
Sum:=0;
for I:=0 to 127 do begin
B:=ByteIn;
Inc(Sum,B);
Buffer[I]:=B;
end;
If Sum=ByteIn then ReadBlock:=true;
end;
label nochmal;
var
BL:TBL;
buf: TBlock;
L:LongInt;
begin
SetStat_LOAD(PChar(18)); {'Turbo MPM'}
InitBL(BL,' %02X%c',4);
repeat
BL.ok:=ReadBlock(Byte(BL.readbl),buf,300);
if BL.readbl in [0,1]
then BL.expect:=BL.readbl
else BL.expect:=$FFFF; {"falscher Block" erzwingen}
HandleBL(BL);
until BL.ok;
AddData(PChar(@Buf),sizeof(buf));
L:=HandleMPMHeader(buf);
repeat
Inc(Byte(BL.expect));
nochmal:
BL.ok:=ReadBlock(Byte(BL.readbl),buf,16);
if (BL.readbl=$FF) {Schlussblock: gekommen?}
and (BL.ok) {und in Ordnung?}
and (BL.expect<>$FF) {und nicht erwartet?}
and (L=MaxLongInt) {Datenmenge: unbekannt?}
then begin
BL.expect:=$FF; {Sei erwarteter Block}
L:=0; {Datenvolumen erfüllt}
end;
HandleBL(BL);
if not BL.ok then goto nochmal;
AddData(PChar(@Buf),sizeof(buf));
until GetSize>=L;
end;
{h#s-Turbo-Format:
Nach 2 Blöcken im normalen KC-Format, die den 128-Byte-Bootstrap-Loader
enthalten, der ab B880h im Modulsteuerwortspeicher landet (und dort leider
die Steuerbytes für den Diskettenaufsatz ruiniert), kommt der Speicherabzug
oder das BASIC-Programm (gemeinhin auch ein Speicherabzug).
Vorton: Vollschwingung 735 Hz (30 Samples)
Trennz: Halbschwingung 1200 Hz ( 9 Samples)
0-Bit: Halbschwingung 5500 Hz ( 4 Samples)
1-Bit: Halbschwingung 2400 Hz ( 9 Samples)
Byte: Bit7, Bit6, ... Bit0
Datei: Vorton (512) - Trennzeichen - n Bytes
(n sowie die Prüfsumme stehen im beim Speichern zusammengestellten
Bootstrap-Loader)
Der Aufbau des Bootstrap-Laders ist meine Dokumentationsschwäche;
ich konnte nie mehr das alles entscheidende Listing auftreiben!
}
procedure ReadDataHSTurbo; far;
{Kontext: MMTASK.TSK}
var
S: array[0..31] of Char;
I,W: integer;
Sum,B:Byte;
begin
ReadDataKCC;
if GetSize<>$100 then EndProcess1(false);
SetStat_LOAD(PChar(19)); {'Turbo h#s'}
BufContent:=NON; {hier: HS-Turbo ist gemeint!}
{fehlt: Ausgeben der Adressen, Bestimmung der Lade-Länge}
for I:=0 to 40 do begin
W:=ReadSwing1;
if W<12 then begin I:=0;continue;end;
end;
repeat
until ReadSwing1<12;
Sum:=0;
Repeat
for I:=0 to 7 do begin
w:=ReadSwing1;
B:=B SHL 1;
if w>20 then begin AddData(PChar(@Sum),1);exit;end;
if w>6 then B:=B or 1;
end;
inc(Sum,B);
AddData(PChar(@B),1);
until false
{fehlt:
if pBuf[$D8]<>pBuf[BufPtr-1] then
if MBox1(MainWnd,111,SFile)=IDCANCEL then exit;}
end;
{Z1013-Format:
Vorton: Halbschwingung 650 Hz (17 Samples) Diskriminator:
Trennz: Vollschwingung 1400 Hz (16 Samples)
0-Bit: Halbschwingung 1200 Hz ( 9 Samples)
1-Bit: Vollschwingung 2400 Hz ( 9 Samples) Diskriminator: 6
Wort: Bit0, Bit1, ... Bit15
Block: Vorton (14..2000) - Trennz - Wort (Zieladr.) - 16 Wort -
Wort (Summe über die vorhergehenden 17(!) Worte)
Datei: HEADERSAVE-Vorblock (bel. Zieladresse), n Datenblöcke
HEADERSAVE-Vorblock (leider optional, aber sehr verbreitet):
WORD aadr,eadr,sadr; CHAR creator[6]; BYTE type; BYTE magic[3];
CHAR filename[16]
}
procedure ReadDataZ1013; far;
{Kontext: MMTASK.TSK}
type
TBlk=array[0..15] of Word;
function Wordin:Word;
var
I:Integer;
W:Word;
begin
for I:=0 to 15 do begin
asm shr [W],1 end;
if ReadSwing1>6 then asm or byte ptr [W+1],80h end else ReadSwing1;
end;
Wordin:=W;
end;
function ReadBlock(var BlkNum:Word; var Buffer:TBlk; VLen:Integer):Boolean;
label step1;
var
I:Integer;
W,Sum:Word;
begin
ReadBlock:=false;
{Schritt 1: Vorton erkennen und aufsynchronisieren}
step1:
I:=VLen;
repeat
W:=ReadSwing1;
if W<12 then goto step1;
if W>22 then goto step1;
Dec(I);
until I=0;
{Schritt 2: 1. Trennzeichen holen}
repeat
W:=ReadSwing1;
if W>22 then goto step1; {Aussetzer im Vorton}
if W<5 then goto step1; {Störnadel im Vorton}
until W<12; {ein halbes Trennzeichen}
W:=ReadSwing1;
if W<5 then goto step1;
if W>=12 then goto step1; {2. halbes Trennzeichen muß folgen!}
{Schritt 3: Bytes lesen}
BlkNum:=Wordin;
Sum:=BlkNum;
for I:=0 to 15 do begin
W:=WordIn;
Sum:=Sum+W;
Buffer[I]:=W;
end;
If Sum=WordIn then ReadBlock:=true;
end;
label nochmal;
var
BL:TBL;
buf: TBlk;
L:LongInt;
begin
SetStat_LOAD(PChar(20)); {'Z1013'}
InitBL(BL,' %04X%c',6);
repeat
BL.ok:=ReadBlock(BL.readbl,buf,50); {langer Vorton ist hier WICHTIG!}
BL.expect:=BL.readbl; {Für den ersten Block kein Hindernis}
HandleBL(BL);
until BL.ok;
AddData(PChar(@Buf),sizeof(buf));
L:=MaxLongInt;
BufContent:=DUM;
if (PChar(@buf)[13]=#$D3) {Headersave-Vorblock?}
and (buf[7]=$D3D3) then begin
GetFileNameZ80(BL.s,PChar(@buf));
CatHex(BL.s,buf[0]);
CatHex(BL.s,buf[1]);
if PChar(@buf)[12]='C' then CatHex(BL.s,buf[2]);
SetStat_LOAD(BL.s);
L:=buf[1]-buf[0]+LongInt(32);
BufContent:=Z80; {Vorzugs-Speicherformat}
BL.expect:=buf[0];
goto nochmal; {kein INC voraus}
end;
repeat
Inc(BL.expect,32);
nochmal:
BL.ok:=ReadBlock(BL.readbl,buf,8);
HandleBL(BL);
if not BL.ok then goto nochmal;
AddData(PChar(@Buf),sizeof(buf));
if (L=MaxLongInt) {Datenmenge: unbekannt?}
and (ReadSwing2>100) {scheint nichts mehr vom Band zu kommen?}
then break; {Schleife beenden!}
until GetSize>=L;
end;
{BASICODE-Format:
Vorton: Vollschwingung 2400 Hz ( 9 Samples) Diskriminator:
Trennz: Vollschwingung 1200 Hz (18 Samples)
0-Bit: Vollschwingung 1200 Hz (18 Samples)
1-Bit: 2 Vollschw. 2400 Hz (9+9 Samples) Diskriminator: 6
Byte: Trennz, Bit0, Bit1, ... Bit7
Block: Kein (oder eben nur ein) Block! Nur für Text.
Datei: Vorton (9000) - 2 (SOT = Start Of Text) - Text -
3 (EOT = End Of Text) - Prüf-XOR xor 80h - Vorton (3500)
}
procedure ReadDataBasicode; far;
{Kontext: MMTASK.TSK}
var
S: array[0..31] of Char;
I: Integer;
B,Sum:Byte;
function Bytein:Byte;
var
I,W:Integer;
B:Byte;
begin
for I:=1 to 2 do begin
W:=ReadSwing1;
if W<7 then begin
I:=0;
continue;
end;
end;
for I:=0 to 7 do begin
w:=ReadSwing2;
B:=B SHR 1;
if w<16 then begin B:=B or $80;ReadSwing2;end;
end;
Sum:=Sum xor B;
Bytein:=B xor $80;
end;
var
w: Word;
begin
SetStat_LOAD(PChar(21)); {'BASICODE'}
BufContent:=BAC;
for I:=1 to 40 do begin
W:=ReadSwing1;
if W>=6 then begin
I:=0;
continue;
end;
end;
Sum:=$0;
If ByteIn=1 then ByteIn;
Repeat
B:=ByteIn;
If B=3 then begin
B:=Sum;
If ByteIn<>B then SetStat_LOAD(PChar(22)); {'Lesefehler erkannt!'}
ShortYield;
exit;
end;
AddData(PChar(@B),1);
if B=$0D then B:=0;
B:=B and $7F;
if B in [1..$1F,$7F] then B:=Byte('.');
S[I]:=Char(B);
if I<sizeof(S)-1 then Inc(I);
if B=0 then begin
SetStat_LOAD(S);
I:=0;
end;
until false;
end;
{Automatische Formaterkennung am Vorton:
2400 Hz -> BASICODE (= MPM-Turbo)
1050 Hz -> KC
600 Hz -> Z1013
}
procedure ReadDataAuto; far;
label step1;
var
I,U,O,D,Z: Integer;
S: TS31;
begin
SetStat_LOAD(PChar(16)); {'Suche...'}
step1:
U:=100; O:=0; Z:=500;
repeat
I:=ReadSwing2;
D:=I div 8 +1; {erlaubte Differenz O-U}
U:=min(U,I); O:=max(O,I);
if U+D<O then goto step1; {Zu große Abweichung: von vorn!}
Dec(Z)
until Z=0;
{ if U>=183 then goto step1; {Nur Netzbrummen: bis 120 Hz}
{ if O<=1 then goto step1; {Zu hochfrequent}
if (U<=9) and (9<=O) then ReadDataBasicode
else if (U<=21) and (21<=O) then ReadDataKCC
else if (U<=34) and (34<=O) then ReadDataZ1013
else begin
Word(I):=44100 div Word(U+O);
wvsprintf(S,'%u Hz ???',I);
SetStat_LOAD(S);
goto step1;
end;
end;
{-Speichern-auf-Kassette-----------------------------------------------------}
procedure WriteB;
{Schreibt ein Sample "Amp" (globale Variable) auf die Wave-Ausgabe}
{Kontext: MMTASK.TSK}
begin
with CurWaveHdr^ do begin
lpData[ByteIndex]:=Char(Integer(Amp)+$80);
Inc(ByteIndex);
if ByteIndex>=LongRec(dwBufferLength).Lo
then begin
{$IFOPT D+} asm int 3 end; {$ENDIF}
ProcessBlock; {vollen Block schreiben}
end;
end;
end;
procedure WriteSwing1(Len:Integer);
{Schreibt eine Halbschwingung der gewünschten Länge mit abgerundeter
Vorderflanke (nur bei Längen >=4) -
ansonsten war die Aufzeichnung futsch sowie keine KC-Direktankopplung
(ohne Magnetband) möglich.}
{Kontext: MMTASK.TSK}
begin
if Len>=4 then asm {bei =4 bleibt die Amplitude auf 7/8}
mov al,[Amp]
neg al
push ax
mov [Amp],0
call WriteB
pop ax
push ax
sar al,1 {/2, Amp ist immer gerade!}
mov [Amp],al
call WriteB
pop ax
push ax
sar al,2 {/4}
add [Amp],al {3/4}
call WriteB
pop ax
push ax
sar al,3 {/8}
add [Amp],al {7/8}
call WriteB
pop ax
sub [Len],4
mov [Amp],al
end else asm
neg [Amp]
end;
while Len<>0 do begin
WriteB; {Rest gerade (Gleichspannung)}
Dec(Len);
end;
end;
procedure WriteSwing2(Len:Integer);
{Ganze Schwingung der gewünschten Länge (in 22-kHz-Samples) schreiben}
{Kontext: MMTASK.TSK}
var
Len1:integer;
begin
Len1:=idiv2(Len); {halbieren...}
WriteSwing1(Len1);
WriteSwing1(Len-Len1); {kann unterschiedliche Längen ergeben!}
end;
procedure WriteDataKCC; far;
{Kontext: MMTASK.TSK}
{Schreibt BytesToWrite Bytes aus dem hBuffer}
procedure WriteByte(B:Byte); assembler;
asm
mov ah,8
mov al,[B]
@@l: shr al,1
push ax
mov ax,11
jnc @@1
mov al,21
@@1: push ax
call WriteSwing2
pop ax
dec ah
jnz @@l
push 39
call WriteSwing2
end;
procedure WriteBlock(BlkNr:Byte; const Buffer:TBlock; Vorton:Integer);
var
I,W:Integer;
B,Sum:Byte;
begin
for I:=1 to Vorton do WriteSwing2(21); {Vorton}
WriteSwing2(39); {Trennzeichen}
WriteByte(BlkNr);
Sum:=0;
for I:=0 to $7F do begin
Inc(Sum,Buffer[I]);
WriteByte(Buffer[I]);
end;
WriteByte(Sum);
end;
var
CurBlk: Byte;
S: array[0..31] of Char;
I: Integer;
buf: TBlock;
Erster_Block, Letzter_Block: Boolean;
begin
SetStat_SAVE(PChar(17));
CurBlk:=1;
Erster_Block:=true;
Letzter_Block:=false;
repeat
I:=GetData(PChar(@buf),sizeof(buf));
if ((I<sizeof(buf)) or (GetSize=0))
and (not Erster_Block)
and (CurBlk<>$FF) then begin
Letzter_Block:=true;
CurBlk:=$FF;
end;
I:=CurBlk;
wvsprintf(S,' %02X<',I);
SetStat_SAVE(S);
I:=$A0;
if Erster_Block then I:=$A00;
WriteBlock(CurBlk,buf,I);
Inc(CurBlk);
Erster_Block:=false;
until Letzter_Block;
end;
procedure WriteDataMPMTurbo; far;
{Kontext: MMTASK.TSK}
{Schreibt BytesToWrite Bytes aus dem hBuffer}
procedure WriteByte(B:Byte); assembler;
asm
mov ah,8
mov al,[B]
@@l: shr al,1
push ax
mov ax,5
jnc @@1
mov al,12
@@1: push ax
call WriteSwing1
pop ax
dec ah
jnz @@l
end;
procedure WriteBlock(BlkNr:Byte; const Buffer:TBlock; Vorton:Integer);
var
I,W:Integer;
B,Sum:Byte;
begin
for I:=1 to Vorton do WriteSwing2(10); {Vorton}
WriteSwing2(24); {Trennzeichen}
WriteByte(BlkNr);
Sum:=0;
for I:=0 to $7F do begin
Sum:=Sum xor Buffer[I];
WriteByte(Buffer[I]);
end;
WriteByte(Sum);
end;
var
CurBlk: Byte;
S: array[0..31] of Char;
I: Integer;
buf: TBlock;
Erster_Block, Letzter_Block: Boolean;
begin
SetStat_SAVE(PChar(18));
CurBlk:=1;
Erster_Block:=true;
Letzter_Block:=false;
repeat
I:=GetData(PChar(@buf),sizeof(buf));
if ((I<sizeof(buf)) or (GetSize=0))
and (not Erster_Block)
and (CurBlk<>$FF) then begin
Letzter_Block:=true;
CurBlk:=$FF;
end;
I:=$70;
if Erster_Block then I:=$1000;
WriteBlock(CurBlk,buf,I);
I:=CurBlk;
Inc(CurBlk);
wvsprintf(S,'%02X<',I);
SetStat_SAVE(S);
Erster_Block:=false;
until Letzter_Block;
end;
procedure WriteDataHSTurbo; far;
{Kontext: MMTASK.TSK}
{Schreibt BytesToWrite Bytes aus dem hBuffer}
var
BTW: Longint;
B:Byte;
S: TS255;
begin
{Loader saven}
BTW:=BytestoWrite;BytestoWrite:=256;
WriteDataKCC;
BytestoWrite:=BTW;
{eigentliche Daten speichern}
SetStat_SAVE(PChar(19)); {'Turbo h#s'}
asm mov cx,512
@@l: push cx
push 30
call WriteSwing2
pop cx
loop @@l
end;
WriteSwing1(9);
while GetData(PChar(@B),1)<>0 do asm
mov cx,8
@@l: mov ax,4 {Null-Bit}
shl [B],1
jnc @@1
mov al,9 {Eins-Bit}
@@1: push cx
push ax
call WriteSwing1
pop cx
loop @@l
end;
end;
procedure WriteDataZ1013; far;
{Kontext: MMTASK.TSK}
{Schreibt BytesToWrite Bytes aus dem hBuffer}
type
TBlk=array[0..15] of Word;
var
S: array[0..31] of Char;
I:integer;
buf:TBlk;
W: Word;
procedure WriteWord(W:Word); assembler;
asm
mov cx,16
mov ax,[W]
@@l: shr ax,1
push cx; push ax
mov ax,offset WriteSwing1
jnc @@1
mov ax,offset WriteSwing2
@@1: push 9
call ax
pop ax; pop cx
loop @@l
end;
procedure WriteBlock(const Buffer:TBlk; W:Word; Vorton:Integer);
var
I:Integer;
Sum:Word;
begin
for I:=1 to Vorton do WriteSwing1(17); {Vorton}
WriteSwing2(16); {Trennzeichen}
WriteWord(W);
Sum:=0;
for I:=0 to 15 do begin
inc(Sum,Buffer[I]);
WriteWord(Buffer[I]);
end;
WriteWord(Sum);
end;
begin
SetStat_SAVE(PChar(20));
I:=2000;
W:=0;
While GetSize>0 do begin
GetData(PChar(@buf),sizeof(buf));
wvsprintf(S,'%04X<',W); Setstat_SAVE(s);
WriteBlock(buf,W,I);
Inc(W,32);
I:=14;
end;
end;
procedure WriteDataBasicode; far;
{Kontext: MMTASK.TSK}
{Schreibt BytesToWrite Bytes aus dem hBuffer}
var
S: array[0..31] of Char;
I: Integer;
B,Sum:Byte;
procedure WriteByte(B:Byte);
var
I,W:Integer;
begin
Sum:=Sum xor B;
WriteSwing2(18);
for I:=0 to 7 do begin
if B and 1 =0 then WriteSwing2(18)
else begin WriteSwing2(9);WriteSwing2(9);end;
B:=B shr 1;
end;
WriteSwing2(9);WriteSwing2(9);
end;
begin
SetStat_SAVE(PChar(21));
Sum:=0;
for I:=1 to 9000 do WriteSwing2(9);
WriteByte(2);
I:=0;
while GetData(PChar(@B),1)<>0 do begin
WriteByte(B);
if B=$0D then B:=0;
B:=B and $7F;
if B in [1..$1F,$7F] then B:=Byte('.');
S[I]:=Char(B);
if I<sizeof(S)-1 then Inc(I);
if B=0 then begin
SetStat_SAVE(S);
I:=0;
end;
end;
WriteByte(3);
WriteByte(Sum xor $80);
for I:=1 to 3500 do WriteSwing2(9);
end;
{-Dialogfunktionen-----------------------------------------------------------}
const
ID_OfnWriteProt=1040;
function OFNHook(Wnd:HWnd; Msg,wParam:Word; lParam:Longint):Word; export;
{trägt lediglich 'mit Vorblock' ein;
sollt bei SAVE aber erkennen, ob es eine BASIC-Datei ist und das
OFN_ReadOnly-Flag entsprechend setzen! }
var
S: TS31;
begin
OFNHook:=0;
case Msg of
WM_InitDialog: begin
LoadString(Seg(HInstance),108,S,sizeof(S)); {"Mit Vorblock"}
SetDlgItemText(Wnd,ID_OfnWriteProt,S);
end;
end{case Msg};
end;
var
SFile: TS255;
SFilter: TS255;
SExt: TS31;
const
hm: Word=WM_User+100; {HelpMessageString-Nachricht}
const
Ofn: TOpenFileName=(
lStructSize: sizeof(TOpenFileName);
hWndOwner: 0;
hInstance: Seg(HInstance);
lpstrFilter: SFilter;
lpstrCustomFilter: SExt;
nMaxCustFilter: sizeof(SExt);
nFilterIndex: 0;
lpstrFile: SFile;
nMaxFile: sizeof(SFile);
lpstrFileTitle: nil;
nMaxFileTitle: 0;
lpstrInitialDir: nil;
lpstrTitle: nil;
Flags: OFN_LongNames or OFN_ShowHelp or OFN_PathMustExist
or OFN_EnableHook or OFN_OverwritePrompt or OFN_HideReadOnly;
nFileOffset: 0;
nFileExtension: 0;
lpstrDefExt: nil;
lCustData: 0;
lpfnHook: OFNHook;
lpTemplateName: nil);
procedure PrepareOFN;
var
SP1,SP2:PChar;
I: Integer;
begin
hm:=RegisterWindowMessage(HelpMsgString);
Ofn.hWndOwner:=MainWnd;
LoadString(Seg(HInstance),107,SFilter,sizeof(SFilter));
AFilter(ofn.nFilterIndex):=Filter;
if Filter<>NON then begin
SP1:=SFilter; {Mißbrauch!}
for I:=Integer(Filter)*2 downto 2 {min. 1x}
do Inc(SP1,lstrlen(SP1)+1);
{Bug der COMMDLG.DLL bereinigen}
if lstrcmp1(SExt+1,SP1,lstrcmp)<>0 then begin
SP2:=SP1+lstrlen(SP1); {String-Ende}
memmove(SP1+lstrlen(SExt+1),SP2,SFilter+sizeof(SFilter)-SP2);
lstrcpy(SP1,SExt+1); {User-Extension einfügen}
end;
end;
SFile[0]:=#0;
end;
function Char_ANSI2KC:Char; assembler;
{6 Zeichencodes (vornehmlich Umlaute) austauschen}
asm mov ah,5Ch {Pipe-Symbol}
cmp al,7Ch
jz @@2
mov ah,5Dh {Negations-Zeichen}
cmp al,0ACh
jz @@2
mov ah,7Bh {ä}
cmp al,0E4h
jz @@2
inc ah {ö}
cmp al,0F6h
jz @@2
inc ah {ü}
cmp al,0FCh
jz @@2
inc ah {ß}
cmp al,0DFh
jnz @@e
@@2: xchg ah,al
@@e:
end;
procedure PutFileName(pBuf:PChar); assembler;
{Dateiname vom globalen String SFile einbauen;
dabei Name und Erweiterung mit Leerzeichen auffüllen;
weitere Voraussetzungen sind gültige Einträge ofn.nFileOffset,
ofn.nFileExtension und ofn.nFilterIndex.
Bei Erweiterungen mit 3 gleichen Buchstaben UND ofn.nFilterIndex=3
(BASIC-Programm) wird diese an den Anfang gezogen und 80h addiert,
als Extrawurst für BASIC-Programme und ~Daten}
asm cld
les di,[pBuf]
xor dx,dx {Merk-Register}
mov al,byte ptr [ofn.nFilterIndex]
cmp al,SSS {BASIC ausgewählt?}
jne @@1 {nein, Extension egal}
mov cx,[ofn.nFileExtension]
jcxz @@1 {keine Endung!}
mov si,offset SFile
add si,cx
lodsb {1. Zeichen der Endung}
cmp [si],al {Vgl. mit 2. Zeichen}
jnz @@1
cmp [si+1],al {Vgl. mit 3. Zeichen}
jnz @@1
and al,not 20h {Großbuchstabe}
cmp al,'S'
jc @@1 {kein .SSS .. .ZZZ}
cmp al,'Z'
ja @@1
or al,80h {zuerst Extension+80h}
stosb
stosb
stosb
inc dl {Kennungs-Bit: Extension geschrieben}
@@1: mov si,offset SFile
add si,[ofn.nFileOffset]
mov cx,8
@@l1: lodsb
or al,al {Ende extensionsloser Dateiname}
jz @@2
cmp al,'.' {Ende Dateiname (Namensbestandteil)}
je @@2 {eigentlich ist der LETZTE Punkt maßgebend!}
call Char_ANSI2KC
stosb
loop @@l1
@@2: mov al,' '
rep stosb {Rest mit Leerzeichen auffüllen (CX=0 = nix)}
or dl,dl {Endung vorgezogen?}
jnz @@3
inc dl {keine Rekursion!}
mov cx,[ofn.nFileExtension]
jcxz @@2 {Keine Extension?}
mov si,offset SFile
add si,cx
mov cx,3 {noch 3 Bytes löschen!}
jmp @@l1 {Extension <blank-padded> anhängen}
@@3: end;
function GetCoding:ACoding;
{Ermittelt Kodierung, löst "automatisch" anhand Filter auf}
begin
case Coding of
AUTO: case Filter of
Z80: GetCoding:=Z1013;
BAC: GetCoding:=BASICODE;
else GetCoding:=KC;
end;
else GetCoding:=Coding;
end;
end;
function LoadFile:Boolean;
label exi, exi2;
{ const
FilterHeaderSizes: array[KCC..BAC] of Byte=($80,0,0,$20,0);}
{ CodingHeaderSizes_C: array[KC..BASICODE] of Byte=($80,$80,0,$20,0);}
{ CodingHeaderSizes_B: array[KC..BASICODE] of Byte=(11,11,0,0,0);}
var
{ BytesWritten:Longint;}
f,Add,Sub,I: Integer;
Sum: Byte;
pBuf: PChar;
hLdr: THandle;
C: Char;
Autostart: Boolean;
aadr,eadr,sadr: Word;
buf: TBlock;
This_Coding: ACoding;
begin
LoadFile:=false;
PrepareOfn;
with ofn do begin
Flags:=Flags or OFN_FileMustExist;
{or OFN_AllowMultiselect: Zukunftsmusik!}
if not GetOpenFileName(ofn) then exit;
Filter:=AFilter(ofn.nFilterIndex);
{$IFOPT D+} asm int 3 end; {$ENDIF}
f:=_lopen(SFile,0);
if f=-1 then goto exi2;
BytesToWrite:=_llseek(f,0,2); {Dateilänge}
if BytesToWrite=0 then goto exi;
_llseek(f,0,0); {Zeiger zurück}
Add:=0;
case Filter of
DUM: begin
if not (Coding in [AUTO,KC,MPM,Z1013]) {Code-Zwang!}
then This_Coding:=KC;
end;
SSS: begin
if not (Coding in [AUTO,KC,MPM,HS]) {Code-Zwang!}
then This_Coding:=KC;
Add:=11;
end;
BAC: if not (Coding in [AUTO,BASICODE]) {Code-Zwang!}
then This_Coding:=BASICODE;
end;
if Coding<>This_Coding
then SendDlgItemMessage(MainWnd,12,CB_SetCurSel,Word(This_Coding),0);
This_Coding:=GetCoding;
case This_Coding of
KC,MPM: begin
if Filter=DUM then add:=$80;
if Filter=Z80 then add:=$60;
end;
HS: begin
add:=$80;
if Filter=DUM then add:=$100;
if Filter=SSS then add:=$0FE; {Längen-Byte verschwindet!}
if Filter=Z80 then add:=$0E0;
hLdr:=LoadResource(Seg(HInstance), {Turboloadervorblock}
FindResource(Seg(HInstance),MakeIntResource(102),RT_RCData));
if hLdr=0 then RunError(220); {Böser Fehler!}
end;
Z1013: begin
if Filter=KCC then add:=-$60;
if Filter=DUM then add:=$20;
end;
end;
Inc(BytesToWrite,Add);
hBuffer:=GlobalAlloc(GMEM_MoveAble or GMEM_Share,BytesToWrite);
pBuf:=GlobalLock(hBuffer);
if This_Coding=HS then begin
MemMove(pBuf,LockResource(hLdr),$100);
UnlockResource(hLdr);
FreeResource(hLdr);
end;
Autostart:=false;
if (Add<>0)
and (_lread(f,PChar(@buf),Add)<>Add) then goto exi; {Datei zu kurz}
case Filter of
KCC: begin {.KCC}
aadr:=PWord(@buf[$11])^;
eadr:=PWord(@buf[$13])^;
if buf[$10]>=3 then begin
Autostart:=true;
sadr:=PWord(@buf[$15])^;
end;
end;
DUM: begin {Speicherabzug: hier fest ab 200h}
aadr:=$200; {CP/M-Freaks würden hier 100h setzen}
eadr:=BytesToWrite+aadr;
end;
SSS: begin {BASIC-Programm}
aadr:=$401; {wird eigentlich nicht benutzt}
eadr:=PWord(@buf[0])^+aadr;
end;
Z80: begin {.Z80 - noch nicht benutzt}
aadr:=PWord(@buf[0])^;
eadr:=PWord(@buf[2])^;
if Char(buf[$0C])='C' then begin
Autostart:=true;
sadr:=PWord(@buf[4])^;
end;
end;
end{case};
if Filter=SSS then begin {Extrawurst für BASIC}
MemMove(pbuf+$80,pbuf+$70,$0A);
PWord(@pbuf[$81])^:=eadr;
end else begin
PWord(pbuf+$81)^:=aadr;
PWord(@pbuf[$84])^:=eadr;
end;
PWord(pbuf+$A8)^:=eadr-aadr;
if Autostart then begin
pbuf[$E0]:=Char($C3); {JMP nn}
PWord(pbuf+$E1)^:=sadr;
end;
if _hread(f,pbuf+$100,BytesToWrite)<>BytesToWrite then goto exi;
Inc(BytesToWrite,$100); {nun: alle Bytes}
sum:=0; {Rest laden - Datei zu kurz?}
for i:=$100 to BytesToWrite-1 do inc(sum,byte(pBuf[I]));
pBuf[$D8]:=char(Sum); {Prüfsumme einsetzen}
GlobalUnlock(hBuffer);
end;
hBuffer:=GlobalAlloc(GMEM_MoveAble or GMEM_Share or GMEM_ZeroInit,
BytesToWrite+Add);
pBuf:=GlobalLock(hBuffer);
if _hread(f,pBuf+Add,BytesToWrite)<>BytesToWrite then goto exi;
Inc(BytesToWrite,Add);
if (Add<>0)
or (Filter=KCC) and (pbuf[0]=#0)
then PutFileName(pBuf);
if (Filter=DUM)
and (coding<>HS) then begin
PByte(pBuf+$10)^:=2; {ARGN; ARG1 bleibt 0}
PWord(pBuf+$13)^:=LongRec(BytesToWrite).Lo; {ARG2=EAdr+1}
end;
if _lclose(f)<>0 then goto exi2;
GlobalUnlock(hBuffer);
LoadFile:=true;
exit;
exi:
_lclose(f);
exi2:
GlobalUnlock(hBuffer);
hBuffer:=GlobalFree(hBuffer);
MBox1(MainWnd,105,SFile);
end{with};
{ end;}
const
InvalChars_LFN: array[0..6] of Char='"<|>:\/'; {' .+,;=[]' für SFN}
procedure StripInvalChars(S:PChar); assembler;
asm cld
push ds
push ds
pop es
lds si,[S]
@@l: lodsb
or al,al
jz @@e
mov cx,7
mov di,offset InvalChars_LFN
repne scasb
jne @@l
mov byte ptr [si-1],'_'
jmp @@l
@@e: pop ds
end;
procedure Convert_HS2KCC(pBuf:PChar);
{schaufelt HS-Turbo-Daten in KC-MC oder KC-BASIC-Daten im Speicher um;
sollte beim Lader geschehen!}
begin
if pBuf[$80]=char($21) then begin {Maschinenprg.}
Dec(BufPtr,$81);
asm
les di,[pBuf]
mov si,di
add di,11
xor ax,ax
mov cx,6
rep stosb
add si,81h
seges lodsw
stosw
inc si
seges lodsw
stosw
add si,5Ah
sub di,5
push si
seges lodsb
cmp al,0C3h
jnz @@1
mov al,3
stosb
add di,4
seges lodsw
jmp @@2
@@1: mov al,2
stosb
add di,4
xor ax,ax
@@2: stosw
mov cx,69h
xor ax,ax
rep stosb
pop si
add si,20h
mov cx,word[BufPtr]
@@3: seges lodsb
stosb
loop @@3
end;
BufContent:=KCC;
end else begin {BASIC Prg.}
Dec(BufPtr,$100);
asm
les di,[pBuf]
mov si,di
add si,81h
seges lodsw
dec ax
stosw
add si,7Eh
mov cx,word[BufPtr]
@@1: seges lodsb
stosb
loop @@1
end;
BufContent:=SSS;
end;
end;
function SaveFile:Boolean;
var
BytesWritten: Longint;
f,Add: Integer; {Add= 0 oder 11}
pBuf: PChar;
C: Char;
WantFilter: AFilter;
begin
SaveFile:=false;
Add:=0;
pBuf:=GlobalLock(hBuffer);
if BufContent=NON then Convert_HS2KCC(pBuf);
Filter:=BufContent;
PrepareOfn;
{für Turbolader h#s und Z1013 ohne Headersave Dateiname weglassen;
durch zweckmäßige Vorgabe wie UnbenanntXXX ersetzen}
case Filter of
KCC,SSS: GetFileNameKC(SFile,pBuf);
Z80: GetFileNameZ80(SFile,pBuf);
end;
StripInvalChars(SFile);
with ofn do begin
if not GetSaveFileName(ofn) then exit;
WantFilter:=AFilter(ofn.nFilterIndex);
f:=_lcreat(SFile,0);
if f=-1 then begin
GlobalUnlock(hBuffer);
exit;
end;
Dec(BufPtr,Add);
BytesWritten:=_hwrite(f,pBuf+Add,BufPtr);
GlobalUnlock(hBuffer);
if (_lclose(f)<>0) or (BytesWritten<>BufPtr) then begin
MBox1(MainWnd,106,SFile);
exit;
end;
SaveFile:=true;
end;
end;
function GetInt(var S: PChar; Def:Integer):Integer;
{macht strtok() und atoi() oder reicht Def durch}
var
SP: PChar;
I,J: Integer;
begin
GetInt:=Def;
if S=nil then exit;
SP:=lStrChr(S,' ');
if SP<>nil then begin
SP^:=#0;
end;
Val(S,I,J);
if SP<>nil then S:=SP+1;
if J=0 then GetInt:=I;
end;
procedure HandleMMError(Code:Integer);
var
S: array[0..255] of Char;
begin
case Code of
MMSysErr_Allocated: MBox1(MainWnd,102,nil);
WAVERR_Sync: MBox1(MainWnd,101,nil);
else begin
WaveInGetErrorText(Code,S,sizeof(S));
MBox1(MainWnd,103,S)
end;
end;
end;
procedure SetModus(NewModus:AModus);
begin
if Modus<>NewModus then begin
Modus:=NewModus;
DragAcceptFiles(MainWnd,Modus=NONE);
EnableWindow(GetDlgItem(MainWnd,1),Modus=NONE);
EnableWindow(GetDlgItem(MainWnd,22),Modus=NONE);
ShowWindow(GetDlgItem(MainWnd,2),Integer(Modus<>NONE));
ShowWindow(GetDlgItem(MainWnd,3),Integer(Modus=NONE));
ShowWindow(GetDlgItem(MainWnd,19),Integer(Modus=LOAD));
end;
if GetFocus=0 then SetFocus(GetDlgItem(MainWnd,12));
end;
function GetLibProc(Entry:PChar):TFarProc;
var
P: TFarProc;
LibName: TS31;
begin
GetLibProc:=nil;
SendDlgItemMsgP(MainWnd,12,CB_GetLbText,Word(coding),@LibName);
LibInst:=LoadLibrary(LibName);
if LibInst<32 then begin
MBox1(MainWnd,114,LibName);
exit;
end;
P:=GetProcAddress(LibInst,Entry);
if P=nil then begin
MBox1(MainWnd,115,Entry);
FreeLibrary(LibInst);
LibInst:=0;
exit;
end;
GetLibProc:=P;
end;
procedure DoEndProcess(OK:Boolean);
begin
if OK and (Modus=LOAD) then SaveFile;
if hBuffer<>0 then hBuffer:=GlobalFree(hBuffer);
if LibInst>=32 then FreeLibrary(LibInst);
SetModus(NONE);
end;
var
DefEditProc:TFarProc;
function EditHook(Wnd:HWnd;Msg,wParam:Word;lParam:LongInt):LongInt; export;
{Anzapfung Edit-Fenster mit Rollbalken zur bequemen Wert-Veränderung mit
Maus und/oder Cursortasten (sog. unterklassifiziertes Fenster)}
var
I,J,E: Integer;
S: TS7;
begin
EditHook:=0;
I:=Msg2VScroll(Msg,wParam,2);
if I=0 then begin
EditHook:=CallWindowProc(DefEditProc,Wnd,Msg,wParam,lParam);
exit;
end;
GetWindowText(Wnd,S,sizeof(S));
Val(S,J,E);
if E<>0 then begin
MessageBeep(MB_IconHand);
exit;
end;
J:=between(J+I,1,7);
wvsprintf(S,'%d',J);
SetWindowText(Wnd,S);
end;
procedure IODev_AddString(W:HWnd; S:PChar; J,K:Integer);
{Fügt String hinzu mit ItemData J, ist J=K wird dieser Eintrag selektiert.
Für das Füllen der Ein- und Ausgabe-Geräte-Kombinationsfenster}
var
I: Integer;
begin
I:=SendMessageP(W,CB_AddString,0,S);
SendMessage(W,CB_SetItemData,I,J);
if J=K then SendMessage(W,CB_SetCurSel,I,0);
end;
function MainDlgProc(Wnd:HWnd; Msg,wParam:Word; lParam:LongInt):Bool; export;
var
lPar: LongRec absolute lParam;
WH: PWaveHdr absolute lParam;
dis: PDrawItemStruct absolute lParam;
S: TS255;
S2: TS31;
WaveOutCaps: TWaveOutCaps;
WaveInCaps: TWaveInCaps absolute WaveOutCaps; {ist kürzer!}
LF: TLogFont absolute S;
R: TRect absolute S;
SP: PChar;
Proc: TFarProc absolute SP;
DC: HDC absolute SP;
W: Word;
I,J: Integer;
B: Bool absolute J;
vsrec: array[0..4] of Integer;
begin
MainDlgProc:=false;
case Msg of
WM_InitDialog: begin
MainWnd:=Wnd; {Globale Variable setzen}
W:=GetSystemMenu(Wnd,false);
DeleteMenu(W,SC_Zoom,MF_ByCommand);
DeleteMenu(W,SC_Size,MF_ByCommand);
ShowWindow(Wnd,CmdShow); {Icon? - Sofort wirksam machen!}
Back:= CreateSolidBrush($FF0000); {KC-Blau}
Green:=CreateSolidBrush($008000); {dunkelgrün}
PostMessage(Wnd,WM_ContinueInit,0,0);
end;
WM_ContinueInit: begin
LongInt(DefEditProc):=
SetWindowLong(GetDlgItem(Wnd,11),GWL_WndProc,LongInt(@EditHook));
SetWindowLong(GetDlgItem(Wnd,13),GWL_WndProc,LongInt(@EditHook));
UpdateWindow(Wnd); {würde in InitDialog nichts bringen !!}
SP:=nil;
if RegGetRoot(RegPath,S,sizeof(S)) then begin
SP:=S;
Installed:=true;
end;
Retries:=GetInt(SP,4);
Coding:=ACoding(GetInt(SP,0));
WaveVol:=GetInt(SP,4);
WaveInDev:=GetInt(SP,0);
WaveOutDev:=GetInt(SP,0);
SetDlgItemInt(Wnd,11,Retries,true);
if WaveInDev=-2 then EnableWindow(GetDlgItem(Wnd,11),false);
LoadString(Seg(HInstance),104,S,sizeof(S)); {Kassetten-Formate}
SP:=S;
W:=GetDlgItem(Wnd,12); {CB Format}
while SP^<>#0 do begin
SendMessageP(W,CB_AddString,0,SP);
Inc(SP,lstrlen(SP)+1);
end;
SendMessageP(W,CB_Dir,0,PChar(UserDllFilter));
SendMessage(W,CB_SetCurSel,Word(Coding),0);
SetDlgItemInt(Wnd,13,WaveVol,true);
W:=GetDlgItem(Wnd,14); {Handle Kombifenster WaveIN}
LoadString(Seg(HInstance),112,S2,sizeof(S2)); {"Datei %s"}
SP:=WaveFileName;
wvsprintf(S,S2,SP);
IODev_AddString(W,S,-2,WaveInDev);
I:=WaveInGetNumDevs;
for J:=-1 to I-1 do begin {mit Wave_Mapper (-1) beginnen}
if (WaveInGetDevCaps(Word(J),@WaveInCaps,sizeof(WaveInCaps))=0)
and (WaveInCaps.dwFormats and WAVE_Format_2M08 <>0)
then IODev_AddString(W,WaveInCaps.szPName,J,WaveInDev);
end;
W:=GetDlgItem(Wnd,15); {Handle Kombobox WaveOUT}
IODev_AddString(W,S,-2,WaveOutDev);
I:=WaveOutGetNumDevs;
for J:=-1 to I-1 do begin {mit Wave_Mapper (-1) beginnen}
if (WaveOutGetDevCaps(Word(J),@WaveOutCaps,sizeof(WaveOutCaps))=0)
and (WaveOutCaps.dwFormats and WAVE_Format_2M08 <>0)
and (WaveOutCaps.dwSupport and WAVECAPS_Sync =0)
then IODev_AddString(W,WaveOutCaps.szPName,J,WaveOutDev);
end;
GetObject(SendMessage(Wnd,WM_GetFont,0,0),sizeof(lf),@lf);
lstrcpy(lf.lfFaceName,'Courier');
lf.lfWeight:=FW_Bold;
Font:=CreateFontIndirect(lf);
W:=GetDlgItem(Wnd,10);
SendMessage(W,WM_SetFont,Font,0);
DC:=GetDC(W);
wParam:=SelectObject(DC,Font);
lParam:=GetTextExtent(DC,'X',1); {So viel Aufwand für eine Zeichenbreite!}
SelectObject(DC,wParam);
ReleaseDC(W,DC);
GetClientRect(W,R);
StatusChars:=max((R.right-R.left) div lPar.hi,31);
DragAcceptFiles(Wnd,true);
end;
WM_CtlColor: if (lPar.Hi=CtlColor_Static)
and (GetDlgCtrlID(lPar.Lo)=10) then begin
MainDlgProc:=Bool(Back);
SetTextColor(wParam,$FFFFFF);
SetBkMode(wParam,Transparent);
end;
WM_EndProcess: DoEndProcess(Boolean(wParam));
WM_SetStatus: begin
SetDlgItemText(Wnd,10,StatusBuf);
end;
WM_ReportWaveError: begin {in MMTASK Fehler bei WaveInOpen()}
HandleMMError(wParam);
hBuffer:=GlobalFree(hBuffer);
SetModus(NONE);
end;
WM_DrawItem: with dis^ do begin
I:=rcitem.left;
J:=rcItem.right;
rcItem.right:=MulDiv(WavePeak,J-I,128)+I;
FillRect(hDC,rcItem,Green);
rcItem.left:=rcItem.right; rcItem.right:=J;
FillRect(hDC,rcItem,GetStockObject(Gray_Brush));
rcItem.left:=I; {restaurieren (sicherheitshalber)}
end;
WM_QueryEndSession: if (Modus<>NONE)
and (MBox1(Wnd,113,nil)<>IDYes) then MainDlgProc:=true;
WM_EndSession: if Bool(wParam) and Installed
then SendMessage(Wnd,WM_Command,23,0);
WM_Close: begin
if (Modus<>NONE) then begin
if MBox1(Wnd,113,nil)<>IDYes then begin
MainDlgProc:=true; {Nicht beenden!}
exit;
end;
SendMessage(Wnd,WM_Command,IDCancel,0);
end;
DeleteObject(Green);
DeleteObject(Back);
DeleteObject(Font);
if Installed then SendMessage(Wnd,WM_Command,23,0);
EndDialog(Wnd,1);
end;
WM_Command: case wParam of
{Änderungen an den Eingabe-Elementen erfassen}
11: if lPar.Hi=EN_Change then begin
I:=GetDlgItemInt(Wnd,wParam,nil,true);
if (I>0) and (I<=7) then Retries:=I;
end;
12: if lPar.Hi=CBN_SelChange then begin
I:=SendMessage(lPar.Lo,CB_GetCurSel,0,0);
if I>=0 then Coding:=ACoding(I){SendMessage(lPar.Lo,CB_GetItemData,I,0)};
end;
13: if lPar.Hi=EN_Change then begin
I:=GetDlgItemInt(Wnd,wParam,nil,true);
if (I>0) and (I<=7) then begin
WaveVol:=I;
I:=sqr(WaveVol)*2;
if Amp>=0 then Amp:=I else Amp:=-I;
end;
end;
14: if lPar.Hi=CBN_SelChange then begin
I:=SendMessage(lPar.Lo,CB_GetCurSel,0,0);
if I>=0 then begin
WaveInDev:=SendMessage(lPar.Lo,CB_GetItemData,I,0);
EnableWindow(GetDlgItem(Wnd,11),WaveInDev<>-2);
end;
end;
15: if lPar.Hi=CBN_SelChange then begin
I:=SendMessage(lPar.Lo,CB_GetCurSel,0,0);
if I>=0 then WaveOutDev:=SendMessage(lPar.Lo,CB_GetItemData,I,0);
end;
{Tastendrücke}
3: SendMessage(Wnd,WM_Close,0,0); {Programmende}
IDCancel: begin {Abbruch}
LoadString(Seg(HInstance),110,S,sizeof(S));
SetDlgItemText(MainWnd,10,S);
if Modus<>NONE then begin
if IsTask(Task) then PostAppMessage(Task,WM_Quit,0,0)
else SetModus(NONE);
end;
end;
1: if Modus=NONE then begin {Datei einlesen}
case coding of
AUTO: Proc:=@ReadDataAuto;
KC: Proc:=@ReadDataKCC;
MPM: Proc:=@ReadDataMPMTurbo;
HS: Proc:=@ReadDataHSTurbo;
Z1013: Proc:=@ReadDataZ1013;
BASICODE: Proc:=@ReadDataBasicode;
else begin
Proc:=GetLibProc('LOAD'); {Benutzer-definiert...}
if Proc=nil then exit;
end;
end;
hBuffer:=GlobalAlloc(GMEM_MoveAble or GMEM_Share,$8000);
BufPtr:=0;
SetModus(LOAD);
MMTaskCreate(@WaveInTask,Task,LongInt(Proc));
end;
22: if Modus=NONE then begin {Datei ausgeben}
if not LoadFile then exit;
case GetCoding of
KC: Proc:=@WriteDataKCC;
MPM: Proc:=@WriteDataMPMTurbo;
HS: Proc:=@WriteDataHSTurbo;
Z1013: Proc:=@WriteDataZ1013;
BASICODE: Proc:=@WriteDataBasicode;
else begin
Proc:=GetLibProc('SAVE');
if Proc=nil then exit;
end;
end;
BufPtr:=0;
Amp:=sqr(WaveVol)*2;
SetModus(SAVE);
MMTaskCreate(@WaveOutTask,Task,LongInt(Proc));
end;
23: begin
Installed:=true;
wvsprintf(S,'%d %d %d %d %d',Retries);
RegSetRoot(RegPath,S);
end;
9: WinHelp(Wnd,HelpFileName,HELP_Contents,0);
end{WM_Command};
else if Msg=hm then WinHelp(Wnd,HelpFileName,HELP_Context,1);
end;
end;
const
wc:TWndClass=(
style: CS_VRedraw or CS_HRedraw;
lpfnWndProc: @DefDlgProc;
cbClsExtra: 0;
cbWndExtra: DlgWindowExtra;
hInstance: Seg(HInstance);
hIcon: 0;
hCursor: 0;
hbrBackground:Color_Background+1;
lpszMenuName: nil;
lpszClassName:'KCLOAD');
{FAR+EXPORT-Wrapper-Routinen (interne Routinen arbeiten mit NEAR)}
function ReadSwing:Integer; export;
begin ReadSwing:=ReadSwing1; end;
procedure WriteSwing(Len:Integer); export;
begin WriteSwing1(Len); end;
procedure EndProcess(OK:Boolean); export;
begin EndProcess1(OK); end;
function SetStatus(S:PChar):Integer; export;
begin
if Modus=LOAD
then SetStatus:=SetStat_LOAD(S)
else SetStatus:=SetStat_SAVE(S);
end;
function GetSizeData:Longint; export;
begin GetSizeData:=GetSize; end;
function GetDataBlock(P:PChar; Size:Integer):Integer; export;
begin GetDataBlock:=GetData(P,Size); end;
procedure AddDataBlock(P:PChar; Size:Integer); export;
begin AddData(P,Size); end;
procedure GetName(P:PChar; Size:Integer); export;
begin lstrcpyn(P,SFile+ofn.nFileOffset,Size); end;
procedure SetNameHint(P:PChar); export;
begin lstrcpyn(SFile,P,sizeof(SFile)); end;
exports
ReadSwing index 2, {Flußwechsel lesen}
WriteSwing index 3, {Flußwechsel schreiben}
EndProcess index 4, {Vorzeitig abbrechen}
SetStatus index 5, {Statuszeile setzen}
GetSizeData index 6, {restliche Bytes ermitteln}
GetDataBlock index 7, {Nächsten Datenblock aus Puffer lesen}
AddDataBlock index 8, {Neuen Datenblock in Puffer anhängen}
GetName index 9, {DOS-Dateiname holen}
SetNameHint index 10; {DOS-Dateiname vorschlagen}
begin
LoadString(Seg(HInstance),100,AppName,sizeof(AppName));
WUtils.StdMBoxTitle:=AppName; {MessageBox-Titel in Unit setzen}
if HPrevInst<>0 then begin {Nicht doppelt starten!}
MainWnd:=FindWindow('KCLOAD',nil);
SetActiveWindow(MainWnd);
ShowWindow(MainWnd,SW_Restore);
halt(221);
end;
wc.hIcon:=LoadIcon(Seg(HInstance),MakeIntResource(100));
wc.hCursor:=LoadCursor(0,IDC_Arrow);
RegisterClass(wc);
DialogBox(Seg(HInstance),MakeIntResource(100),0,@MainDlgProc);
end.
{
Log der Veränderungen:
01/02:
* Wave-Datei-Verarbeitung auf Windows-Chunk-Befehle umgestellt
- Wave-Datei-Voreinstellung in Kombinationsfenster korrigiert
}
Detected encoding: OEM (CP437) | 1
|
|