40 заметок с тегом

99

У американцев есть такая считалочка — «песня о пиве», наверняка её многие слышали в американских фильмах. У меня есть хобби — делать вывод этой считалочки на разных языках программирования. Со временем набралась приличная коллекция.

99 бутылок: Ассемблер 6502 для Apple I

66. Ассемблер 6502 для «Эпл I». 6502 — восьмибитный микропроцессор, выпускавшийся в 1975 году компанией «МОС Текноложи».

У процессора всего 56 команд (не считая недокументированные), доступны три регистра — A, X и Y. Очень необычно устроена адресация — первые 256 байт памяти доступны через восьмибитную адресацию, всё остальное пространство — через шестнадцатибитную.

Мнемоники трёхбуквенные, в основном — смысловые сокращения или акронимы, запоминаются легко. Например, LDY — LoaD Y, ASL — Arithmetic Shift Left, JSR — Jump to SubRoutine и так далее.

Процессор использовался в первом персональном компьютере «Эпл», для него я и написал «песню про пиво», использовав для печати на экран куцее встроенное АПИ.

Очень помогло, что в наборе команд есть десятичный режим — в этом режиме после прибавления единицы к «09h» получается «10h», а не «0Ah» (надо только помнить, что в этом режиме работают всего две команды — ADC и SBC). «Бутылки» в этом режиме считать значительно легче.

.ORG    00280h
            .CPU    6502

ECHO        =       $FFEF
PRBYTE      =       $FFDC
PRHEX       =       $FFE5
MONITOR     =       $FF1F

            SED
            LDA     #$99
LOOP_:
            PHA
            JSR     BOTTLES
            JSR     B_OF_T_WALL
            LDX     #(ENDPASSSTR - STRS - 2) ; ", "
            LDY     #2
            JSR     PRINT
            PLA
            PHA
            JSR     BOTTLES
            JSR     OF_BEER
            JSR     DOT_CR
            JSR     TAKE_ONE_DOWN
            PLA

            SEC
            SBC     #1

            PHA
            JSR     BOTTLES
            JSR     B_OF_T_WALL

            JSR     DOT_CR

            PLA

            CMP     #0
            BNE     LOOP_

            JSR     NO_MORE

            JMP     MONITOR

PRINTNUMBER:         ; in A (number), use A
            CMP     #$F
            BCS     $+5
            JMP     PRHEX
            JMP     PRBYTE

PRINT_PSTR:          ; in X (offset), use A, Y
            LDY     (STRS),X
            INX
PRINT:               ; in X (offset), Y (len), use A
            LDA     (STRS),X
            JSR     ECHO
            INX
            DEY
            BNE     PRINT
            RTS

BOTTLES:             ; in A (quantity), use X, Y
            CMP     #0
            BEQ     NO_
            PHA
            JSR     PRINTNUMBER
            LDX     #3 ; skips len+"NO"
            LDY     #8 ; length of " BOTTLES"
            PLA
            CMP     #1
            BNE     NO_ONE_
            DEY      ; 7 - length of " BOTTLE"
NO_ONE_:
            JMP     PRINT

NO_:
            LDX     #0
            JMP     PRINT_PSTR

B_OF_T_WALL:         ; use X, Y, A
            LDX     #(BEERONTHEWALLSTR - STRS)
            JMP     PRINT_PSTR

OF_BEER:             ; use X, Y, A
            LDX     #(BEERONTHEWALLSTR - STRS + 1) ; skips len
            LDY     #8 ; length of " OF BEER"
            JMP     PRINT

DOT_CR:              ; use X, Y, A
            LDX     #(END - STRS - 2) ; "." + CR
            LDY     #2
            JMP     PRINT

TAKE_ONE_DOWN:       ; use X, Y, A
            LDX     #(PASSSTR - STRS)
            JMP     PRINT_PSTR

NO_MORE:             ; use X, Y, A
            LDX     #(ENDSTR - STRS)
            JMP     PRINT_PSTR

STRS:
            .PSTR   "NO BOTTLES"
BEERONTHEWALLSTR:
            .PSTR   " OF BEER ON THE WALL"
PASSSTR:
            .PSTR   "TAKE ONE DOWN AND PASS IT AROUND, "
ENDPASSSTR:

