Bestand kopiëren met de KERNAL

Alles over C64/C128 software
-
Everything about C64/C128 software
dmantione
Junior
Berichten: 65
Lid geworden op: za okt 04, 2014 10:01
Locatie: Purmerend

Bestand kopiëren met de KERNAL

Berichtdoor dmantione » zo nov 12, 2017 15:33

Goedemiddag,

In wat gehobby kwam ik een tijdje geleden bij de problematiek van het kopiëren van een bestand vanuit een programma. Dat is iets waar je normaal niet bij nadenkt, maar het bleek toch aanzienlijk complexer dan gedacht: Een bestand zomaar in het geheugen laden met een load en weer wegschrijven met een save, betekent dat je nooit controle hebt waar het in het geheugen terecht komt en je op die manier alleen specifieke bestanden kunt kopiëren. Een generieke subroutine om een bestand te kopiëren kan dus niet van load/save gebruik maken, maar van algemenere KERNAL-aanroepen voor seriële communicatie.

Documentatie en voorbeeldcode over dit onderwerp bleek bijzonder lastig te vinden. Op veel plaatsen vind je wel een beschrijving van KERNAL-aanroepen als LISTEN en TALK e.d., maar hoe je ze kunt combineren om te doen wat je wilt, dat valt nog tegen om daar wat over te vinden. Ik heb me er de afgelopen dagen even flink boos op gemaakt en ben geslaagd en deel het resultaat graag even. Ik ben lui in C met cc65 aan de slag gegaan, voor het principe maakt dat het ook het best duidelijk en de conversie naar machinetaal is redelijk triviaal. Mijn resultaat is:

Code: Selecteer alles

#include <stdio.h>
#include <stdlib.h>
#include <cbm.h>

char* name_src;
char* name_dest;
char dev_src;
char dev_dest;

char buffer[256];

int counter;

void cbm_untlk();

char copyfile() {
char r,n,p;
cbm_k_setlfs(2,dev_src,2);
cbm_k_setnam(name_src);
cbm_k_open();
if (r=cbm_k_readst())
goto exit;


cbm_k_setlfs(3,dev_dest,3);
cbm_k_setnam(name_dest);
cbm_k_open();
if (r=cbm_k_readst())
goto exit;


do {
cbm_k_chkin(2);
n=0;
do {
buffer[n]=cbm_k_getin();
++n;
counter++;
if (r=cbm_k_readst())
break;
} while (n!=0);
cbm_untlk();
cbm_k_ckout(3);
p=0;
do {
cbm_k_bsout(buffer[p]);
if (r=cbm_k_readst())
goto exit;
++p;
} while (p!=n);
cbm_k_unlsn();
} while (n==0);

exit:
cbm_k_close(2);
cbm_k_close(3);
cbm_k_clrch();
return r;
}

int main (void) {
char r;
counter=0;
printf ("Begin met kopieren\n");
name_src = "bestand,p,r";
name_dest = "bestand,p,w";
dev_src = 8;
dev_dest = 9;
r=copyfile();
printf("klaar %d %d\n",counter,r);
if (r == 128) {
printf("DEVICE NOT PRESENT\n");
}
if (r == 64) {
printf("OK\n");
}
return EXIT_SUCCESS;
}
De routine copyfile() kopieert het bestand. Omdat de 6502-architectuur niet bepaald ontworpen is om efficiënt om te gaan met hogere programmeertalen is het het efficiëntst om met globale variabelen te werken. name_src en name_dest zijn de bron- en doelbestandsnaam, dev_src en dev_dest het bron- en doeldevice. De routine kan dus bestanden van het ene naar het andere device kopiëren, maar ook kopiëren op hetzelfde device onder een andere bestandsnaam. Ook maakt het soort bestand niet uit, PRG, SEQ, USR, kan allemaal. Kwestie van de variabelen juist invullen.

Je kunt compileren met:

cl65 -O -t c64 copygame.c

... en dan het bestand via bijvoorbeeld een USB-stick in de 1541U2 overbrengen naar je C64. Het zou compatibel moeten zijn met alle Commodore-computers die een KERNAL hebben, je kunt de c64 in het commando bijvoorbeeld vervangen door vic20 of c16 of c128.

Wat het experiment me leert is dat je met de KERNAL zonder meer meerdere bestanden tegelijk kunt openen, die zich op verschillende devices kunnen bevinden. Je moet echter wel heel goed opletten welke KERNAL-aanroep op welke aanroep mag volgen. Bijvoorbeeld na het openen van het bestand is de verleiding groot alvast met chkin het lezen te starten, maar omdat het betreffende device dan in TALK-mode staat en baas is over de seriële bus kun je dan niet het bestand op het andere device openen. De KERNAL is genadeloos, elke schending van de regels, die ik nergens op papier zie staan, leidt tot een hangende computer waarbij je maar bij jezelf te rade moet gaan wat er mis is.

Het principe is dus dat je beide bestanden eerst opent en dan afwisselend leest en schrijft. Een leesactie wordt geïnitieerd door CHKIN en moet je netjes afsluiten door een UNTALK. Even zo moet je een schrijfactie initiëren door een CKOUT en afsluiten met een UNTALK. Dat wisselen tussen lezen en schrijven kun je zo vaak doen als je wilt en ik heb hier gekozen om het in eenheden van 256 bytes te doen.

Erg snel gaat het kopiëren niet: Cartridgefastloaders werken alleen in op LOAD en SAVE en omdat het bestand byte voor byte leest en schrijft, heb je niets aan een fastloadcartridge. KERNAL-fastloaders zoals JiffyDOS en de burstmode van de Commodore 128 versnellen het kopiëren wel.

Nog even over de:

void cbm_untlk();

...in het programma. Tot mijn grote verbazing ontbreekt in de cbm.h van cc65 de UNTALK-aanroep. Dat moet haast een bug zijn en is een veeg teken hoe weinig mensen zich hiermee bezig houden. Het bestuderen van de broncode geeft aan dat UNTALK wel geïmplementeerd is onder de naam cbm_untlk, maar waarom de naamgeving afwijkt en waarom UNLISTEN wel en UNTALK niet aanwezig in de cbm.h is, daar lijkt geen zinnige reden voor te bedenken.

Terug naar “Software”

Wie is er online

Gebruikers op dit forum: Geen geregistreerde gebruikers en 1 gast