ArSid - Arduino Sid Synthesizer

Sat 20-Apr-24
02:13:00


Lib: I/O

Datum: Sat 19 January 2019
Samenvatting: Het binnenste software schilletje om de I/O heen.
Soort Artikel: Software
Status: Afgerond


[Tekst] [Afbeeldingen] [Aansluitingen] [Broncodes]
lib_io_1-megalines.jpg
1/2: lib_io_1-megalines.jpg.
lib_io_2-timing6502.jpg
2/2: lib_io_2-timing6502.jpg.
[Tekst] [Afbeeldingen] [Aansluitingen] [Broncodes]

Deze Library is het binnenste schilletje om de I/O (= Input/Output) heen. Het zorgt ervoor dat het ArSid-programma rechtstreeks data kan schrijven in EN kan uitlezen uit de registers van de Latches-print. EN de Sids.

Dit houdt in dat het een Adres op de Adresbus zet, de Control-signalen regelt en vervolgens de Data schrijft naar OF inlees van de Databus. De Decoder-print decodeert dan de Adres-bus naar signalen naar de juiste registers, c.q. Sid-IC. De Latches-print regelt de Data naar de registers voor het aansturen van de Lcd-display en de (kleuren-)Leds op het bovenpaneel.

De I/O-Library doet dit met behulp van 4 functies, waarvan er twee hele bekende Basic namen hebben, namelijk POKE() en PEEK().

De Mega Pinnen.

Allereerst een herhaling van welke I/O-pinnen van de Arduino Mega voor welke functies op de drie bussen zijn.

De Reset-puls.

Reset(): De enige taak van deze functie is het geven van een puls op de Reset-lijn van de Control-bus. Deze puls is in alle 65xx-systemen nodig en zorgt ervoor dat alle (aangesloten) IC's hardware-matig gereset worden. Dit is inclusief de processor zelf. In een echt 65xx-systeem wordt de Reset-puls hardware matig opgewekt met meestal een RC-netwerk als basis.

In de ArSid moet deze puls echter software-matig worden opgewekt. In de ArSid zit de Reset-lijn op PortD van de Arduino-Mega en wel (bij het begin ontwerpen) op bit 7. Er is een variabele RESET_MASK dit deze bit bevat.

Software-matig wordt de Reset-lijn eerst als OUTPUT gedefini-eerd en wordt deze (weer) HOOG gemaakt (in het geval deze LAAG was), daarna LAAG en vervolgens weer HOOG gemaakt (en HOOG gehouden).

De Adres-bus klaarzetten.

Set_Address_Out(): Deze functie defini-eert eerst ALLE adres-bus pinnen als uitgang. Dit betreft twee Arduino poorten, namelijk alle bits van PortC en PortL. De adres-bus is in het basis-ontwerp van de ArSid 9 bits breed, maar deze functie is voorbereidt op een bus van 15-bits breed. De 16de-bit is gereserveerd voor een adres-lijn met de naam AdresEnable en heeft als functie om aan te geven of een adres wel of niet geldig is. LAAG is geldig, HOOG is niet geldig of te wel Low-Active.

Deze Adres-Enable-lijn is terug te vinden op de Latches-print en regelt daar de uiteindelijke activering van de Register-Clocks en de Sid CS-lijnen (van Chip Select).

Daarna defini-eert deze functie de lijn Read/~Write als OUTPUT en de lijnen PHI2 en LCD_MASK als INPUT (dit gebeurt in twee losse regels) om als laatste de lijn Read/~Write hoog te maken, ten teke dat de Arduino op LEZEN staat. Op deze manier worden er geen gegevens in de diverse IC's veranderd.

Data schrijven naar een IC.

Poke(aa, dd): Een bekende Basic-naam. En deze functie doet precies wat het Basic-commano zelf ook doet. Het schrijft een byte data DD naar een adres AA. Hierbij is Timing cruciaal.

