Documentation Vinace Introduction Vinace est pour VINtage Apple Computer Emulator. Pour l'instant ce n'est qu'un émulateur d'Apple //e 64Ko. Il devrait pouvoir facilement évoluer vers un émulateur d'Apple ][ tout modèle. Ce document comporte deux parties. La partie Utiliser Vinace explique comment utiliser Vinace et la partie A l'intérieur de Vinace parle du fonctionnement de Vinace. Ce document est encore en cours d'écriture, ce n'est pas une version finale. Beaucoup d'informations peuvent manquer. Utiliser Vinace Démarrage rapide Si vous êtes pressé, voici les instructions pour utiliser Vinace. Pour démarrer Vinace, lancez simplement vinace depuis l'intérieur de son répertoire. Pour que l'Apple arrête de lire depuis le disque, pressez Ctrl - Pause. Pour redémarrer l'Apple, pressez simultanément Alt gauche - Ctrl - Pause. Pour insérer un disque, glissez un fichier d'image disque sur le lecteur de disquette. Clavier Voici les touches du clavier original de l'Apple //e : Touches du clavier de l'Apple //e GroupeTouchesLettresA à Z (majuscules, minuscules et contrôle)Nombres0 à 9Symbols! @ # $ % ^ & * ( ) - _ + = { } [ ] : ; " ' ~ `| \ < > , . ? /Autres touchesEsc, Tab, Retour, Del, Flèches (gauche, droite, bas, haut)Touches spécialesReset, Pomme ouverte, Pomme pleine
Le clavier virtuel de l'Apple est associé au clavier de votre ordinateur. Les lettres (majuscules, minuscules et contrôles), les nombres, les symboles et les autres touches sont directement connectés au touches correspondantes. Les touches Pomme ouverte et Pomme pleine sont connectées aux touches Alt gauche et Alt droite. La combinaison de touches Ctrl - Reset est connectée à la combinaison Ctrl - Pause (la touche Reset seule n'a aucun effet sur le clavier de l'Apple). Ceci signifie que, si vous voulez redémarrer l'Apple //e, vous devez presser Alt gauche - Ctrl - Pause là où, sur le clavier original, vous pressiez Pomme ouverte - Ctrl - Reset.
Joystick Dans Vinace, il y a une émulation d'un joystick simple, utilisant le pavé numérique. En réalité, le joystick de l'Apple est capable de détecter 256 positions sur chaque axe. Le joystick Vinace n'est capable de simuler que trois positions sur chaque axe (0, 128 et 255). Ceci doit convenir pour la plupart des jeux mais ne sera pas suffisant pour ceux faisant un usage précis du joystick. La touche "5" du pavé numérique est la position centrale. La touche "8" est pour le haut, la touche "2" pour le bas, la touche "4" pour la gauche et la "6" pour la droite. Les touches en diagonale (7, 9, 1 et 3) fonctionnent aussi.
Emulation du joystick par le pavé numérique L'émulation du joystick par le pavé numérique : comment les touches sont liées aux positions du joystick.
Il n'y a pas encore d'émulation des boutons, la touche Alt gauche doit donc être utilisée pour le bouton 0 et Alt droite pour le bouton 1.
Disquettes et lecteurs Les lecteurs de disquette dans Vinace sont faciles à utiliser. Pour ouvrir ou fermer le lecteur, cliquez simplement sur le fermoir. Pour insérer une disquette dans un lecteur, glissez le fichier image disque sur le lecteur. En cas de succès, le nom de la disquette va apparaître sur le lecteur et il se refermera automatiquement. Seuls les fichiers .dsk (Images disque en ordre DOS) sont supportés pour l'instant. D'autres formats (images disque en ordre ProDOS, images nibbles) seront supportés plus tard. L'écriture sur disque n'est pas encore implémentée. Des images disque peuvent être trouvées sur le dépôt Asimov, dans la section Images ici : ftp://ftp.apple.asimov.net/pub/apple_II/images/
A l'intérieur de Vinace Principes généraux Avant d'aller plus profond dans Vinace, voici, en tant qu'introduction, quelques principes généraux.
Pourquoi un nouvel émulateur ? Les émulateurs existant ont une interface graphiques vieille et peu pratique (pas d'utilisation de la souris, ligne de commande pour changer de disquette). Les émulateurs existants sont écrits pour être performants et pas pour être compréhensibles ou modulaires. Les émulateurs existants sont monolithiques, pas assez proche du matériel original et pas très facile à faire évoluer. Je souhaitais développer un projet pas trop difficile pour me remettre au C++.
Qu'est-ce qu'un ordinateur ? Fondamentalement, un ordinateur c'est un processeur (CPU), de la mémoire, et des périphériques. Le processeur est le composant central de l'ordinateur. Il récupère des données depuis la mémoire, il les traite et remet le résultat dans la mémoire. Le processeur peut recevoir des signaux, comme le signal de remise à zéro (reset) ou les interruptions. Il reçoit aussi un signal périodique de l'horloge lui indiquant quand un cycle doit être effectué (une instruction est "faite" de plusieurs cycles). A part les signaux, la seule communication entre le processeur et le reste du monde s'effectue via la mémoire. Comme différent types de mémoire sont disponibles (RAM, ROM, etc.), un bus mémoire est chargé d'interfacer le processeur avec ces différent types. Selon l'adresse demandée par le processeur, le bus transfert la requête à la mémoire correspondante. Pour piloter les périphériques, le processeur utilise la mémoire (une fois encore, c'est la seule porte vers le monde extérieur). Un type de mémoire spécifique, l'unité d'entrée sortie (IOU), est connectée au bus mémoire. L'unité d'entrée sortie n'est pas réellement une mémoire puisqu'elle ne stocke pas d'information. Quand une adresse est accédée dans l'IOU, cela active quelque chose dans un périphérique ou lit une information en provenance de ce périphérique. Par exemple, l'accès à l'adresse 15 peut activer une lumière, ou bien l'accès à l'adresse 17 peut lire l'état d'un bouton. C'est en gros comme ça que le processeur interagit avec les périphériques. Si nous mettons tout cela ensemble, nous obtenons un schéma "simple" de ce qu'est un ordinateur :
Un ordinateur simplifié Un ordinateur simplifié : un processeur (CPU), un bus mémoire, de la RAM, de la ROM et une unité d'entrée sortie (IOU).
Le rectangle orage, contenant le processeur, la mémoire et l'unité d'entrée sortie mais ni les périphériques, ni l'horloge, représente ce que j'ai appelé le "noyau" (core). Le noyau est la partie minimale pour que l'ordinateur fonctionne. Les périphériques ne font que connecter l'ordinateur au monde et, en particulier, à l'utilisateur, mais ils ne sont pas indispensables. J'ai mis l'horloge en dehors car je considère l'horloge comme une interface entre l'ordinateur et le temps réel. On peut modifier la fréquence de l'horloge sans affecter le comportement de l'ordinateur. On peut même utiliser une horloge manuelle pour étudier l'ordinateur pas à pas.
Le modèle d'émulateur à trois couches Je voulais avoir un émulateur modulaire qui soit flexible pour lequel il soit facile de développer de nouvelles fonctionnalités. Je voulais aussi quelque chose qui soit très près de la réalité. Par exemple, je voulais être capable de développer un nouveau périphérique que je pourrais "brancher" dans mon émulateur, de la même façon que l'on branche des périphériques dans une machine. J'ai décidé d'utiliser un modèle à trois couches. Voici ces trois couches dans un schéma :
Le modèle à trois couches Le modèle à trois couches
Couche noyau La couche noyau (core) contient tous les composants, et seulement eux, nécessaires pour avoir un ordinateur qui fonctionne. Cela signifie que l'on peut instancier un noyau seul et jouer avec lui. C'est intéressant dans un but d'étude mais cela donne un ordinateur sourd et aveugle. Le noyau peut être vu comme l'ordinateur vu depuis l'intérieur de lui même (dans cette couche, les programmes tournent comme s'ils étaient dans un ordinateur réel).
Couche matériel La couche matériel (hardware) est la représentation logicielle de l'ordinateur émulé. Cette représentation est indépendante de l'interface graphique. Par exemple, la sortie vidéo est représentée par une matrice de pixels et le bouton d'un joystick par un simple booléen. Cette couche doit contenir la représentation de tous les objets du monde émulé, comme les disquettes ou les cartouches. Elle doit aussi contenir toute la logique de l'émulateur de façon à ce que la couche suivante ne soit en charge que de l'interface homme machine.
Couche IHM La couche Interface Homme-Machine (GUI) est chargée de représenter à l'utilisateur la couche matériel.
Processeur Le processeur est un dispositif capable d'exécuter une séquence d'instructions stockées en mémoire. Ces instructions peuvent nécessiter l'usage de registres (petit morceaux de mémoire interne). Elles peuvent aussi nécessiter la lecture et l'écriture en mémoire. La périodicité de l'exécution des instruction est controllée par une horloge indiquant au processeur quand il doit effectuer un cycle. Un cycle est une partie d'instruction : décodage de l'instruction, récupération des données depuis la mémoire, écriture en mémoire, etc. Le nombre de cycles pour une instruction dépend de sa complexité. Le processeur peut aussi réagir à certains signaux externes comme le signal de remise à zéro (reset) et les demandes d'interruption. Ces signaux sont habituellement émis par les périphériques. Donc, un processeur a trois types d'interaction avec le monde extérieur : Lecture et écriture en mémoire Requête de cycle par l'horloge Signaux Dans Vinace, les processeur sont représentés par des classes héritant de la classe CProcessor. Pour créer un nouveau processeur, une classe enfant doit être créée et les méthodes suivantes doivent être implémentées : Constructeur: le constructeur de CProcessor prend deux paramètre : la mémoire qui doit être liée au processeur et un nombre de signaux. Le constructeur de la classe enfant doit appeler celui de CProcessor avec un nombre donnée de signaux. C'est le nombre de signaux possibles différents. Par exemple, pour un processeur n'ayant que le signal de remise à zéro, ce nombre est 1. reset(): cette méthode est censée remettre l'état du processeur à zéro. Elle est appelée une fois lorsque le processeur est instancié. C'est à la classe enfant de connecter le signal de remise à zéro à cette méthode. process_instruction(): cette méthode est censée traiter l'instruction suivante. Elle est appelée par cycle() une fois les cycles de l'instruction précédente terminés. process_instruction() doit augmenter le membre cycles du bon nombre de cycles correspondant à l'instruction. process_signals(): Cette méthode est censée traiter les signaux. Elle est appelée par cycle(). Elle doit renvoyer faux si un signal a été traité et vrai s'il n'y avait pas de signal à traiter. Dans ce cas, cycle() appelle alors process_instruction. Mémoire Une mémoire est un composant capable d'être lu et écrit. Dans Vinace, la classe qui représente la mémoire est CMemory. Un objet CMemory n'a pas à retenir quoi que ce soit en réalité. Par exemple, CDummyMemory, une classe enfant de CMemory, est une mémoire qui renvoie des valeurs bidon. Elle est utilisée pour remplir les espaces mémoire non connectés. La RAM et la ROM sont représentées par les classes CRamMemory et CRomMemory, enfants de CMemory.
Proxys mémoire Les proxys mémoire, représentés par la classe CMemoryProxy, sont des proxys qui permettent d'adresser une partie d'un objet CMemory. En réalité, ils ne font que décaler l'adresse selon l'adresse de départ de la cible.
Bus mémoire Les bus mémoire sont des enfants de CMemory qui dispatchent les requêtes de lecture et d'écriture vers des sous-mémoire selon l'adresse de la requête ou selon des drapeaux d'un objet CUnit. Il n'y a pas de classe commune pour les bus mémoire car ils n'ont rien de spécifique en commun, à part le fait qu'ils soient des mémoires.
La mémoire de l'Apple //e Voici un schéma de la mémoire d'un Apple //e 64Ko.
Bus mémoire de l'Apple //e Bus mémoire de l'Apple //e
Le processeur accède seulement au bus mémoire principal (main memory bus). l'IOU est l'unité d'entrée/sortie. Il y a un banc de 64Ko de RAM et plusieurs bancs de ROM : La ROM de la carte language (LC ROM) contient les routines moniteur et celles du basic (Applesoft sur le //e, Integer sur le ][+). Elle est visible dans l'espace $D000-$FFFF. La ROM interne contient des routines additionnelles pour le //e. Les ROM des cartes périphériques (card ROM) sont les ROMs des cartes d'extension. Voici une explication de chacun des bus.
Bus mémoire principal (Main memory bus) Ce bus est la partie visible de la mémoire (depuis le point de vue du processeur). Ce bus divise la mémoire en quatre parties : $0000-$BFFF est connecté à la RAM. $C000-$C0FF est connecté à l'unité d'entrée sortie (IOU). $C100-$CFFF est connecté au bus de la ROM E/S (peut être connecté à la ROM des cartes périphériques ou bien à la ROM interne). $D000-$FFFF est connecté au bus de la carte langage (habituellement, la ROM principale est visible ici).
Bus de la carte language (Language Card bus) Référence : Apple IIe Technical Reference Manual pages 79 to 83 (PDF pp113-117) Le bus de la carte langage est en charge des 12Ko les plus hauts de la mémoire. Cet espace était originellement dédié à la ROM Basic et moniteur dans les Apple ][ 48Ko. Sur les Apples 64Ko, une nouvelle possibilité était d'adresser de la RAM dans cet espace. Cette fonctionnalité est utilisé pour charger des langages alternatifs en mémoire (comme le basic Integer du ][+ sur un //e). La lecture et l'écriture peuvent être modifiés indépendamment. Ceci permet d'écrire en RAM pendant que la ROM est visible. C'est particulièrement pratique lorsque l'on prépare la RAM car bien des routines bas niveau sont en ROM (affichage du texte, lecture du clavier...). Comme 16Ko de RAM sont disponibles mais l'espace ne fait que 12Ko, les 4Ko inférieurs ($D000-$FFFF) peuvent adresser deux bancs de RAM. Les 8Ko supérieurs sont toujours connectés à la même RAM. Ce bus est piloté par l'Unité de carte langage en utilisant les interrupteurs logiciels LCRAM, LCWRITE et LCBNK2. Par défaut (LCRAM et LCWRITE sur off), la ROM est lisible et les écritures n'ont aucun effet. Les requêtes d'écriture sont transmises à la RAM si LCWRITE est actif. Sinon, les requête d'écriture n'ont pas d'effet (comme pour une ROM). Les requêtes de lecture sont transmises à la RAM si LCRAM est actif et à la ROM sinon. Les 8Ko supérieurs ($E000-$FFFF) sont connectés aux 8Ko supérieurs de la RAM ($E000-$FFFF). Les 4Ko inférieurs ($D000-$DFFF) sont connectés à la RAM en $D000-$DFFF si LCBNK2 est actif ou à celle en $C000-$CFFF sinon.
Bus de la ROM E/S (I/O Rom Bus) L'Apple //e et les modèles suivants ont plus que 12Ko de ROM. Pour accéder au reste de la ROM, l'espace $C100-$CFFF, habituellement réserver aux ROM des cartes périphériques peut être utilisé. Une fenêtre, en $C300-$C3FF, correspondant à la carte 80 colonnes, peut être sélectionnée spécifiquement. Ce bus est piloté par l'unité de ROM E/S en utilisant les interrupteurs logiciels CXROM et C3ROM. Si CXROM est actif, les requêtes sont transmises au bus des ROM périphériques. Si CXROM est actif, la ROM interne est visible. C3ROM permet de voir la ROM interne en $C300-$C3FF quelque soit l'état de CXROM. Ceci est utilisé pour masquer la ROM de la carte d'extension 80 colonnes et pour utiliser les routines internes à la place.
Bus des ROM périphériques (Slot ROM Bus) Chaque carte d'extension peut avoir 256 octets de ROM basique plus une extension ROM de 4Ko. La ROM basique est visible en $Cn00-$CnFF, où n est le numéro du port. Lorsque la ROM basique est lue, l'extension ROM (s'il y en a une) est sélectionnée et devient visible en $C800-$CFFF. $CFFF est aussi un interrupteur logiciel (nommée CLRROM) qui désélectionne la ROM. Ceci est expliqué dans le Apple IIe Technical Reference Manual page 132. Le bus des ROM périphériques est en charge de transférer les requêtes de lecture à la bonne ROM. Comme l'unité des ports d'extension, il doit être informé des insertions et retraits de cartes.
Insérer et retirer des cartes La méthode insert_card indique au bus qu'une carte a été insérée : void CSlotRomBus::insert_card(int slot, CMemory *cardRom, CMemory *cardRomExt) slot est le numéro du port (1-7). cardRom est la ROM basique de la carte et cardRomExt est sont extension. La méthode remove_card indique au bus qu'une carde a été retirée : void CSlotRomBus::remove_card(int slot) slot est le numéro du port (1-7).
Fichiers de ROM Les fichiers d'image des ROM sont nécessaire aux émulateurs mais ils sont sous copyright et ne peuvent être distribués librement. Un bon endroit pour récupérer des images de ROM est la section émulateurs du dépôt Asimov. Vinace a besoin des ROM internes ainsi que de la ROM du Disk II si un contrôleur Disk II est branché. Voici une description des fichiers de ROM utilisés par Vinace.
apple_iie_rom.zip Emplacement : ftp://ftp.apple.asimov.net/pub/apple_II/emulators/rom_images/apple_iie_rom.zip Contient un seul fichier : APPLE2E.ROM Carte du fichier APPLE2E.ROM DeÀDescription0x00000x01ffVide0x02000x02ffInconnu, probablement la ROM d'un périphérique en port 20x03000x05ffVide0x06000x06ffROM 16 secteurs du contrôleur Disk II0x07000x0fffVide0x10000x3fffROM de l'Integer Basic ?0x40000x40ffVide0x41000x4fffROM interne $C100-$CFFF0x50000x7fffROM principale $D000-$FFFF ROM
apple_ii_rom.zip Emplacement : ftp://ftp.apple.asimov.net/pub/apple_II/emulators/rom_images/apple_ii_rom.zip Identique à apple_iie_rom.zip.
apple_ii+_rom.zip Emplacement : ftp://ftp.apple.asimov.net/pub/apple_II/emulators/rom_images/apple_ii+_rom.zip Contient un seul fichier : APPLE2.ROM Carte du fichier APPLE2.ROM DeÀDescription0x00000x05ffVide0x06000x06ffROM 16 secteurs du contrôleur Disk II0x07000x0fffVide0x10000x15ffVide0x16000x16ffROM 16 secteurs du contrôleur Disk II0x20000x4fffROM principale $D000-$FFFF ROM
Entrées / Sorties
L'unité d'entrées/sorties du ][+ Adresses de l'unité d'entrées/sorties de l'Apple ][+ AdrNomAccèsDescriptionUnitéC000KBDRDernière touche presséeClavierC010KBDSTRBRWRemise à zéro du clavierClavierC020TAPEOUTR7Bascule la sortie cassetteCassetteC030SPKRRBascule l'état du haut parleurHaut parleurC040STROBEREnvoie une impulsion sur la sortie jeuxJeuC050TXTCLRWRMode graphiqueMode graphiqueC051TXTSETWRMode texteMode graphiqueC052MIXCLRWRMode plein écranMode graphiqueC053MIXSETWRMode mixteMode graphiqueC054TXTPAGE1WRAffiche la page 1Mode graphiqueC055TXTPAGE2WRAffiche la page 2Mode graphiqueC056LORESWRGraphiques basse résolutionMode graphiqueC057HIRESWRGraphiques haute résolutionMode graphiqueC058CLRAN0WRMise à 0 de l'annonciateur 0JeuC059SETAN0WRMise à 1 de l'annonciateur 0JeuC05ACLRAN1WRMise à 0 de l'annonciateur 1JeuC05BSETAN1WRMise à 1 de l'annonciateur 1JeuC05CCLRAN2WRMise à 0 de l'annonciateur 2JeuC05DSETAN2WRMise à 1 de l'annonciateur 2JeuC05ECLRAN3WRMise à 0 de l'annonciateur 3JeuC05FSETAN3WRMise à 1 de l'annonciateur 3JeuC060TAPEINR7Lit l'état de l'entrée cassetteCassetteC061PB0R7Bouton poussoir 0JeuC062PB1R7Bouton poussoir 1JeuC063PB2R7Bouton poussoir 2JeuC064PADDL0R7Lit l'entrée analogique 0JeuC065PADDL1R7Lit l'entrée analogique 1JeuC066PADDL2R7Lit l'entrée analogique 2JeuC067PADDL3R7Lit l'entrée analogique 3JeuC070PTRIGRRemise à zéro des entrées analogiquesJeuC080RLecture de la RAM page 2, pas d'écritureCarte langageC081RRLecture de la ROM, écriture de la RAM page 2Carte langageC082RLecture de la ROM, pas d'écritureCarte langageC083RRLecture et écriture en RAM page 2Carte langageC084-C087Identique à C080-C083Carte langageC088RLecture de la RAM page 1, pas d'écritureCarte langageC089RRLecture de la ROM, écriture de la RAM page 1Carte langageC08ARLecture de la ROM, pas d'écritureCarte langageC08BRRLecture et écriture en RAM page 1Carte langageC08C-C08FRIdentique à C088-C08BCarte langageC090-C09FAdresses pour le port d'extension 1Ports d'extensionC0A0-C0AFAdresses pour le port d'extension 2Ports d'extensionC0B0-C0BFAdresses pour le port d'extension 3Ports d'extensionC0C0-C0CFAdresses pour le port d'extension 4Ports d'extensionC0D0-C0DFAdresses pour le port d'extension 5Ports d'extensionC0E0-C0EFAdresses pour le port d'extension 6Ports d'extensionC0F0-C0FFAdresses pour le port d'extension 7Ports d'extension
R - Lire pour agir ou récupérer l'information, W - Écrire pour agir, RR - Lire deux fois pour agir, R7 - Lire l'information dans le bit 7
L'unité d'entrées/sorties du //e L'unité d'entrées/sorties de l'Apple //e est basée sur celle de l'Apple ][+, sauf pour les adresses TXTPAGE1 et TXTPAGE2 qui sont gérées par l'unité AuxMemory (qui passe la requête à l'unité GraphicMode). L'unité d'entrées/sorties de l'Apple //e I/O possède quelques interrupteurs logiciels supplémentaires ainsi que des accès à l'état des interrupteurs existants. Adresses de l'unité d'entrées/sorties de l'Apple //e AdrNomAccèsDescriptionUnitéC000KBDRDernière touche presséeClavierC00080STOREOFFWUtilise $C002-$C005 pour la mémoire auxiliaireMémoire auxiliaireC00180STOREONWUtilise PAGE2 pour la mémoire auxiliaireMémoire auxiliaireC002RDMAINRAMWSi 80STORE est à 0: Lit la mémoire principale en $0200-$BFFFMémoire auxiliaireC003RDCARDRAMWSi 80STORE est à 0: Lit la mémoire auxiliaire en $0200-$BFFFMémoire auxiliaireC004WRMAINRAMWSi 80STORE est à 0: Écrit la mémoire principale en $0200-$BFFFMémoire auxiliaireC005WRCARDRAMWSi 80STORE est à 0: Écrit la mémoire principale en $0200-$BFFFMémoire auxiliaireC006SETSLOTCXROMWROM des périphériques ($C100-$CFFF)Rom E/SC007SETINTCXROMWROM interne ($C100-$CFFF)Rom E/SC008SETSTDZPWPile et page zéro principalesMémoire auxiliaireC009SETALTZPWPile et page zéro auxiliairesMémoire auxiliaireC00ASETINTC3ROMWROM interne en $C300-$C3FFRom E/SC00BSETSLOTC3ROMWROM des périphériques $C300-$C3FFRom E/SC00CCLR80VIDW40 ColonnesMode texteC00DSET80VIDW80 ColonnesMode texteC00ECLRALTCHARWSélectionne la police de caractère primaireMode texteC00FSETALTCHARWSélectionne la police de caractère alternativeMode texteC010KBDSTRBRWRemise à zéro du clavierClavierC011RDLCBNK2R7État de visibilité de la page 1 (0) ou de la page 2 (1) de RAM en $D000-DFFF LanguageCardC012RDLCRAMR7État de visibilité de la ROM (0) our de la RAM() en $D000-$FFFFLanguageCardC013RDRAMRDR7État de lecture de la RAM principale/auxiliaireMémoire auxiliaireC014RDRAMWRTR7État d'écriture dans la RAM principale/auxiliaireMémoire auxiliaireC015RDCXROMR7État de l'accès à la ROM des périphériques (0) ou interne (1)Rom E/SC016RDALTZPR7État de la pile et de la page zéro principale ou auxiliaireMémoire auxiliaireC017RDC3ROMR7État de la ROM visible en $C300-$C3FF : Port 3 (0) ou Port auxiliaire (1)Rom E/SC018RD80STORER7État de 80STOREOFFMémoire auxiliaireC019RDVBLR7État de l'effacement vertical (1=en cours d'affichage)C01ARDTEXTR7État du mode Texte (1) ou Graphique (0)Mode graphiqueC01BRDMIXEDR7État du mode plein écran (0) ou mode mixte (1)Mode graphiqueC01CRDPAGE2R7État de l'affichage de la page 1 (0) ou page 2 (1)Mode graphiqueC01DRDHIRESR7État de la résolution basse (0) ou haute (1)Mode graphiqueC01ERDALTCHARR7État de la police de caractère primaire ou alternativeMode texteC01FRD80VIDR7État de l'affichage 40 ou 80 colonnesMode texteC020TAPEOUTR7Bascule la sortie cassetteCassetteC030SPKRRBascule l'état du haut parleurSpeakerC040STROBEREnvoie une impulsion sur la sortie jeuxGameC050TXTCLRWRMode graphiqueMode graphiqueC051TXTSETWRMode texteMode graphiqueC052MIXCLRWRMode plein écranMode graphiqueC053MIXSETWRMode mixteMode graphiqueC054TXTPAGE1WRAffiche la page 1Mode graphiqueC055TXTPAGE2WRSi 80STORE est à 0: Affiche la page 2, Si 80STORE est à un: Écriture/Lecture de la mémoire auxiliaire d'affichageAuxMemory, GraphicModeC056LORESWRGraphiques basse résolutionMode graphiqueC057HIRESWRGraphiques haute résolutionMode graphiqueC058CLRAN0WRMise à 0 de l'annonciateur 0JeuC059SETAN0WRMise à 1 de l'annonciateur 0JeuC05ACLRAN1WRMise à 0 de l'annonciateur 1JeuC05BSETAN1WRMise à 1 de l'annonciateur 1JeuC05CCLRAN2WRMise à 0 de l'annonciateur 2JeuC05DSETAN2WRMise à 1 de l'annonciateur 2JeuC05ECLRAN3WRMise à 0 de l'annonciateur 3JeuC05FSETAN3WRMise à 1 de l'annonciateur 3JeuC060TAPEINR7Lit l'état de l'entrée cassetteCassetteC061PB0R7Bouton poussoir 0 / Touche Pomme OuverteJeuC062PB1R7Bouton poussoir 1 / Touche Pomme PleineJeuC063PB2R7Bouton poussoir 2 / Touche Shift (sur certains modèles)JeuC064PADDL0R7Lit l'entrée analogique 0JeuC065PADDL1R7Lit l'entrée analogique 1JeuC066PADDL2R7Lit l'entrée analogique 2JeuC067PADDL3R7Lit l'entrée analogique 3JeuC070PTRIGRRemise à zéro des entrées analogiquesJeuC073BANKSELWSélection du banc de mémoire si plus de 128KC07FRDDHIRESR7État du graphique en Double Haute RésolutionC080RLecture de la RAM page 2, pas d'écritureCarte langageC081RRLecture de la ROM, écriture de la RAM page 2Carte langageC082RLecture de la ROM, pas d'écritureCarte langageC083RRLecture et écriture en RAM page 2Carte langageC084-C087Identique à C080-C083Carte langageC088RLecture de la RAM page 1, pas d'écritureCarte langageC089RRLecture de la ROM, écriture de la RAM page 1Carte langageC08ARLecture de la ROM, pas d'écritureCarte langageC08BRRLecture et écriture en RAM page 1Carte langageC08C-C08FRIdentique à C088-C08BCarte langageC090-C09FAdresses pour le port d'extension 1Ports d'extensionC0A0-C0AFAdresses pour le port d'extension 2Ports d'extensionC0B0-C0BFAdresses pour le port d'extension 3Ports d'extensionC0C0-C0CFAdresses pour le port d'extension 4Ports d'extensionC0D0-C0DFAdresses pour le port d'extension 5Ports d'extensionC0E0-C0EFAdresses pour le port d'extension 6Ports d'extensionC0F0-C0FFAdresses pour le port d'extension 7Ports d'extension
R - Lire pour agir ou récupérer l'information, W - Écrire pour agir, RR - Lire deux fois pour agir, R7 - Lire l'information dans le bit 7
Unités d'entrées/sorties Les unités d'E/S sont des interfaces entre la mémoire de l'Apple et les périphériques. Dans l'émulateur, c'est une interface entre les couches noyau et matériel.
Classe unité Les unités sont représentés par des classes descendantes de la classe CUnit. Du côté mémoire de l'Apple, seules deux méthodes sont utilisées : read (lecture) et write (ecriture). C'est à peu près la même chose que pour une mémoire excepté que l'adressage est en 8 bits au lieu de 16. Du côté périphérique, il n'y a pas de spécification. La plupart des unités ne comportent que des interrupteurs logiciels. A chaque interrupteur correspoind une méthode get_<switch> et, si modifiable, une méthode set_<switch>, où <switch> est le nom de l'interrupteur en minuscules. Les unités ont une méthode reset qui est censée remettre à zéro l'unité. Elle est appelée à l'instanciation de l'unité mais aussi lorsque l'utilisateur appuie sur le bouton "reset" (ça c'est dans la couche matériel). Toutes les unités sont observables et doivent notifier de tout changement pouvant subvenir. Les objets de la couche matériel doivent observer les unités pour être informés de ce qu'il se produit.
Clavier (Keyboard) Compatibilité : Tous modèles. Cette unité est en charge du clavier. Sur les Apple II, il n'y avait pas de tampon clavier. Cette unité ne peut donc gérer qu'une touche à la fois.
Méthodes void press_key(BYTE key); Indique à l'unité quelle touche est en train d'être pressée. Notez que si la touche précédente n'a pas encore été lue, elle sera oubliée. Les codes de touche doivent être en code ASCII Apple. void release_key(); Indique à l'unité que la touche a été relâchée. bool key_waiting(); Indique s'il y a une touche en attende de lecture par l'Apple. Cette méthode peut être particulièrement utile pour développer un tampon clavier ou un outil de copier coller.
Addresses AdresseNomAccèsEffet 0x00KBDRBits 0-6 : Dernière touche pressée ou en train de l'être. Bit 7 : est à 1 si une touche a été pressée depuis la dernière remise à zéro. 0x10KBDSTROBERWRemet à zéro le clavier. Bits 0-6 : Dernière touche pressée ou en train de l'être. Bit 7 : à 1 si une touche est enfoncée.
Cassette (Tape) Compatibilité : Tous modèles. Malheureusement cette unité n'est pas encore implémentée.
Haut parleur (Speaker) Compatibilité : Tous modèles. Le haut parleur de l'Apple est plutôt basique : il n'a que deux niveau. Le niveau du haut parleur est inversé en accédant à l'interrupteur logiciel SPKR. Du côté périphérique, le niveau du haut parleur peut être lu.
Interrupteurs InterrupteurOffOn SPKRHaut parleur au niveau basHaut parleur au niveau haut
Adresses AdresseNomAccèsEffet 0x30SPKRRWChange l'état de SPKR
Jeu (Game) Compatibilité : Tous modèles (sauf peut être le //c). L'unité de jeu possède trois sortes de dispositif : Trois boutons poussoirs, quatre annonciateurs et quatre entrées analogiques. Les boutons poussoirs sont connectés à ceux du joystick ou des paddles. A partir du //e, le bouton 0 est aussi connecté à la touche du clavier Pomme Ouverte et le bouton 1 à la touche Pomme Pleine. Sur certains modèles de //e, le bouton 2 était connecté à la touche Shift. Les annonciateurs sont des sorties sur la prise jeu, capable de sortir à deux niveaux (ils n'ont pas été beaucoup utilisé, peut être pas du tout). Les entrées analogiques sont connectées aux axes X et Y du joystick (habituellement, les entrées 0 et 1) ou aux paddles. Ces entrées analogiques fonctionnaient en mesurant le temps de décharge d'un condensateur dans le potentiomètre du joystick ou de la paddle. L'unité n'est capable que de déclencher la charge du condensateur et de détecter lorsqu'il est déchargé. La mesure de temps est effectuée par un programme en ROM. Il est important de simuler ce mécanisme car certains programmes implémentent leur propre routine de mesure. La difficulté est qu'il est important de synchroniser le simulateur à l'horloge pour que le décompte de cycles, vu depuis l'intérieur, soit correct.
Interrupteurs InterrupteurOffOn AN0Annonciateur 0 au niveau basAnnonciateur 0 au niveau haut AN1Annonciateur 1 au niveau basAnnonciateur 1 au niveau haut AN2Annonciateur 2 au niveau basAnnonciateur 2 au niveau haut AN3Annonciateur 3 au niveau basAnnonciateur 3 au niveau haut PB0Bouton 0 relâchéBouton 0 appuyé PB1Bouton 1 relâchéBouton 1 appuyé PB2Bouton 2 relâchéBouton 2 appuyé PADDL0Condensateur de l'entrée 0 déchargéCondensateur de l'entrée 0 chargé PADDL1Condensateur de l'entrée 1 déchargéCondensateur de l'entrée 1 chargé PADDL2Condensateur de l'entrée 2 déchargéCondensateur de l'entrée 2 chargé PADDL3Condensateur de l'entrée 3 déchargéCondensateur de l'entrée 3 chargé PTRIGLes condensateurs sont en déchargeLes condensateurs sont en charge
Adresses AdresseNomAccèsEffet 0x58CLRAN0WRÉteint AN0 0x59SETAN0WRAllume AN0 0x5ACLRAN1WRÉteint AN1 0x5BSETAN1WRAllume AN1 0x5CCLRAN2WRÉteint AN2 0x5DSETAN2WRAllume AN2 0x5ECLRAN3WRÉteint AN3 0x5FSETAN3WRAllume AN3 0x61PB0R7Lit l'état de PB0 0x62PB1R7Lit l'état de PB1 0x63PB2R7Lit l'état de PB2 0x64PADDL0R7Lit l'état de PADDL0 0x65PADDL1R7Lit l'état de PADDL1 0x66PADDL2R7Lit l'état de PADDL2 0x67PADDL3R7Lit l'état de PADDL3 0x70PTRIGRDéclenche la charge des condensateurs* * : Quand la charge est déclenchées, PTRIG passe à vrai, les observateurs sont notifiés, puis il repasse à faux (les observateurs sont de nouveau notifiés). Tout cela dans le même cycle d'horloge (en réalité, pendant le même appel de méthode). C'est la façon dont l'unité de jeu informe les observateurs qu'il doivent commencer la simulation de la décharge.
Mode graphique (GraphicMode) Compatibilité : Tous modèles. Réference: Apple IIe Technical Reference Manual page 29 (PDF p63) Cette unité s'occupe du passage du mode texte aux modes graphiques, excepté le mode double haute résolution qui a été implémenté plus tard. L'Apple ][ a un mode texte et deux modes graphiques (basse et haute résolution). Un mode mixte permet de partager l'écran en deux, en ayant 4 lignes de texte en bas et le reste en graphique. Chaque mode possède deux pages correspondant à deux espaces mémoire différents.
Interrupteurs InterrupteurOffOn TEXTMode graphiqueMode texte MIXEDMode plein écranMode mixte PAGE2Page 1 affichéePage 2 affichée HIRESBasse résolution Haute résolution
Adresses AdresseNomAccèsEffet 0x50TXTCLRRWPasse TEXT à 0 0x51TXTSETRWPasse TEXT à 1 0x52MIXCLRRWPasse MIXED à 0 0x53MIXSETRWPasse MIXED à 1 0x54TXTPAGE1RWPasse PAGE2 à 0 0x55TXTPAGE2RWPasse PAGE2 à 1 0x56LORESRWPasse HIRES à 0 0x57HIRESRWPasse HIRES à 1 Et, sur le //e et les modèles suivants, quelques adresses de lectures ont été ajoutées : AdresseNomAccèsEffet 0x1ARDTEXTR7Lit l'état de TEXT 0x1BRDMIXEDR7Lit l'état de MIXED 0x1CRDPAGE2R7Lit l'état de PAGE2 0x1DRDHIRESR7Lit l'état de HIRES
Ports d'extension (Slots) Compatibilité : Tous modèles. Cette unité gère les ports d'extension de l'Apple. Elle ne fait que transférer les requêtes de lectures et d'écriture sur l'espace $C090-$C0FF vers l'unité d'entrée/sortie de la carte correspondante. Comme le Bus des ROM périphériques, cette unité doit être informée quand une carte est insérée ou retirée.
Méthodes void insert_card(int slot, CUnit *cardUnit); Indique à l'unité qu'une carte a été insérée. slot est le numéro du port (1-7) et cardUnit est l'unité d'E/S de la carte d'extension. void remove_card(int slot); Indique à l'unité qu'une carte a été retirée. slot est le numéro du port (1-7).
Adresses AdresseNomAccèsEffet 0x90-0x9FRWTransféré aux adresses 0x00-0x0F de la carte du port 1 0xA0-0xAFRWTransféré aux adresses 0x00-0x0F de la carte du port 2 0xB0-0xBFRWTransféré aux adresses 0x00-0x0F de la carte du port 3 0xC0-0xCFRWTransféré aux adresses 0x00-0x0F de la carte du port 4 0xD0-0xDFRWTransféré aux adresses 0x00-0x0F de la carte du port 5 0xE0-0xEFRWTransféré aux adresses 0x00-0x0F de la carte du port 6 0xF0-0xFFRWTransféré aux adresses 0x00-0x0F de la carte du port 7
Carte langage (Language Card) Compatibilité : Tous modèles (probablement sauf le ][). Réference : Apple IIe Technical Reference Manual pages 79 à 83 (PDF pp113-117) Cette unité pilote le Bus de la carte langage en utilisant trois interrupteurs : LCRAM, LCBNK2 et LCWRITE. Seuls LCRAM et LCBNK2 peuvent être lus depuis l'intérieur de l'Apple. LCWRITE n'est pas le nom officiel pour cet interrupteur (j'ai choisis celui là car je n'ai pas trouvé d'autre nom dans la documentation). Pour changer l'état de LCWRITE, deux requêtes de lectures doivent être effectuées à l'adresse appropriée. Un quatrième interrupteur est utilisé dans Vinace pour gérer cette double lecture. Il est appelé LCWCHG.
Interrupteurs InterrupteurOffOn LCBNK2Page 2 RAM utilisée en $D000-$DFFFPage 1 RAM utilisée en $D000-$DFFF LCRAMROM lue en $D000-$FFFFRAM lue en $D000-$DFFF LCWRITEPas d'écriture en $D000-$FFFFÉcriture en RAM en $D000-$FFFF (même si elle n'est pas visible)
Adresses AdresseNomAccèsEffet 0x11RDLCBNK2R7Lit l'état de LCBNK2 0x12RDLCRAMR7Lit l'état de LCRAM 0x81RPasse LCBNK2 à 1, LCRAM à 1 et LCWRITE à 0 0x81RRPasse LCBNK2 à 1, LCRAM à 0 et LCWRITE à 1 0x82RPasse LCBNK2 à 1, LCRAM à 0 et LCWRITE à 0 0x83RRPasse LCBNK2 à 1, LCRAM à 1 et LCWRITE à 1 0x84-0x87Identique à 0x80-0x83 0x88RPasseLCBNK2 à 0, LCRAM à 1 et LCWRITE à 0 0x89RRPasse LCBNK2 à 0, LCRAM à 0 et LCWRITE à 1 0x8ARPasse LCBNK2 à 0, LCRAM à 0 et LCWRITE à 0 0x8BRRPasse LCBNK2 à 0, LCRAM à 1 et LCWRITE à 1 0x8C-0x8FIdentique à 0x88-0x8B
Mémoire auxiliaire (Aux Memory) Compatibilité: //e et suivants. Malheureusement cette unité n'est pas encore implémentée.
Mode texte (Text Mode) Compatibilité: //e et suivants. Cette unité s'occupe du passage entre les modes 40 et 80 colonnes, ainsi que du changement de police de caractère.
Interrupteurs InterrupteurOffOn 80VIDAffichage 40 colonnesAffichage 80 colonnes ALTCHARUtilisation de la police primaireUtilisation de la police alternative
Adresses AdresseNomAccèsEffet 0x0CCLR80VIDWPasse 80VID à 0 0x0DSET80VIDWPasse 80VID à 1 0x0ECLRALTCHARWPasse ALTCHAR à 0 0x0FSETALTCHARWPasse ALTCHAR à 1 0x1ERDALTCHARR7Lit l'état de ALTCHAR 0x1FRD80VIDR7Lit l'état de 80VID
Rom E/S (I/O Rom) Compatibilité : //e, IIgs Certains modèles d'Apple avaient plus de 12Ko de ROM. Pour accéder aux reste de la ROM, l'espace mémoire $C100-$CFFF, habituellement réservé aux ROMs des périphériques, pouvait être utilisé. Une fenêtre en $C300-$C3FF, correspondant à la carte 80 colonne, pouvait être sélectionnée spécifiquement. Cette unité pilote le Bus de la ROM E/S qui s'occupe de la commutation entre la ROM interne et la ROM des périphériques en $C100-$CFFF.
Interrupteurs InterrupteurOffOn CXROMROM périphériques en $C100-$CFFFROM interne en $C100-$CFFF C3ROMROM sélectionnée par CXROM en $C300-$C3FFROM interne en $C300-$C3FF
Adresses AdresseNomAccèsEffet 0x06SETSLOTCXROMWPasse CXROM à 0 0x07SETINTCXROMWPasse CXROM à 1 0x0ASETINTC3ROMWPasse C3ROM à 0 0x0BSETSLOTC3ROMWPasse C3ROM à 1 0x15RDCXROMR7Lit l'état de CXROM 0x17RDC3ROMR7Lit l'état de C3ROM
Pas encore implémenté Voici des fonctionnalités qui doivent être implémentées : Entrée et sortie cassette 128Ko de mémoire (AuxMemory) Signal d'effacement vertical Lecture des images disques en format "ordre ProDOS" (.po) et en format "nibbles" (.nib) Écriture sur disquettes D'autres fonctionnalités peuvent aussi être implémentées mais elle sont moins importantes : Un clavier capable de faire du "coller" Un lecteur de disquette faisant du bruit Références
Sur internet
Asimov Le dépôt Asimov est le site internet où il faut aller pour trouver des trucs sur l'Apple II. Il y a des images disque, la documentation originale, des images des ROM, des émulateurs, etc. Le dépôt Asimov est ici : ftp://ftp.apple.asimov.net/pub/apple_II
Jon Relay's Apple II Info Archives Jon Relay's Apple II Info Archives contient des trucs très utiles que l'on peut aussi trouver dans les manuels de référence, excepté la fabuleuse page sur la mémoire d'entrée sortie qui résume les entrées/sorties de tous les modèles d'Apple II en un seul grand tableau : http://www.kreativekorp.com/miscpages/a2info/iomemory.shtml
Livres anglais Voici les livres les plus intéressants. Ils peuvent tous être trouvés dans le dépôt Asimov.
Apple IIe Technical Reference Manual C'est la bible. Pratiquement tout y est expliqué. Je possède la version française. La version originale anglaise se trouve ici : ftp://ftp.apple.asimov.net/pub/apple_II/documentation/Apple%20IIe%20Technical%20Reference%20Manual.pdf
Apple II Reference Manual January 1978 C'est une bible plus ancienne. Aussi connu sous le nom du livre rouge. Par chance, j'en ai récupéré un exemplaire sur ebay. Une version PDF se trouve ici : ftp://ftp.apple.asimov.net/pub/apple_II/documentation/Apple_II_Redbook.pdf
Understanding the Apple II J'ai lu le chapitre 8, à propos du Disk II Controller car les manuels Apple ne fournissaient aucune information à son propos. Je n'ai pas lu le reste du livre (n'ayant pas de version papier) mais il semble vraiment intéressant, allant en profondeur dans la description de l'Apple II. Une version PDF se trouve ici : ftp://ftp.apple.asimov.net/pub/apple_II/documentation/understanding the apple ii.pdf
Emulateurs existants
YAE - Yet Another Apple Emulator Avant que je ne décide d'écrire Vinace, j'ai utilisé YAE. Il a beaucoup de limitation : pas de joystick simple, pas d'interface facile pour les disquettes. J'ai tout d'abord essayé de l'améliorer mais j'ai rapidement réalisé que réécrire un nouvel émulateur ne serait pas plus compliqué. J'ai beaucoup étudié le code source de YAE pendant l'écriture de Vinace. J'ai récupéré les routines pour l'encodage et le décodage des nibbles, utilisées dans les lecteurs de disquettes. Les fichiers gcr.h et gcr.cpp sont quasiment identiques à ceus dans YAE. Il semble que le site de YAE n'existe plus (http://quark.netfront.net:6502/) mais le code source peut toujours être trouvé sur le dépôt Asimov ici : ftp://ftp.apple.asimov.net/pub/apple_II/emulators/yae/yae-0.1.tar.gz
Appleblossom - Portable Open-Source Apple IIe Emulator Quand je ne trouvais pas les réponses ni dans la documentation ni dans le code source de YAE, je cherchais dans le source de Apple Blossom. J'ai pris le code pour la police de caractère depuis Apple Blossom. Il est maintenant dans le fichier CCharset.cpp. Le code source de Apple Blossom est ici : ftp://ftp.apple.asimov.net/pub/apple_II/emulators/appleblossom/appleblossom.zip