Архітектура та програмування комп'ютера Texas Instruments TI-99/4a

Комп'ютер Texas Instruments TI-99/4a майже невідома за межами США, однак він був там дуже популярний (випущено більше двох мільйонів машин). Хоча цей комп'ютер створювався як домашній, суттєвою особливістю (багато в чому визначило його архітектуру, а потім і долю) було те, що за основу взяли вже існував серйозний міні-комп'ютер TI-990, зібраний на звичайній ТТЛ логіці. Фактично, мікропроцесор TMS9900 в комп'ютері TI99/4A є реалізацією TI-990, але у вигляді чіпа. TI-990 був випущений в 1975 році, а TMS9900 в 1976 році.



Таким чином, TI99/4a (у 1979 був випущений трохи більш простий TI-99/4, а в 1981 вже TI-99/4a) отримав у спадок вкрай дивну, для домашніх комп'ютерів, архітектуру. По-перше, мікропроцесор TMS9900 в ньому 16-розрядний — з чесної 16-розрядною шиною даних (це в кінці 1970-х!). По-друге, на чіпі немає регістрів (крім PC, прапорів і покажчика «регістрів» WP). Те, що можна назвати регістрами, знаходиться в окремій мікросхемі 16-розрядного статичного ОЗП розміром 256 байт і може адресуватися як пам'ять і (перші 16 слів) як регістри R0..R15. Називається це «scratchpad».
Апаратного стека немає, натомість збереження значень при виклику підпрограм здійснюється зміною покажчика початку регістрів WP у цьому самому ОЗП (нагадує регістрові вікна в Sparc'ах). В предка (TI-990) це також використовувалося для перемикання контексту при реалізації багатозадачності.

Хоча тактова частота TMS9900 — 3 МГц, інструкції займають досить багато тактів — не менше 8. При цьому реалізовано навіть множення і ділення (124 такту).




Всі, крім процесора, на жаль, більш аскетично. Крім цього крихітного статичного ОЗП є ще відеопам'ять — 16кб повільної 8-розрядної DRAM, доступ до якої здійснюється через відеоконтролер. Іншими словами, без додаткової периферії TI99/4A являє собою 16-розрядний комп'ютер з ОЗП 256 байт, вбудованими в ПЗУ BASIC і програмами на картриджах (у них до 8кб чесно адресуемого ПЗУ або більше, з різними хитрощами ).

В голий комп'ютер навіть з касети завантажити нормальну програму неможливо, оскільки немає для цього ОЗУ (256 байт не в рахунок, а решта — відеопам'ять).
Тому мінімально-адекватна конфігурація комп'ютера, це PEB (Peripheral Expansion Box) — спеціальна велика корзина для плат розширення, дисковод та розширення пам'яті 32kb. Або NanoPEB — сучасний пристрій, що включає все перераховане.
До PEB/NanoPEB необхідний ще картридж Editor/Assembler (без якого можна завантажувати та запускати лише програми на Бейсіку і який заодно містить згадані додаткові 32kb ОЗП).



Відеоконтролер TMS9918 (також використовується в MSX і ColecoVision) підтримує графічний (256x192, 15 кольорів) і текстовий режими, дозволяє змінювати знакогенератор, може виводити одночасно 32 апаратних спрайту 8x8 і 16x16 (правда, в одному рядку може перебувати одночасно не більше чотирьох штук).

Звук виводиться через TMS9919 (його аналог, SN76489, використовувався в IBM PCjr, BBC Micro, ColecoVision, деяких консолях Sega):
4 канали (три генератора прямокутників і один — шуму), 16 рівнів гучності.

Вважається, що така нерівномірна в плані розрядності і місцями недолуга архітектура вийшла з-за того, що не була під-час готова 8-розрядна версія процесора TMS9900 (TMS9985).

Що стосується програмного забезпечення, то TI99/4A призначався для цілей освіти (фактично, на противагу Atari і Commodore). У сукупності з описаними вище особливостями архітектури це призвело до того, що хороших ігор на ньому дуже мало (якщо взагалі є). Той факт, що комп'ютер був поширений виключно в США, додав до цього ще й відсутність демосцени (моя інтро 99tro була, здається, другий на цій платформі).

СУЧАСНИЙ СТАН СПРАВ
Як вже було згадано, з голим TI99/4a мало що можна зробити, хіба що з картриджів щось запускати (якщо вони є). Необхідна для нормальної роботи кошик розширення PEB коштує досить недешево і займає багато місця. Оскільки історично склалося так, що вся серйозна розробка і взагалі які-небудь дії з TI були зав'язані на той самий PEB, сучасні фанати машинки пішли по шляху його емуляції (з емулятором ROM картриджів навіть ніхто, схоже, і не морочився).

Сучасним замінником PEB є NanoPEB. При певному терпінні його можна знайти на ebay або в окремих продавців за 50...80e.
Це пристрій встромляється в правий порт комп'ютера (туди ж, куди і синтезатор мови. В т. ч. можна увіткнути його через синтезатор) і емулює 32кб додаткового ОЗУ, три дисководу (на CompactFlash карти) і порт RS-232.
Існує також пристрій CF7+, в якому все теж саме, але замість RS-232 є паралельний порт Centronics.

Сама по собі наявність NanoPEB дозволяє завантажувати лише програми на Бейсіку (а з стандартного TI Бейсика неможливо запустити програму в машинних кодах, якщо не вважати зовсім вже брудних і нетривіальних хаків).
Тому абсолютно необхідний ще картридж Editor/Assembler (EA), втыкаемый у верхній порт. З ним вже можна завантажувати та запускати нормальні програми. Картридж також шукається на Ebay. Начебто, замість нього можна використовувати картридж Extended Basic (XB).