Na wat correctie AND-operaties schrijft deze functie eerst het adres AA naar de adres-bus (PortC en PortL). De Read/~Write wordt LAAG gemaakt, ten teke dat er geschreven gaat worden. De aangesloten IC's kunnen zich dan voorbereiden om hun databus INPUT te maken, om conflicten te voorkomen als er meerdere uitgangen op 1 lijn zijn.

Pas daarna wordt de Data-bus (Port A) op uitgang gezet en wordt de Data zelf op de Data-bus geplaatst.

Er wordt gewacht zolang de bus-clock Φ2 HOOG is. Dit wachten is om twee redenen nodig. De Arduino-Mega draait op een clokc-frequentie van 16MHz, terwijl de 65xx-bussen hier op 1MHz draaien. De Arduino moet dus wachten op de 65xx-bussen. Tevens mag een Read/~Write-signaal alleen worden veranderd, als de Φ2 niet meer HOOG is. Dan pas wordt de Adres-Enable-lijn LAAG gemaakt, ten teke dat de Adres en Data geldig is om over te nemen.

Er wordt gewacht tot de Φ2 weer HOOG is en de Read/~Write-lijn wordt WEER op LAAG gezet. Deze tweede keer veroorzaakt echter een kleine tijdsvertraging, die kleiner is dan 1msec (en dus kleiner dan de functie DelayMicroSeconds(1) kan geven). Het blijkt dat deze kleine vertraging net genoeg is.

Als laatste worden diverse handelingen weer in omgekeerde volgorde teruggezet. Dat is: de Read/~Write wordt hoog gemaakt (er wordt weer gelezen), de Adres-Enable wordt HOOG gemaakt (ten teke dat de Adressen niet meer geldig (kunnen) zijn) en de Databus worden allemaal weer op INPUT gezet. Alles staat weer op "lezen van de bus".

That's It. Vrij recht-toe-recht-aan.

Data lezen van een IC.

dd=Peek(aa): Een bekende Basic-naam. En deze functie doet precies wat het Basic-commano zelf ook doet. Het leest een byte data DD van een adres AA. Ook hier is Timing is essentieel.

Allereerst wordt de Data-bus op INGANG gezet (tot zover dit nog niet gebeurd was).

Na wat correctie AND-operaties schrijft deze functie het adres AA naar de adres-bus (PortC en PortL). De Read/~Write wordt HOOG gemaakt, ten teke dat er gelezen gaat worden.

De Adres-Enable-lijn wordt LAAG gemaakt, ten teke dat het adres geldig is. Het betreffende IC kan zich dan voorbereiden om zijn databus OUTPUT te maken. Er mag slechts 1 IC zijn Databus op OUTPUT hebben, dit om conflicten te voorkomen als er meerdere uitgangen op 1 lijn zijn.

De Adres-Enable-lijn wordt voor een tweede keer (achter elkaar) LAAG gemaakt, ook hier om een kleine tijdsvertraging te veroorzaken, kleiner dan 1msec (en dus kleiner dan de functie DelayMicroSeconds(1) kan geven). Ook hier blijkt dat deze kleine vertraging net genoeg is.

Pas daarna wordt de Data-bus gelezen.

Als laatste worden ook hier diverse handelingen weer in omgekeerde volgorde teruggezet. De Adres-Enable-lijn wordt HOOG gemaakt (ten teke dat de Adressen niet meer geldig (kunnen) zijn) en de Databus worden allemaal weer op INPUT gezet en de Read/~Write wordt (nog een keer) hoog gemaakt (er wordt nog steeds gelezen).

En de ingelezen Data wordt aan de functie-naam teruggegeven (return dd).

That's It. Ook hier is het vrij recht-toe-recht-aan.

Er wordt niet gewacht op de Φ2. De status van de Φ2 doet niet ter zake bij het lezen van de register (om de schakelaars uit te lezen) en de Sid-IC's worden niet gelezen.

Interupts ...

De hierboven 4 beschreven functies beginnen allemaal met de functie noInterrupts() en eingiden met de functie noInterrupts(). Timing in bovenstaande functies zijn soms zo cruciaal dat zij niet verstoort mogen worden door een Arduino interupt, zoals die opgewekt kunnen worden door de Interupt-Timer of de Seriele-Poort.

