nedoPC.org

Electronics hobbyists community established in 2002
Atom Feed | View unanswered posts | View active topics It is currently 28 Mar 2024 15:29



Reply to topic  [ 21 posts ]  Go to page 1, 2  Next
Программируем порты 
Author Message
Supreme God
User avatar

Joined: 21 Oct 2009 08:08
Posts: 7777
Location: Россия
Reply with quote
VituZz wrote:
Lavr wrote:
Здесь лишь приведу пример элегантного обращения к порту К580ВВ55,
который недоступен по IN/OUT.

Предположим, что 16 линий портов А и В К580ВВ55 управляют каким-то устройством.
И хорошо бы выдавать сигналы этому устройству в один момент времени.
По IN/OUT - не выйдет никак. :-?

А когда порты в памяти - элегантное решение есть:
1) управляющее слово собираем в HL.
2) выдаём его практически одновременно в порты А и В К580ВВ55:
SHLD ADDR_PORT_A.
Разве не элегантно? :wink:

Может, я не понял идеи, но "практически одновременно" в данном случае не означает "совершенно одновременно", а лишь в несколько раз быстрее, чем с IN/OUT. Если бы мне нужно было вывести сразу слово одновременно, то я бы просто использовал три регистра, в первый (промежуточный) писал бы первый байт, и этот первый байт переписывался бы во второй регистр стробом, который пишет второй байт в третий регистр. Таким образом была бы достигнута одновременность выдачи двух байт (из второго и третьего регистра), и такое решение мне кажется более элегантным. Но это, несомненно, дело вкуса, и каждое решение имеет право на жизнь, если оно достигает целей, заложенных при проектировании.

Да нет - ты всё верно понял. Конечно, совершенно одновременного в природе
просто ничего не бывает.
Но я тебе показал просто пример элегантного решения простой, но красивой
задачи с портом К580ВВ55.
На языке процессора К580ВМ80 я решил её одной командой практически одновременно.
Я подчеркнул преимущестово проецирования регистров порта на память в то же время
ты сам признал, что оно "в несколько раз быстрее, чем с IN/OUT".

Второй положительный пример привёл Хардыч, и ты его тоже обсуждал:
MOV M,A; где в HL - адрес порта.
Можно также элегантно косвенным образом обращаться к портам.

У К580ВМ80 не было ведь более гибкой команды OUT DX,REG ?
Даже у Z80 - работа с портами гибче! :lol:

Ну а то, что любую проблему в микропроцессоорной системе можно решить
как программно, так и аппаратно - я даже пытаться оспорить не буду.

Да - можно приделать 3 регистра, и ещё что-то приделать. Но порт К580ВВ55
тем и интересен, что настривается гибко.
Оттого его и используют до сих пор как неплохое параллельное УВВ на 24-линии.
Мы же не угадаем заранее, чем надумает поуправлять владелец копьютера
общего назначения?
Ну а если у него не сростается на встроенном К580ВВ55 - ему и дают адреса
под плату-прототим, как в IBM-совместимых! :wink:

Но многие любят управлять и через LPT, хотя он совсем не гибок и не для этого
был исторически введен в состав компьютера.

_________________
iLavr


Last edited by Lavr on 29 Sep 2012 08:29, edited 2 times in total.



29 Sep 2012 04:32
Profile
Supreme God
User avatar

Joined: 21 Oct 2009 08:08
Posts: 7777
Location: Россия
Reply with quote
Post 
HardWareMan wrote:
Lavr wrote:
HardWareMan wrote:
Lavr wrote:
Здесь лишь приведу пример элегантного обращения к порту К580ВВ55, который недоступен по IN/OUT.

Предположим, что 16 линий портов А и В К580ВВ55 управляют каким-то устройством. И хорошо бы выдавать сигналы этому устройству в один момент времени.
По IN/OUT - не выйдет никак. :-?
А когда порты в памяти - элегантное решение есть:
1) управляющее слово собираем в HL.
2) выдаём его практически одновременно в порты А и В К580ВВ55:
SHLD ADDR_PORT_A.
Разве не элегантно? :wink:

Только так и делали, когда работали с ROM диском на порту! Нам SHLD IOP_B/LDA IOP_A фактически заменяло MOV A,M, который только для памяти работает. ;)

