Съдържание
Периферна платка за Правец 8 от нулата
Автор: Милен Христов
Прочети оригиналната статия
Преди около година взех от OLX един умрял Правец 8C. Имах желание, но нямах знанията да го съживя. След около 6 месеца усилия успях. Любопитството гарнирано с детските спомени за игри на Каратека, ме накараха да задълбая отново в старите книги и да потърся как точно работи този компютър. По-долу ще опиша какво (пре)открих и как успях да си направя периферна платка, която показва часът в реално време. Трябва да отбележа, че Правец 8 фабрично няма часовник (real time clock).
Идеята за периферна платка
От около 5-6 години се занимавам с Ардуино (вкл. esp8266 и esp32) като хоби, пиша по-елементарен код на C, както и да правя не-сложни схеми със spi/i2c сензори и TTL логически компоненти. Естественото продължение на това би било да съчетая технологични познания за съвременните микроконтролери с технологията от преди 40г - Правец 8 / Apple II. Колко трудно би било? 🤷🏻♂️
Правец 8С е клонинг на Apple IIe (not enhanced), затова освен оскъдното ръководство на компютъра на български език, трябваше да разровя и доста сканирани книги за Apple II от онази епоха. За разлика от сегашната политика на Apple Inc., тогава компанията е описвала подробно схемите на компютрите си, както и изходният код на фърмуера. В резултат на това доста хора са написали подробни книги, насочени към потребителите. Не на последно място вероятно това е причината и за появата на Правец 8.
След доста четене по темата, както на български, така и на английски, се спрях на идеята да направя проста периферна платка базирана на Ардуино Нано. Последният работи на 5V логика, което пасва перфектно на проекта. Микроконтролерът трябва единствено да подава данни към Правец 8С.
TL;DR Получи се 😎
Шина за данни и адресна шина
Правец 8С (както и цялата фамилия Правец 8) има 8 битова шина за данни (data bus) и 16 битова адресна шина (address bus). Когато компютърът реши да чете от или праща данни към периферна платка, забодена в някой от слотовете той го прави по следният начин:
- Процесорът поставя последователност от битове на адресната шина, така че 16-те пина на адресната шината (А0-А15) формират определен адрес.
- Процесорът активира конкретният слот.
- Процесорът казва дали иска да чете или пише на конкретният адрес.
- В зависимост от това какво е поискал процесорът в предходната точка (ще подаде данни или ще чака данни от периферията), следва четене или подаване на данни по шината за данни.
Слотове и адреси
Всеки слот има резервирано адресно пространство. То може да е различно по големина, в зависимост как се обръщате към него (на кой адрес от паметта).
На всеки слот има следните пинове /DEVSEL
, /IOSEL
, /IOSTRB
. Те активират конкретният слот, на който се намират, когато е подадена логичска 0 на този пин. Чрез тези пинове се избира кой слот да е активен, когато процесорът иска да говори с периферия в него. Например, за да комуникираме с платка в слот 5, трябва да се подаде 0 на /IOSEL
на слот 5. Това става с обръщане към конкретен адрес в паметта, който отговаря на конкретният номер на слот.
Защо са три пина за активиране на всеки от слотовете? Защото всеки от тези пинове дава достъп до различно по големина адресно пространство. Няма да влизам в подробни обяснения, защото самият аз не мога да се оправям много с шеснайсетични адреси, но ще спомена следното:
/DEVSEL
дава достъп до 16 адреса за конкретният слот
/IOSEL
дава достъп до 256 адреса
/IOSTRB
дава достъп до още 2Кб адресно пространство, споделено между всички карти
Ако искаме да ползваме пина /DEVSEL
, трябва да ползваме следната формула, за да определим адреса: $C0nx
, където 'n' е номерът на слота, в който е картата + 8 в шеснайсетичен режим (например адреса на карта в слот 5 ще бъде $C0Dx
), а 'x' е номерът на адреса между 0 и F (т.е. 16 адреса). Така ако искаме да прочетем адрес 0 от карта в слот 5, трябва да прочетем адреса $C0D0
.
Ако имаме нужда от повече от 16 адреса, тогава трябва да ползваме за активация на конкретният слот пинът /IOSEL
. В този случай, адресите се формират по формулата $Cnxx
, където 'n' е номерът на слота а 'хх' е номерът на адреса в шеснайсетичен режим (00-FF). Ако ползваме предходният пример за карта в слот 5 и адрес 0, тогава се обръщаме към адрес $C500
.
Четене и запис
Процесорът може да чете или пише от/на периферията. Това става с пинът R/W
, който е наличен на всеки от слотовете. Когато този пин е логическа нула - процесорът записва, когато е логическа единица - чете. Четенето и писането може се прави от софуерът, който ще управлява периферната платка. Например в Бейсик това става с командите PEEK
(четене на адрес) и POKE
(писане в адрес). В моят случай, платката която направих е само за четене, така че ползвам само PEEK
.
Хардуерен дизайн
Както споменах по-горе в сърцето на платката е Ардуино нано. Това решение следва практиката на всички съвременни софтуерни карти (softcard) за Apple][. Основната логика на периферията се изработва от софтуерът на самият микроконтолер, което спестява пари за хардуер и най-вече дава доста повече гъвкавост и възможности за творчество.
За да може платката да разбере какво точно иска от нея процесорът на Правеца, трябва да ползваме адресен дешифратор, който да чете адресната шина на Правеца. Аз ползвам схемата 74LS138, която дава достъп до 16 адреса, но реално позлвам три (0x00
, 0х01
и 0х02
).
Всеки един от тези адреси активира един от трите 8 битови отместващи регистъра 74HCT595N. Ако е активиран адрес 0х00
, данните от първият отместващ регистър се подават на адресната шина към процесора на Правеца, ако е активиран адрес 0х01
или 0x02
тогава вторият, съответно третият отместващ регистър подава данните, които са в него към шината за данни. Така реално подаваме цели 24 байта един след друг към процесора.
Отместващите регистри се пълнят постоянно с данни от Ардуиното, но изходите им са блокирани и не подават нищо към шината за данни. В момента, в който процесорът реши да прочете един от трите адреса, пинът на 74LS138, който е активиран чрез обръщане към конкретният адрес, „отпушва“ данните на свързаният с него отместващ регистър и данните от последния протичат по шината за данни към процесора.
Трите отместващи регистъра са свързани последователно, като данните от Ардуино се подават към тях под формата на 24 последователни бита (по 8 бита за всеки регистър). Данните се подават по серийна линия (1 бр. пин от Ардуино), но излизат като паралелен сигнал към шината за данни на Правеца. Clock
и Latch
сигналите за отместващите регистри се контролират от Ардуино, но OE
(output enable) линиите на всеки се активират от адресният дешифратор.
Софтуер
Софуерът за Ардуино е елементарен. Ползвам библиотеката на часовник модула DS3231, като чета в безкраен цикъл час, минути и секунди и ги подавам като три последователни отмествания по 8 бита със shiftOut()
командата към отместващите регистри. Така от Ардуино излизат 24 бита данни към регистрите на платката. Това е всичко от страната на Ардуино. Реално може да се закачи всяка i2c библиотека, която управлява сензор (темепратура, влажност и т.н.) и той да подава данните към Правец-а.
#include <DS3231.h> #include <Wire.h> DS3231 myRTC; bool h12Flag = false; bool pmFlag; int latchPin = 4; int clkPin = 3; int dataPin = 2; byte hour, minute, second; unsigned long int total; unsigned long int total_u16; void setup() { Serial.begin(9600); Wire.begin(); pinMode(latchPin, OUTPUT); pinMode(dataPin, OUTPUT); pinMode(clkPin, OUTPUT); } void loop() { hour = myRTC.getHour(h12Flag, pmFlag); minute = myRTC.getMinute(); second = myRTC.getSecond(); // Nibble test pattern // hour = 0b00000001; // minute = 0b00000010; // second = 0b00000011; Serial.print("\n"); Serial.print("hour:"); Serial.print(hour,DEC); Serial.print(" "); Serial.print("minute:"); Serial.print(minute,DEC); Serial.print(" "); Serial.print("second:"); Serial.print(second,DEC); Serial.print("\n"); digitalWrite(latchPin, LOW); shiftOut(dataPin, clkPin, MSBFIRST, hour); shiftOut(dataPin, clkPin, MSBFIRST, minute); shiftOut(dataPin, clkPin, MSBFIRST, second); delay(2); digitalWrite(latchPin, HIGH); delay(1000); }
От страна на самия Правец 8, елементарна програмка на Бейсик чете трите адреса на периферната платка и показва стойностите записани в тях. В долният пример адресите са изчислени за слот 5.
10 ? PEEK($C0D0) 20 ? PEEK($C0D1) 20 ? PEEK($C0D2) ] RUN
Имайте предвид, че бейсик на Правец 8С разбира адресите в шеснайстетичен режим (hex), но за Правец 8М и Правец 82, ще трябва да ги подавате в десетичен режим (decimal).
Схема на интерфейсната платка
Схема на интерфейсната платка можете да свалите и в този PDF файл
Други съвременни платки за Правец 8 / Apple II
a2heaven - Богата колекция от периферия, разработени в Пловдив 😊🇧🇬
esp32 SoftCard - универсална платка, с която можете да играете Doom II на Правец 8. Разработена в София 😊🇧🇬
Dan 2 Contoler - перфиерна платка с богати възможности, включително и мрежов интернет.
a2idiot - esp32 карта с общо предназначение
Apple2-IO-RPi - Интерфейсна платка с Распъри Пай
ProDOS rom card - ПроДОС ром карта
AppleII VGA - VGA карта за Apple 2