Компилятор Pascal для i8048 (msc-48)

8-битные микроконтроллеры и микропроцессоры от Intel и их клоны, а также компьютеры на них построенные

Moderator: Shaos

Tronix
Doomed
Posts: 662
Joined: 18 Nov 2013 02:38
Location: Москва

Компилятор Pascal для i8048 (msc-48)

Post by Tronix »

Рано или поздно всплывает на форумах вопрос, почему нет компилятора языка высокого уровня для MSC-48? Отвечают практически всегда одно и то-же - мол, мало памяти, стек не прозрачный и тд. Но мне кажется, что некий "упрощенный" вариант Паскаля/Си реализовать можно.

Сначала я смотрел в сторону SDCC - Small Device C Compiler. Но немного повтыкав в сорцы кодогенератора для 8051 удалил их, поняв что я ничего не понимаю. Может для кого-то он конечно и "easy portable to another architecture", но явно не для меня.

А потом вспомнил, что я, давным давно, читал замечательную статью Let's Build a Compiler, by Jack Crenshaw, она же, внезапно на русском в PDF. А потом вдобавок ко всему нашел еще и сорцы некого Power Pascal for OS/2. Эта вещь написана явно под влиянием вышеупомянутой статьи. Генерирует ASM-листинг для x86. Собирается хоть Turbo Pascal, хоть FreePascal с небольшими правками, и что удивительно, в принципе работает. Тем более Паскаль как бы мне более "родной", чем Си (хотя в последнее время пишу почти всегда на Си, но не суть).

Итак, Паскаль. Конечно, памяти маловато. На классическом 8048 имеем два банка 8-битных регистров R0 - R7 (16 байт), восьмиуровневый 16-битный стек (16 байт), остальные 32 байта на пользовательские переменные.
В контроллере есть внешнее прерывание и таймер. И вектора обработчиков от них по адресам .org 3 и .org 7.

Поэтому решено сделать пока так - в основной программе работаем только с первым банком регистров R0 - R7, не трогая второй банк. Второй банк регистров использовать только в обработчиках прерываний. Таким образом не нужно морочиться с сохранением/восстановлением текущих регистров в обработчиках прерываний. Дальше, A - акуумулятор, в него помещается текущее значение с которым работаем. R0 - операнд. R1 - для временного хранения аккумулятора, если с ним производятся логические действия. С R2 по R7 находятся значения в "стеке". Если "стека" из регистров не хватит, писать в пользовательскую память, если там есть место. Переменные хранятся в памяти начиная с адреса .org 32. Всего 32 байта.

Ну и первый простой тест на операции сложения/вычитания:

Code: Select all

Var
	x,y : byte;
Begin
	x := 6;
	y := 2;
	x := x + y;
    y := y - 3;
End.
Дает такой вот листинг:

Code: Select all

;;    MSC-48 PASCAL
;;    Tronix (c) 2017

      .org 0			; reset vector
      dis   i
      jmp   MAIN

      .org 3			; external interrupt vector--trap
      retr

      .org 7			; timer interrupt vector
      retr
MAIN:
      mov   a,6			; load const
      mov   r0,_X		; store variable
      mov   @r0,a
      mov   a,2			; load const
      mov   r0,_Y		; store variable
      mov   @r0,a
      mov   r0,_X		; load variable
      mov   a,@r0
      mov   r2,a		; push to stack
      mov   r0,_Y		; load variable
      mov   a,@r0
      mov   r1,a		; save V0->V1, get V0 from stack
      mov   a,r2
      add   a,r1		; make addiction
      mov   r0,_X		; store variable
      mov   @r0,a
      mov   r0,_Y		; load variable
      mov   a,@r0
      mov   r2,a		; push to stack
      mov   a,3			; load const
      mov   r1,a		; save V0->V1, get V0 from stack
      mov   a,r2
      xch   a,r1		; make substraction
      cpl   a
      inc   a
      add   a,r1
      mov   r0,_Y		; store variable
      mov   @r0,a