ENDSTR:
            DB      128,"NO MORE BOTTLES OF BEER ON THE WALL, NO MORE BOTTLES OF BEER.",$8D
            DB      "GO TO THE STORE AND BUY SOME MORE, 99 BOTTLES OF BEER ON THE WALL.",$8D
END:

Для отладки, написания программы и перевода в машинные коды использовался довольно удобный «онлайн-ассемблер Асм80», для исполнения программы — «ОупенЭмулятор».

Сначала программу я вводил в эмулятор вручную, но потом обленился и написал программу для ввода шестнадцатеричного дампа внутрь эмулятора. Выложил на «Гитхаб», если кому-нибудь интересно.

Надеюсь как-нибудь повторно схожу в музей «Эпл» в Москве и введу эту программу в настоящий компьютер.

2018   99   apple

99 бутылок: Эль-76

65. Эль-76 — язык высокого уровня советских многопроцессорных вычислительных комплексов «Эльбрус». Разработан в Институте точной механики и вычислительной техники АН СССР имени С. А. Лебедева в 1972—73 годах.

Язык процедурный, как и следовало ожидать, но с обработкой исключений, который тут называются «ситуациями». Довольно необычно. Из-за задуманной авторами близости к железу, в языке разделено понятие представления и типа — то есть того как нечто хранится и что мы под ним понимаем. Ключевое слово «ф32», которое постоянно используется в программе — это 32-битный формат хранения.

Внешний вид интегрированной среды разработки языка программирования «Эль-76» в эмуляторе L76

Что-то написать на нём удалось благодаря найденному на «Вебархиве» эмулятору и документации с примерами кода. Эмулятор далеко не полный, поэтому некоторые вещи приходилось писать вовсе не так, как задумывали создатели языка.

% Песня о пиве 99.l76
программа
  процедура пиво = проц(ф32 бутылок)
  начало
    если бутылок = 0 то
      печатьмс(стр8 "no bottles")
    инес бутылок = 1 то
      печатьмс(стр8 "1 bottle")
    иначе
      печать(бутылок);
      печатьмс(стр8 " bottles")
    все
  конец;

  ф32 бутылок;
  для бутылок от 99 вниздо 1 цикл
    пиво(бутылок);
    печатьмс(стр8 " of beer on the wall, ");
    пиво(бутылок);
    печатьмс(стр8 " of beer.");
    печатькс();
    печатьмс(стр8 "Take one down and pass it around, ");
    пиво(бутылок - 1);
    печатьмс(стр8 "of beer on the wall.");
    печатькс();
    печатькс()
  повторить;

  печатьмс(стр8 "No more bottles of beer on the wall, no more bottles of beer.");
  печатькс();
  печатьмс(стр8 "Go to the store and buy some more, 99 bottles of beer on the wall.")
конец

Тем не менее, программа запускается и выводит задуманное, хотя могла бы выглядеть менее уродливо, будь у меня компилятор, реализующий «Эль-76» полностью.

2018   99   программинг

99 бутылок: Rust

64. Rust — довольно известный язык, на слуху, очень интересный своими концепциями. Пока писал «песню о пиве», даже захотелось познакомиться с ним поближе.

Язык является «близким к железу», нацелен на безопасность и многопоточность — немало ошибок, которые приводят к уязвимостям в программах на Си, тут выявляются ещё на этапе компиляции. Правда за это приходится платить, порог входа в язык очень высокий.

Ниже программа на этом языке, но выбранный формат позволяет показать лишь маленькую толику фишек языка.

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

Тип u8 — это тип «байт», match — конструкция проверяющая совпадение с образцом, мощная вещь, умеет сопоставлять что угодно — значения, типы, реализованные методы и так далее, mut — ключевое слово, указывающая, что переменную можно модифицировать, по-умолчанию они константны, println с восклицательным знаком — макрос, в языке очень хороший макроязык, с поддержкой рекурсии.

// Beer song. Evgeny Stepanischev, 2018
fn bottles(beer: u8) -> String {
    match beer {
    	0 => "no bottles".to_string(),
    	1 => "1 bottle".to_string(),
    	_ => format!("{} bottles", beer)
    }
}