Слегка не понял: если порты в пространстве памяти, то с ними сработает
и SHLD IOP_B/LDA IOP_A, и MOV A,M.

Что же тогда и что "фактически заменяло"?
Чем LDA IOP_A отличается от MOV A,M, если HL = IOP_A?
Или ты имеешь ввиду, что байты самого ROM диска не доступны по MOV A,M?

А если внимательно посмотреть? Ладно, танкистам срачного фронта объясняю: команда MOV A,M позволяет удобно работать с куском памяти, пользуя [HL] как указатель. Когда твой ROM диск подключен к IOP, то MOV А,M уже не сработает. А тут просто бедер и делаем SHLD в порт IOP_B ([L] упадет в IOP_B, а [H] упадет в IOP_C), а потом LDA IOP_A - и получаем фактически команду MOV A,M, только для внешнего оборудования.

Ну очевидное ты мог бы старому танкисту не повторять.

Неясно вот что: когда твой ROM диск подключен к IOP, то стробы
/CS и /RD мы делаем программно через IOP, так?
Если я правильно угадываю по твоему примеру - /CS формируется
дешифратором выбора конкретной ROM, а /RD у всех ROM
подключен всегда активным?

Я не вижу схемы твоего ROM-диска, поэтому могу только предполагать...



PS. И, кстати, что такое "бедер" ?

_________________
iLavr


29 Sep 2012 04:35
Profile
Maniac

Joined: 05 Nov 2008 19:47
Posts: 287
Location: 81.28.208.238
Reply with quote
Post 
Еще хорощий пример преимущества отображения
портов В/В в память - в процедуре инициализации
ВГ75, ВТ57 в том-же РК86.


29 Sep 2012 06:54
Profile
Supreme God
User avatar

Joined: 21 Oct 2009 08:08
Posts: 7777
Location: Россия
Reply with quote
Post 
aav8 wrote:
Еще хорощий пример преимущества отображения
портов В/В в память - в процедуре инициализации
ВГ75, ВТ57 в том-же РК86.

Если не трудно - напомни... У меня под рукой только от "Микроши":
Code:
; Пpогpаммиpование CCRT и CDMA  ;
;    КР580ВГ75  и  КР580ВТ57    ;
; тест настpойки  -  вывод  на  ;
; экpан символов  с клавиатуpы  ;

        ORG  0000H

        CALL CLSSCR;   очистим экp. ОЗУ
        CALL INICRT;   инициализация CCRT и CDMA
K01:
        LXI  H,BEGSCR; начало экp. ОЗУ
        LXI  D,ENDSCR; конец  экp. ОЗУ
        LXI  B,CURPOS; поз.куpсоpа C-столбец, B-стpока
KSC:
        CALL SETCUR;   установим куpсоp
K02:
        CALL GETKEY;   опpос клавиатуpы
        CPI  1BH;      'ESC' - ?
        JZ   SYSTEM;   да - выход в систему
        CPI  80H;      меньше кода 129 ASCII
        JC   K0A;      да - выводим на экpан
        ANI  7FH;      иначе сбpосит ст.бит
        ORA  A;        если A не pавно 00H
        JNZ  K0A;      да - выводим на экpан
        MVI  A,20H;    иначе заменим 00H на 20H
K0A:
        MOV  M,A;      выводим на экpан символ клавиши
        INX  H;        сдвиг в экp. ОЗУ на байт
        CALL CPHLDE;   достигли конца экp. ОЗУ ?
        JNZ  K03;      нет - выводим куpсоp в след.поз.
        JMP  K01;      достигли конца -> повтоpим сначала
K03:
        INR  C;        сдвинем куpсоp по стpоке
        MOV  A,C;      пpовеpим, не пpевысил ли он
        CPI  78;       pазмеp стpоки ?
        JC   KSC;      не пpевысил -> куpсоp на экpан
        MVI  C,00H;    иначе - куpсоp в начало стpоки
        INR  B;        пеpеход на стpоку ниже
        MOV  A,B;      пpовеpим, не пpевысили ли
        CPI  31;       количество стpок на экpане ?
        JC   KSC;      не пpевысил -> куpсоp на экpан
        MVI  B,00H;    иначе - куpсоp в начало экpана
        JMP  KSC;      куpсоp на экpан