_PEND:
      jmp  _PEND		; end program
; ***** Library Code ***** 
; ***** Library Ends *****
; Variable Area
.equ	_Y	32
.equ	_X	33
; String constants

Немного подзастрял пока на логических операциях. Но думаю, победим мы его.
jdigreze
God
Posts: 1388
Joined: 02 Jan 2006 02:28
Location: Abakan

Re: Компилятор Pascal для i8048 (msc-48)

Post by jdigreze »

Здорово! :kruto:
Книжку на досуге почитаю.
angry_troll
Doomed
Posts: 449
Joined: 08 Apr 2013 04:04
Location: 213.247.249.139

Re: Компилятор Pascal для i8048 (msc-48)

Post by angry_troll »

готов потестить на своей платке.
Кроссплатформенная версия будет или wanacryptor-only? :)

Поэтому решено сделать пока так - в основной программе работаем только с первым банком регистров R0 - R7, не трогая второй банк. Второй банк регистров использовать только в обработчиках прерываний.
да, так обычно и делают, причем во 2 наборе 1 рег убивается сразу и навсегда для схоронения в него А во время входа.
Еще есть такая жопа, что из прерывания моюно только проецедуры первых 2к вызывать, вторые 2к недоступны вообще (от момента входа до выполнения retr)
привет засранцу лавру :)
angry_troll
Doomed
Posts: 449
Joined: 08 Apr 2013 04:04
Location: 213.247.249.139

Re: Компилятор Pascal для i8048 (msc-48)

Post by angry_troll »

вычитание в интеловом аппноте рекомендуют делать так:

Code: Select all

    cpl a
    add a,r4
    cpl a   ; a=a-r4
привет засранцу лавру :)
angry_troll
Doomed
Posts: 449
Joined: 08 Apr 2013 04:04
Location: 213.247.249.139

Re: Компилятор Pascal для i8048 (msc-48)

Post by angry_troll »

А еще кстати, прерывания от таймера и внешние независимо запрещаются. Чтобы запретить все, надо 2 команды:

Code: Select all

    dis i
    dis tcnti
привет засранцу лавру :)
Tronix
Doomed
Posts: 662
Joined: 18 Nov 2013 02:38
Location: Москва

Re: Компилятор Pascal для i8048 (msc-48)

Post by Tronix »

angry_troll wrote:вычитание в интеловом аппноте рекомендуют делать так:

Code: Select all

    cpl a
    add a,r4
    cpl a   ; a=a-r4
Что за апноут? Я читаю какой-то, там так:

Code: Select all

Subtract 8-bit subtrahend from 8-bit minuend using two's complement addition and store difference in register 7.
SUB8: MOV A,#5UBHND
CPL A ;ONE'S COMPLEMENT A
INC A ;TWO'S COMPLEMENT A
ADD A,#MINEND
MOV R7,A 
Но ваше решение попроще, попробую.
UPD: Попробовал, работает. Спасибо.
Насчет таймера - где-то вроде видел что по умолчанию он выключен. Но можно конечно dis tcnti воткнуть.
angry_troll
Doomed
Posts: 449
Joined: 08 Apr 2013 04:04
Location: 213.247.249.139

Re: Компилятор Pascal для i8048 (msc-48)

Post by angry_troll »

Tronix wrote: Что за апноут? Я читаю какой-то, там так:
Мды, с аппноутом я погорячился. Сейчас посмотрел, вот тут https://github.com/AngryTroll/i8048_boa ... Manual.pdf на странице 133, например, упоминается. Ну и вообще моя подборка док: https://github.com/AngryTroll/i8048_boa ... /pdf/mcs48

Насчет таймера - где-то вроде видел что по умолчанию он выключен. Но можно конечно dis tcnti воткнуть.
Ну и внешнее прерывание после ресета тоже выключено.
Я рассуждаю так -- если вдруг придётся программно рестартовать контрольник переходом на 0, то тогда смысл запрещать ВСЕ прерывания есть.
привет засранцу лавру :)
User avatar
Lavr
Supreme God
Posts: 16689
Joined: 21 Oct 2009 08:08
Location: Россия