fn main() {
	let mut beer = 99;
	while beer > 0 {
	    println!("{} of beer on the wall, {0} of beer.", bottles(beer));
	    beer -= 1;
	    println!("Take one down and pass it around, {} of beer on the wall.\n", bottles(beer));
	}

	println!("No more bottles of beer on the wall, no more bottles of beer.");
	println!("Go to the store and buy some more, 99 bottles of beer on the wall.");
}

99 бутылок: SCSS

63. SCSS — язык шаблонизации для файлов CSS. Я написал реализацию «99 бутылок» на нём ещё в 2012 году, но по неизвестной причине так никуда и не выложил. Возможно к лучшему — посмотрел свежим взглядом и исправил несколько неточностей.

Язык полностью процедурный, с примитивными типами (есть массивы, включая ассоциативные и собственный тип — «цвет»), поддержкой интерполяции в строках и с массой своих особенностей, диктуемых областью применения.

Шаблонизатор не имеет обособляющих элементов (типа двойных фигурных скобок), его конструкции «живут» прямо в коде, который генерируется, поэтому иногда нужно ставить немного уродливые подпорочки — например, символ деления используется и в математической операции, и в синтаксисе таблиц стилей, так что иногда приходится указывать что имеется ввиду.

Переводы строк пришлось оставить в строках как есть, поскольку, по всей видимости, нет никаких специализированных управляющих последовательностей для этого — в практике такой задачи не встречается.

@function bottles($beer) {
    @if $beer == 0 { @return "no bottles" }
    @if $beer == 1 { @return "1 bottle" }

    @return $beer + " bottles"
}

@function beer($beer) {
    $b: bottles($beer);

    @return "#{ $b } on the wall, #{ $b }.
Take one down and pass it around, " + bottles($beer - 1) + " of beer on the wall.

"
}

$result: "
";

@for $i from 99 through 1 {
    $result: $result + beer($i)
}

@debug $result +
"No more bottles of beer on the wall, no more bottles of beer.
Go to the store and buy some more, 99 bottles of beer on the wall."

99 бутылок: Postscript

62. Postscript — интерпретируемый стековый язык программирования. Предназначен для стандартизации вывода документов на различные устройства.

Люди на нём программы пишут крайне редко, в основном это делают другие программы — например, при печати на принтер. Этот же язык программирования используется внутри формата PDF.

Язык строго типизирован, система типов включает: целые числа, числа с плавающей запятой, логический тип, строку, массив, процедуру, словарь и имя. Для всех операций используется стек и обратная польская запись.

%!
%%Title: (Beer song)
%%Creator: Evgeny Stepanischev
%%EndComments

/beer % (int) -> (int)
{
	dup 0 eq {
		(no bottles) print
	} {
		dup 1 eq {
			(1 bottle) print
		} {
			dup //=string cvs print ( bottles) print
		} ifelse
	} ifelse
} bind def

99 -1 1 {
	beer ( of beer on the wall, ) print beer (\n) print
	(Take one down and pass it around, ) print
	dup 1 sub beer pop
	( of beer on the wall.\n\n) print
} for

(No more bottles of beer on the wall, no more bottles of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.) print

quit

99 бутылок: Cmm, интернет-археология

61. Cmm. Язык «Cmm» был разработан в 1992 году компанией «Nombas». Основной идеей языка было упрощение языка «Си», отсюда и название — C minus minus. Официальное руководство говорит, что это «Си, минус объявление типов, минус указатели». Позже язык был переименован в ScriptEase, так как упоминание «Си» отпугивало людей. Язык существовал для ДОСа, Виндоуз, ОС/2 и операционной системы компании «Новелл».

В конце 1995 года компания создала версию скриптового языка для веб-страниц, внедрённую в браузер Нетскейп. Таким образом «Си-минус-минус» был одним из первых языков фронтенда!

Язык действительно простой — типы выводятся из использования, массивы без указания размера, нет явного выделения и освобождения памяти, указателей не видно.

Массивы и строки, хоть и являются указателями (есть даже арифметика с ними), но этого не замечаешь — выглядят, как обычные переменные. Все lvalue передаются по указателю, для передачи по значению есть специальный синтаксис. Забавно, кстати, что точка с запятой не обязательна, прямо как в ДжаваСкрипте.