SETCUR:
        MVI  A,0C0H;   команда 'Установка Куpсоpа' -
        STA  CMDREG;   в pегистp команд CCRT
        MOV  A,C;      установим позицию столбца
        STA  PRMREG;   в pегистp параметров
        MOV  A,B;      установим позицию стpоки
        STA  PRMREG;   в pегистp параметров
        RET

;======= Инициализация CCRT и CDMA ===============
INICRT:
        LXI  H,CMDREG; в pегистp команд CCRT
        MVI  M,00H;    запишем команду 'СБРОС'
        DCX  H;        в pегистp параметров
        MVI  M,4DH;    нормальные символы, 78 в строке (0-77)
        MVI  M,1DH;    1 строка в кадровом синхроимпульсе,
;                      30 строк на экране
        MVI  M,99H;    высота символа - 10
;                      линия подчеркивания в 10 стpоке,
        MVI  M,93H;    режим MODE1, формат курсора - мигающий
;                      штрих под символом,
;                      8 символов в строчном синхроимпульсе
        LXI  H,CMDREG; в pегистp команд CCRT
        MVI  M,27H;    Команда начать отображение:
;                      7 тактов между запросами ПДП,
;                      8 циклов ПДП за один сеанс
        MVI  M,0E0H;   внутренние счетчики - в соответствии с
;                      положением курсора в верхнем левом углу.
K05:
        MOV  A,M;      Читаем регистр статуса CCRT
        ANI  20H;      в цикле и выходим из него
        JZ   K05;      по условию IR=1
;--------------------- Настpоим контpоллеp ПДП
        MVI  A,80H;    10000000b = 80H флаг автозагрузки
        STA  REGRJM;   занесли в регистр режима РгР
        LXI  H,RGDMA2; указали на рег.адреса через M=([HL])
        MVI  M,0D0H;   загружаем младший байт адреса
        MVI  M,76H;    загружаем старший байт адреса
        INX  H;        указали на регистр счета
        MVI  M,23H;    заносим младший байт количества циклов,
;                      76D0H-7FF3H=923H
        MVI  M,49H;    заносим старший байт количества циклов,
;                      и D14=1 - чтение: 4.923H
        MVI  A,0A4H;   установили флаги: автозагрузка, удлиненная
;                      запись и разрешение работы канала 2 (D3=1),
        STA  REGRJM;   записываем в РгР.
        RET
;--------------------- Очистка экp. ОЗУ
CLSSCR:
        LXI  H,BEGSCR
        LXI  D,ENDSCR
K07:
        MVI  M,20H;    символом 'пpобел'
        INX  H
        CALL CPHLDE
        JNZ  K07
        RET
;--------------------- Сpавнение HL=DE ?
CPHLDE:
        MOV  A,D
        CMP  H
        RNZ
        MOV  A,E
        CMP  L
        RET

CURPOS:EQU  0000H
BEGSCR:EQU  76D0H
ENDSCR:EQU  7FF4H
PRMREG:EQU  0D000H
CMDREG:EQU  0D001H
SYSTEM:EQU  0F800H
RGDMA2:EQU  0F804H
REGRJM:EQU  0F808H
GETKEY:EQU  0F803H
       END


В "РК86" - аналогично?

_________________
iLavr


29 Sep 2012 08:08
Profile
Admin
User avatar

Joined: 08 Jan 2003 23:22
Posts: 22412
Location: Silicon Valley
Reply with quote
Post 
А кстати есть лазить во внешний ROM-драйв, то старший байт адреса на каждые 256 байт будет одинаковым - тогда нафига каждый раз засылать туда "одновременно" оба байта адреса? В таком случае один OUT N (10 тактов) будет быстрее одного SHLD Nadr (16 тактов)...

_________________
:dj: https://mastodon.social/@Shaos


29 Sep 2012 20:29
Profile WWW
Maniac

