Le format des écrans textes, y compris sur les dernières versions de Linux ou windows se basent sur des nombres de colonnes comme 80 ou 132. L'informaticien lambda peut se demander d'ou viennent ces valeurs. 64 ou 128 colonnes auraient été sans doute plus proche de valeurs facilement manipulables par une machine.
L'origine
En fait on doit ces valeurs au nombre de caractères enregistrables sur une carte perforée. Les 80 colonnes viennent d'un format de carte datant de 1928, breveté par IBM. Par la suite on a vu apparaitre des cartes au format 132 colonnes.
Pour quoi faire ?
Ces cartes étaient destinées à des machines mécanographiques. Le principe était le suivant : On enregistre des données à l'aide d'une perforatrice sur les cartes, puis les passent dans le lecteur de carte présent sur une tabulatrice. Cette tabulatrice est un engin électromécanique capable de faire des opérations simples sur les données lues sur les cartes. Il peut ensuite restituer ces données sur une imprimantes dont la largeur est celle des cartes, c'est à dire 80 ou 132 colonnes.
Les tabulatrices étaient capablent d'exécuter un petit pogramme simple, définie sur un panneau de connections électriques. Cette machine effectuaient aussi des calculs.
Lorsque les premiers calculateurs électroniques sont apparus, ils ont d'abord servi des périphériques de stockage et de calcul rapide pour les tabulatrices, avant de devenir le "processeur" de l'ensemble du système.
Par la suite l'évolution a été la suivante : l'ordinateur est devenue l'élément central du système et donc controlait les imprimantes de sortie ainsi que les lecteurs de cartes. Les imprimantes sont au format 80 ou 132 colonnes. Les premiers terminaux était en fait des claviers associés à une imprimante (de 80 ou 132 colonnes). Lorsque la techonologie a permit d'avoir des mémoires suffisament rapides pour faire de la vidéo, les imprimantes des terminaux ont été remplacées par des tubes cathodiques, dont le contrôleur était lui aussi, de fait en 80 ou 132 colonnes.
dimanche 5 novembre 2006
80 colonnes...
Un deux-deux lignes pour zx81
Petit clin d'oeil au journal Hebdogiciel.
Il s'agit d'un petit programme pour Zx81 capable de faire des affichages étranges en overscan. Ce programme fonctionne sur la version de base avec 1 ko de RAM
10 RAND USR 5678
20 GOTO 10
jeudi 2 novembre 2006
Comment créer ses propres classes Tfield ?
L’intérêt de la chose est de pouvoir intégrer des traitements spécifiques associés à une données spécifiques. Exemple : mon champ de type float est un peu plus qu'un float, il s'agit d'un montant.
On peut lui associer une valeur de TVA (calculée) et une devise. Autre exemple je souhaite différencier les champs concernant les désignations. Il s'agit là d’un champ string particulier. Bien entendu, je souhaite pouvoir exploiter ce genre de champ de façon automatique au moment de l'ouverture d'un dataset. Les exemples que je fourni concernent le Tquery.
Rappels sur les composants de classe Tdataset ou descendant:
Pour commencer au moment de l’ouverture on la construction d’un tableau de champ de type Tfield ou descendants. (TstringField, TfloatField, etc….) Ce tableau décrit la structure d’un enregistrement. Exemple : Cette procédure remplie la « Liste » avec la description de « Dataset »
procedure ListeClasseField(Liste:TStrings;Dataset:TDataset);
var
i:integer;
begin
Liste.Clear;
for i:=0 to Dataset.FieldCount-1 do
Liste.add(Dataset.Fields[i].Classname);
end;
Pour retrouver les classes de Tfield qui correspondent aux champs de la base, le dataset s’appuie sur la méthode protégée function TDataSet.GetFieldClass(FieldType: TFieldType): TFieldClass; qui retourne la classe du champ à contruire en fonction du type de champ.
La construction effective se trouve dans la classe TfieldDef dans la méthode CreateFieldComponent. Cette méthode construit les champs en faisant appel à la méthode GetFieldClass pour retrouver la classe du champ (TStringField, TfloatField, etc….)
Pour cela cette méthode de TFields fait appel au TDataset pour retrouver sa classe grâce à la fonction GetFieldClass(FieldType: TFieldType): TFieldClass; virtual;
Le problème rencontré est le suivant : la fonction GetFieldClass n’a qu’un seul paramètre : le type du champ. Ceci est insuffisant si l’on souhaite avoir plusieurs type de float différents dépendant du nom du champ. Pour contourner ce problème on peut réimplémenter la méthode CreateFields; virtual; lors de la préparation des champs, stocker le nom du champ dans une variable privée, et exploiter cette valeur lors de la recherche de la classe de type TField par la méthode TDataSet.GetFieldClass. Le principe de fonctionnement de la méthode CreateFields est simple : Il s’agit simplement d’un parcourt de la liste des champs à créer, puis d’un appel à CreateField pour chacun de ces champs. C’est juste avant cet appel que l’on peut mémoriser dans un ou plusieurs champs privés les caractéristiques intéressantes du champ à créer, pour ensuite appeler CreateField Voici un exemple de source qui construit un objet de type TStringField en s’appuyant sur le nom du champ.
On part du principe que tous les champs de la base qui stocke un nom s’appellent ‘NAME’. Bien entendu, si vous utiliser des noms de construction plus complexe dans votre base (avec prefixe, suffixe, etc…rien ne vous empêche d’implémenter l’algo qui va bien dans la méthode GetFieldClass. Si vous souhaitez plusieurs classes en fonction du nom de champ vous pouvez aussi utiliser un case of. ;)
unit MyQuery;
interface
uses
Windows, Messages, SysUtils, Classes, DB, DBTables;
type
TNameField=class(TStringField);
TMyQuery = class(TQuery)
private
fName:string;
protected
procedure CreateFields; override;
function GetFieldClass(FieldType: TFieldType): TFieldClass; override;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('OT', [TMyQuery]);
end;
{ TMyQuery }
procedure TMyQuery.CreateFields;
var
I: Integer;
procedure SetKeyFields;
var
Pos, j: Integer;
KeyFields, FieldName: string;
begin
KeyFields := PSGetKeyFields;
Pos := 1;
while Pos <= Length(KeyFields) do
begin
FieldName := ExtractFieldName(KeyFields, Pos);
for j := 0 to FieldCount - 1 do
if AnsiCompareText(FieldName, Fields[j].FieldName) = 0 then
begin
Fields[j].ProviderFlags := Fields[j].ProviderFlags + [pfInKey];
break;
end;
end;
end;
begin
if ObjectView then
begin
for I := 0 to FieldDefs.Count - 1 do
with FieldDefs[I] do
if (DataType <> ftUnknown) and
not ((faHiddenCol in Attributes) and not FIeldDefs.HiddenFields) then
begin
fName:=FieldDefs[I].DisplayName;
CreateField(Self);
end;
end
else
begin
for I := 0 to FieldDefList.Count - 1 do
with FieldDefList[I] do
if (DataType <> ftUnknown) and not (DataType in ObjectFieldTypes) and
not ((faHiddenCol in Attributes) and not FIeldDefs.HiddenFields) then
begin
fName:=FieldDefList[I].DisplayName;
CreateField(Self, nil, FieldDefList.Strings[I]);
end;
end;
SetKeyFields;
end;
function TMyQuery.GetFieldClass(FieldType: TFieldType): TFieldClass;
begin
if uppercase(fName)='NAME' then
result:=TNameField
else
result:=inherited GetFieldClass(FieldType);
end;
initialization
RegisterClass(TNameField);
end.
Comment faire un case of sur une chaine ?
Voici un doux rêve :
Procedure CaseOf ;
Var S :string ;
Begin
S :=AppelAFonctionMachin ;
Case s of
’truc1’ :begin ... end ;
’truc2’ :begin ... end ;
’truc3’ :begin ... end ;
....
End ;
End ;
Et manque de chance cela n’est pas possible avec Delphi...
Il est facile de remplacer ceci par un rateau qui ressemble à ceci :
Procedure CaseOf ;
Var
S :string ;
Begin
S :=AppelAFonctionMachin ;
If s=’truc1’ then
Begin...end
Else
If s=’truc2’ then
Begin...end
Else
If s=’truc3’ then
Begin ...end ;
End ;
mais bon cela n’est pas très satisfaisant pour faire du code efficace.
Voici un petit truc pour contourner le problème :
Type TMesTrucs=(TRUC1,TRUC2,TRUC3) ;
Procedure CaseOf ;
Var
S :string ;
Begin
S :=AppelAFonctionMachin ;
Case TMesTrucs(GetEnumValue (TypeInfo(TMesTrucs), uppercase(s))) of;
TRUC1 :begin ... end ;
TRUC2 :begin ... end ;
TRUC3 :begin ... end ;
....
End ;
End ;
La fonction GetEnumValue fait partie de l’unité TypInfo. Il faut donc penser à faire le uses qui va bien. Pour rajouter des valeurs il suffit de compléter le type TMesTrucs. C’est certe plus complexe que le doux rêve exposé initialement, mais cela reste quand même assez simple et pratique.
mercredi 1 novembre 2006
Comment fonctionne le ZX81 ?
Le processeur
Il s’agit d’un Z80A standard. La seule curiosité le concernant est que le compteur de rafraichissement des mémoires dynamiques qui est intégré au processeur est détourné de son usage d’origine pour gérer la vidéo
La sortie vidéo
La vidéo est issue de l’ULA. Elle passe ensuite par un étage d’adaptation consitué d’un transistor NPN (plusieurs modèles différents ont été utilisés suivant les versions des cartes mères, comme par exemple, le 2N2369). La particularité de ce transistor est d’être cablé avec 2 pattes en l’air, contre le modulateur UHF. Ce modulateur est un UM1233 réglé sur le canal 36.
Les connecteurs magnétophones
Il s’agit de connecteurs jack femelles de 3,5mm. Les informations sont modulés à 250 bauds en modulation d’amplitude, ce qui a pour conséquence un réglage du volume magnétophone délicat.
L’horloge
Sans doute pour des raisons économiques l’horloge du zx81 n’est pas réalisée à l’aide d’un quartz, mais à partir d’un résonnateur céramique de 6,5 mhz. Cette fréquence sert au registre à décalage de la sortie vidéo, et une fois divisée par 2, au z80a.
L’ULA
Ce circuit intégré spécifique se présente sous la forme d’un boitier DIP 40 broches. Il remplace les 18 circuits intégrés TTL LS du ZX80. Toutefois à la différence de son prédécesseur, l’ULA permet en plus la gestion du mode « SLOW » dont le ZX80 était dépouvu.
La vidéo
Le zx81 n’est pas doté de compteur d’adresse vidéo matériel. Pour simuler ce composant essentiel, l’ULA utilise le processeur. L’ULA « fait croire » au z80 que la RAM vidéo est une mémoire de code. A chaque fois que le moniteur vidéo en ROM tente d’exécuter du code en RAM vidéo, le contrôleur vidéo récupére l’octet lu et envoie au z80 une instruction NOP. Cette astuce permet de simplifier le schéma du zx81, mais a l’inconvénient de ralentir terriblement son fonctionnement.
Le clavier
Il s’agit d’un simple clavier matriciel. Pour simplifier le décodage des colonnes, celle-ci sont directement cablées, via des diodes 1N4148 aux 8 bits de poids forts du bus d’adresse.
Le décodage d’adresse
Là aussi il s’agit d’un décodage simple. Si le bit A14 est à 0 c’est la ROM qui répond sinon c’est la RAM. A noter toutefois que le bit A15 est exploité par le controleur vidéo. Ce décodage rudimentaire provoque l’apparition de nombreux fantômes dans l’espace d’adressage du z80.
La mémoire
Le zx81 est équipé d’une ROM programmé par masque de 8Ko en boitier DIP 24 borches. La caractéristique originale est que le contrôleur vidéo et le processeur l’exploite. Le premier pour la table des caractères qui se trouve dans les 512 derniers octets, le deuxième pour le moniteur système et l’interpreteur BASIC.La ram est constitué, suivant les modèles d’un boitier DIP 24 broche de 1K mot de 8 bits (MK4801 par exemple) ou de deux boitiers DIP de 18 broches de 1K mot de 4 bits (MM2114)
L’extention 16ko
Elle est basé sur 8 boitiers de RAM dynamiques tri-tensions MM4116. L’extention embarque aussi toute la gestion du rafraichissement de la mémoire, le décodage d’adresse et la multiplexage des adresses. La première version de l’extention mémoire sinclair était réalisé en circuit TTL LS classiques sur deux circuits imprimés surperposé. Par la suite le cablage a été simplifié un utilisant un seul circuit imprimé et un boitier ULA spécifique à la place des circuits TTL LS.