Остальные отличия от «Си» не столь заметны. Языки почти что близнецы, есть даже ограниченный предпроцессор, да и встроенные функции, в основном, сишные. Язык, видимо, претендовал сразу на несколько ниш, на нишу коммандных оболочек в том числе — в составе предпроцессора есть команда, позволяющая импортировать куски программ, указывая с какой по какую строку производить импорт. Насколько я смог заметить, это широко используется, чтобы внедрять «СиЭмЭм» внутрь шелл-скриптов различных ОС.

// Written by Evgeny Stepanischev, 2017

BottlesPlural(Beer)
{
    switch ( Beer )
    {
        case 0:
            return `no bottles`
        case 1:
            return `1 bottle`
        default:
            sprintf(Bottles, `%d bottles`, Beer)
            return Bottles
    }
}

i = 99
Bottles = BottlesPlural(i)

while ( i > 0 ) {
    printf("%s of beer on the wall, %s of beer.\n", Bottles, Bottles)
    Bottles = BottlesPlural(--i)
    printf("Take one down and pass it around, %s of beer on the wall.\n\n", Bottles)
}

printf("No more bottles of beer on the wall, no more bottles of beer.\n")
printf("Go to the store and buy some more, 99 bottles of beer on the wall.\n")

Если кому-то интересно, в интернете легко гуглятся интерпретаторы языка с описанием. Я брал 32-битный интерпретатор версии 2.11 под Виндоуз. Кстати, под ОС/2 находится и более ранняя — 1.008, выпущенная в 1993 году.

99 бутылок: R

60. R. Как многие наверное помнят, в качестве хобби я время от времени пишу на разных языках программирования американскую «Песню о пиве»

Это довольно известное развлечение — реализовывать на куче языков что-то простое, обычно выбирают числа Фибоначчи, «Песню» или ещё что-то незатейливое.

Я выбрал именно «Песню о пиве», так как не всем языкам из моего списка, под силу что-то большее. До сегодняшнего дня в списке было 57 языков, сегодня, 58-м пунктом, к ним вполне ожидаемо присоединится язык «Эр», о нём я уже немного писал, собираюсь писать и дальше как время образуется.

# Written by Evgeny Stepanischev, 2017

bottles <- function(beer) {
    ifelse(beer == 0, "no bottles",
        ifelse(beer > 1, paste(beer, "bottles"),  "1 bottle")
    )
}

for (i in 99:1) {
    paste(bottles(i), "of beer") -> b

    cat(b, " on the wall, ", b, ".\n", sep = "")
    cat("Take one down and pass it around,", bottles(i - 1), "of beer on the wall.\n\n")
}

cat("No more bottles of beer on the wall, no more bottles of beer.\n")
cat("Go to the store and buy some more, 99 bottles of beer on the wall.\n")

«99 бутылок» на языке «Электроники МК-61»

«Электроника МК-61» (59.82КиБ)
«Электроника МК-61» с запущенной на нём программой

59. Язык калькулятора Электроника МК-61. «Электроника МК-61» — устройство из класса «программирумых калькуляторов». Были когда-то такие гаджеты, позволявшие со всеми ограничениями калькуляторов (небольшая клавиатура, типичный для калькулятора экран) писать программы.

В детстве такие калькуляторы я видел только на картинках в журнале «Наука и жизнь», а программы для них были каким-то любопытным закорючками, в которых не разобраться.

Вчера мне в руки попал настоящая «Электроника МК-61» (для передачи компьютерному музею) и я наконец-то на обеде и потом вечером разобрался в этих странных значках.

Так выглядят мои «99 бутылок…» на этом калькуляторе:

8112000 Х→П c 8 + ИНВ Х→П b Вх 10 ÷ [X] Х→П d 8 + ИНВ Х→П e В/О ПРГ
П→Х 0 1 0 ÷ [x] Х→П 1 Вх {x} 1 0 × Х→П 2
1 1 Х→П 8 1 2 Х→П 7 П→Х 2 ПП 34 Х→П a
1 4 Х→П 8 1 3 Х→П 7 П→Х 1 ПП 34 П→Х a
^ С/П
6 - /-/ x<0 45 9 + К П→Х 7 + ИНВ В/О
1 + К П→Х 7 + ИНВ К П→Х 8 ^ В/О

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