Joined: 05 Nov 2008 19:47
Posts: 287
Location: 81.28.208.238
Reply with quote
Post 
Вот этот кусочек - занесли в HL адрес,
и стали кидать в порты команды/данные
Code:
;======= Инициализация CCRT и CDMA ===============
INICRT:
        LXI  H,CMDREG; в pегистp команд CCRT
        MVI  M,00H;    запишем команду 'СБРОС'
        DCX  H;        в pегистp параметров
        MVI  M,4DH;    нормальные символы, 78 в строке (0-77)
        MVI  M,1DH;    1 строка в кадровом синхроимпульсе,
                         ;                      30 строк на экране
        MVI  M,99H;    высота символа - 10
                        ;                      линия подчеркивания в 10 стpоке,
        MVI  M,93H;    режим MODE1, формат курсора - мигающий
                       ;                      штрих под символом,
                       ;                      8 символов в строчном синхроимпульсе
        LXI  H,CMDREG; в pегистp команд CCRT
        MVI  M,27H;    Команда начать отображение:
                        ;                      7 тактов между запросами ПДП,
                         ;                      8 циклов ПДП за один сеанс
        MVI  M,0E0H;   внутренние счетчики - в соответствии с
                            ;                      положением курсора в верхнем левом углу.
K05:
        MOV  A,M;      Читаем регистр статуса CCRT
        ANI  20H;      в цикле и выходим из него
        JZ   K05;      по условию IR=1


29 Sep 2012 21:32
Profile
Supreme God
User avatar

Joined: 21 Oct 2009 08:08
Posts: 7777
Location: Россия
Reply with quote
Post 
aav8 wrote:
Вот этот кусочек - занесли в HL адрес,
и стали кидать в порты команды/данные...

Конечно, яйцо было раньше курицы, но это типичный приём в IBM-совместимых...

Указать в DX базовый адрес контроллера, и по DX кидать
в порты команды/данные, изменяя значение DX.
Как мы делаем через псевдо-регистр М.

Тем более, что в IBM-совместимых базовый адрес всегда можно взять из служебной
области BIOS.

Но у нас в 580ВМ80 не было IN/OUT по адресу в регистре. Хотя, с учетом
того, что нам никто не запрещал писать в область команд, есть изощренный
приём похожей работы с IN/OUT.

Но уже в Z80 IN/OUT по адресу в регистре появился.

_________________
iLavr


30 Sep 2012 00:08
Profile
Banned
User avatar

Joined: 20 Mar 2005 13:41
Posts: 2141
Location: От туда
Reply with quote
Post 
Lavr wrote:
PS. И, кстати, что такое "бедер" ?

Фиг его знает, как так получилось. Имелось в виду "берем".
Lavr wrote:
Но уже в Z80 IN/OUT по адресу в регистре появился.

Конечно, они сделали IN R,(C) и OUR (C),R, но поломали простой IN/OUT, которые у них записаны как IN A,(N) и OUT (N),A. Ибо, если заюзать 16 бит адреса порта, то использовать их получится только через расширенную префиксом #ED команду, где адресом выступает [BC], ибо формирование адреса в обычном IN/OUT теперь формируется так: AN. Т.е., N так и остался младшим адресом, а [A] стал старшим. И если в случае с IN еще можно задать любой нужный адрес, результат чтения с которого затрет [A], то при выводе OUT старшим байтом адреса будет аргумент записи, который в [A]. Что нужно было курить, чтобы так сделать? Уж лучше бы оставили совместимую с i8080 модель поведения, а именно [NN], т.е. копирование младшего в старший.


30 Sep 2012 02:54
Profile
Supreme God
User avatar

Joined: 21 Oct 2009 08:08
Posts: 7777
Location: Россия
Reply with quote
Post 
HardWareMan wrote:
Lavr wrote:
PS. И, кстати, что такое "бедер" ?

Фиг его знает, как так получилось. Имелось в виду "берем".

Но ты ничего не сказал про стробы ROM-ов за IOP-ом... :-?

_________________
iLavr


30 Sep 2012 04:01
Profile
Banned
User avatar

Joined: 20 Mar 2005 13:41
Posts: 2141
Location: От туда
Reply with quote
Post 
Lavr wrote:
HardWareMan wrote:
Lavr wrote:
PS. И, кстати, что такое "бедер" ?

Фиг его знает, как так получилось. Имелось в виду "берем".

Но ты ничего не сказал про стробы ROM-ов за IOP-ом... :-?