Basis-ontwerp functies - overbodige functies.

Als laatste staan er nog drie functies onderaan deze Librarie. Zij werden het eerst geschreven met als doel om een beter begrip te krijgen van wat er moet gebeuren bij het schrijven naar het Adres-bus en bij het omschakelen van de Data-bus naar INPUT of OUTPUT.

Zij worden hier niet gebruikt en zijn daarom als COMMENTAAR gemerkt.


Noot (jan-2019): Het negeren van de Φ2 bij het lezen (Peek()-functie) zou echter niet meer kunnen bij gebruik van FpgaSids.


[Tekst] [Afbeeldingen] [Aansluitingen] [Broncodes]

Afbeeldingen

lib_io_1-megalines.jpg
1/2: lib_io_1-megalines.jpg.
lib_io_2-timing6502.jpg
2/2: lib_io_2-timing6502.jpg.

[Tekst] [Afbeeldingen] [Aansluitingen] [Broncodes]
Broncode: [1: I/O (.cpp)] [2: I/O (.h)]

1: De broncode van I/O (.cpp)

(Mon 01 January 2018) ArSid_IO.cpp: Het binnenste software schilletje om de I/O heen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//=======================================================================================
//
// ArSid_IO - The ArSid_IO Library
//
// All Functions, needed for the hardware IO-calls.
//
//=======================================================================================

#ifndef ARSID_IO_CPP
#define ARSID_IO_CPP

// Include some ArSid_ Libraries
#include "Arduino.h"
#include "ArSid_IO.h"

//=====================================================================================

//  3333333333            33      333333
//      33                33    33      33
//      33              33      33      33
//      33            33        33      33
//      33          33          33      33
//      33        33            33      33
//  3333333333    33              333333

const byte reset_mask     = 0b10000000;   // Mask Reset at the Controlbus.
const byte phi2_mask      = 0b00000001;   // Mask Phi2 signal at the Controlbus.
const byte readwrite_mask = 0b00000010;   // Mask R/~W bit at the de Controlbus.
const byte lcd_mask       = 0b00000100;   // Mask Lcd Ready Signal (not used here).
const byte addr_en_mask   = 0b10000000;   // Mask Addresse Enable (is MSB of the address).
const byte all_output     = 0b11111111;   // All Pins are Output
const byte all_input      = 0b00000000;   // All Pins are Input

void Reset()
{ // Put a RESET-pulse on the Controlbus (Reset is Low-Active)
  noInterrupts();                             // Disable Interrupts
  DDRD  = DDRD  | reset_mask;                 // Make Output
  PORTD = PORTD | reset_mask;                 // Make High
  delayMicroseconds(10);
  PORTD = PORTD & (~ reset_mask);             // Make Low
  delayMicroseconds(10);
  PORTD = PORTD | reset_mask;                 // Make High again
  delayMicroseconds(10);
  interrupts();                               // Enable Interrupts
}

void Set_Address_Out()
{ // Set Addressbus on Output
  noInterrupts();                             // Disable Interrupts
  DDRC = all_output;                          // Low-Byte
  DDRL = all_output;                          // High-Byte + Enable
  // Set Address 0x0000 at the Addressbus, include Disable.
  PORTC = 0x00;                               // Low-Byte
  PORTL = 0x00 | addr_en_mask;                 // High-Byte + Disable
  // Set Controlbus Phi2 and Lcd at Input  and  R/~W at Output
  DDRG = DDRG & (~phi2_mask) & (~lcd_mask);
  DDRG = DDRG | readwrite_mask;
  PORTG = PORTG | readwrite_mask;             // Set Controlbus at Read
  interrupts();                               // Enable Interrupts
}