Для перенесення програм на CompactFlash карту необхідно використовувати спеціальні утиліти. Найпопулярніша — ti99dir (під Windows).

Варто згадати проект F18A — реалізацію відеоконтролера TMS9918A на FPGA, з низкою доповнень. Тобто відеочіп в звичайному TI-99/4a замінюється на новий, при цьому весь старий софт йде, як раніше, але з'являється можливість виводити зображення на VGA монітор, встановлювати раніше недоступні відеорежими і т. п.

РОЗРОБКА
Крім програм на Basic, про яких ми говорити не будемо, існує два основних формати, у яких може бути представлена програма в машинних кодах — для завантаження з диска через Editor/Assembler і для картриджа.

Файли, що завантажуються через EA, називаються EA3 (тому, що у Editor/Assembler для їх завантаження треба вибрати третій пункт меню — «LOAD AND RUN»). Вони можуть працювати в будь-якій області пам'яті (тобто EA при завантаження виконує лінковку). Для емулятора такі файли зберігаються на образі дисків (.DSK) і мають формат Dis/Fix 80.
Так само, рідше, можуть потрапляти memory image файли звані EA5 (EA завантажуються вибором п'ятого пункту меню — «RUN PROGRAM FILE»). Це вже мають посилання файли, прив'язані до адресами пам'яті. В емуляторі також зберігаються в образах дисків як «Program».

Завантаження з диска програми в кодах в обов'язковому порядку вимагає катриджа Editor/Assembler (або аналога).

Програми для картриджів жорстко прив'язані до адрес і являють собою просто бінарники, який прошивається в ПЗУ (для емуляторів це формат .RPK, рідше — .BIN).

Існує два істотно різні підходи до написання програм на асемблері. Перший — використання т. зв. мови GPL. Це щось середнє між Extended Basic (XB) і Assembler, як по продуктивності, так і за можливостями. Тобто для всього інтенсивно використовуються підпрограми існуючі в ПЗП і, що важливо, мають бути включені переривання.

Другий варіант — асемблер, про який і піде мова.

Основна незвичайність TI-99/4A, по суті, в процесорі TMS9900. Від більшості процесорів, які використовувалися в домашніх комп'ютерах того часу, його відрізняє наступне:

Фізично в процесорі всього три регістра — ST (прапори), PC (покажчик команди) і WP (покажчик робочої області). Регістри R0-R15 знаходяться у статичному ОЗП зовні чіпа. Адреса, з якої вони починаються, зберігається в регістрі WP і може бути змінений. Звичайно його встановлюють рівним >8300 (знак ">" означає шестнадцатиричную систему, знак ":" — двійкову).
З >8300 >83FF знаходиться 256 байт статичного 16-розрядного ОЗУ, званого «scratchpad».
Робота з ним приблизно в 2 рази швидше, ніж з рештою динамічним 8-розрядним (стандартним розширенням 32kb).

При цьому є багато режимів адресації і однією командою можна, наприклад, перенести значення з комірки пам'яті в іншу, заодно змінивши його.

В процесорі немає стека — передбачається використання відображення регістрів на будь-яку область пам'яті командою LWPI addr для збереження і відновлення контексту.
Виклик підпрограм здійснюється командою BL ADDR, яка заносить в регістр R11 наступний після неї адресу. Відповідно, для повернення використовується B *R11 (вона ж RT). Є також BLWP, зберігає крім адреси повернення ще й workspace (але при цьому повільна).

Зазвичай насамперед вимикають всі переривання і встановлюють початок регістрів на початок статичного ОЗП. Тому заготівля програми виглядає так:

DEF START

WRKSP EQU >8300

START LIMI 0
LWPI WRKSP



* SOME CODE HERE

END START

З-за такої реєстрової архітектури з'являються особливості програмування. А саме:

1.До регістрів можна звертатися не лише як до R1, R2 і т. д., але (оскільки вони знаходяться в адресному просторі) і як до комірок пам'яті. Це, зокрема, дозволяє працювати окремо з молодшими і старшими байтами регістра, які для цього заздалегідь ставлять константами:

ws0 equ >8300; Workspace 0

; Register direct low-byte access

R0L equ ws0+1; Workspace 0 R0 low byte
R1L equ ws0+3; Workspace 0 R1 low byte
R2L equ ws0+5; Workspace 0 R2 low byte

Однак зрозуміло, що далеко не завжди в команді можна просто замінити регістр на пам'ять (для даної команди може просто не виявитися потрібного режиму адресації).
З іншого боку, це все одно буває необхідно, т. к. команд, які працюють з половинками регістрів — трохи (MOVB, SWPB, AB, SB, деяка логіка).

Звичайно, незалежно від поточного встановленого workspace завжди є доступ до будь-якого регістру в будь-якому workspace (за відповідним йому адресу в пам'яті).

2.Оскільки стека немає (іноді, втім, його програмно імітують), збереження і відновлення регістрів здійснюється перемиканням workspace. Якщо користуватися цим інтенсивно, наявні 256 байт швидкого SRAM закінчаться швидко — їх не вистачає завжди.

Щоб показати, як виглядає код для TMS9900, невеликий приклад — Hello World:

DEF START

WRKSP EQU >8300

VDPWD EQU >8C00 * VDP RAM write data
VDPWA EQU >8C02 * VDP RAM read/write address

START LIMI 0 * disable interrupts
LWPI WRKSP * set default workspace

* set VDP RAM start address (low and high byte)

LI R0,>0000
ORI R0,>4000
SWPB R0
MOVB R0,@VDPWA
SWPB R0
MOVB R0,@VDPWA

LI R1,HELLOWORLD * ascii string address
LI R2,12 * total chars

NEXTCHAR
MOVB *R1+,@VDPWD * put next char on screen
DEC R2
JNE NEXTCHAR

LOOPBACK
JMP LOOPBACK * stop and do nothing

HELLOWORLD
TEXT 'HELLO WORLD!' * string data
BYTE 0

END START

Це сучасний (не зовсім традиційний) асемблер TMS9900. У первісному, крім відсутності двокрапки після міток, регістри записувалися не як R з цифрою, а як просто цифра. Тобто LI 4,5 і MOV 7,8 у старому асемблері це LI R4,5 і MOV R7,R8 в новому.

Як легко бачити, це не сильно додавало читання, так що тепер пишуть R (хоча, навіть сучасні ассемблеры розуміють старий варіант запису). Деякі люди, втім, заважають обидва синтаксису. В результаті в одному і тому ж исходнике можна зустріти поруч мнемоніки SLA R5,0 (зсув R5 вліво на кількість розрядів з регістра R0) і SLA R5,1 (зсув R5 вліво на _один_ розряд).

Деякий (медичний :) інтерес представляє команда X, яка виконує інструкцію, код якої передається їй у якості параметра.

; Тут насправді виконується інструкція B *R11

LI R9,>045B; код інструкції B *R11
X R9

; А тут насправді виконується інструкція LI R1,>1234

LI R0,>0201; код інструкції LI R1,xxx
X R0
DATA >1234; це операнд xxx (>1234) для цієї інструкції

Вважається, що X можна використовувати в цілях налагодження, а також для переходів з табличок. Ну і в будь-яких ситуаціях, де потрібен самомодифицирующийся код.

КАРТА ПАМ'ЯТІ
>0000 -----------
Console ROM
>2000 -----------
Low memory expansion
>4000 -----------
Peripheral cards ROM
>6000 -----------
Cartridge ROM/RAM
>8000 -----------
Scratchpad RAM memory mapped devices
(З >8300 >83FF знаходиться статичне ОЗП обсягом 256 байт.
Регістри процесора найчастіше відображають починаючи з >8300. З >8400 >9C02 адреси для доступу до відео, звуку, синтезатору, GROM )
>A000 -----------
High memory expansion
(стандартне розширення ОЗУ 32кб. З >A000 зазвичай починається типова завантажується з диска програма)

>FFFF

Що стосується ПЗУ, TI-99/4a використовуються так звані GROM. Вони не відображаються в адресний простір процесора, а доступ до даних здійснюється послідовно — за спеціальною адресою заноситься початковий адресу даних в GROM і потім по іншому спеціальному адресою звідти читаються дані. При цьому поточний адресу даних GROM збільшується автоматично, при кожному читанні.

Друк рядка символів з GROM

li r0, >0874; starting GROM address of small character set GROMSC
movb r0,@GROMWA; set GROM read pointer to address 06B0 (high part)
nop; wait a beat (not sure if necessary)
movb @WR0LB,@GROMWA; set GROM read pointer to address 06B0 (low part)

li r0,>0000+(8*32*8); VDP Pattern Table start address

movb @R0L,@vdpwa; Send low byte of VDP RAM write address
ori r0,>4000; Set read/write bits 14 and 15 to write (01)
movb r0,@vdpwa; Send high byte of VDP RAM write address

li r1,GROMRD

li r2,32
nextchar:
movb *r1,@vdpwd
movb *r1,@vdpwd
movb *r1,@vdpwd
movb *r1,@vdpwd
movb *r1,@vdpwd
movb *r1,@vdpwd
movb *r1,@vdpwd
; movb *r1,@vdpwd
clr @vdpwd; skip one byte
dec r2
jne nextchar

ГРАФІКА
Для виведення графіки і тексту, TI-99/4a встановлений відеочіп VDP TMS9918a, який використовується також в MSX1 і ColecoVision.
VDP дозволяє виводити графіку з роздільною здатністю 256x192 в 15 не надто вдалих кольорах (+1 прозорий, через який просвічує фон) і одноколірні спрайт — 32 штуки одним розміром 8x8 або 16x16 з істотним обмеженням: не більше 4-х спрайтів на одному рядку.
У VDP 16kb DRAM і вона розподіляється між графікою (текстом), квітами і спрайтами
Відеопам'ять не відображається в адресний простір процесора. Для запису в неї потрібно спочатку занести за адресою початковий адреса відеопам'яті, за яким буде здійснюватися запис, а потім писати поспіль потрібні значення за іншою адресою (одному). При цьому кожне наступне значення виявиться у відеопам'яті збільшується на одиницю адресою.

Взалежності від відеорежиму і розподілу пам'яті можна вибрати оптимальний з точки зору зручності адресації, використовуваної пам'яті і кількості кольорів (в піксельному блоці) режим. За своєю суттю всі режими — текстові. У тому сенсі, що у всіх випадках є поняття «символи» (characters), які можна поміщати в різні місця екрану і які можна змінювати. Але, оскільки можна перепрограмувати символи так, щоб їх вигляд відповідав їх коду, то за фактом режим вже виявляється «графічним». Втім, це типово для багатьох відеоконтролерів 1980-х. Навіть у порівняно сучасному VGA була така можливість (автор використовував її для реалізації швидкого режиму 640x400 з окремою пам'яттю для квітів).

Найчастіше в TI використовується Graphic Mode I (саме в цьому режимі виявляється комп'ютер при включенні та ініціалізації). У ньому можна задати два кольори (фону і зображення) на блок 8x8. Всього блоків 32 х 24. Єдина перевага цього режиму — порівняно висока швидкість зміни кольорів.

Є також Multicolor mode — графічний режим з дозволом 64x48, де на кожен великий піксель задається колір (але при такому дозволі хіба що плазму яку-небудь малювати або ANSI art ) і пара чисто текстових режимів 40x24 символу — кольоровий і монохромний.

Найбільш потужним та цікавим режимом VDP видається так званий Graphic Mode II (він же «bitmap mode»). Цей режим дозволяє використовувати два кольори (фону і зображення) в межах піксельного блоку 8x1. Іншими словами — 2 кольори на кожні 8 горизонтальних пікселів. На жаль, порядок блоків (що пікселів, що їх кольорів) вкрай незручний для всього, крім виводу символів — байти йдуть зверху вниз 0-8, потім наступний правіше знову зверху вниз 8-15 і т. д. до правого краю. Далі все повторюється знову зліва направо.

При встановленні режиму також задаються адреси для атрибутів (Color Table), бітмапи (Pattern), порядку символів, що утворюють блоки для бітмапи (Name table), даних для спрайтів (Sprites data) і атрибутів спрайтів (Sprites Attributes). Адреси можна задати будь — там просто кілька варіантів, які потрібно вибрати так, щоб у підсумку без нахлестів раціонально використовувалися всі 16384 байт відеопам'яті. Приміром, для своєї intro «99tro» я розподілив пам'ять так (Graphics Mode II):

>0000 — >1800 Pattern table — 6144 bytes (>1800)
>1800 — >2000 Sprite patterns — 64 sprites x 32 bytes = 2048 bytes (>800)
>2000 — >3800 Color table — 6144 bytes (>1800)
>3800 — >3b00 Name table — 768 байт (>300)
>3b00 — >3b80 Sprite attrs =32 sprites x 4 bytes = 128 байт (>80)

Спрайт можуть бути двох розмірів — 8x8 або 16x16 (перемикаються відразу всі спрайт) і додатково можна розтягнути їх (теж відразу все) в два рази.
Всього спрайтів відображається одночасно на екрані 32 (але не більше 4 в одному рядку). При цьому 2кб пам'яті для Sprite patterns вистачає на два набори по 32 спрайту 16x16.

Індивідуально для кожного спрайту можна встановити наступні параметри (через область Sprite attrubutes, для кожного байта в серії):

0 — вертикальна координата (-32 — 191)
1 — горизонтальна координата (0-255). 255 — спрайт за правим краєм екрану.
2 — вказівник на місце звідки брати патерн (в Sprites patterns)
3 — біти 4-7 визначають колір, біт 0 — early clock (зрушує спрайт по горизонталі на 32 пікселя вліво)

Відсутність прямої адресації відеопам'яти в комбінації з невдалою її організацією і відсутністю в процесорі команд пересилання більш ніж байта за раз — не дозволяє її поновлювати. Наприклад, реалізація горизонтального гладкого скролінгу області 256x8 пікселів при паралельному програванні VGM музики майже повністю забирає час зворотного ходу променя по кадру. Тобто більше часу майже ні на що не залишається.
В іграх (сучасних) ця проблема вирішується досить хитрими способами, коли при скролінгу оновлюється не вся скроллируемая область, а тільки вимагає змін. Відповідно, вміст екрану продумується таким чином, щоб цих змін було мінімум. Також зазвичай використовується Graphics Mode I з більш низьким колірним дозволом (два кольори на блок 8x8).

Як і в багатьох інших системах, в TI-99/4a вкрай бажано проводити обробку зображення вкладаючись у зворотний хід променя по кадру (vertical retrace). Для цього VDP генерує відповідне переривання — на початку малювання нижній частині рамки.



Існують наступні варіанти дій:

1. Переривання можна відключити повністю (інструкцією LIMI 0 + конкретно для VDP — установкою відповідного біта в регістрі 1). При цьому все буде швидше, але не вийде відстежити зворотний хід променя ніяким чином.

2.Переривання включити повністю (LIMI 2 і VDP). Тоді можна повісити власний обробник переривань, яка що-небудь робити.

3.Включити переривання в VDP, але вимкнути інструкцією LIMI 0. У цьому випадку ніякі обробники викликатися не будуть, але за зворотним ходом променя можна стежити вручну, перевіряючи біт в регістрі стану VDP (після читання регістра цей біт автоматично скидається. Якщо його не скинути, то не сгенерится чергове переривання).

vwait:
movb >8802,r12; read VDP status register
andi r12,>8000
jeq vwait; Wait for vsync

Досить типова схема, коли переривання від VDP включені, загальні вимкнені (LIMI 0), але в основному циклі на короткий проміжок часу вони включаються (LIMI 2), щоб висить на обробник переривання (наприклад, грає музику) отримував іноді управління.

Не все емулятори правильно імітують це переривання. Але, скажімо, MESS робить це більш-менш адекватно. На малюнку можна бачити простий тест — колір рамки змінюється з деякою затримкою щодо vertical retrace (на цьому TI і на двох емуляторах — MESS і js99er).

ЗВУК
1. Стандартний звук
Для отримання музики і звукових ефектів в TI-99/4a встановлений чіп TMS9919 (аналоги: SN76489, SN76496). Він досить простий: три незалежних генератора тони і один — шуму. 15 рівнів гучності.

Оскільки це досить популярний чіп (використовувався в деяких консолях, у IBM PCjr/Tandy-1000 та ін), інформації про нього багато.

Для TI-99/4a існує трэкер MOD2PSG (нормально запускається під Win7 64bit). Зокрема, він дозволяє завантажувати файли формату psgmod і експортувати vgm і epsgmod.
Є два працюючих плеєра — EPSGMOD Player by Tursi ( вимагає дані у форматі epsgmod, вішає свій обробник на переривання VDP).

Підключається він наступним чином:

def start

start:
lwpi >8320

li r0,musicdata
bl @SGPLAY

again:

limi 2
limi 0

;… your code (vsync etc..)…

b @again

musicdata:
bcopy "music.epsgmod"

copy "test_music_playervbr.a99"
copy "test_music_player.a99"

end start

При такому підключенні плеєра, необхідно з його исходников прибрати REF і END (оскільки вони орієнтовані на використання з традиційними ассемблером).

Зверніть увагу, що в основному циклі на мить включаються переривання (limi 2). Це необхідно для роботи плеєра, який працює по перериваннях VDP від зворотного ходу променя (відбувається кожну 1/60 секунди). При цьому викликається підпрограма SGTICK плеєра.

Відповідно важливо, що для NTSC версією комп'ютера і музика повинна бути теж NTSC (частота кадрів повинна збігатися).

Другий плеєр (того ж автора) програє файли формату VGM (включає також утиліту VGMComp для стиснення VGM). Цей свій оброблювач не вішає, потрібно викликати вручну з частотою 60Гц…

Виглядає це так:

def start

vdpsta equ >8802; VDP RAM status

start:
limi 0

lwpi ws0; player expects our workspace to be >8300

clr r2
li r1,musicdata
bl @stinit

again:

clr r12; set CRU base (doesn't need to be in loop, but provides delay)
tb 2; check VDP interrupt input (Tests a CRU bit)
jeq loop; if not set, skip calling music
movb @vdpsta,r12; read status register, which in turn clears the interrupt bit
bl @stplay; call play function — it returns with the wrong workspace

* check song for end

movb @songwp+14,r3
jne notdone

clr r2; play again
li r1,musicdata
bl @stinit

notdone:
lwpi >8300; so restore the right one

;… your code (vsync etc..)…

b @again

musicdata:

bcopy "music.vgm"
copy "tiplayer.a99"

end start

На мій погляд, у другого варіанту плеєра краще якість (хоча, це може залежати від конкретної музики) і ручної виклик гнучкіше, ніж обробник (який ви і самі можете навісити, якщо дуже треба) — не потрібно включати переривання.

Також у нього є варіант плеєра на 30гц. При цьому викликати його потрібно теж з частотою 60гц, але він буде використовувати кожен другий виклик.
Відповідно, знижується завантаження процесора, але потенційно може впасти якість звучання музики (залежить від використовуваних в ній ефектів).
Мабуть, в плеєрі (або при експорті в VGM) є баг, створює незрозумілі проблеми з зацикленням музики.

2. Синтезатор мови


Оскільки синтезатор мови є дуже популярним периферійним пристроєм для TI-99/4a, досить багато програм його використовують. Синтезатор приєднується до порту в правій частині комп'ютера (при наявності NanoPEB його можна увіткнути прямо в синтезатор). Всередині знаходиться ПЗУ з даними для слів, LPC декодер (TMS5220) і ЦАП.

Є два режими роботи. У першому комп'ютер вказує синтезатору, які слова з ПЗУ слід вимовляти (слів там трохи). У другому (Speak-External) є можливість передавати безпосередньо стислі LPC дані, які синтезатор просто буде відтворювати. Для упаковки даних використовується стародавня програма QBox Pro, яка запускається під WinXP (процес показаний тут).

Синтезатором можна управляти безпосередньо з програми на асемблері. Якщо ж хочеться просто погратися, то необхідний картридж — наприклад, Speech Editor (дозволяє промовляти набраний текст, а також додає в стандартний Бейсік команди для відтворення слів). Картридж Terminal Emulator дозволяє вимовляти окремі фонеми і з'єднувати їх у довільні слова.

У найбільш відомих емуляторах синтезатор підтримується. Вважається, що найкраща підтримка (повністю емулюється залізка) реалізована в MESS/MAME.

Нижче приклад, який вимовляє слово «HELLO» беручи LPC дані з ПЗУ синтезатора. Документація вказує, що перевірка статусу неможлива, якщо код виконується в ОЗП на 8 бітній шині (тобто потрібно щоб код перебував у 16 бітному scratchpad), однак після перевірки на емуляторах і реальному комп'ютері я вважаю, що це перестраховка (ймовірно, як і у випадку з VDP, всі ці вимоги відносяться до якихось зовсім раннім версіям заліза). Вийшов ось такий короткий код:

; speaks 'HELLO' from speech synth ROM

def start

SPCHRD equ >9000; addr to read from synth
SPCHWT equ >9400; addr to write to synth (>10 — read, >4x — Load-Address, >50 — speak, >60 = speak-ext )

H50 BYTE >50; speak command

start:
limi 0

; loading speech address (5 nibbles of data required) using >4x Load-Address command
; >4x >4x >4x >4x >40 (end marker >0)

li r0,>351a; address of 'HELLO' in synth ROM
li r2,4; 4 nibbles
loadlp:
src r0,4; start with least significant nibble
mov r0,r1
src r1,4
andi r1,>0f00; get only particular nibble
ori r1,>4000; put in >4x00 format
movb r1,@SPCHWT; write nibble
dec r2
jne loadlp
li r1,>4000
movb r1,@SPCHWT; write fifth nibble (end marker)

movb @H50,@SPCHWT; execute speak command

loopback:
b @loopback
end start

Адреси всіх слів з ПЗУ можна знайти в книжці Editor Assembler Manual.

Якщо потрібно відтворити довільну мова перетворену в LPC за допомогою QBox Pro, то в найпростішому випадку код буде виглядати так:

def SPEAK

SPEAK
li r2,1750; number of BYTEs to speech poke (see end of QBox data)
li r3,speech; speech data address

limi 0; no interrupts

loop
movb *r3+,@>9400; poke a BYTE of speech data
dec r2
jeq quit; repeat until end of data

jmp loop; else go 'round again

quit
limi 2; interrupts allowed
rt

speech

; 10 Voiced 4 Unvoiced 8 kHz 5220
; [Coded LPC]

;… дані з *.sfm отриманому за допомогою QBox Pro

; nb bytes: [1750]

end

Для QBox Pro потрібно підготувати wav файл з промовою. Краще всього буде звучати чітка чоловіча мова. Одна фраза буде займати близько кілобайта.
Передати спів або ноти — не вийде. Після кодування в LPC подібні звуки будуть абсолютно невпізнавані.

Налаштування та операції в QBox Pro:

byte
8khz
5220

Project/Add files
select file

Process/Medium bit rate /OK
Edit/Concatenations
Insert/Concatenation/Concat Name/ "TEST"
Insert/Phrase/OK
Format/LPC 10V, 4UV /OK
Project/Exit/Save

Результат: test.sfm

КРОС-ЗАСОБИ
Щоб розробляти і тестувати програми на PC, а потім завантажувати їх на TI-99/4a, знадобиться наступне:

Залізо:

— PC/Win7 (64bit годиться)
— TI-99/4a
— Картридж Editor/Assembler (або будь-який інший, що дозволяє завантажувати і запускати програми в машинних кодах)
— NanoPEB або CF7+ (для емуляції дисків і завантаження програми з CompactFlash карти в TI-99/4a)

Софт:

— Один з численних емуляторів TI-99/4a — MESS/MAME або js99er (а краще кілька).

— Пакет xdt99, що включає асемблер і утиліти для роботи з файлами і образами дисків (зокрема, xvm99 для запису образу диска на CompactFlash карту в потрібному для NanoPEB форматі)

Організація процесу:

Програми для картриджа і для завантаження з диска — різні речі. Не тільки за форматом (RPK і DSK) але і в плані роботи з пам'яттю (другий варіант подрузамевает розширення пам'яті 32кб). Якщо є мета в результаті запустити код на реальній машині, орієнтуватися треба на дискову версію. Її розглянемо.

Емулятори істотно відрізняються один від одного, кращий назвати дуже складно. Це не тільки питання якості емуляції, але і зручності/простоти запуску готової програми.
Найбільш відомі: mess(mame), js99er, v9t9, classic99.
Всі вони не ідеальні. Цілком можна очікувати 10% різниці в швидкості між емулятором і реальної залізком (конкретно я спостерігав, що mess і js99er трохи швидше виконують весь код ніж реальний TI. В одному і тому ж NTSC)

При використанні в якості асемблера xdt99 процес виглядає так:

xas99.py -R --jumpstart %1.a99 (отримуємо об'єктний файл)

Параметр -R дозволяє нормально іменувати регістри в вихідному тексті (r0, r1, ...). У традиційному асемблері TMS9900 регістри в командах записуються просто цифрами, що вносить дику плутанину.

xdm99.py work.dsk -a %1.obj -n %1-O -f DIS/FIX80 (поміщаємо отриманий об'єктний файл на образ диска)

Потрібно враховувати, що об'єктний файл являє собою текстовий файл з шестнадцатиричными кодами всередині. Хоча це не кінцевий бінарний файл, тим не менше з-за особливостей завантаження програм на TI-99/4a (через Editor/Assembler) його часто вважають готовою програмою. Тим більш зручно, що він не прив'язаний до конкретної ділянки пам'яті при завантаженні.
Якщо потрібна саме кінцевий бінарний файл, для xas99 слід додати параметр " і". Завантажувати його тоді потрібно буде не через пункт 3, а через пункт 5 у Editor/Assembler. Якщо потрібен кінцевий бінарний файл більше 8к, без розбивки на окремі частини, замість -i потрібно використовувати -b.

Додатково можна отримати файл лістингу .lst вказавши параметр "-L".

Дещо спрощує роботу спеціальний образ картриджа для автоматичного завантаження (див. «jumpstarting» в документації на xdt99). Спосіб дозволяє в емуляторі обійтися без Editor/Assembler, натискання в ньому кнопок і введення імені файлу. Готовий код завантажується з диска автоматично при виборі картриджа. Приміром mess(mame) в цьому випадку запускається так:

mame64.exe ti99_4a -peb:slot8 tifdc -peb:slot2 speech -flop1 %1.dsk -cart jumpstart.rpk -window -ui_active -skip_gameinfo

Якщо потрібен відладчик, можна ще додати -debug (вхід в відладчик з емулятора при умові, що була вказана опція — клавішею ` )

Параметр «ti99_4a» подрузамевает NTSC версію комп'ютера (тобто найбільш поширену). Для PAL версії це буде ti99_4ae.
Відмінність дуже суттєва, як мінімум з-за різної частоти кадрів. у першому випадку кадрова 60гц, у другому 50гц. Неправильний вибір може призвести, наприклад, до сповільненого проигрыванию музики (якщо вона писалася під NTSC, а обраний PAL).

Якщо розробляється програма має істотний розмір або що вважає відчутний час, можна прискорити емуляцію параметром -speed 20

Для перенесення запису програми на CompactFlash у форматі NanoPEB можна використовувати xvm99.py

\\.\PHYSICALDRIVE2 1 -w work.dsk

Переконайтеся, що на вашому комп'ютері PHYSICALDRIVE2 відповідає саме пристрою в який вставлений CF (інакше можете зіпсувати дані на своїх дисках!). У Windows це можна подивитися командою

wmic diskdrive list brief /format:list

Оскільки на одному CF може бути кілька образів для емуляції декількох дисководів, другий параметр відповідає дисковода — тобто DSK1 для 1).
Попередньо CF рекомендується форматувати під FAT16, після чого обов'язково на самому TI зі вставленим NanoPEB з TI Basic набрати «call format(1)», де 1 — номер формат віртуального дисковода.

Перегляд вмісту флешки:

xvm99.py \\.\PHYSICALDRIVE2 1 -i

При завантаженні програми на самому TI потрібно вставити картридж з Editor/Assembler і NanoPEB. Включити NanoPEB, потім комп'ютер. У меню вибрати опцію «2» — «Editor/Assembler», в ньому пункт 3 (5), набрати DSK1.Ім'я ФАЙЛУ й натиснути ENTER.
Регістр в імені файлу важливий.

ДОДАТОК 1 — інтро 99TRO
У процесі вивчення TI-99/4a мною була написана «99tro» (ісходник) — невелика invitation intro на Chaos Constructions'2016, представлена на Revision'2016. Додатково до її исходниками (код вельми жахливий — не пишіть так!) тут будуть надані деякі пояснення.

Основна ідея в тому, щоб інтро по можливості передавало стилістику старих cracktro для Commodore 64, природно, з поправкою на більш скромні можливості відео і звукового чіпів TI-99/4a.
Грубо кажучи, хотілося щось типу кольорового логотипу, плавно скроллящейся текстового рядка, блискучих кольорових смужок і болтающейся написи з спрайтів.



Незабаром стали очевидні наступні суттєві обмеження і недоліки комп'ютера:

— Апаратного скролінгу немає, його можна зробити тільки зрушеннями і перезаписом відеопам'яті

— Обмін з відеопам'яттю (це стосується і спрайтів і графіки і тексту) дуже повільний і порівняно висока частота процесора не рятує (так само як і його 16-розрядність, т. к. обмін з VDP йде по 8-розрядній шині).

— VDP має ряд документованих або невиразно документованих багів (що не дивно, TMS9918 — один з перших спеціалізованих відеочіпів). Пару раз це призводило до того, що вже практично закінченою частини доводилося відмовлятися.

— Сильно обмежений функціонал спрайтів — тільки 4 рядок (що автоматично означає, що нічого шириною в екран спрайтами не зробиш), однобарвність, велика кількість інструкцій процесора для простої зміни кольору або координати спрайту.

— Програвання музики (vgm) забирає досить істотне час процесора. Як заявлено в исходниках плеєра від Tursi, це 2-20% для 30гц версії, в залежності від конкретної музики. В моєму конкретному випадку виходило не менше 10-15%.

— Біда з зручними отладчиками (насправді, та ж картина була і з емулятором Vectrex — в обох випадках є потужні відладчики, але незручні настільки, що, за фактом, їх використання швидше уповільнює роботу, ніж прискорює)

— Неможливість швидко перевірити код на реальному залозі. Для цього потрібно все вимкнути, вийняти флешку, увіткнути в PC, скопіювати на неї код, витягнути з PC і вставити у TI, включити емулятор диска і сам TI, вибрати Editor/Assembler, вибрати пункт завантаження програми, набрати DSK1.ім'я програми, натиснути ENTER, дочекатися завантаження (близько хвилини).

У результаті поступово від колишніх ідей залишилося тільки те, що дозволяла реалізувати залізка (звичайно, з урахуванням, що це була моя перша програма для цієї платформи і цього процесора):

— Напис/лого CHAOS CONSTRUCTIONS'2016 з головою робота
— Блимають в такт музиці очі і рот робота
— З'являється буква за буквою, слідом за пульсуючим курсором, текст у декілька рядків
— Плавно скроллящаяся рядок тексту
— Фонова музика (vgm)

Щоб хоч якось використати сильні сторони TI-99/4a, я вибрав режим з максимальним графічним та кольоровим вирішенням — Graphics mode II (Bitmap). Для ігор його використовують вкрай рідко, оскільки він вимагає всіх 16кб відеопам'яті і, відповідно, дуже повільне з точки зору оновлення зображення.
Однак, на відміну від ігор, в моєму випадку можна було піти на хитрість (що взагалі характерно для demo і intro) — кольори я динамічно не оновлював взагалі ніде. Все, що стосується кольорів, було встановлено єдиний раз на початку, поза основного циклу.
У циклі ж змінювався тільки bitmap і переключалися два спрайту.



Верхнє лого було намальовано спочатку в Photoshop, потім перетворено в дамп відеопам'яті для VDP за допомогою утиліти Convert9918 і далі ці два етапи циклічно повторювалися — тобто у вихідну картинку вносилися уточнення, щоб після перетворення (в 15-ти досить кривих кольорах і колірному вирішенні 2 кольори на 8 горизонтальних пікселів) вона виглядала максимально адекватно. Треба сказати, що спотворення характерні для композитного сигналу NTSC зіграли, як часто в таких випадках буває, на руку — на CRT моніторі картинка виглядає явно краще, ніж в емуляторі (до того ж, тепліше і ламповее).
Треба сказати, я вперше серйозно усвідомив, наскільки важливий для успіху платформи правильний вибір 16 можливих квітів (платформи) розробниками. У випадку з TI-99/4a ці кольори вкрай невдалі (до того ж, їх 15).
Крім того, вперше довелося зіткнутися з проблемою кодування кольору NTSC відеосигнал. На фото видно, на що перетворюються кольори окремих пікселів, PAL це теж викликає проблеми, але незрівнянно менші).

Нижній плавний скролінг був узятий з єдиного подібного прикладу (Matthew Hagerty) і модифікований. Чесно кажучи. я б за такий короткий термін навряд чи зміг перейнятися ассемблером TMS9900 досить глибоко, щоб повторити деякі використані там прийоми, без яких просто не вистачило б загальної продуктивності для всієї роботи.
У скролінгу досить хитро зсуваються вліво частини символів і результат копіюється в VRAM. Всього скроллится область у 8 пікселів заввишки.
Як я вже сказав вище, операції проводяться виключно над bitmap'ом — кольоровий фон у вигляді біло-синьої смуги намальований заздалегідь і скролінгом не зачіпається.

Оскільки скролінг — найбільш витратна операція в основному циклі, все подальше відштовхувався від нього і від музики (якщо перестаєш укладатися в зворотний хід променя, музика почне гальмувати).
На жаль, по факту вона все одно трохи гальмує оскільки обидва емулятора, на яких я отлаживал код, працювали трохи швидше, ніж реальна залізяка. У чому саме полягає проблема — неясно.

В середній частині екрану пишеться текст за курсором. У мене було багато ідей, як це красиво реалізувати, але в підсумку від всіх довелося відмовитися із-за повільної відеопам'яті і багів VDP. В результаті, за кожну ітерацію циклу малюється одна буква (тобто оновлюється 8 байт відеопам'яті). Для всіх можливих восьми рядків тексту заздалегідь встановлений фон з плавним переходом між відтінками синього і сірим.
Пульсуючий курсор являє собою спрайт, для якого зображення береться циклічно з чотирьох місць пам'яті — тобто виходить чотири кадри квадратика різного розміру.

Що стосується мерехтливих очей і рота робота, то вони реалізовані трьома кадрами одного спрайту 16x16 (знову ж таки — з метою виграти продуктивності). Той чи інший кадр вибирається в залежності від гучності одного з каналів звукового чіпа (ці дані повертає плеєр).

Спрайт і шрифти редагувалися у програмі Magellan.

У коді використовується три різних workspace для регістрів процесора — одне для плеєра (а він вимагає собі не тільки області, але і ще шматок scratchpad — цінного 16-розрядної RAM, з-за чого не вдалося збільшити швидкість виконання коду перенесенням його частин туди), друге для скролінгу і третє-для решти (лічильники в циклі, координати курсору і ін)

У результаті сумарний розмір intro вийшов у районі 22кб. Основна частина, звичайно, це нестиснений графіка (12kb) і RLE стисла музика (1.8 кб). Таким чином, сам код займає десь в районі 6-7кб (та й то там є фрагменти, які використовувалися лише для налагодження).

ДОДАТОК 2 — інтро Speechtro
Однокилобайтное інтро під назвою «Speechtro» (ісходник) було написано незабаром після 99tro і представлено на DIHALT'2016 (де зайняло перше місце в 1kb low end intro).

В основі були дві ідеї:

1). Зробити щось, що використовує синтезатор мови (серед власників TI-99/4a він дуже поширений, а також добре підтриманий емуляторами — MESS/MAME і js99er).
2). Спробувати зобразити візуально велика кількість квітів з імітацією растрових ефектів.



Після деяких вагань було вирішено обидві ідеї об'єднати в одну роботу.

Мова використовує слова прошиті в ПЗУ синтезатора (так як у випадку з прямим LPC стисненням у 1кб стільки мови не влізло б). Оскільки їх в ПЗУ вельми небагато, пішло чимало часу, щоб скласти з наявних слів зв'язне мовлення на відповідну тему. На жаль паузи в словнику синтезатора не передбачено. Як паузи я використовував досить випадковий адреса, дані з якого звучать як «булькання». Це, по-перше, економить і спрощує код, по-друге звучить цікавіше, ніж просто тиша.

Що стосується зображення, то спочатку хотілося спробувати реалізувати растрові ефекти (змінюючи колір фону в момент, коли луч йде по потрібному рядку). Це виявилося технічно неможливим (на скріншотах вище можна бачити спроби зробити це на цьому TI і на емуляторах — MESS і js99er), тому був обраний інший варіант — імітувати растрові ефекти через маніпуляції з пам'яттю атрибутів. У Graphics Mode 2 кожен блок 8x1 може мати незалежний колір фону і колір зображення, тому кожна смужка висотою в 1 піксел може мати різні кольори для фону і тексту. Незважаючи на те, що квітів 15 (а в смужках використовується лише 10), створюється враження, що їх набагато більше.

Окремі труднощі викликав висновок тексту. Особливості конструкції TI-99/4a такі, що ПЗУ (так званий GROM) не відображається в адресний простір процесора, тому дані для відображення шрифту витягуються з GROM тільки послідовно (побайтно) — завданням початкового адреси і читанням однієї і тієї ж комірки пам'яті.
Вибраний набір «small caps» має лише літери A-Z (без цифр), що нескладно помітити :)

Зірки, як і повзе навскоси баг, реалізовані спрайтами 8x8.
У процесі промовляння слів у двох лінійках квадратиків відображається (умовно) частину адреси поточного слова в ПЗУ синтезатора.

ПОСИЛАННЯ НА РЕСУРСИ ПО TI-99/4a


Тут можна подивитися мої роботи під різні ретро-платформи, а тут исходники на github.

Користуючись нагодою, хочу запросити всіх зацікавлених на фестиваль Chaos Constructions, який ми щорічно (з минулого століття) проводимо в Пітері в кінці серпня, і де можна помацати різні ретро-комп'ютери живцем.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.