А зачем стробы? Все по классике: ПЗУ на 64КБ в сумме (была такая коробочка с МРН32 разъемом, в которой была платка на РФ8), постоянно выбранное и подключенное по классической схеме: IOP_A это шина данных на ввод и IOP_B/IOP_C шина адреса на вывод. Изначально, в свете нашего треда, я писал про прошедшее время, имея ввиду мое посещение кружка и работу на Специалисте в далёких 90х. ROM диск для MX^2 с его 2МБ на борту даже не планируется вводить в обсуждение.


30 Sep 2012 04:35
Profile
Supreme God
User avatar

Joined: 21 Oct 2009 08:08
Posts: 7777
Location: Россия
Reply with quote
Post 
HardWareMan wrote:
Все по классике: ПЗУ на 64КБ в сумме (была такая коробочка с МРН32 разъемом, в которой
была платка на РФ8 ), постоянно выбранное и подключенное по классической схеме:
IOP_A это шина данных на ввод и IOP_B/IOP_C шина адреса на вывод.

Lavr wrote:
Я не вижу схемы твоего ROM-диска, поэтому могу только предполагать...

_________________
iLavr


30 Sep 2012 04:43
Profile
Doomed

Joined: 10 Mar 2012 16:21
Posts: 598
Location: РФ
Reply with quote
Post 
Lavr wrote:
...
Но у нас в 580ВМ80 не было IN/OUT по адресу в регистре. Хотя, с учетом
того, что нам никто не запрещал писать в область команд, есть изощренный
приём похожей работы с IN/OUT..

Хочу уточнить у многоуважаемых коллег, верно ли я понимаю следуещее :
Если дешифруем байт состояния "по полной программе" так сказать, ( смещаясь от "фон-Неймана" к "Гарварду" частично,) то есть не только, (как Вы изволиили назвать) "мешок памяти" - 64К - отдельный "слой" адресного пространства при обращении через SP , но и ещё "слой" адресного пространства ну допустим верхние 32К ( нижние 32К не трогаю, ибо с клоном РК-86 экспериментирую ) - по названию цикла и обзовём - M1-"слой" адресного пространства будет использоваться ,
то тогда получается, что при PC=x и К.оп.=^[PC=x]=0D3h в слое M1 ,( т.е. OUT ) при значаении байта в основном слое в ячейке с adr=x+1 например 0FEh произойдёт именно пересыл содержимого A в порт по адресу 0FEh .
Так или не так ?

Это я к чему собственно - если в слое M1 будет ПЗУ-шка, то там, ясный пень, могут быть только подпрограммы, состоящие исключительно из однобайтовых комманд, если только не захочется применить такой вот изящный "спецэффект" со вторым байтом команды ( или со вторым и третьим байтами ), подверженным произвольному изменению.
(Это второе направление, как позабавляться с РК-шкой - про первое - с "прикольной" дешифрацией на "сп"-шке я в другой теме уже консультировался. )


02 Oct 2012 01:46
Profile
Supreme God
User avatar

Joined: 21 Oct 2009 08:08
Posts: 7777
Location: Россия
Reply with quote
Post 
Ну раз ты "наживую" делаешь эксперименты, может ты нам и расскажешь? :wink:

Я в своей практике отлавливал только байты, нужные для организации
IN/OUT в триггеры ТМ2.
И, честно говоря, я бы не стал делать "мешок", ориентируясь на выделение
обращения к стеку, по той простой причине, что в этом мешке будет, как
ни крути, шариться сам стек.

А стековые команды удобны и в обычном пространстве.
В "Специалисте" через PUSH, к примеру, чистят экран,
и делают это гораздо быстрее, чем:
Code:
   XRA  A
   LXI  H,SCR_BEGIN
   LXI  B,SCR_LENGTH
MT0:
   MOV  M,A
   INX  H
   DCR  C
   JNZ  MT0
   DCR  B
   JNZ  MT0

_________________
iLavr


02 Oct 2012 14:02
Profile
Doomed

Joined: 10 Mar 2012 16:21
Posts: 598
Location: РФ
Reply with quote
Post 
Насчёт "мешка памяти"- чтоб по возможности не "пересекаться" стекам программ и "мешку"- можно сделать оный не 64К ,а 32К верхних.
Ну а как "поместить в стек сам указатель стека"- это Вы полагаю знаете.
Кто нашу беседу читает и не знает, подскажу :
после DI ; PUSH PSW ; PUSH B ; PUSH D ; PUSH H ; надо :
FIXSP EQU 0xxxxh ; где xxxx- адрес ячейки-указателя адреса хранения предыдущего значения SP (SP_for_SP так сказать )
LXI D , 00h
LHLD FIXSP ;
XCHG ; теперь SP_for_SP лежит в DE ,а HL=0
DAD SP ; по сути как бы перемещение из SP в HL получается
XCHG ; теперь значение SP уже в DE ,а SP_for_SP в HL
INX H ;
INX H ;
SPHL ; теперь в SP лежит SP_for_SP+2 ,то есть адрес, куда поместить своё предыдущее значение
SHLD FIXSP ; сохраняем новый адрес хранения предыдущего значения SP
XCHG ; старое значение SP в HL
XTHL ; вот наконец то сохранили старое значение SP в памяти
VDSKPTR EQU 0yyyyh ; yyyy- адрес ячейки-указателя "дорожки" виртуального диска
LHLD VDSKPTR ;
SPHL ; мы в режиме работы с виртуальным диском ("мешком памяти")
EI ; теперь можно
Вот как то так.. Впрочем это было небольшое нелирическое отступление для благодарных читателей.

Но я Вас спрашивал вообще то в первую очередь про "слой M1"
Quote:
..."слой" адресного пространства ну допустим верхние 32К ( нижние 32К не трогаю, ибо с клоном РК-86 экспериментирую ) - по названию цикла и обзовём - "M1-слой" адресного пространства будет использоваться ,
то тогда получается, что при PC=x и К.оп.=^[PC=x]=0D3h в слое M1 ,( т.е. OUT ) при значаении байта в основном слое в ячейке с adr=x+1 например 0FEh произойдёт именно пересыл содержимого A в порт по адресу 0FEh .
Так или не так ?
Спрашиваю, ибо слегка не уверен, что верно понял всё в "дэйташиит" про M1


03 Oct 2012 00:36
Profile
Supreme God
User avatar

Joined: 21 Oct 2009 08:08
Posts: 7777
Location: Россия
Reply with quote
Post 
petrenko wrote:
Ну а как "поместить в стек сам указатель стека"- это Вы полагаю знаете.

Ну мы это, безусловно, знаем, только непонятно, зачем это делать?

Если мы хотим сохранить текущее значение указателя стека, то вполне
достаточно следующих манипуляций:
Code:
   DI
   PUSH PSW ; PUSH B ; PUSH D ; PUSH H ;
   LXI  H,0000H
   DAD  SP
   SHLD FIXSP; просто сохранили старое значение SP   
;              а зачем нам запихивать его в стек?
   LHLD DISKSP; адрес "дорожки" виртуального диска
   SPHL; укажем в стеке
   ...
   ...  ; здесь работаем со стековым виртуальным диском
   ...
   SHLD DISKSP; сохраним адрес "дорожки" виртуального диска
   LHLD FIXSP; загрузим в HL старое значение SP
   SPHL; восстановим старое значение SP
   POP H ; POP D ; POP B ; POP PSW ;
   EI;  и только завершив работу с SP-диском можно разрешить прерывания!

   FIXSP  DW 0000H; для хранения стека
   DISKSP DW 0000H; текущий адрес в "стековом диске"


А вот как ты предлагаешь - так делать ну никак нельзя!!! :o

Code:
   SPHL ; мы в режиме работы с виртуальным диском ("мешком памяти")
   EI ; теперь можно


Первый же вызов прерывания испортит твою "дорожку" виртуального диска!!!
Во время работы с виртуальным диском ("мешком памяти") ты не можешь
пользоваться вызовом подпрограмм CALL ADDR - также адрес возврата
тут же испортит твою "дорожку" виртуального диска!!!

Так что с виртуальным диском ("мешком памяти") в стеке ты вынужден работать
при запрещенных прерываниях и не использовать
CALL ADDR / RST N.

Не стоит вводит в заблуждение "благодарных читателей" - всё не так просто! :wink:

_________________
iLavr


03 Oct 2012 02:49
Profile
Display posts from previous:  Sort by  
Reply to topic   [ 21 posts ]  Go to page 1, 2  Next

Who is online

Users browsing this forum: No registered users and 8 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group
Designed by ST Software.