void Poke(word aa, byte dd)
{ // Write a byte to the Databus - Just like the Basic-command POKE address,data
  noInterrupts();                              // Disable Interrupts
  //////////////////////////////////////////// // Write Addres to Addres-bus
  PORTC = aa & 0xFF;                           // Low-Byte
  PORTL = ((aa >> 8) & 0x01 ) | addr_en_mask;  // High-Byte + Disable
  PORTG = PORTG & (~readwrite_mask);           // Put Write at Control-bus
  DDRA = all_output;                           // Set Databus as Output
  PORTA = dd;                                  // Write Data
  // Wait until Phi2 is High
  while (PING & phi2_mask) != 0)             // PING = P INput port G
    { // Do Nothing                            // Do Nothing
    }
  PORTL = PORTL & (~addr_en_mask);             // Address Enable
  // Wait until Phi2 is Low again (The "moment" of writing)
  while (PING & phi2_mask) == 0)             // PING = P INput port G
    { // Do Nothing                            // Do Nothing
    }
  PORTG = PORTG & (~readwrite_mask);           // Put Write at Control-bus
                                               // (for a 2nd time) (just to create an extra
                                               // short delay, much shorter than
                                               // DelayMicroSeconds can create)

  PORTG = PORTG | readwrite_mask;              // Put Read at Control-bus
  PORTL = PORTL | addr_en_mask;                // Address Disable
  DDRA = all_input;                            // Set Databus as Input again
  interrupts();                                // Enable Interrupts
}

byte Peek(word aa)
{ // Read a byte from the naar Databus - Just like the Basic-function data=PEEK(address)
  byte dd = 0;
  noInterrupts();                              // Disable Interrupts
  //////////////////////////////////////////// // Write Address to Address-bus
  DDRA = all_input;                            // Set Databus as Input
  PORTC = aa & 0xFF;                           // Low-Byte
  PORTL = ((aa >> 8) & 0x01 ) | addr_en_mask;  // High-Byte + Enable
  // PORTA = dd;                                  // Twice for a short delay
  PORTG = PORTG | readwrite_mask;              // Put Read at Control-bus
  PORTL = PORTL & (~addr_en_mask);             // Enable Address
  PORTL = PORTL & (~addr_en_mask);             // Twice for a short delay
  dd = PINA;                                   // Lees de Data
  PORTL = PORTL | addr_en_mask;                // Disable Address
  PORTG = PORTG | readwrite_mask;              // Put Read at Control-bus
  interrupts();                                // Enable Interrupts
  return dd;                                   // Return de data
}

//=====================================================================================
// Not Used Functions ... but here to make clear somethings.
//=====================================================================================

// void Write_Address(word aa)
// { // Write byte to Addressbus
//   PORTC = aa & 0xFF;
//   PORTL = ((aa >> 8) & 0x01) | 0x80;
// }

// void Set_Data_Out()
// { // Set de Databus op Output
//   DDRA = 0xFF;
// }

// void Set_Data_In()
// { // Set de Databus op Input
//   DDRA = 0x00;
// }

//=====================================================================================

#endif // ARSID_IO_CPP

Broncode: [1: I/O (.cpp)] [2: I/O (.h)]

2: De broncode van I/O (.h)

(Mon 01 January 2018) ArSid_IO.h: Het binnenste software schilletje om de I/O heen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//=======================================================================================
//
// ArSid_IO - The ArSid_IO Library
//
// All Functions, needed for the hardware IO-calls.
//
//=======================================================================================

#ifndef ARSID_IO_H
#define ARSID_IO_H

#include "Arduino.h"

//=====================================================================================

void Reset();
  // Put a RESET-pulse on the Controlbus (Reset is Low-Active)

void Set_Address_Out();
  // Set Addressbus on Output

void Poke(word aa, byte dd);
  // Write a byte to the Databus - Just like the Basic-command POKE address,data

byte Peek(word aa);
  // Read a byte from the naar Databus - Just like the Basic-function data=PEEK(address)

//=====================================================================================

#endif // ARSID_IO_H


Broncode: [1: I/O (.cpp)] [2: I/O (.h)]
[Tekst] [Afbeeldingen] [Aansluitingen] [Broncodes]