Re: Компилятор Pascal для i8048 (msc-48)

Post by Lavr »

Tronix wrote:решение попроще, попробую.
UPD: Попробовал, работает.
Что-то мне на взгляд кажется, что дополнение до двух (TWO'S COMPLEMENT) теряется...
Нет ошибки на единицу?
iLavr
Tronix
Doomed
Posts: 662
Joined: 18 Nov 2013 02:38
Location: Москва

Re: Компилятор Pascal для i8048 (msc-48)

Post by Tronix »

Lavr wrote:Что-то мне на взгляд кажется, что дополнение до двух (TWO'S COMPLEMENT) теряется...
Нет ошибки на единицу?
Да не, нормально. Чуть разобрался с условиями < > =. Правда пока не сделал <= и >=. Прикрутил основные циклы (for/while/repeat). Набросал небольшой тест. Внутренняя процедура Debug(N) просто выставляет число N в порт 1 и уходит на бесконечный цикл.

Code: Select all

{MSC-48 PASCAL exerciser}
Var
   x,y : byte;
   i   : byte;
Begin

   x := 2;
   y := 253;

   {Do IF...THEN..ELSE tests}
   If (x+y) = 255 then
      Begin
         If (x+y) <> 255 then
            Debug(1);            {Error 1 - not equal false}


         If (x+y) < 255 then
            Debug(2);            {Error 2 - less false}

         y := y - 1;

         If (x+y) > 254 then
            Debug(3);            {Error 3 - greater false}
      End
   Else
      Debug(4);                  {Error 4 - equal false}


   x := 0;
   
   {Do FOR test}
   For i := 1 to 200 do
     x := x + 1;

   If x <> 200 then
      Debug(10);                 {Error 10 - FOR loop failed}

   {Do WHILE test}
   While x > 1 do
      x := x - 1;
   
   If x <> 1 then
      Debug(11);                 {Error 11 - WHILE loop failed}
      
   x := 0;
   
   {Do REPEAT..UNTIL test}
   Repeat
      x := x + 1;
   Until x < 123;
   
   If x <> 123 then
      Debug(12);                 {Error 12 - REPEAT loop failed}
      
   {All test's passed}
   Debug($5A);
End.

 Кодогенерация:

Code: Select all

;;    MSC-48 PASCAL
;;    Tronix (c) 2017

      .org 0                       ; reset vector
      dis   i
      jmp   MAIN

      .org 3                       ; external interrupt vector--trap
      retr

      .org 7                       ; timer interrupt vector
      retr
MAIN:
      dis   tcnti
      mov   a,2                    ; load const
      mov   r0,_X                  ; store variable
      mov   @r0,a
      mov   a,253                  ; load const
      mov   r0,_Y                  ; store variable
      mov   @r0,a
      mov   r0,_X                  ; load variable
      mov   a,@r0
      mov   r2,a                   ; push to stack
      mov   r0,_Y                  ; load variable
      mov   a,@r0
      mov   r1,a                   ; save R0->R1, get R0 from stack
      mov   a,r2
      add   a,r1                   ; make addiction
      mov   r2,a                   ; push to stack
      mov   a,255                  ; load const
      mov   r1,a                   ; save R0->R1, get R0 from stack
      mov   a,r2
      cpl   a                      ; make substraction A-R1
      add   a,r1
      cpl   a
      jnz    L0                    ; if NOT jump to
      mov   r0,_X                  ; load variable
      mov   a,@r0
      mov   r2,a                   ; push to stack
      mov   r0,_Y                  ; load variable
      mov   a,@r0
      mov   r1,a                   ; save R0->R1, get R0 from stack
      mov   a,r2
      add   a,r1                   ; make addiction
      mov   r2,a                   ; push to stack
      mov   a,255                  ; load const
      mov   r1,a                   ; save R0->R1, get R0 from stack
      mov   a,r2
      cpl   a                      ; make substraction A-R1
      add   a,r1
      cpl   a
      jz     L2                    ; if jump to
      mov   a,1                    ; load const
      outl  p1,a                   ; !!! DEBUG. Set P1 and forever loop !!!
      jmp   _PEND
L2:
L3:
      mov   r0,_X                  ; load variable
      mov   a,@r0
      mov   r2,a                   ; push to stack
      mov   r0,_Y                  ; load variable
      mov   a,@r0
      mov   r1,a                   ; save R0->R1, get R0 from stack
      mov   a,r2
      add   a,r1                   ; make addiction
      mov   r2,a                   ; push to stack
      mov   a,255                  ; load const
      mov   r1,a                   ; save R0->R1, get R0 from stack
      mov   a,r2
      cpl   a                      ; make substraction A-R1
      add   a,r1
      cpl   a
      jz     L4                    ; logical less
      jnc    L4
      mov   a,2                    ; load const
      outl  p1,a                   ; !!! DEBUG. Set P1 and forever loop !!!
      jmp   _PEND
L4:
L5:
      mov   r0,_Y                  ; load variable
      mov   a,@r0
      mov   r2,a                   ; push to stack
      mov   a,1                    ; load const
      mov   r1,a                   ; save R0->R1, get R0 from stack
      mov   a,r2
      cpl   a                      ; make substraction A-R1
      add   a,r1
      cpl   a
      mov   r0,_Y                  ; store variable
      mov   @r0,a
      mov   r0,_X                  ; load variable
      mov   a,@r0
      mov   r2,a                   ; push to stack
      mov   r0,_Y                  ; load variable
      mov   a,@r0
      mov   r1,a                   ; save R0->R1, get R0 from stack
      mov   a,r2
      add   a,r1                   ; make addiction
      mov   r2,a                   ; push to stack
      mov   a,254                  ; load const
      mov   r1,a                   ; save R0->R1, get R0 from stack
      mov   a,r2
      cpl   a                      ; make substraction A-R1
      add   a,r1
      cpl   a
      jz     L6                    ; logical greater
      jc     L6
      mov   a,3                    ; load const
      outl  p1,a                   ; !!! DEBUG. Set P1 and forever loop !!!
      jmp   _PEND
L6:
L7:
      jmp   L1
L0:
      mov   a,4                    ; load const
      outl  p1,a                   ; !!! DEBUG. Set P1 and forever loop !!!
      jmp   _PEND
L1:
      mov   a,0                    ; load const
      mov   r0,_X                  ; store variable
      mov   @r0,a
      mov   a,1                    ; load const
      mov   r0,_I                  ; store variable
      mov   @r0,a
      mov   a,200                  ; load const
      mov   r0,Lim_I               ; store variable
      mov   @r0,a
L8:
      mov   r0,_X                  ; load variable
      mov   a,@r0
      mov   r2,a                   ; push to stack
      mov   a,1                    ; load const
      mov   r1,a                   ; save R0->R1, get R0 from stack
      mov   a,r2
      add   a,r1                   ; make addiction
      mov   r0,_X                  ; store variable
      mov   @r0,a
      mov   r0,_I                  ; load variable
      mov   a,@r0
      mov   r2,a                   ; push to stack
      mov   r0,Lim_I               ; load variable
      mov   a,@r0
      mov   r1,a                   ; save R0->R1, get R0 from stack
      mov   a,r2
      cpl   a                      ; make substraction A-R1
      add   a,r1
      cpl   a
      jz     L9                    ; logical less
      jnc    L9
      mov   r0,_I                  ; increase var by 1
      mov   a,@r0
      inc   a
      mov   @r0,a
      jmp   L8
L9:
      mov   r0,_X                  ; load variable
      mov   a,@r0
      mov   r2,a                   ; push to stack
      mov   a,200                  ; load const
      mov   r1,a                   ; save R0->R1, get R0 from stack
      mov   a,r2
      cpl   a                      ; make substraction A-R1
      add   a,r1
      cpl   a
      jz     L10                   ; if jump to
      mov   a,10                   ; load const
      outl  p1,a                   ; !!! DEBUG. Set P1 and forever loop !!!
      jmp   _PEND
L10:
L11:
L12:
      mov   r0,_X                  ; load variable
      mov   a,@r0
      mov   r2,a                   ; push to stack
      mov   a,1                    ; load const
      mov   r1,a                   ; save R0->R1, get R0 from stack
      mov   a,r2
      cpl   a                      ; make substraction A-R1
      add   a,r1
      cpl   a
      jz     L13                   ; if jump to
      mov   r0,_X                  ; load variable
      mov   a,@r0
      mov   r2,a                   ; push to stack
      mov   a,1                    ; load const
      mov   r1,a                   ; save R0->R1, get R0 from stack
      mov   a,r2
      cpl   a                      ; make substraction A-R1
      add   a,r1
      cpl   a
      mov   r0,_X                  ; store variable
      mov   @r0,a
      jmp   L12
L13:
      mov   r0,_X                  ; load variable
      mov   a,@r0
      mov   r2,a                   ; push to stack
      mov   a,1                    ; load const
      mov   r1,a                   ; save R0->R1, get R0 from stack
      mov   a,r2
      cpl   a                      ; make substraction A-R1
      add   a,r1
      cpl   a
      jz     L14                   ; if jump to
      mov   a,11                   ; load const
      outl  p1,a                   ; !!! DEBUG. Set P1 and forever loop !!!
      jmp   _PEND
L14:
L15:
      mov   a,0                    ; load const
      mov   r0,_X                  ; store variable
      mov   @r0,a
L16:
      mov   r0,_X                  ; load variable
      mov   a,@r0
      mov   r2,a                   ; push to stack
      mov   a,1                    ; load const
      mov   r1,a                   ; save R0->R1, get R0 from stack
      mov   a,r2
      add   a,r1                   ; make addiction
      mov   r0,_X                  ; store variable
      mov   @r0,a
      mov   r0,_X                  ; load variable
      mov   a,@r0
      mov   r2,a                   ; push to stack
      mov   a,123                  ; load const
      mov   r1,a                   ; save R0->R1, get R0 from stack
      mov   a,r2
      cpl   a                      ; make substraction A-R1
      add   a,r1
      cpl   a
      jnz    L16                   ; if NOT jump to
      mov   r0,_X                  ; load variable
      mov   a,@r0
      mov   r2,a                   ; push to stack
      mov   a,123                  ; load const
      mov   r1,a                   ; save R0->R1, get R0 from stack
      mov   a,r2
      cpl   a                      ; make substraction A-R1
      add   a,r1
      cpl   a
      jz     L17                   ; if jump to
      mov   a,12                   ; load const
      outl  p1,a                   ; !!! DEBUG. Set P1 and forever loop !!!
      jmp   _PEND
L17:
L18:
      mov   a,90                   ; load const
      outl  p1,a                   ; !!! DEBUG. Set P1 and forever loop !!!
      jmp   _PEND
_PEND:
      jmp  _PEND                   ; end program
; ***** Library Code ***** 
; ***** Library Ends *****
; Variable Area
.equ	_Y	32
.equ	_X	33
.equ	_I	34
.equ	Lim_I	35
; String constants


Без оптимизации 309 байт. Однако.
Вот думаю, какой-бы реальный простенький пример можно реализовать.. Не знаю, число ПИ там вычислить или еще чего. Есть идеи? С ПИ немного сложно, потому что пока нет 16-битных чисел. 8 бит онли.
Annett
Senior
Posts: 137
Joined: 30 Mar 2017 00:55

Re: Компилятор Pascal для i8048 (msc-48)

Post by Annett »

Уххх!

Tronix, спасибо за ссылку на книжку!

Там как раз под 68К !! :)
Mixa64
Doomed
Posts: 481
Joined: 25 Aug 2009 07:02
Location: Москва

Re: Компилятор Pascal для i8048 (msc-48)

Post by Mixa64 »

У меня к сочетанию Паскаль и М68К какое-то недоверие и настороженность. Зимой развлекался дизассемблированием ПЗУшки процессора с телефонной станции AXE-10, там уж очень симпатичная керамическая с золотом PGAшная микросхема системного контроллера, дизассемблировал на тему ее реверс-инжиниринга с прицелом применить в своих целях, уж больно красива, в сочетании с PGAшным же TMP68HC000. По результатам работы - к большому сожалению, микросхема сильно заточена на специфику архитектуры AXE, в системе общего назначения неприменима. Но более всего я намучился разбирать логику кода, который, по признакам передачи параметров и еще каким-то, как я понял, был сгенерен с Паскаля. Получился ассемблерный ужос, все как-то неоптимально, медленно, куча лишних назначений, пересылок и т.п. Сочетание M68K и C выглядело гораздо лучше, тоже был опыт.
Annett
Senior
Posts: 137
Joined: 30 Mar 2017 00:55

Re: Компилятор Pascal для i8048 (msc-48)

Post by Annett »

Ничего страшного. Всё хорошо.
Ассемблер 68-го, к которому у меня очень особенное и близкое отношение, никуда же не делся ;)
Во-многом из-за его удобства я и выбрала этот процессор для нашей "левой" системы ;)