Вкратце о языке. Больше всего это похоже на крайне примитивный ассемблер с гипертрофированным математическим сопроцессором. Всё то же — переходы на адрес, одно- и двухбайтные инструкции с операндами, регистры с определёнными назначениями, сильно ограниченная память (105 команд), наличествует косвенная адресация и подпрограммы (стек вызовов размером в пять вхождений).

До запуска программы нужно в регистр «0» поместить стартовое значение (например, «99», «Х→П», «0») и запустить программу «В/О», «С/П».

Это я сделал из-за того, что цикла в программе нет — из-за мельтишения разрядов совершенно не понять, что происходит. Хотя добавить его очень просто — до команды «С/П» надо вставить одну проверку с переходом и сдвинуть адреса.

Поэтому на вход подаётся нужное число, например, «42», а на выходе мы можем полюбоваться фразой «BEEr 42». Не весть что, но что вы хотели от калькулятора? Кстати, выводимая фраза — не строка (спойлер: а число), о том как мне пришлось помучаться я ещё расскажу.

99 бутылок: Эллочка

58. Эллочка. Мне написал автор языка программирования «Эллочка» с предложением написать на нём «99 бутылок…», как вы знаете, у меня есть хобби — писать такие программы на разных языках.

Для этого языка есть единственный, авторский интерпретатор под ДОС (и очень важно использовать в файле программы символы перевода ДОС/Виндоуз, иначе ничего не будет работать), а сам язык напоминает странноватый Бейсик, без поддержки функций и процедур. Их можно эмулировать, используя трюк со специальной переменной «@», которая содержит номер текущей строки.

В языке есть ряд интересных возможностей, но их затмевают недостатки разного калибра, что характерно для языков без сообщества. Даже в таком простом примере мне пришлось побороться за работоспособность программы. Например, перед числами зачем-то всегда выводится символ табуляции (я его срезаю функцией %MID), а когда вывод достигает размера экрана (80×25), то прокрутки не происходит, программа просто жалуется, что «операция не позволяется» (поэтому я просто жду нажатия клавиши после каждой итерации и очищаю экран).

!99 beer song by Evgeny Stepanischev
!Used variables: B, R, $1
B=99
@cycl
    R=@+2 
    goto @beer
    list ' of beer on the wall, '
    R=@+2
    goto @beer
    list '.'\
    list 'Take one down and pass around'\
    decr B
    R=@+2
    goto @beer
    list ' of beer on the wall.'\
    list ''\
    wait
    clsc
esli B >> 0; @cycl
list 'No more bottles of beer on the wall, no more bottles of beer.'\
list 'Go to the store and buy some more, 99 bottles of beer on the wall.'
exit
@beer
esli B >> 0; +3\
list 'No bottles'
goto R
esli B == 1; +5\
$1=B
$1=%MID($1,2,10)
list $1+' bottles'
goto R
list '1 bottle'
goto R

99 бутылок: CMake

57. CMake. Писать «Песни про пиво» на различных языках программирования — давнее моё увлечение, ему столько же лет, сколько этому блогу. В этот раз «поёт» «Си-мэйк».

CMake — автоматизированная система сборки пакетов, имеющая развитый, хотя и несколько уродливый макроязык. Инструкции, большей частью, знакомые, разве что способ возврата значений несколько из макросов и функций несколько странный — через параметры и глобальные переменные.

Никакого ООП, само собой, и всё весьма примитивно по меркам развитых языков.

# 99.cmake
# to run: cmake -P 99.cmake
# Written by Evgeny Stepanischev, 2015

cmake_minimum_required(VERSION 3.0)

macro(bottles beer ret)
    if(${beer} EQUAL 0)
        set(${ret} "No bottles")
    elseif(${beer} EQUAL 1)
        set(${ret} "1 bottle")
    else()
        set(${ret} "${${beer}} bottles")
    endif()
endmacro()

foreach(beer RANGE 99 1 -1)
    bottles(beer bottles)
    math(EXPR beer "${beer}-1")
    bottles(beer bottless)

    message("${bottles} of beer on the wall, ${bottles}.")
    message("Take one down and pass it around, ")
    message("${bottless} of beer on the wall.")
    message("")
endforeach()

message("No more bottles of beer on the wall, no more bottles of beer.")
message("Go to the store and buy some more, 99 bottles of beer on the wall.")

Строка для запуска во второй строке файла.

Ранее Ctrl + ↓