Last active
August 17, 2018 21:28
-
-
Save evost/8492b871c24f556e843eb2e148c90016 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| номер | команда | тип первого аргумента | тип второго аргумента | пояснение | | |
| ----- | ------- | --------------------- | --------------------- | -----------------------------------------| | |
| 0 | nop | - | - | ничего не делать | | |
| 1 | ret | - | - | выход из подпрограммы | | |
| 2 | clf | - | - | закрыть открытый файл | | |
| 3 | end | - | - | завершает программу | | |
| 4 | clr | - | - | очистить экран и установить X=0, Y=0 | | |
| 5 | xch | var1 | var2 | swap(var1, var2) | | |
| 6 | add | var1 | var2/num | var1 += var2 | | |
| 7 | sub | var1 | var2/num | var1 -= var2 | | |
| 8 | mul | var1 | var2/num | var1 *= var2 | | |
| 9 | div | var1 | var2/num | var1 /= var2 | | |
| 10 | mod | var1 | var2/num | var1 %= var2 | | |
| 11 | pow | var1 | var2/num | var1 = var1 ^ var2 | | |
| 12 | mov | var1 | var2/num | var1 = var2 | | |
| 13 | orl | var1 | var2/num | var1 |= var2 | | |
| 14 | and | var1 | var2/num | var1 &= var2 | | |
| 15 | not | var1 | var2/num | var1 ~= var2 | | |
| 16 | xor | var1 | var2/num | var1 ^= var2 | | |
| 17 | neg | var1 | var2/num | var1 = -var2 | | |
| 18 | rol | var1 | var2/num | var1 = var1 << var2 + var1 >> (16 - var2)| | |
| 19 | ror | var1 | var2/num | var1 = var1 >> var2 + var1 << (16 - var2)| | |
| 20 | shl | var1 | var2/num | var1 = var1 << var2 | | |
| 21 | shr | var1 | var2/num | var1 = var1 >> var2 | | |
| 22 | rnd | var1 | var2/num | var1 = random(var2) | | |
| 23 | rci | var1 | var2/num | считать с консоли var2 целых | | |
| 24 | rcc | var1 | var2/num | считать с консоли var2 символов | | |
| 25 | wci | var1 | var2/num | вывести в консоль var2 целых | | |
| 26 | wcc | var1 | var2/num | вывести в консоль var2 символов | | |
| 27 | rfi | var1 | var2/num | считать с файла var2 целых | | |
| 28 | rfc | var1 | var2/num | считать с файла var2 символов | | |
| 29 | wfi | var1 | var2/num | вывести в файл var2 целых | | |
| 30 | wfc | var1 | var2/num | вывести в файл var2 символов | | |
| 31 | rdd | var1 | var2/num | var1 = digitalRead(var2) | | |
| 32 | rda | var1 | var2/num | var1 = analogRead(var2) | | |
| 33 | wfd | var1 | var2/num | digitalWrite(var1, var2) | | |
| 34 | wfa | var1 | var2/num | analogWrite(var1, var2) | | |
| 35 | wsd | var1 | var2/num | digitalWrite(var2, var1) | | |
| 36 | wsa | var1 | var2/num | analogWrite(var2, var1) | | |
| 37 | stx | var1/num | - | cursorX = var1 * LCD.getFontXsize() | | |
| 38 | sty | var1/num | - | cursorY = var1 * LCD.getFontYsize() | | |
| 39 | stc | var1/num | - | устанавливает цвет фона и курсора | | |
| 40 | pni | var1/num | - | pinMode(var1, INPUT) | | |
| 41 | pno | var1/num | - | pinMode(var1, OUTPUT) | | |
| 42 | wsr | var1/num | - | Serial.Write(var1) | | |
| 43 | rsr | var1 | - | var1 = Serial.Read() | | |
| 44 | inc | var1 | - | var1++ | | |
| 45 | dec | var1 | - | var1-- | | |
| 46 | ofr | var1 | - | открыть файл на чтение | | |
| 47 | ofw | var1 | - | открыть файл на запись | | |
| 48 | ofa | var1 | - | открыть файл на дополнение | | |
| 49 | mkd | var1 | - | создать каталог | | |
| 50 | rmd | var1 | - | удалить каталог | | |
| 51 | rmf | var1 | - | удалить файл | | |
| 52 | ife | var1 | lab | если равно нулю перейти по метке | | |
| 53 | ifn | var1 | lab | если не равно нулю перейти по метке | | |
| 54 | ifb | var1 | lab | если больше нуля перейти по метке | | |
| 55 | ifs | var1 | lab | если меньше нуля перейти по метке | | |
| 56 | jmp | lab | - | безусловный переход по метке lab | | |
| 57 | cal | lab | - | вызвать подпрограмму по метке lab | | |
| ----- | ------- | --------------------- | --------------------- | -----------------------------------------| | |
| 58 | lab | new_lab | - | объявление метки new_lab | | |
| 59 | prс | new_lab | - | объявление подпрограммы new_lab | | |
| 60 | dat | new_var | num | объявление массива из num>0 целых | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <string> | |
#include <fstream> | |
#include <map> | |
#include <algorithm> | |
#include <regex> | |
using namespace std; | |
uint8_t current_var = 0; | |
uint16_t current_str = 0; | |
uint16_t p_size = 0; | |
ofstream outf; | |
map <string, uint8_t> var_names; | |
map <string, uint16_t> label_names; | |
map <string, uint8_t> coms = { | |
{ "nop", 0 }, | |
{ "ret", 1 }, | |
{ "clf", 2 }, | |
{ "end", 3 }, | |
{ "rst", 4 }, | |
{ "clr", 5 }, | |
{ "xch", 6 }, | |
{ "add", 7 }, | |
{ "sub", 8 }, | |
{ "mul", 9 }, | |
{ "div", 10 }, | |
{ "mod", 11 }, | |
{ "pow", 12 }, | |
{ "mov", 13 }, | |
{ "orl", 14 }, | |
{ "and", 15 }, | |
{ "not", 16 }, | |
{ "xor", 17 }, | |
{ "neg", 18 }, | |
{ "rol", 19 }, | |
{ "ror", 20 }, | |
{ "shl", 21 }, | |
{ "shr", 22 }, | |
{ "rnd", 23 }, | |
{ "rci", 24 }, | |
{ "rcc", 25 }, | |
{ "wci", 26 }, | |
{ "wcc", 27 }, | |
{ "rfi", 28 }, | |
{ "rfc", 29 }, | |
{ "wfi", 30 }, | |
{ "wfc", 31 }, | |
{ "rdd", 32 }, | |
{ "rda", 33 }, | |
{ "wfd", 34 }, | |
{ "wfa", 35 }, | |
{ "wsd", 36 }, | |
{ "wsa", 37 }, | |
{ "stx", 38 }, | |
{ "sty", 39 }, | |
{ "stс", 40 }, | |
{ "pni", 41 }, | |
{ "pno", 42 }, | |
{ "wsr", 43 }, | |
{ "rsr", 44 }, | |
{ "inc", 45 }, | |
{ "dec", 46 }, | |
{ "ofr", 47 }, | |
{ "ofw", 48 }, | |
{ "ofa", 49 }, | |
{ "mkd", 50 }, | |
{ "rmd", 51 }, | |
{ "rmf", 52 }, | |
{ "ife", 53 }, | |
{ "ifn", 54 }, | |
{ "ifb", 55 }, | |
{ "ifs", 56 }, | |
{ "jmp", 57 }, | |
{ "cal", 58 }, | |
{ "lab", 59 }, | |
{ "prс", 60 }, | |
{ "dat", 61 } | |
}; | |
map <uint8_t, string> errors = { | |
{0, "UNKNOWN COMMAND"}, | |
{1, "BAD NEW VARIABLE"}, | |
{2, "VARIABLES OVERFLOW"}, | |
{3, "BAD NEW LABEL"}, | |
{4, "UNNECESSARY ARGUMENT"}, | |
{5, "PROGRAM SIZE OVERFLOW"}, | |
{6, "UNKNOWN SYMBOL"}, | |
{7, "UNKNOWN VARIABLE"}, | |
{8, "BAD VARIABLE"}, | |
{9, "UNKNOWN LABEL"} | |
}; | |
bool is_number(string s) { | |
if (s.length() == 0) | |
return false; | |
uint8_t i = 0; | |
if (s[0] == '-') { | |
i++; | |
if (s.length() == 1) | |
return false; | |
} | |
for (; i < s.length(); i++) | |
if (!isdigit(s[i])) | |
return false; | |
return true; | |
} | |
void push_data(int8_t data) { | |
outf.put(data); | |
p_size++; | |
} | |
uint16_t get_lab_num(string s) { | |
if (label_names.find(s) == label_names.end() || s.length() == 0) | |
throw 9; | |
return label_names[s]; | |
} | |
uint8_t get_var_num(string s) { | |
string var = "", index = ""; | |
for (uint8_t i = 0; i < s.length(); i++) | |
if (s[i] != '[') | |
var += s[i]; | |
else | |
for (i++; i < s.length(); i++) | |
if (s[i] != ']') | |
index += s[i]; | |
else | |
if (i + 1 != s.length() || !is_number(index)) | |
throw 8; | |
if (var_names.find(var) == var_names.end()) | |
throw 7; | |
else | |
if (index.length() > 0) | |
return var_names[var] + stoi(index); | |
else | |
return var_names[var]; | |
} | |
int8_t bigbits(int16_t i) { | |
i = i >> 8; | |
return (int8_t)i; | |
} | |
int8_t litbits(int16_t i) { | |
i = i << 8; | |
i = i >> 8; | |
return (int8_t)i; | |
} | |
void parse(string s) { | |
current_str++; | |
transform(s.begin(), s.end(), s.begin(), ::tolower); | |
s = regex_replace(s, regex("^ +| +$|( ) +"), "$1"); | |
if (s.length() > 0) { | |
if (s[0] != ';') { | |
string op = "", arg1, arg2; | |
for (uint8_t i = 0; i < s.length(); i++) | |
if (!isspace(s[i]) && !isdigit(s[i]) && !isalpha(s[i]) && s[i] != '_' && s[i] != '[' && s[i] != ']' && s[i] != '-') | |
throw 6; | |
uint8_t i = 0; | |
for (; i < s.length() && !isspace(s[i]); i++) | |
op += s[i]; | |
for (i++; i < s.length() && isspace(s[i]); i++) {}; | |
for (; i < s.length() && !isspace(s[i]); i++) | |
arg1 += s[i]; | |
for (i++; i < s.length() && isspace(s[i]); i++) {}; | |
for (; i < s.length() && !isspace(s[i]); i++) | |
arg2 += s[i]; | |
if (coms.find(op) != coms.end()) { | |
if (p_size >= 65536) | |
throw 5; | |
if (coms[op] <= coms["clr"]) { | |
if (arg1 != "" || arg2 != "") | |
throw 4; | |
push_data(coms[op]); | |
} | |
else if (coms[op] <= coms["xch"]) { | |
push_data(coms[op] + 128); | |
push_data(get_var_num(arg1)); | |
push_data(get_var_num(arg2)); | |
} | |
else if (coms[op] <= coms["wsa"]) { | |
if (is_number(arg2)) { | |
push_data(coms[op]); | |
push_data(get_var_num(arg1)); | |
push_data(bigbits(stoi(arg2))); | |
push_data(litbits(stoi(arg2))); | |
} | |
else { | |
push_data(coms[op] + 128); | |
push_data(get_var_num(arg1)); | |
push_data(get_var_num(arg2)); | |
} | |
} | |
else if (coms[op] <= coms["wsr"]) { | |
if (arg2 != "") | |
throw 4; | |
if (is_number(arg1)) { | |
push_data(coms[op]); | |
push_data(bigbits(stoi(arg1))); | |
push_data(litbits(stoi(arg1))); | |
} | |
else { | |
push_data(coms[op] + 128); | |
push_data(get_var_num(arg1)); | |
} | |
} | |
else if (coms[op] <= coms["rmf"]) { | |
if (arg2.length() != 0) | |
throw 4; | |
push_data(coms[op]); | |
push_data(get_var_num(arg1)); | |
} | |
else if (coms[op] <= coms["ifs"]) { | |
push_data(coms[op]); | |
push_data(get_var_num(arg1)); | |
push_data(bigbits(get_lab_num(arg2))); | |
push_data(litbits(get_lab_num(arg2))); | |
} | |
else if (coms[op] <= coms["cal"]) { | |
if (arg2.length() != 0) | |
throw 4; | |
push_data(coms[op]); | |
push_data(bigbits(get_lab_num(arg1))); | |
push_data(litbits(get_lab_num(arg1))); | |
} | |
else if (coms[op] <= coms["prc"]) { | |
if (!isalpha(arg1[0]) || label_names.find(arg1) != label_names.end() || label_names.size() == 4096)//why 4096 | |
throw 3; | |
else label_names[arg1] = p_size; | |
} | |
else if (coms[op] <= coms["dat"]) { | |
if (!isalpha(arg1[0]) || !is_number(arg2) || var_names.find(arg1) != var_names.end()) | |
throw 2; | |
if (current_var + stoi(arg2) > 255) | |
throw 1; | |
var_names[arg1] = current_var; | |
current_var += stoi(arg2); | |
} | |
cout << current_str << ": " << op << ' ' << arg1 << ' ' << arg2 << ' ' << '\n'; | |
} | |
else | |
throw 0; | |
} | |
} | |
} | |
int main(int argc, char *argv[]) { | |
if (argc == 1) | |
return 0; | |
string buff = "", outname, name = argv[1]; | |
try { | |
ifstream inpf(name); | |
outname = name; | |
outname[outname.length() - 3] = 'b'; | |
outname[outname.length() - 2] = 'i'; | |
outname[outname.length() - 1] = 'n'; | |
outf = ofstream(outname, ios::out | ios::binary); | |
while (getline(inpf, buff)) | |
parse(buff); | |
inpf.close(); | |
outf.close(); | |
cout << "DONE " << p_size << " BYTES, " << to_string(current_str) << " STRINGS, IN " << clock() / 1000.0 << " S" << '\n'; | |
} | |
catch (int a) { | |
cout << errors[a] << ". LINE " << to_string(current_str) << ": " << buff << '\n'; | |
} | |
catch (...) { | |
cout << "UNKNOWN ERROR. LINE " << to_string(current_str) << ": " << buff << '\n'; | |
} | |
cin.get(); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define ps2key_int_pin 3 | |
#define ps2key_data_pin 4 | |
#define pin_BL 5 | |
#define pin_SD 6 | |
#define exe_ok 0 | |
#define exe_end 1 | |
#define stack_overflow 2 | |
#define division_by_zero 3 | |
#define end_of_file 4 | |
#define file_not_exist 5 | |
#define opening_error 6 | |
#define rewrite_error 7 | |
#define off_by_one 8 | |
#include <SD.h> | |
#include <UTFT.h> | |
#include <PS2Keyboard.h> | |
#include <Wire.h> | |
#include <RTClib.h> | |
#include <MemoryFree.h> | |
char exe_file_name[256];//текущий исполняемый файл, 255 для имени и пути и 1 для нуль-терминатора | |
char work_file_name[256];//файл, с которым работает программа (не больше 64 кбайт) | |
int8_t exe_code;//код, возвращаемый функцией обработки операции | |
int8_t delta = 0;//количество байт, на которое нужно перескочить после выполнения операции | |
int8_t data[4];//место для предварительной выгрузки из файла кода операции и трех следующих байт | |
uint8_t BL = 64;//яркость подсветки | |
int16_t registers[256]; | |
uint16_t call_stack[255];//т.к. call не может быть больше 255 | |
uint8_t call = 0;//номер следующей записи в стэк вызовов | |
uint16_t cursorY = 0; | |
uint16_t cursorX = 0; | |
char sTime[3]; | |
File exe_file; | |
File work_file; | |
PS2Keyboard keyboard; | |
UTFT LCD(ILI9486, 38, 39, 40, 41); | |
RTC_DS3231 rtc; | |
extern uint8_t BigFont[]; | |
const unsigned int VGA_COLORS[16] = {VGA_BLACK, VGA_WHITE, VGA_RED, VGA_GREEN, VGA_BLUE, VGA_SILVER, VGA_GRAY, VGA_MAROON, VGA_YELLOW, VGA_OLIVE, VGA_LIME, VGA_AQUA, VGA_TEAL, VGA_NAVY, VGA_FUCHSIA, VGA_PURPLE}; | |
inline int16_t dbytes(const int8_t &a, const int8_t &b) { | |
return (((int16_t)a) << 8) + b; | |
} | |
void setc8(const char &k) { | |
char c[2] = {k, 0}; | |
if ((cursorX > LCD.getDisplayXSize() - LCD.getFontXsize()) || k == '\n') { | |
cursorX = 0; | |
cursorY += LCD.getFontYsize(); | |
if (cursorY > LCD.getDisplayYSize() - LCD.getFontYsize()) { | |
LCD.clrScr(); | |
cursorY = 0; | |
} | |
} else if (k >= 32 && k != 127) { | |
LCD.print(c, cursorX, cursorY); | |
cursorX += LCD.getFontXsize(); | |
} | |
} | |
void echo(const char* s, const uint8_t &len) { | |
for (uint8_t i = 0; i < len; i++) | |
setc8(s[i]); | |
} | |
void echoln(const char* s, const uint8_t &len) { | |
echo(s, len); | |
setc8('\n'); | |
} | |
void setc16(const int16_t &k) { | |
char s[6]; | |
itoa(k, s, 10); | |
echo(s, strlen(s)); | |
} | |
void setf8(const int8_t &u) { | |
work_file.write(u); | |
} | |
void setf16(const int16_t &u) { | |
setf8(u >> 8); | |
setf8((u << 8) >> 8); | |
} | |
void getstr(char* str, const uint8_t &len = 255) { //len - длинна строки без нулевого символа | |
echo("> ", 2); | |
uint8_t i = 0, buf; | |
while (buf != PS2_ENTER) { | |
while (!keyboard.available()); | |
buf = keyboard.read(); | |
if (buf >= 32 && buf != PS2_DELETE && i < len) { | |
str[i++] = buf; | |
setc8(buf); | |
} else if (buf == PS2_DELETE) { | |
if (i > 0) { | |
str[i] = ' '; | |
i--; | |
cursorX -= LCD.getFontXsize(); | |
setc8(' '); | |
cursorX -= LCD.getFontXsize(); | |
} | |
} else { | |
str[i] = 0; | |
cursorY += LCD.getFontYsize(); | |
cursorX = 0; | |
if (cursorY > LCD.getDisplayYSize() - LCD.getFontYsize()) { | |
LCD.clrScr(); | |
cursorY = 0; | |
} | |
break; | |
} | |
} | |
while (keyboard.available()) | |
keyboard.read(); | |
} | |
void getc16(int16_t &u) { | |
char s[6]; | |
getstr(s, 5); | |
u = (int16_t)atoi(s); | |
} | |
void getc8(int16_t &u) { | |
char s[2]; | |
getstr(s, 1); | |
u = s[0]; | |
} | |
void getf8(int16_t &u) { | |
if (work_file.available()) | |
u = work_file.read(); | |
else | |
u = 0; | |
} | |
void getf16(int16_t &u) {//если остался один байт в файле - вернет его как старший! | |
int16_t a, b; | |
getf8(a); | |
getf8(b); | |
u = dbytes(a, b); | |
} | |
uint8_t exe() { | |
int8_t opcode = data[0]; | |
int16_t swpbuf; | |
if (opcode > 128) | |
opcode -= 128; | |
delta = 0; | |
switch (opcode) { | |
case 0: | |
delta = 1; | |
break; | |
case 1: | |
if (call == 0) | |
return off_by_one; | |
call--; | |
exe_file.seek(call_stack[call]); | |
break; | |
case 2: | |
work_file.close(); | |
delta = 1; | |
break; | |
case 3: | |
return exe_end; | |
break; | |
case 4: | |
LCD.clrScr(); | |
cursorX = 0; | |
cursorY = 0; | |
break; | |
case 5: | |
swpbuf = registers[data[1]]; | |
registers[data[1]] = registers[data[2]]; | |
registers[data[2]] = swpbuf; | |
delta = 3; | |
break; | |
case 6: | |
if (data[0] >= 128) { | |
registers[data[1]] += registers[data[2]]; | |
delta = 3; | |
} else { | |
registers[data[1]] += dbytes(data[2], data[3]); | |
delta = 4; | |
} | |
break; | |
case 7: | |
if (data[0] >= 128) { | |
registers[data[1]] -= registers[data[2]]; | |
delta = 3; | |
} else { | |
registers[data[1]] -= dbytes(data[2], data[3]); | |
delta = 4; | |
} | |
break; | |
case 8: | |
if (data[0] >= 128) { | |
registers[data[1]] *= registers[data[2]]; | |
delta = 3; | |
} else { | |
registers[data[1]] *= dbytes(data[2], data[3]); | |
delta = 4; | |
} | |
break; | |
case 9: | |
if (data[0] >= 128) { | |
if (registers[data[2]] == 0) | |
return division_by_zero; | |
registers[data[1]] /= registers[data[2]]; | |
delta = 3; | |
} else { | |
if (dbytes(data[2], data[3] == 0)) | |
return division_by_zero; | |
registers[data[1]] /= dbytes(data[2], data[3]); | |
delta = 4; | |
} | |
break; | |
case 10: | |
if (data[0] >= 128) { | |
if (registers[data[2]] == 0) | |
return division_by_zero; | |
registers[data[1]] %= registers[data[2]]; | |
delta = 3; | |
} else { | |
if (dbytes(data[2], data[3] == 0)) | |
return division_by_zero; | |
registers[data[1]] %= dbytes(data[2], data[3]); | |
delta = 4; | |
} | |
break; | |
case 11: | |
if (data[0] >= 128) { | |
registers[data[1]] = pow(registers[data[1]], registers[data[2]]); | |
delta = 3; | |
} else { | |
registers[data[1]] = pow(registers[data[1]], dbytes(data[2], data[3])); | |
delta = 4; | |
} | |
break; | |
case 12: | |
if (data[0] >= 128) { | |
registers[data[1]] = registers[data[2]]; | |
delta = 3; | |
} else { | |
registers[data[1]] = dbytes(data[2], data[3]); | |
delta = 4; | |
} | |
break; | |
case 13: | |
if (data[0] >= 128) { | |
registers[data[1]] |= registers[data[2]]; | |
delta = 3; | |
} else { | |
registers[data[1]] |= dbytes(data[2], data[3]); | |
delta = 4; | |
} | |
break; | |
case 14: | |
if (data[0] >= 128) { | |
registers[data[1]] &= registers[data[2]]; | |
delta = 3; | |
} else { | |
registers[data[1]] &= dbytes(data[2], data[3]); | |
delta = 4; | |
} | |
break; | |
case 15: | |
if (data[0] >= 128) { | |
registers[data[1]] = ~registers[data[2]]; | |
delta = 3; | |
} else { | |
registers[data[1]] = ~dbytes(data[2], data[3]); | |
delta = 4; | |
} | |
break; | |
case 16: | |
if (data[0] >= 128) { | |
registers[data[1]] ^= registers[data[2]]; | |
delta = 3; | |
} else { | |
registers[data[1]] ^= dbytes(data[2], data[3]); | |
delta = 4; | |
} | |
break; | |
case 17: | |
if (data[0] >= 128) { | |
registers[data[1]] = -registers[data[2]]; | |
delta = 3; | |
} else { | |
registers[data[1]] = -dbytes(data[2], data[3]); | |
delta = 4; | |
} | |
break; | |
case 18: | |
if (data[0] >= 128) { | |
registers[data[1]] = (registers[data[1]] << (registers[data[2]] % 16)) + (registers[data[1]] >> (16 - registers[data[2]] % 16)); | |
delta = 3; | |
} else { | |
registers[data[1]] = (registers[data[1]] << (dbytes(data[2], data[3]) % 16)) + (registers[data[1]] >> (16 - dbytes(data[2], data[3]) % 16)); | |
delta = 4; | |
} | |
break; | |
case 19: | |
if (data[0] >= 128) { | |
registers[data[1]] = (registers[data[1]] >> (registers[data[2]] % 16)) + (registers[data[1]] << (16 - registers[data[2]] % 16)); | |
delta = 3; | |
} else { | |
registers[data[1]] = (registers[data[1]] >> (dbytes(data[2], data[3]) % 16)) + (registers[data[1]] << (16 - dbytes(data[2], data[3]) % 16)); | |
delta = 4; | |
} | |
break; | |
case 20: | |
if (data[0] >= 128) { | |
registers[data[1]] = registers[data[1]] << registers[data[2]]; | |
delta = 3; | |
} else { | |
registers[data[1]] = registers[data[1]] << dbytes(data[2], data[3]); | |
delta = 4; | |
} | |
break; | |
case 21: | |
if (data[0] >= 128) { | |
registers[data[1]] = registers[data[1]] >> registers[data[2]]; | |
delta = 3; | |
} else { | |
registers[data[1]] = registers[data[1]] >> dbytes(data[2], data[3]); | |
delta = 4; | |
} | |
break; | |
case 22: | |
if (data[0] >= 128) { | |
registers[data[1]] = random(registers[data[2]]); | |
delta = 3; | |
} else { | |
registers[data[1]] = random(dbytes(data[2], data[3])); | |
delta = 4; | |
} | |
break; | |
case 23: | |
if (data[0] >= 128) { | |
for (int i = 0; i < registers[data[2]]; i++) | |
getc16(registers[data[1] + i]); | |
delta = 3; | |
} else { | |
for (int i = 0; i < dbytes(data[2], data[3]); i++) | |
getc16(registers[data[1] + i]); | |
delta = 4; | |
} | |
break; | |
case 24: | |
if (data[0] >= 128) { | |
for (int i = 0; i < registers[data[2]]; i++) | |
getc8(registers[data[1] + i]); | |
delta = 3; | |
} else { | |
for (int i = 0; i < dbytes(data[2], data[3]); i++) | |
getc8(registers[data[1] + i]); | |
delta = 4; | |
} | |
break; | |
case 25: | |
if (data[0] >= 128) { | |
for (int i = 0; i < registers[data[2]]; i++) | |
setc16(registers[data[1] + i]); | |
delta = 3; | |
} else { | |
for (int i = 0; i < dbytes(data[2], data[3]); i++) | |
setc16(registers[data[1] + i]); | |
delta = 4; | |
} | |
break; | |
case 26: | |
if (data[0] >= 128) { | |
for (int i = 0; i < registers[data[2]]; i++) | |
setc8(registers[data[1] + i]); | |
delta = 3; | |
} else { | |
for (int i = 0; i < dbytes(data[2], data[3]); i++) | |
setc8(registers[data[1] + i]); | |
delta = 4; | |
} | |
break; | |
case 27: | |
if (work_file.available()) { | |
if (data[0] >= 128) { | |
for (int i = 0; i < registers[data[2]]; i++) | |
getf16(registers[data[1] + i]); | |
delta = 3; | |
} else { | |
for (int i = 0; i < dbytes(data[2], data[3]); i++) | |
getf16(registers[data[1] + i]); | |
delta = 4; | |
} | |
} else | |
return end_of_file; | |
break; | |
case 28: | |
if (work_file.available()) { | |
if (data[0] >= 128) { | |
for (int i = 0; i < registers[data[2]]; i++) | |
getf8(registers[data[1] + i]); | |
delta = 3; | |
} else { | |
for (int i = 0; i < dbytes(data[2], data[3]); i++) | |
getf8(registers[data[1] + i]); | |
delta = 4; | |
} | |
} else | |
return end_of_file; | |
break; | |
case 29: | |
if (data[0] >= 128) { | |
for (int i = 0; i < registers[data[2]]; i++) | |
setf16(registers[data[1] + i]); | |
delta = 3; | |
} else { | |
for (int i = 0; i < dbytes(data[2], data[3]); i++) | |
setf16(registers[data[1] + i]); | |
delta = 4; | |
} | |
break; | |
case 30: | |
if (data[0] >= 128) { | |
for (int i = 0; i < registers[data[2]]; i++) | |
setf8(registers[data[1] + i]); | |
delta = 3; | |
} else { | |
for (int i = 0; i < dbytes(data[2], data[3]); i++) | |
setf8(registers[data[1] + i]); | |
delta = 4; | |
} | |
break; | |
case 31: | |
if (data[0] >= 128) { | |
registers[data[1]] = digitalRead(registers[data[2]]); | |
delta = 3; | |
} else { | |
registers[data[1]] = digitalRead(dbytes(data[2], data[3])); | |
delta = 4; | |
} | |
break; | |
case 32: | |
if (data[0] >= 128) { | |
registers[data[1]] = analogRead(registers[data[2]]); | |
delta = 3; | |
} else { | |
registers[data[1]] = analogRead(dbytes(data[2], data[3])); | |
delta = 4; | |
} | |
break; | |
case 33: | |
if (data[0] >= 128) { | |
digitalWrite(registers[data[1]], registers[data[2]]); | |
delta = 3; | |
} else { | |
digitalWrite(registers[data[1]], dbytes(data[2], data[3])); | |
delta = 4; | |
} | |
break; | |
case 34: | |
if (data[0] >= 128) { | |
analogWrite(registers[data[1]], registers[data[2]]); | |
delta = 3; | |
} else { | |
analogWrite(registers[data[1]], dbytes(data[2], data[3])); | |
delta = 4; | |
} | |
break; | |
case 35: | |
if (data[0] >= 128) { | |
digitalWrite(registers[data[2]], registers[data[1]]); | |
delta = 3; | |
} else { | |
digitalWrite(dbytes(data[2], data[3]), registers[data[1]]); | |
delta = 4; | |
} | |
break; | |
case 36: | |
if (data[0] >= 128) { | |
analogWrite(registers[data[2]], registers[data[1]]); | |
delta = 3; | |
} else { | |
analogWrite(dbytes(data[2], data[3]), registers[data[1]]); | |
delta = 4; | |
} | |
break; | |
case 37: | |
if (data[0] >= 128) { | |
cursorX = registers[data[1]] * LCD.getFontXsize(); | |
delta = 3; | |
} else { | |
cursorX = dbytes(data[1], data[2]) * LCD.getFontXsize(); | |
delta = 4; | |
} | |
break; | |
case 38: | |
if (data[0] >= 128) { | |
cursorY = registers[data[1]] * LCD.getFontYsize(); | |
delta = 3; | |
} else { | |
cursorY = dbytes(data[1], data[2]) * LCD.getFontYsize(); | |
delta = 4; | |
} | |
break; | |
case 39: | |
if (data[0] >= 128) { | |
LCD.setBackColor(VGA_COLORS[(registers[data[1]] >> 8) % 16]); | |
LCD.setColor(VGA_COLORS[((registers[data[1]] << 8) >> 8) % 16]); | |
delta = 3; | |
} else { | |
LCD.setBackColor(VGA_COLORS[(dbytes(data[1], data[2]) >> 8) % 16]); | |
LCD.setColor(VGA_COLORS[((dbytes(data[1], data[2]) << 8) >> 8) % 16]); | |
delta = 4; | |
} | |
break; | |
case 40: | |
if (data[0] >= 128) { | |
pinMode(registers[data[1]], OUTPUT); | |
delta = 3; | |
} else { | |
pinMode(dbytes(data[1], data[2]), OUTPUT); | |
delta = 4; | |
} | |
break; | |
case 41: | |
if (data[0] >= 128) { | |
pinMode(registers[data[1]], INPUT); | |
delta = 3; | |
} else { | |
pinMode(dbytes(data[1], data[2]), INPUT); | |
delta = 4; | |
} | |
break; | |
case 42: | |
if (data[0] >= 128) { | |
Serial.write(registers[data[1]]); | |
delta = 3; | |
} else { | |
Serial.write(dbytes(data[1], data[2])); | |
delta = 4; | |
} | |
break; | |
case 43: | |
registers[data[1]] = Serial.read(); | |
delta = 2; | |
break; | |
case 44: | |
registers[data[1]] = registers[data[1]] + 1; | |
delta = 2; | |
break; | |
case 45: | |
registers[data[1]] = registers[data[1]] - 1; | |
delta = 2; | |
break; | |
case 46: | |
for (int8_t i = 0; i < 256; i++) { | |
work_file_name[i] = registers[data[1] + i]; | |
if (registers[data[1] + i] == 0) | |
break; | |
} | |
if (!SD.exists(work_file_name)) | |
return file_not_exist; | |
work_file = SD.open(work_file_name, FILE_READ); | |
if (!work_file) | |
return opening_error; | |
delta = 2; | |
break; | |
case 47: | |
for (int8_t i = 0; i < 256; i++) { | |
work_file_name[i] = registers[data[1] + i]; | |
if (registers[data[1] + i] == 0) | |
break; | |
} | |
if (SD.exists(work_file_name) && !SD.remove(work_file_name)) | |
return rewrite_error; | |
work_file = SD.open(work_file_name, FILE_WRITE); | |
if (!work_file) | |
return opening_error; | |
delta = 2; | |
break; | |
case 48: | |
for (int8_t i = 0; i < 256; i++) { | |
work_file_name[i] = registers[data[1] + i]; | |
if (registers[data[1] + i] == 0) | |
break; | |
} | |
work_file = SD.open(work_file_name, FILE_WRITE); | |
if (!work_file) | |
return opening_error; | |
delta = 2; | |
break; | |
case 49: | |
for (int8_t i = 0; i < 256; i++) { | |
work_file_name[i] = registers[data[1] + i]; | |
if (registers[data[1] + i] == 0) | |
break; | |
} | |
SD.mkdir(work_file_name); | |
break; | |
case 50: | |
for (int8_t i = 0; i < 256; i++) { | |
work_file_name[i] = registers[data[1] + i]; | |
if (registers[data[1] + i] == 0) | |
break; | |
} | |
SD.rmdir(work_file_name); | |
break; | |
case 51: | |
for (int8_t i = 0; i < 256; i++) { | |
work_file_name[i] = registers[data[1] + i]; | |
if (registers[data[1] + i] == 0) | |
break; | |
} | |
SD.remove(work_file_name); | |
break; | |
case 52: | |
if (registers[data[1]] == 0) | |
exe_file.seek(dbytes(data[2], data[3])); | |
else | |
delta = 4; | |
break; | |
case 53: | |
if (registers[data[1]] != 0) | |
exe_file.seek(dbytes(data[2], data[3])); | |
else | |
delta = 4; | |
break; | |
case 54: | |
if (registers[data[1]] > 0) | |
exe_file.seek(dbytes(data[2], data[3])); | |
else | |
delta = 4; | |
break; | |
case 55: | |
if (registers[data[1]] < 0) | |
exe_file.seek(dbytes(data[2], data[3])); | |
else | |
delta = 4; | |
break; | |
case 56: | |
exe_file.seek(dbytes(data[1], data[2])); | |
break; | |
case 57: | |
if (call == 255) | |
return stack_overflow; | |
call_stack[call] = exe_file.position(); | |
exe_file.seek(dbytes(data[1], data[2])); | |
call++; | |
break; | |
default: | |
return data[0]; | |
break; | |
} | |
return exe_ok; | |
} | |
void printDirectory(File dir, int numTabs) { | |
while (true) { | |
File entry = dir.openNextFile(); | |
if (! entry) | |
break; | |
for (int8_t i = 0; i < numTabs; i++) | |
setc8('-'); | |
echo(entry.name(), strlen(entry.name())); | |
if (entry.isDirectory()) { | |
setc8('/'); | |
setc8('\n'); | |
printDirectory(entry, numTabs + 1); | |
} else { | |
echo(" ", 4); | |
setc16(entry.size()); | |
setc8('\n'); | |
} | |
entry.close(); | |
} | |
} | |
void nano() { | |
File edit; | |
char text_buf[128][16], t; | |
for (uint8_t ti = 0; ti <= 127; ti++) | |
for (uint8_t tk = 0; tk <= 15; tk++) | |
text_buf[ti][tk] = 0; | |
int8_t i = 0, k = 0, page = 0;//i-тая строка, k-ый символ | |
if (SD.exists(exe_file_name)) { | |
edit = SD.open(exe_file_name, FILE_READ); | |
while (edit.available() && i <= 127) { | |
text_buf[i][k] = edit.read(); | |
if (text_buf[i][k] == '\n' || text_buf[i][k] == 0) { | |
text_buf[i][k] = 0; | |
k = 0; | |
i++; | |
} else if (text_buf[i][k] == '\r') | |
text_buf[i][k] = 0; | |
else | |
k++; | |
if (k == 15) { | |
text_buf[i][k] = 0; | |
k = 0; | |
i++; | |
} | |
} | |
edit.close(); | |
} | |
cursorX = 0; | |
cursorY = LCD.getFontYsize(); | |
i = 0; | |
k = 0; | |
bool refresh = true; | |
while (t != PS2_ESC) { | |
if (k >= 15) { | |
text_buf[page * 16 + i][15] = 0; | |
k = 0; | |
i++; | |
} | |
if (k < 0) { | |
k = 0; | |
i--; | |
} | |
if (i > 15 || i < 0 || refresh) { | |
if (i < 0) | |
page--; | |
else if (i > 15) | |
page++; | |
if (!refresh) { | |
k = 0; | |
i = 0; | |
} | |
refresh = false; | |
if (page > 15) | |
page = 15; | |
if (page < 0) | |
page = 0; | |
LCD.clrScr(); | |
cursorX = 0; | |
cursorY = 0; | |
echo("ESC-exit TAB-save PAGE=", 23); | |
setc16(page); | |
setc8('\n'); | |
setc8('\n'); | |
for (uint8_t x = 0; x <= 15; x++) | |
for (uint8_t y = 0; y <= 15; y++) | |
if (text_buf[page * 16 + x][y] == 0) { | |
setc8('\n'); | |
y = 16; | |
} | |
else | |
setc8(text_buf[page * 16 + x][y]); | |
} | |
cursorX = k * LCD.getFontXsize(); | |
cursorY = (i + 2) * LCD.getFontYsize(); | |
setc8('_'); | |
cursorX -= LCD.getFontXsize(); | |
while (!keyboard.available()); | |
t = keyboard.read(); | |
if (t == PS2_TAB) { | |
SD.remove(exe_file_name); | |
edit = SD.open(exe_file_name, FILE_WRITE); | |
for (uint8_t ti = 0; ti <= 127; ti++) | |
for (uint8_t tk = 0; tk <= 15; tk++) | |
if (text_buf[ti][tk] != 0) | |
edit.write(text_buf[ti][tk]); | |
else { | |
if (tk != 0 || text_buf[ti - 1][0] != 0) | |
edit.write('\n'); | |
tk = 16; | |
} | |
edit.close(); | |
} else if (t == PS2_ENTER) { | |
setc8(text_buf[page * 16 + i][k]); | |
i++; | |
if (text_buf[127][0] == 0) { | |
for (int8_t ti = 127; ti >= i + 1; ti--) | |
for (int8_t tk = 15; tk >= 0; tk--) | |
text_buf[ti][tk] = text_buf[ti - 1][tk]; | |
for (int8_t tk = 0; tk < 15 - k; tk++) { | |
text_buf[i][tk] = text_buf[i - 1][k + tk + 1]; | |
text_buf[i - 1][k + tk + 1] = 0; | |
} | |
for (int8_t tk = 15 - k; tk <= 15; tk++) | |
text_buf[i][tk] = 0; | |
refresh = true; | |
} | |
k = 0; | |
} else if (t == PS2_DELETE){ | |
if (text_buf[page * 16 + i][0] == 0) { | |
for (int8_t ti = i; ti <= 126; ti++) | |
for (int8_t tk = 0; tk <= 15; tk++) | |
text_buf[ti][tk] = text_buf[ti + 1][tk]; | |
for (int8_t tk = 0; tk <= 15; tk++) | |
text_buf[127][tk] = 0; | |
refresh = true; | |
} else if (k > 0) { | |
cursorX -= LCD.getFontXsize(); | |
for (int8_t tk = k; tk <= 15; tk++) { | |
text_buf[i][tk - 1] = text_buf[i][tk]; | |
setc8(text_buf[i][tk]); | |
} | |
k--; | |
} | |
} else if (t == PS2_RIGHTARROW){ | |
setc8(text_buf[page * 16 + i][k]); | |
if (text_buf[page * 16 + i][k] != 0) { | |
k++; | |
} else { | |
k = 0; | |
i++; | |
} | |
} else if (t == PS2_LEFTARROW){ | |
setc8(text_buf[page * 16 + i][k]); | |
k--; | |
} else if (t == PS2_DOWNARROW){ | |
setc8(text_buf[page * 16 + i][k]); | |
i++; | |
k = 0; | |
} else if (t == PS2_UPARROW){ | |
setc8(text_buf[page * 16 + i][k]); | |
i--; | |
k = 0; | |
} else if (t == PS2_PAGEDOWN){ | |
i = 16; | |
k = 0; | |
} else if (t == PS2_PAGEUP){ | |
i = -1; | |
k = 0; | |
} else if (text_buf[i][14] == 0) { | |
for (int8_t tk = 14; tk > k; tk--) | |
text_buf[i][tk] = text_buf[i][tk - 1]; | |
text_buf[page * 16 + i][k] = t; | |
for (int8_t tk = k; tk < 15; tk++) | |
setc8(text_buf[page * 16 + i][tk]); | |
k++; | |
} | |
} | |
LCD.clrScr(); | |
cursorX = 0; | |
cursorY = 0; | |
} | |
void setup() { | |
pinMode(pin_SD, OUTPUT); | |
pinMode(pin_BL, OUTPUT); | |
LCD.InitLCD(); | |
LCD.clrScr(); | |
LCD.setFont(BigFont); | |
LCD.setColor(VGA_COLORS[10]); | |
analogWrite(pin_BL, BL); | |
echoln("MicConOS 0.8b", 13); | |
if (!rtc.begin()) | |
echoln("RTC fail", 8); | |
if (rtc.lostPower()) | |
rtc.adjust(DateTime(__DATE__, __TIME__)); | |
keyboard.begin(ps2key_data_pin, ps2key_int_pin); | |
if (!SD.begin(pin_SD)) | |
echoln("SD fail", 7); | |
#if (__AVR__) | |
File root = SD.open("/");//без этих двух строк не работает freeMemory() | |
root.close(); | |
#endif | |
} | |
void loop() { | |
echo("/", 1); | |
getstr(exe_file_name); | |
if (strlen(exe_file_name) > 0) | |
if (!strcmp(exe_file_name, "ls")) { | |
File root = SD.open("/"); | |
printDirectory(root, 0); | |
root.close(); | |
} else if (!strcmp(exe_file_name, "uptime")) { | |
setc16(millis() / 1000); | |
echoln(" s", 2); | |
} else if (!strcmp(exe_file_name, "time")) { | |
itoa(rtc.now().hour(), sTime, 10); | |
echo(sTime, 2); | |
echo(":", 1); | |
itoa(rtc.now().minute(), sTime, 10); | |
echoln(sTime, 2); | |
} else if (!strcmp(exe_file_name, "ram")) { | |
setc16(freeMemory()); | |
echoln(" bytes free RAM", 15); | |
} else if (!strcmp(exe_file_name, "nano")) { | |
getstr(exe_file_name); | |
nano(); | |
} else if (!strcmp(exe_file_name, "mkdir")) { | |
getstr(exe_file_name); | |
SD.mkdir(exe_file_name); | |
} else if (!strcmp(exe_file_name, "rmdir")) { | |
getstr(exe_file_name); | |
SD.rmdir(exe_file_name); | |
} else if (!strcmp(exe_file_name, "mk")) { | |
File temp = SD.open(exe_file_name, FILE_WRITE); | |
temp.close(); | |
} else if (!strcmp(exe_file_name, "rm")) { | |
getstr(exe_file_name); | |
SD.remove(exe_file_name); | |
} else if (!strcmp(exe_file_name, "+")) { | |
BL += 8; | |
analogWrite(pin_BL, BL); | |
} else if (!strcmp(exe_file_name, "-")) { | |
BL -= 8; | |
analogWrite(pin_BL, BL); | |
} else if (!strcmp(exe_file_name, "color")) { | |
getstr(exe_file_name); | |
LCD.setColor(VGA_COLORS[atoi(exe_file_name)]); | |
getstr(exe_file_name); | |
LCD.setBackColor(VGA_COLORS[atoi(exe_file_name)]); | |
} else if (!SD.exists(exe_file_name)) | |
echoln("File not exist or bad command", 29); | |
else { | |
exe_file = SD.open(exe_file_name, FILE_READ); | |
if (exe_file) { | |
while (exe_file.available()) { | |
for (int8_t i = 0; i < 4; i++) | |
if (exe_file.available()) | |
data[i] = exe_file.read(); | |
else | |
data[i] = 0; | |
exe_code = exe(); | |
if (exe_code == exe_end) | |
break; | |
else if (exe_code != exe_ok) { | |
setc16(exe_code); | |
echoln(" runtime error", 14); | |
break; | |
} | |
if (delta != 0 && delta != 4) | |
exe_file.seek(exe_file.position() - (4 - delta)); | |
} | |
exe_file.close(); | |
} else { | |
echo(exe_file_name, strlen(exe_file_name)); | |
echoln(" error opening", 14); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
- исходный код программы пишется на assembler-like языке и транслируется (ассемблером, макропроцессором) в объектный файл, содержащий интерпретируемый байт-код | |
- среда исполнения (интерпретации) байт-кода представляет собой регистровую виртуальную машину (подобную Java, Dalvik, .Net) | |
- при выполнении программы используется массив переменных (регистры по своей сути) и стек вызовов | |
- размер стека вызовов - 254, количество вложенных подпрограмм во время выполнения не должно превышать это количество | |
- имена переменных и меток должны начинаться с маленькой или большой латинской буквы и состоять из букв, цифр и символа "_" | |
- все переменные глобальны, всего uint8_max = 256 переменных (регистров) | |
- все переменные имеют тип int16, объявляются как массив размерности uint8 > 0 с мусорными начальными значениями | |
- индексация массивов с 0 | |
- обращение к массиву без указания номера элемента эквивалентна указанию нулевого уэлемента | |
- однострочный комментарий после ';' | |
- длинна "машинного слова" 8 бит | |
- первый бит в команде - тип аргумента, остальные 7 - опкод операции | |
- всего возможно 2^7 = 128 команды (RISC-принцип), из них используется 57, свободно 71 | |
- типы аргументов в байт-коде: | |
0 - uint16 / метка / null (2 или 0 байт) | |
1 - переменная (1 байт) | |
- int16 передаются и хранятся в байт-коде с порядком big-endian | |
- при выводе символов используется ASCII кодировка, учитывается только младший байт int16-числа | |
- файловая система - fat16 (или fat32, с теми же ограничениями, что и fat16) | |
- имена файлов записываются массивом (с обязательным нуль терминатором) в нотации 8.3, каталоги и файлы разграничиваются символом '/' | |
- рабочий каталог - всегда корневой каталог SD-карты, его можно не указывать при обращении к файлу | |
- в качестве символа переноса строки используется '\n', все строки обязательно оканчиваются нуль терминатором | |
- подпрограмма начинается с (prс) и заканчивается (ret) | |
- условный (if*) и безусловный (jmp) переход по метке отличается от вызова подпрограммы (prc) тем, что не вносит текущий адрес в стек вызовов | |
- отсутствует проверка подпрограмма-метка, т.е. "безусловный переход по подпрограмме" и "вызов метки" не вызовут никаких ошибок | |
- интерпретатор использует 16тибитную адресацию, размер итогового объектного файла или файлов, с которыми работает программа не должен превышать 2^16=65536 байт (исполняемые файлы могут быть больше этого размера (но не поддерживаются транслятором), однако переход по меткам и вызов подпрограмм из/в сверх этого размера будет невозможен) | |
- команды в исходном коде записываются в виде | |
OPERATION {ARG_1{[X]} {ARG_2{[Y]}}} {;COMMENT} | |
где | |
OPERATION - имя команды | |
ARG_1 - первый аргумент команды | |
ARG_2 - второй аргумент операции | |
X, Y - индекс элемента при работе с массивами | |
COMMENT - однострочный комментарий, необязателен | |
- список свободных пинов | |
Digital: 0 1 14 15 16 17 18 19 42 43 44 45 46 47 48 49 53 | |
PWM: 8 9 10 11 12 13 | |
Analog in: 54 55 56 57 58 59 60 61 62 63 64 65 | |
I2C: 20 21 | |
Other: 66 67 68 69 | |
Due only: 50 51 52 | |
TODO | |
- ассемблер (asm) | |
COMMANDS | |
- ls - список файлов на SD карте | |
- ram - количество свободной ОЗУ | |
- uptime- количество секунд непрерывной работы МК | |
- time - системное время | |
- mkdir - создать каталог | |
- rmdir - удалить каталог | |
- mk - создать файл | |
- rm - удалить файл | |
- nano - текстовый редоактор | |
- + - увеличить яркость подсветки | |
- - - уменьшить яркость подсветки | |
- file - запуск файла с расположением "/file" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment