POKEY Atari XL/XE oraz inne układy I/O, klawiatura, dźwięk, urządzenia, sterowanie, transfer, etc.

Tworząc Atari XL/XE w Turbo Pascal 5.5 trzeba zrealizować układ POKEY, który obsługuje klawiaturę, dźwięk, szeregowy transfer danych, manipulatory, generator liczb losowych, przerwania i inne.

Trzeba zacząć od prostej procedury w Turbo Pascal 5.5:

procedure do_POKEY;
begin
do_sound;
do_keyboard;
do_paddle;
do_serial_port;
set_RND;
do_irq;
end;

Teraz można pracować dalej, etapami – programując każdy element układu POKEY osobno, na koniec ułoży się wszystko w sprawny układ programowy działający na wzór oryginalnego elektronicznego.


Najpierw klawiatura.

Klawisze i znaki i ich numery w Atari XL/XE są trochę inne niż w standardzie ASCII, więc trzeba zrobić jakąś funkcję zamieniającą numery ASCII na ATASCII lub odwrotnie – w zależności od potrzeby:

function ascii2at_conv(d :byte; c :byte) :byte;
begin
if c=1 then
case d of
0,2,4,5,6,7,10,11,12:   d:=32;
{ CTL+1 – zatrzymanie / wznowienie listowania programu }
{ CTL+3 – koniec danych przy wprowadzeniu (EOF) }
{ kursory z CTL }
27: d:=27; { Esc }
72: d:=28; { CrsrUp ctl + – }
80: d:=29; { CrsrDown ctl + = }
75: d:=30; { CrsrLeft ctl + + }
77: d:=31; { CrsrRight ctl + *}
3: d:=125;   { Clrscr – ctl+< lub shift+< }
8: d:=126; { Backspace }
9: d:=127; { Tab }
13: d:=155; { RETURN }
{d:=156} { DelLine – shift+backspace}
{d:=157} { InsertLine – shift+ >}
{d:=158} { DelTab }
{d:=159} { SetTab }
{  3: d:=253;} { BeepSnd CTL2 }
83: d:=254; { Del }
82: d:=255  { Ins }
else begin
{if d<27 then d:=32;
if d>156 then d:=32;}
end;
end
else begin
{         if d<27 then d:=32;
if d>156 then d:=32;}
end;
ascii2at_conv:=d;
end;

Potem tę funkcję się poprawi, uzupełni, zrobi szybką i sprawną, ważne, że jest od czego zacząć, można będzie pracować dalej.

Niektóre znaki w Atari XL/XE nie są wyświetlane na ekranie – są wykonywane jako znaki sterujące, a nie znaki do wyświetlenia. Trzeba zrobić procedurę realizującą wykonanie znaków sterujących Atari XL/XE:

procedure run_char_ctrl(d :byte);
begin
if RAM[$02,$fe]=0 then { wykonanie znakow sterujacych (wartosc nie zerowa w $2fe) }
case d of
27: begin end; {Esc}
28: begin {crsrU}
if ram[0,84]>0 then ram[0,84]:=ram[0,84]-1
else ram[0,84]:=19; {max text line}
end;
29: begin {crsrD}
if ram[0,84]<19 {max tex line}  then ram[0,84]:=ram[0,84]+1
else ram[0,84]:=0;
end;
30: begin {crsrL}
if ram[0,85]>ram[0,82] then ram[0,85]:=ram[0,85]-1
else ram[0,85]:=ram[0,83];
end;
31: begin {crsrR}
if ram[0,85]<ram[0,83] then ram[0,85]:=ram[0,85]+1
else ram[0,85]:=ram[0,82];
end;
125: begin clear_char_DL; RAM[0,84]:=0; RAM[0,85]:=RAM[0,82]; end; {clrscr}
{ pamiec ekranu dla DL zapełnić spacjami $20 }
126: begin {bckspc}
if ram[0,85]>ram[0,82] then
begin
ram[0,85]:=ram[0,85]-1;
end
else
if ram[0,84]>0 then
begin
ram[0,84]:=ram[0,84]-1;
ram[0,85]:=ram[0,83];
end;
DB:=$20; memw;
end;
127: begin  {tab} { zwieksz x kursora o wartosc tab }
if ram[0,85]+ram[0,201]<=ram[0,83] then RAM[0,85]:=RAM[0,85]+ram[0,201]
else
begin
if ram[0,84]<19 { max text line } then
RAM[0,84]:=RAM[0,84]+1
else RAM[0,84]:=0;
RAM[0,85]:=RAM[0,82]+RAM[0,201]+RAM[0,85]-RAM[0,83]-1
end;
end;
155: begin {return}
if ram[0,84]<19 { max text line } then
ram[0,84]:=ram[0,84]+1
else ram[0,84]:=0;
ram[0,85]:=ram[0,82];
end;
156: begin end; {delline}
157: begin end; {insline}
158: begin end; {deltab}
159: begin end; {settab}
253: begin end; {bipsnd(ctl2)}
254:  begin {delundercrsr} {przesun linie w lewo, na koncu wstaw pusty znak}
DB:=$20; memw;
end;
255:  begin {inschar}
DB:=$20; memw;
end;
else
if ram[0,85]<39 then ram[0,85]:=ram[0,85]+1 else
begin
ram[0,85]:=ram[0,82];
if ram[0,84]<19 { max text line}
then ram[0,84]:=ram[0,84]+1
else ram[0,84]:=0;
end;
end;
end;

Początek gotowy. Można pracować dalej…

[..]


Teraz wstępnie procedury, które można łatwo zaprogramować wprost z dokumentacji technicznej do układu POKEY, jako materiał do dalszej pracy, żeby mieć nad czym pracować dalej.

Proste bezpośrednie ustawienie wartości losowych na rejestrach Atari XL/XE (później zrealizuje się procedurę tak, jak należy, ważne, żeby na początek działała jak się oczekuje w efektach bezpośrednich oczekiwanych prostych gotowych):

procedure set_RND; { set random value to RND register }
begin
reg_BAH:=RNDh;
reg_BAL:=RNDl;
DB:=random(256); memw;
end;


Teraz porty szeregowe i transmisje:

procedure do_serial_port;
begin
{ port szeregowy – serial I/O port:
linia wyjscia, linia wejscia, linia zegarowa wyjscia,
dwukierunkowa linia zegara / danych, oraz rejestry kontrolne
uzywane do konfigurowania portu szeregowego }

if SEROUT=255 then; { pocz. transmisji }
{ przerwanie do CPU informacja o pustym SEROUT }

if SERIN=255 then; { pocz. transmisji }
{ przerwanie do CPU informacja o pobraniu do SERIN }

{ serial I/O: reg_BAH:=$d2  reg_BAL:=$0d; DB:=serial_io_data;}
end;


Teraz dźwięki:

procedure do_sound;
begin

{ cztery niezalezne kanaly dzwiekowe z czestotliwoscia, szumami i glosnoscia:

AUDF1 00, AUDF2 02, AUDF3 04, AUDF4 06

Kazdy kanal kontrolowany przez osobny AUDC do wyboru szumu i glosnosci:

AUDC1 01, AUDC2 03, AUDC3 05, AUDC4 07

bity: 00-03: volume
04: volume control only – forcing

}

{ dzielnik czestotl. N i 8-bit rej. kontroli ktory wybiera szum i glosnosc }

if STIMER=1 then; { kanaly dzwiekowe resetowane }

Fin_N:=0.064;

Fin_N1:=Fin_N;
Fin_N2:=Fin_N;
Fin_N3:=Fin_N;
Fin_N4:=Fin_N;

if AUDCTL and 128 = 128 {7bit} then; { 17bit licznik redukuje sie do 9bit}
if AUDCTL and  64  = 64 {6bit} then Fin_N1:=1.79; { 1.79MHz zamiast 64kHz }
if AUDCTL and  32  = 32 {5bit} then Fin_N3:=1.79; { 1.79MHz zamiast 64kHz }
if AUDCTL and  16  = 16 {4bit} then Fin_N2:=Fin_N1; { N2 taktowane wyjsciem N1 zamiast 64kHz (16-bit) }
if AUDCTL and   8  =  8 {3bit} then Fin_N4:=Fin_N3; { N4 taktowane wyjsciem N3 zamiast 64kHz (16bit) }
if AUDCTL and   4  =  4 {2bit} then; { ustaw Hi Pass Filter na CH1, taktowany CH3 }
if AUDCTL and   2  =  2 {1bit} then; { ustaw Hi Pass Filter na CH2, taktowany CH4}
if AUDCTL and   1  =  1 {0bit} then Fin_N:=0.015; { zmien 64kHz na 15kHz}

{ opcje: 4 kanaly 8-bitowe lub 2 kanay 16-bit, lub 1 kanal 16-bit i 2 kanaly 8-bit }

{ 3 liczniki 1.79MHz do generowania losowych szumow: 17bit, 5bit, 4bit }

{ 1.79MHz = 1.78979 MHz Fout=Fin/2(AUDF+M), M=4 if AUDCTL bit 3 or 4 = 0,
M=7 if AUDCTL bit 3 or 4 = 1}
{ 64 kHz  = 63.9210 kHz }
{ 15 kHz  = 15.6999 kHz Fout=Fin/2N, N=AUDF+1}

{ PIN37 – AUDIO – Audio Out }
end;


Teraz przerwania:

procedure do_IRQ; { $fffe-$ffff }
begin
{ 8 przerwan IRQ: BRK, OTHER key, SERIAL INPUT READY, SERIAL OUTPUT NEEDED,
TRANSMISSION FINISHED, TIMER #4, TIMER #2, TIMER #1 – moga byc wl. lub wyl.
programowo, istnieje rejestr do odczytania stanu przerwan}

{ IRQEN (0E) – stan przerwan, 0 – brak przerwania, 1 – przerwanie }

if IRQEN and 128 = 128 then;  { D7 BREAK KEY}
if IRQEN and 064 = 064 then;  { D6 OTHER KEY }
if IRQEN and 032 = 032 then;  { D5 SERIAL INPUT READY }
if IRQEN and 016 = 016 then;  { D4 SERIAL OUTPUT NEEDED }
if IRQEN and 008 = 008 then;  { D3 TRANSMISSION FINISHED }
if IRQEN and 004 = 004 then;  { D2 TIMER #4 – audio dzielnik #4 osiagnal 0 }
if IRQEN and 002 = 002 then;  { D1 TIMER #2 – audio dzielnik #2 osiagnal 0 }
if IRQEN and 001 = 001 then;  { D0 TIMER #1 – audio dzielnik #1 osiagnal 0 }

{ IRQST (0E) – testowanie stanu IRQEN, 0 – przerwanie, 1 – brak przerwania }

{ zainicjalizowac wektory i adresy przerwan prawidlowo }

if reg_p.i=0 then
begin

{ wykonaj JSR dla IRQ (nie naruszajac stanow pracy CPU) :
dane na stos i jsr, przywrocenie danych, powrot: }

irq_vect_l:=$fe; irq_vect_h:=$ff;
exec_irq;

reg_BAH:=IRQENh;
reg_BAL:=IRQENl; mem;
b:=DB;

{ ustawienie adresow IRQ wartosciami pracy POKEY }

{..}

{    008:;  VSEROC = $20e }
{    016:;  VSEROR = $20c }
{    032:;  VSERIN = $20a }
{    064:;  VKEYBD = $208 }
{    128:;  BRKKY = $236 }

end;
{ powrot do programu CPU }
end;


Wiosełka (manipulatory) na końcu się dorobi.


Jeszcze z czego korzystamy w układzie POKEY, żeby na koniec wszystkie procedury dostosować do struktur realnych, którymi można operować:

var

CMOS4052_key_value :byte;

{ POKEY ADDRESS TABLE – WRITE, READ }

{ 0 } AUDF1,  POT0,
{ 1 } AUDC1,  POT1,
{ 2 } AUDF2,  POT2,
{ 3 } AUDC2,  POT3,
{ 4 } AUDF3,  POT4,
{ 5 } AUDC3,  POT5,
{ 6 } AUDF4,  POT6,
{ 7 } AUDC4,  POT7,
{ 8 } AUDCTL, ALLPOT,
{ 9 } STIMER, KBCODE,
{ A } SKRES,  RANDOM_,
{ B } POTGO,
{ C }
{ D } SEROUT, SERIN,
{ E } IRQEN,  IRQST,
{ F } SKCTLS, SKSTAT :byte;

AB, DB, K, SKCTL, BINCNT :byte;

Fout, Fin_N, Fin_N1, Fin_N2, Fin_N3, Fin_N4 :real;

pin_array :array [1..40] of byte;

{

01 – VSS  – Ground              – I
02 – D3   – Data Bus            – I/O
03 – D4   – Data Bus            – I/O
04 – D5   – Data Bus            – I/O
05 – D6   – Data Bus            – I/O
06 – D7   – Data Bus            – I/O
07 – O2   – Phase 2 Clock       – I
08 – P6   – Pot Scan            – I
09 – P7   – Pot Scan            – I
10 – P4   – Pot Scan            – I
11 – P5   – Pot Scan            – I
12 – P2   – Pot Scan            – I
13 – P3   – Pot Scan            – I
14 – P0   – Pot Scan            – I
15 – P1   – Pot Scan            – I
16 – /KR2 – Keyboard Scan       – I
17 – VDD  – 5 V Power           – I
18 – /K5  – Keyboard Scan       – O
19 – /K4  – Keyboard Scan       – O
20 – /K3  – Keyboard Scan       – 0
21 – /K2  – Keyboard Scan       – 0
22 – /K1  – Keyboard Scan       – 0
23 – /K0  – Keyboard Scan       – 0
24 – SID  – Serial Input Data   – I
25 – /KR1 – Keyboard Scan       – I
26 – BCLK – Bidirection Clock   – I/O
27 – OCLK – Serial Output Clock – O
28 – SOD  – Serial Output Data  – O
29 – /IRQ – Interrupt Request   – O
30 – /CSO – Chip Select         – I
31 – CS1  – Chip Select         – I
32 – R/W  – Read/Write Control  – I
33 – A3   – Address Bus         – I
34 – A2   – Address Bus         – I
35 – A1   – Address Bus         – I
36 – A0   – Address Bus         – I
37 – AUDIO – Audio Out          – O
38 – D0    – Data Bus           – I/O
39 – D1    – Data Bus           – I/O
40 – D2    – Data Bus           – I/O

}

Czyli wstępnie POKEY READY

[]

Teraz uzupełnianie i poprawianie procedur, aż POKEY będzie działał jak oryginalny elektroniczny układ Atari XL/XE.

[..]


Dostosowanie układu POKEY Atari XL/XE do pracy w architekturze 64 bit wymaga jedynie dodania końcówek na układzie, żeby móc skorzystać w układzie z rejestrów 64 bitowych, w tym końcówki odpowiedzialne za wysyłanie sygnałów na urządzenia dźwiękowe dla każdego kanału dodanego do oryginalnego POKEY i końcówki do obsługi klawiatury multimedialnej, oraz ewentualnie końcówki do 64 bitowej transmisji szeregowej na osobnych liniach danych in/out z osobnymi liniami do adresowania in /out (równoległe niezależne strumienie danych i adresów in/out) w ilościach linii sensownych użytkowo, oraz osobne linie do transmisji audio in/out 64-bit w całym zakresie kanałów udostępnionych dla użytkownika.

41-69        – A4-A33    – Address Bus
70-126     – D8-D63    – Data Bus
127            – /K6             – Keyboard scan
128            – /K7             – Keyboard scan
129-157  – Audio        – Audio Out

Potem testowanie i zabawa POKEY-64.

Profesjonalne i naukowe zastosowanie układu POKEY-64 we współpracy z różnymi urządzeniami zewnętrznymi – w dokumentacji naukowej – nie do użytku ogólnego.

Czyli POKEY-64 READY.


Literatura:

1. Asembler 6502 / Jan Ruszczyc ; Stołeczny Ośrodek Elektronicznej Techniki Obliczeniowej., Warszawa, Wydaw. SOETO, 1987

2. POKEY CO12294, Atari Inc., 03.1982


POKEY-64 standard 256-pin chipset.