Но вариант с языком - тоже интересен, пусть даже там куча лишнего кода, да еще наверное пересылки в стеке и всё такое... Есть же всякие применения, где это уместно. Например, какой-нибудь редактор паттернов в секвенсоре или что-то подобное интерактивное - удобней закодить на языке. А вот отрисовка графики и генерация MIDI в достаточно жестком реальном времени - пусть себе крутится на асме по прерываниям, а частично даже хардварно в плисах. И всё будет замечательно:)

Mixa, да наверное эти специализированные чипы лучше просто оставить "в музее" и не мучиться с ними...Пусть лежат в ящичке, как фетиш :)
А вот сам PGAшный 68К -это уже прикольно :)

(извиняюсь за оффтоп):)
User avatar
Lavr
Supreme God
Posts: 16689
Joined: 21 Oct 2009 08:08
Location: Россия

Re: Компилятор Pascal для i8048 (msc-48)

Post by Lavr »

Tronix wrote:Рано или поздно всплывает на форумах вопрос, почему нет компилятора языка высокого уровня для MSC-48?
...
Но мне кажется, что некий "упрощенный" вариант Паскаля/Си реализовать можно.
А я вот тут призадумался - не реализовать ли более интересную штуку, раз уж ты взялся,
с учетом ограниченности ресурсов MSC-48.

Короче, был давно такой "якобы BASIC", который назывался ASIC... :wink:
Ко мне он попал довольно поздно и пользы от него я уже не поимел,
поскольку программировать научился уже совершенно по-другому.

Но суть следующая (в общем-то идея напоминает немного С ): это язык программирования,
который сочетает простоту и синтаксис BASIC с возможностью прямого использования
ассемблерного кода, где это надо, причем генерит (как говорят) весьма хороший код!

Примерно так:

Code: Select all

FOR I=0 TO 99
...
' здесь пишем на ассемблере
...
NEXT I
То есть ASIC удачно сочетает конструкции языка высокого уровня с ассемблерным синтаксисом.

Я полистал интернет, что про ASIC на сей день известно... на русском - практически ничего.
Есть вот такие ссылки которые еще не протухли:
https://en.wikipedia.org/wiki/ASIC_programming_language
http://www.atarimagazines.com/compute/i ... s_back.php


Я на что намекаю - может и в твоем "микро-Паскале_48" реализовать похожий подход?
iLavr