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», для исполнения программы — «ОупенЭмулятор».
Сначала программу я вводил в эмулятор вручную, но потом обленился и написал программу для ввода шестнадцатеричного дампа внутрь эмулятора. Выложил на «Гитхаб», если кому-нибудь интересно.
Надеюсь как-нибудь повторно схожу в музей «Эпл» в Москве и введу эту программу в настоящий компьютер.
Евгений заходите в группу vk.com/club190222366
Спасибо, загляну на огонёк)
Комментарии для тех, кто захочет запустить программу:
1) программа для автоматического ввода в эмулятор запускается только под третим и выше Пайтоном, под вторым будет ошибка синтаксиса
2) вывод программы автоматического ввода надо направить в интерпретатор ЭплСкрипт: python3 transfer2openemulator.py | osascript
3) если возникает ошибка 1002 («execution error: System Events got an error: osascript is not allowed to send keystrokes» или «execution error: Получена ошибка от System Events: Отправка нажатий клавиш для osascript не разрешена»), погуглите «osascript 1002 permission issue», в интернете есть рецепт.