Floppy Intern
Ich heisse Sie herzlich willkommen zum
5 . Teil unseres Floppykurses.
Da in den letzten 4 Teilen die Routinen
etwas zu kurz gekommen sind, möchte ich
Ihnen diesmal folgende Routinen vorstellen:
1 . Whole-Load + Whole-Save Diese beiden Files ermöglichen es auch
den Bereich ab $ d000 zu laden und zu
speichern!
2 . Directory Dieses File beschreibt eine Directoryroutine, die beim Lesen der BAM nicht
den Bereich ab $0800 zerstört!
3 . Read 41+ Write 41 Diese beiden Files beinhalten eine Leseund schreibe Routine fuer den Track 41 !
Diese Files befinden sich auf ihrer
Diskette und können mit $ CD00 gestartet
werden.
Beginnen möchte nun ich mit der Whole-Load Routine.
Wie Sie als Assemblerprogrammierer sehr
wahrlich wissen läßt sich auch der
Bereich :$a000-$c000
$d000-$ffff
nutzen, indem man in die Speicherstelle
$01 Werte zwischen $34 und $37 poked.
Wie aber läßt sich der Bereich abspeichern?
Von Modulen wie z. B. die Action Replay
wissen wir, daß dies ohne weiteres funktioniert.
Was aber ist wenn Sie zum Beispiel ein
Spiel geschrieben haben mit Highscore-Save und Sie nur noch einen Bereich bei
$ d000 frei haben?
Die normale Systemroutine zum laden und
Speichern von Files funktioniert hier
nicht und auch Ihr Modul kann Ihnen bei
diesem Problem nicht helfen!
Ich stelle Ihnen nun zuerst eine Routine
zum laden eines Files ab $ d000 vor
und danach die dazugehörige Speicherroutine.
lda #$01 ;Filenummer
ldx #$08 ;Geraetenummer
ldy #$00 ;Sekundaeradresse
jsr $fe00 ;setzen
lda #$ ;laenge Filename
ldx #$ ;Lo- und
ldy #$ ;Hi-Byte Filename
jsr $fdf9 ;setzen
jsr $f34a ;open
ldx #$01 ;geraet auf
jsr $f20e ;Empfang schalten
jsr $ee13 ;startadresse
sta $ae ;des
jsr $ee13 ;Files
sta $af ;holen
m02 jsr $ee13 ;1 Byte lesen
sei ;IRQ setzen
ldy #$00 ;Zaehler auf null
ldx #$34 ;kompletten
stx $01 ;Speicher freigeben
sta ($ae),y ;und abspeichern
ldx #$37 ;Urzustand
stx $01 ;wieder herstellen
cli ;IRQ loeschen
inc $ae ;Zaehler erhoehen
bne $m01 ;schon Null?
m01 inc $af ;dann naechster Block
bit $90 ;Ende des Files schon
bvc $m02 ;erreicht,sonst weiter
jsr $f333 ;Empfang aus
lda #$01 ;Close
jmp $f291 ;File
Wie Sie sehen, bestand der Trick darin, daß wir das File byteweise reingeladen
und vor dem Poke in den Speicher einfach
den Vektor $01 verändert haben.
Somit stand uns der komplette Speicher
zur Verfügung.
Das dieser Trick auch beim Speichern
funktioniert versteht sich von selbst.
Hier nun die Speicherroutine:
lda #$01 ;Filenummer
ldx #$08 ;Geraetenummer
ldy #$00 ;Sekundaeradresse
jsr $fe00 ;setzen
lda #$ ;laenge Filename
ldx #$ ;Lo- und
ldy #$ ;Hi-Byte Filename
jsr $fdf9 ;setzen
lda #$61 ;Kanal 1+$60 fuer Save in
sta $b9 ;Sekundaeradresse poken
jsr $f3d5 ;IEC-Bus eroeffnen
lda #$08 ;Geraeteadresse
jsr $ed0c ;Listen
lda #$61 ;Sekundaeradresse
jsr $edb9 ;Seclst
ldx #$ ;Lo- und
ldy #$ ;Hi-Byte
stx $ac ;der
sty $ad ;Startadresse
ldx #$ ;Lo- und
ldy #$ ;Hi-Byte
stx $ae ;der
sty $af ;Endadresse
lda $ac ;Startadresse
jsr $eddd ;ins
lda $ad ;File
jsr $eddd ;schreiben
m01 sei ;IRQ setzen
ldy #$00 ;Zaehler=0 und kompletten
sty $01 ;Speicher freigeben
lda ($ac),y ;Byte holen
ldy #$37 ;Urzustand wieder
sty $01 ;herstellen
cli ;IRQ loeschen
jsr $eddd ;und Speichern
jsr $fcdb ;diese Routinen werden im
jsr $fcd1 ;Anschluss hieran
bcc $m01 ;erklaert
jsr $edfe ;Unlisten
lda #$08 ;Geraeteadresse
jsr $ed0c ;Listen
lda #$e1 ;Sekundaeradresse + Bit 7
jsr $edb9 ;Seclst
jmp $edfe ;Unlisten
In dieser Routine haben wir nun zwei Unbekannte. Zunächst mal die Kombination:
lda #$61
sta $b9
jsr $f3d5
lda #$08
jsr $ed0c
lda #$61
jsr $edb9
Diese Routine ist eine andere Form der
' Open' Routine und dient lediglich der
neuen Erkenntnis!
Dann waren im Code noch 2 Systemadressen
die ich angesprungen habe.
jsr $fcdb
+jsr $fcd1
In $ fcdb steht folgende Routine:
inc $ac
bne $m01
inc $ad
m01 rts
Hier werden eigentlich nur die Counter
für den Speicherbereich erhöht.
In $ fcd1 steht dann:
sec
lda$ac
sbc$ae
lda$ad
sbc$af
rts
Hier werden die noch verbleibenden von
den schon geschriebenen Bytes abgezogen.
Damit die Routine so kurz wie möglich
bleibt, habe ich diese beiden Systemroutinen angesprungen!
Das File WholeSave speichert den Bereich
von $ d000-$ e000 ab und kann mit dem File
WholeLoad wieder reingeladen werden.
Das File das abgespeichert und geladen
werden kann habe ich ' TEST' genannt!
Nachdem wir nun über das Laden und
Speichern von Files bestens bescheid
wuessen, möchte ich Sie nun mit einer
neuen nützlichen Routine vertraut
machen, der Directory Routine!
Vor allem deshalb nützlich, weil diese
Routine bei einer längeren Directory
nicht in den eventuellen Programmbereich
ab $0800 reinschreibt!
Denn ich denke, jedem ist es schon mal
passiert, der gerade ein tolles Programm
geschrieben hat und jetzt gerne wissen
moechte, ob noch genug Platz auf der Disk
ist.
Die Directory wird geladen, zufrieden
stellt man fest:
Es ist ja noch genug Platz frei!
Als nun der letzte Blick auf das Programm erfolgt, muß man voller Wut feststellen, daß die Directory, das neue
Programm zum Teil überschrieben hat.
C-64 Besitzer die von Beginn an ein
Modul besessen haben, werden dieses
Dilemma nie erlebt haben, da eigentlich
alle Module eine Directory Routine benutzen, die nicht in den Programmbereich
ab $0800 schreibt.
Die Routine die ich Ihnen jetzt vorstelle, verhindert nicht nur, daß ein
Programm überschrieben wird, sondern
ist auch schneller und kürzer als die
System Routine!
ldx #$08 ;Geraeteadresse;
ldy #$00 ;Sekundaeradresse;
jsr $fe00 ;setzen;
lda #$01 ;1 Byte ab;
ldx #$60 ;$a360='$' fuer;
ldy #$a3 ;BAM Zugriff;
jsr $fdf9 ;setzen;
jsr$f34a open;
ldx#$01 Eingabegeraet;
jsr$f20e setzen;
jsr$f157 Header holen;
jsr$f157 mit BASIN;
m03 jsr$f157 Track + Sektor;
jsr$f157 holen mit BASIN;
lda$90 Status-Bit;
bne$m01 testen;
jsr$f157 in A/X 16-Bit;
tax Blockzahl;
jsr$f157 holen und in;
jsr$bdcd Dezimal umrechnen;
jsr$ab3b Space;
m02 jsr$f157 1 Byte vom Filename;
jsr$f1ca holen und ausgeben;
bne$m02 Schon alle Bytes?;
lda#$0d dann ASCII $0d fuer;
jsr$f1ca Return ausgeben;
lda$dc01 Komplette;
cmp#$7f Directory schon;
bne$m03 ausgegeben?;
m01 lda#$01 dann;
jsr$f291 Close;
jmp$ f333 aktiven Kanal schliessen;
Wie Sie sehen ist diese Routine gar
nicht so schwer zu verstehen.
Lediglich zwei neue System Routinen
habe ich benutzt!
1 . jsr $ bdcd Diese Routine wandelt eine Hexadezimale
Zahl in eine Dezimale Zahl um.
Vorher muß man nur in die Register Akku
und X Hiund Lo-Byte der Hexzahl eintragen.
2 . jsr $ ab3 b
Diese Systemroutine gibt ein Leerzeichen
auf dem Bildschirm aus, denn zwischen
Blockzahl und Filename ist stets eine
Leerstelle.
Kopierschutz
Das letzte Thema, mit dem wir uns heute
befassen werden ist wohl eines der umstrittensten in der C-64 Geschichte. Ich
spreche vom altbekannten Kopierschutz!
In den Jahren 1985-1988 lieferten sich
Softwarehaeuser und Raubkopierer ein
packendes Duell!
Die Softwarefirmen steckten sehr viel
Zeit und Geld in ihre Kopierschütze!
Die Raubkopierer entwicklelten unterdessen nach jeder neuen Kopierschutzvariante ein neues Kopierprogramm! Hier
ein paar Varianten von Kopierschuetzen:
1 . Halftracks ( der Kopf wird nur um einen halben Track
bewegt)
2 . Speedchange ( die Bitrate wurde auf eine andere
Geschwindigkeit eingestellt)
3 . Readerrors ( absichtliches erzeugen eines Fehlers)
4 . Format 41( eine Diskette wird statt 35 Tracks auf 41 Tracks formatiert.)
Vom Floppy-DOS aus sind dieses alles
Fehler, die nicht verarbeitet werden
koennen, folglich beim kopieren nicht
beruecksichtigt werden.
Die Kopie enthält also den absichtlich
erzeugten Fehler nicht mehr.
Hier genau ist der Trick eines Schutzes!
Denn fragte man diesen Fehler im Spiel
ab und er war nicht mehr vorhanden, waren
auf einmal entweder keine Sprites mehr
zu sehen oder das Spiel hing sich völlig
auf!
Der schlaue User kaufte sich deshalb
lieber ein Original um es auch 100% ig
Fehlerfrei spielen zu können!
Nicht aber die Raubkopierer, die sofort
versuchten ein besseres Kopierprogramm
zu schreiben, dass auch den neuen Schutz
kopierte!
Hiermit war das Programm zwar kopiert
und 100% ig lauffähig, nicht aber der
Schutz entfernt!
Später fingen dann ein paar Freaks an
das Programm nicht nur zu kopieren, sondern auch die Schutzabfrage zu übergehen, so daß das Spiel von jedem Xbe- liebigen Kopierprogramm kopiert werden
konnte.
Es gab fortan also zwei Arten von Softwarefirmengegnern:
1 . Die Raubkopierer - sie waren noch recht
harmlos für die Firmen, da nur wenige
so gute Kopierprogramme besassen.
2 . Die Crackersie waren die eigentlichen
Firmenschaedlinge, da sie den Schutz
komplett entfernten und jeder es kopieren konnte.
Soviel zur Geschichte!
Ich stelle Ihnen jetzt einen Schutz
aus dem oben genannten Sortiment vor.
Ich spreche vom Format 41 Schutz! Wie
schon erwähnt, muß die Diskette vorher
mit einem Disketteneditor auf 41 Tracks
formatiert werden.
Hier für eignet sich zum Beispiel der
Disk-Demon!
Nachdem nun die Diskette dementsprechend
formatiert wurde, müssen wir zunächst
mal eine Write-Routine für den Track 41 entwickeln.
Auf der Diskette befindet sich das dazu
gehörige Programm, welches auch die
Floppyroutine in die Floppy verfrachtet.
Da die Routine zum starten eines Programms in der Floppy eigentlich aus den
vergangenden Kursteilen bekannt sein
müßte, erkläre ich nun lediglich die
Floppyroutine:
lda#$03 Puffer ab
sta$31 $0300
jsr$f5e9 Parity fuer Puffer
sta$3a berechnen u. speichern
jsr$f78f Puffer in GCR umrechnen
jsr$f510 Headerblock suchen
ldx#$09
m01 bvc$m01 Byte ready?
clv
dex 9 Bytes GAP nach Header-
bne$m01 block ueberlesen
lda#$ff Port A (Read/Writehead)
sta$1c03 auf Ausgang
lda$1c0c PCR auf
and#$1f Ausgabe
ora#$c0 umschalten
sta$1c0c CB2 lo
lda#$ff Sync
ldx#$05 5x
sta$1c01 auf die
clv Diskette schreiben
m02 bvc$m02 Byte ready?
clv
dex
bne$m02
ldy#$bb Bytes $01bb bis $01ff
m04 lda$0100,y =69 GCR Bytes
m03 bvc$m03 auf die
clv Diskette
sta$1c01 schreiben
iny
bne$m04
m06 lda($30),y Datenpuffer 256 Bytes
m05 bvc$m05 GCR-Code auf die
clv Diskette schreiben
sta$1c01
iny
bne$m06
m07 bvc$m07 Byte ready?
lda$1c0c PCR wieder
ora#$e0 auf Eingabe umschalten
sta$1c0c CB2 hi
lda#$00 Port A (Read/Writehead)
sta$1c03 auf Eingang
jsr$f5f2 GCR in Normalcode wandeln
lda#$01 Ok
jmp$f969 Meldung!
Start der Floppy Routine:
ldx#$09 10 Bytes
m08 lda text,x Text in
sta$0300,x Writepuffer
dex ab $0300
bpl$m08 poken
ldx#$29 Track 41
ldy#$00 Sektor 0
stx$0a abspeichern
sty$0b und
lda#$e0 Programm
sta$02 ab $0500
m09 lda$02 starten
bmi$m09 und ausfuehren
rts Ende
text." protect41 !"
Sehen wir uns nun zunächst mal den
Start der Floppyroutine an.
Hier wird ein Programm durch Jobcode $ E0 ab $0500 gestartet um einen Text, der
nach $0300 geschoben wurde, auf die Disk
zu schreiben ( auf Track 41, Sektor 0) .
Gehen wir nun noch mal die Schritte zum schreiben eines Blocks auf Diskette
durch:
A. Write Puffer angeben ($31) B. Paritaet berechnen ($ f5 e9) C. Normalcode in GCRcode wandeln ($ f78 f) D. Headerblock suchen ($ f510) E.9 Bytes GAP nach dem Headerblock ueberlesen ( Warteschleife) F. Port A auf Ausgang ($1 c03) G. PCR auf Ausgabe umschalten ($1 c0 c) H.5 Syncs (#$ ff) auf Diskette schreiben I. durch die GCR Umwandlung wurden aus 256,325 Bytes ( siehe Kurs 4), also 69 Bytes mehr im Ausweichpuffer von $01 bb-$01 ff zuerst geschrieben werden.
J. dann folgen die restlichen 256 Bytes die von $0300-$03 ff stehen.
Die Adresse zum Lesen und Schreiben von Bytes ist ($1 c01) .
K. PCR auf Eingabe umschalten ($1 c0 c) L. Port A auf Eingang ($1 c03) M. GCRcode in Normalcode wandeln ($ f5 f2) N. Programm beenden mit ($ f969)