From: Jason Self Date: Sun, 23 Aug 2020 15:36:28 +0000 (-0700) Subject: Added error messages displaying using source file name and line number X-Git-Url: https://jxself.org/git/?a=commitdiff_plain;h=633e24778bccbc5f035bdb8516e8cc75c85123d9;p=zilutils.git Added error messages displaying using source file name and line number Added .END and .INSERT directives --- diff --git a/zilasm/Makefile.am b/zilasm/Makefile.am index 5faa8ef..06a834b 100644 --- a/zilasm/Makefile.am +++ b/zilasm/Makefile.am @@ -2,7 +2,7 @@ # Process this file with automake to produce Makefile.in. # # Copyright (C) 2015 Alexander Andrejevic -# Copyright (C) 2019 Jason Self +# Copyright (C) 2019, 2020 Jason Self # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -21,5 +21,5 @@ bin_PROGRAMS = zilasm man_MANS = zilasm.1 -zilasm_SOURCES = main.cpp opcodes.c symtable.c header.cpp parser.cpp directives.c labels.cpp string_table.cpp zmem.c +zilasm_SOURCES = main.cpp opcodes.c symtable.c header.cpp parser.cpp directives.cpp labels.cpp string_table.cpp zmem.c include_HEADERS = opcodes.h symtable.h header.h parser.h directives.h labels.h string_table.h zmem.h diff --git a/zilasm/directives.c b/zilasm/directives.c deleted file mode 100644 index 7c4eb19..0000000 --- a/zilasm/directives.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - * directives.c -- part of ZilUtils/ZilAsm - * - * Copyright (C) 2016 Jason Self - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see - * - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -#include /* bsearch */ -#include /* strcmp */ - -#include "directives.h" - -#define ARRAY_SIZE(x) ((sizeof(x)) / (sizeof(x[0]))) - -static int -byte_handler (const char *args) -{ - /* !!! TODO !!! */ - return 0; -} - -static int -end_handler (const char *args) -{ - /* !!! TODO !!! */ - return 0; -} - -static int -endi_handler (const char *args) -{ - /* !!! TODO !!! */ - return 0; -} - -static int -endt_handler (const char *args) -{ - /* !!! TODO !!! */ - return 0; -} - -static int -fstr_handler (const char *args) -{ - /* !!! TODO !!! */ - return 0; -} - -static int -funct_handler (const char *args) -{ - /* !!! TODO !!! */ - return 0; -} - -static int -gstr_handler (const char *args) -{ - /* !!! TODO !!! */ - return 0; -} - -static int -gvar_handler (const char *args) -{ - /* !!! TODO !!! */ - return 0; -} - -static int -insert_handler (const char *args) -{ - /* !!! TODO !!! */ - return 0; -} - -static int -len_handler (const char *args) -{ - /* !!! TODO !!! */ - return 0; -} - -static int -new_handler (const char *args) -{ - /* !!! TODO !!! */ - return 0; -} - -static int -object_handler (const char *args) -{ - /* !!! TODO !!! */ - return 0; -} - -static int -prop_handler (const char *args) -{ - /* !!! TODO !!! */ - return 0; -} - -static int -str_handler (const char *args) -{ - /* !!! TODO !!! */ - return 0; -} - -static int -strl_handler (const char *args) -{ - /* !!! TODO !!! */ - return 0; -} - -static int -table_handler (const char *args) -{ - /* !!! TODO !!! */ - return 0; -} - -static int -vocbeg_handler (const char *args) -{ - /* !!! TODO !!! */ - return 0; -} - -static int -vocend_handler (const char *args) -{ - /* !!! TODO !!! */ - return 0; -} - -static int -word_handler (const char *args) -{ - /* !!! TODO !!! */ - return 0; -} - -static int -zword_handler (const char *args) -{ - /* !!! TODO !!! */ - return 0; -} - -// Sorted array -static Directive Directives[] = { - "BYTE", byte_handler, - "END", end_handler, - "ENDI", endi_handler, - "ENDT", endt_handler, - "FSTR", fstr_handler, - "FUNCT", funct_handler, - "GSTR", gstr_handler, - "GVAR", gvar_handler, - "INSERT", insert_handler, - "LEN", len_handler, - "NEW", new_handler, - "OBJECT", object_handler, - "PROP", prop_handler, - "STR", str_handler, - "STRL", strl_handler, - "TABLE", table_handler, - "VOCBEG", vocbeg_handler, - "VOCEND", vocend_handler, - "WORD", word_handler, - "ZWORD", zword_handler -}; - -typedef struct -{ - const char *contents; - unsigned length; -} Name; - -static int -namecmp (const void *key, const void *elem) -{ - const Name *p = (Name *) key; - const Directive *d = (Directive *) elem; - - int len1 = p->length; - int len2 = strlen (d->name); - - int rc = memcmp (p->contents, elem, len1 < len2 ? len1 : len2); - return rc ? rc : (len1 - len2); -} - -Directive_handler -directive_lookup (const char *name, unsigned namelen) -{ - Name n = { name, namelen }; - Directive *p = - (Directive *) bsearch (&n, Directives, ARRAY_SIZE (Directives), - sizeof (Directive), namecmp); - return p ? p->handler : NULL; -} diff --git a/zilasm/directives.cpp b/zilasm/directives.cpp new file mode 100644 index 0000000..d15ba89 --- /dev/null +++ b/zilasm/directives.cpp @@ -0,0 +1,284 @@ +/* + * directives.c -- part of ZilUtils/ZilAsm + * + * Copyright (C) 2016, 2020 Jason Self + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include /* bsearch */ +#include /* strcmp */ +#include +#include +#include +using namespace std; + +#include "header.h" +#include "parser.h" +#include "directives.h" + +#define ARRAY_SIZE(x) ((sizeof(x)) / (sizeof(x[0]))) + +int g_stopParsing = 0; +stack g_fileHandlers; + + + +static int +byte_handler (const char *args) +{ + /* !!! TODO !!! */ + return 0; +} + +static int +end_handler (const char *args) +{ + g_stopParsing = 1; + return 0; +} + +static int +endi_handler (const char *args) +{ + /* !!! TODO !!! */ + return 0; +} + +static int +endt_handler (const char *args) +{ + /* !!! TODO !!! */ + return 0; +} + +static int +fstr_handler (const char *args) +{ + /* !!! TODO !!! */ + return 0; +} + +static int +funct_handler (const char *args) +{ + /* !!! TODO !!! */ + return 0; +} + +static int +gstr_handler (const char *args) +{ + /* !!! TODO !!! */ + return 0; +} + +static int +gvar_handler (const char *args) +{ + /* !!! TODO !!! */ + return 0; +} + +static int +insert_handler (const char *args) +{ + string file_name; + char *p = (char*)args; + while (*p == ' ') p++; + + if (*p == '"') + { + p++; + do + { + file_name += *p; + p++; + } + while (*p != '"' && *p != 0 ) ; + } + else + { + do + { + file_name += *p; + p++; + } while (*p != ' ' && *p != '\n' && *p != '\r' && *p != '\t' && *p != 0); + } + + if (file_name.find('.') == string::npos) + { + file_name += ".zap"; + } + +#ifdef WIN32 + char delimeter = '\\'; +#else + char delimeter = '/'; +#endif + + Parsing_Context pc; + if (file_name.rfind(delimeter) == string::npos) + { + pc.current_directory = ""; + pc.current_file_name = file_name; + } + else + { + pc.current_directory = file_name.substr(0, file_name.rfind(delimeter)+1); + pc.current_file_name = file_name.substr(file_name.rfind(delimeter)+1); + } + + pc.current_directory = g_parsing_contexts.top().current_directory + pc.current_directory; + //pc.current_file_name = s; + g_parsing_contexts.push(pc); + + + unsigned saveLineNumber = g_currentLineNumber; + parse_file(); //s.c_str()); + g_currentLineNumber = saveLineNumber; + return strlen(args); +} + +static int +len_handler (const char *args) +{ + /* !!! TODO !!! */ + return 0; +} + +static int +newdirective_handler (const char *args) +{ + /* !!! TODO !!! */ + return 0; +} + +static int +object_handler (const char *args) +{ + /* !!! TODO !!! */ + return 0; +} + +static int +prop_handler (const char *args) +{ + /* !!! TODO !!! */ + return 0; +} + +static int +str_handler (const char *args) +{ + /* !!! TODO !!! */ + return 0; +} + +static int +strl_handler (const char *args) +{ + /* !!! TODO !!! */ + return 0; +} + +static int +table_handler (const char *args) +{ + /* !!! TODO !!! */ + return 0; +} + +static int +vocbeg_handler (const char *args) +{ + /* !!! TODO !!! */ + return 0; +} + +static int +vocend_handler (const char *args) +{ + /* !!! TODO !!! */ + return 0; +} + +static int +word_handler (const char *args) +{ + /* !!! TODO !!! */ + return 0; +} + +static int +zword_handler (const char *args) +{ + /* !!! TODO !!! */ + return 0; +} + +// Sorted array +static Directive Directives[] = { + "BYTE", byte_handler, + "END", end_handler, + "ENDI", endi_handler, + "ENDT", endt_handler, + "FSTR", fstr_handler, + "FUNCT", funct_handler, + "GSTR", gstr_handler, + "GVAR", gvar_handler, + "INSERT", insert_handler, + "LEN", len_handler, + "NEW", newdirective_handler, + "OBJECT", object_handler, + "PROP", prop_handler, + "STR", str_handler, + "STRL", strl_handler, + "TABLE", table_handler, + "VOCBEG", vocbeg_handler, + "VOCEND", vocend_handler, + "WORD", word_handler, + "ZWORD", zword_handler +}; + +typedef struct +{ + const char *contents; + unsigned length; +} Name; + +static int +namecmp (const void *key, const void *elem) +{ + const Name *p = (Name *) key; + const Directive *d = (Directive *) elem; + + int len1 = p->length; + int len2 = strlen (d->name); + + int rc = memcmp (p->contents, elem, len1 < len2 ? len1 : len2); + return rc ? rc : (len1 - len2); +} + +Directive_handler +directive_lookup (const char *name, unsigned namelen) +{ + Name n = { name, namelen }; + Directive *p = + (Directive *) bsearch (&n, Directives, ARRAY_SIZE (Directives), + sizeof (Directive), namecmp); + return p ? p->handler : NULL; +} diff --git a/zilasm/directives.h b/zilasm/directives.h index 5988c0a..b1ddd23 100644 --- a/zilasm/directives.h +++ b/zilasm/directives.h @@ -1,7 +1,7 @@ /* * directives.h -- part of ZilUtils/ZilAsm * - * Copyright (C) 2016 Jason Self + * Copyright (C) 2016, 2020 Jason Self * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -31,5 +31,7 @@ typedef struct } Directive; Directive_handler directive_lookup (const char *name, unsigned namelen); +extern int g_stopParsing; +extern stack g_fileHandlers; #endif /* ifndef ZILASM_DIRECTIVES */ diff --git a/zilasm/main.cpp b/zilasm/main.cpp index 22b7e71..68846b0 100644 --- a/zilasm/main.cpp +++ b/zilasm/main.cpp @@ -2,7 +2,7 @@ * main.c * * Copyright (C) 2015 Alexander Andrejevic - * Copyright (C) 2015, 2019 Jason Self + * Copyright (C) 2015, 2019, 2020 Jason Self * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -24,6 +24,12 @@ #include #include #include +#include +#include +#include +using namespace std; + + extern "C" { #include @@ -39,9 +45,7 @@ extern "C" } #include "parser.h" -#include -#include -using namespace std; + const int DEFAULT_ZVERSION = 6; @@ -143,7 +147,7 @@ CMain::assembly () ++m_code_size; // write instructions' codes - for (int i = 0; i < g_numberOfInstructions; ++i) + for (int i = 0; i < g_number_of_instructions; ++i) { for (int j = 0; j < g_codes[i]->used_size; ++j) { @@ -424,9 +428,33 @@ main (int argc, char *argv[], char *envp[]) init_parser (); - for (int i = optind; i < argc; i++) - parse_file (argv[i]); - main.assembly (); + //for (int i = optind; i < argc; i++) + // parse_file(argv[i]); + + string file_name = argv[optind]; + Parsing_Context pc; + +#ifdef WIN32 + char delimeter = '\\'; +#else + char delimeter = '/'; +#endif + + if (file_name.rfind(delimeter) == string::npos) + { + pc.current_directory = ""; + pc.current_file_name = file_name; + } + else + { + pc.current_directory = file_name.substr(0, file_name.rfind(delimeter)+1); + pc.current_file_name = file_name.substr(file_name.rfind(delimeter)+1); + } + g_parsing_contexts.push(pc); + + parse_file();// argv[optind]); + if ( !g_haveErrors ) + main.assembly (); /* TODO! List global symbols */ /* TODO! Find abbreviations */ diff --git a/zilasm/parser.cpp b/zilasm/parser.cpp index 31e257f..b819792 100644 --- a/zilasm/parser.cpp +++ b/zilasm/parser.cpp @@ -1,7 +1,7 @@ /* * parser.c -- part of ZilUtils/ZilAsm * - * Copyright (C) 2016, 2019 Jason Self + * Copyright (C) 2016, 2019, 2020 Jason Self * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -23,15 +23,16 @@ #include /* strlen */ #include #include +#include using namespace std; #include "header.h" #include "parser.h" +#include "directives.h" extern "C" { -#include "directives.h" #include "opcodes.h" } #include "labels.h" @@ -42,21 +43,45 @@ extern "C" #define isbindigit(c) ((c) == '0' || (c) == '1') /* !!! TODO !!! */ -#define fatal_error(errmsg) printf(errmsg) +//#define fatal_error(errmsg) printf(errmsg) #define PC NULL -unsigned g_numberOfInstructions = 0; +unsigned g_number_of_instructions = 0; + +unsigned g_currentLineNumber = 0; +//string g_source_directory; +bool g_haveErrors = false; + +stack g_parsing_contexts; ZMemblock (*g_codes[MAX_NUMBER_OF_INSTRUCTIONS]); +string +build_error_message(const char *message) +{ + char buff[300]; + sprintf(buff, "error at %s%s line %d: %s", g_parsing_contexts.top().current_directory.c_str(), + g_parsing_contexts.top().current_file_name.c_str(), g_currentLineNumber, message); + return string(buff); +} + + +void +fatal_error(const char *errmsg) +{ + printf( "%s\n", errmsg); + g_haveErrors = true; +} + + void checksep (const char *p) { if (!*p || iscomment (*p) || isspace (*p)) return; - fatal_error ("wrong chars"); + fatal_error (build_error_message ("wrong chars").c_str()); } @@ -91,7 +116,7 @@ tryparse_directive (const char *p) Directive_handler f = directive_lookup (a, b - a); if (!f) return 0; - return (*f) (b); + return (b-a) + (*f) (b); } @@ -115,7 +140,7 @@ tryparse_label (const char *a, const char *b, const char *c) } else { - fatal_error ("wrong label type"); + fatal_error (build_error_message("wrong label type").c_str()); } while (*c++ == ':'); @@ -144,72 +169,96 @@ tryparse_name (const char *a) int tryparse_instruction (const char *a) { - const char *b = pass_alnums (a); - if (b != a) - { - int len = b ? b - a : strlen (a); - ZOpcode *op = (ZOpcode *) symtable_lookup2 (Opcodes, a, len); - if (!op) - return 0; - ZOpcode_flags flags = op->flags; - /* !!! TODO !!! */ - - - ZMemblock *mem_additional = NULL; - - switch (op->opcode) + bool display_error = false; + int len = 0; + const char *b = pass_alnums(a); + if (b != a) { - case Opcode_CRLF: - case Opcode_PRINT: - case Opcode_QUIT: - break; - - case Opcode_PRINTI: - case Opcode_PRINTR: - { - char *p = (char *) a + len; - p = (char *) pass_spaces (p); - if (*p == '\"') - { - p++; - string str; - while (*p != '\"') - { - str += *p; - ++p; - } - len = p - a; - mem_additional = - String_table::encrypt_string (str.c_str (), NULL); - } - break; - } - default: - fatal_error ("error! instruction not supported"); - return 0; + len = b ? b - a : strlen(a); + ZOpcode *op = (ZOpcode *)symtable_lookup2(Opcodes, a, len); + if (op) + { + ZOpcode_flags flags = op->flags; + ZMemblock *mem_additional = NULL; + + switch (op->opcode) + { + case Opcode_CRLF: + case Opcode_PRINT: + case Opcode_QUIT: + break; + + case Opcode_PRINTI: + case Opcode_PRINTR: + { + char *p = (char *)a + len; + p = (char *)pass_spaces(p); + if (*p == '\"') + { + p++; + string str; + while (*p != '\"') + { + str += *p; + ++p; + } + len = p - a; + mem_additional = + String_table::encrypt_string(str.c_str(), NULL); + } + break; + } + default: + { + display_error = true; + } + } + + + if (display_error == false) + { + + int instruction_size = 1; + if (mem_additional) + instruction_size += mem_additional->used_size; + + g_codes[g_number_of_instructions] = zmem_init(instruction_size); + zmem_putbyte(g_codes[g_number_of_instructions], op->opcode); + + if (mem_additional) + { + for (int i = 0; i < mem_additional->used_size; ++i) + zmem_putbyte(g_codes[g_number_of_instructions], + mem_additional->contents[i]); + zmem_destroy(mem_additional); + } + + ++g_number_of_instructions; + return len; + } + } + else + { + //display_error = true; + string message = "wrong line \"" + string(a) + string("\""); + fatal_error(build_error_message(message.c_str()).c_str()); + } } - int instruction_size = 1; - if (mem_additional) - instruction_size += mem_additional->used_size; - //printf("instruction %s", a); - - g_codes[g_numberOfInstructions] = zmem_init (instruction_size); - zmem_putbyte (g_codes[g_numberOfInstructions], op->opcode); - - if (mem_additional) + if (display_error) { - for (int i = 0; i < mem_additional->used_size; ++i) - zmem_putbyte (g_codes[g_numberOfInstructions], - mem_additional->contents[i]); - zmem_destroy (mem_additional); + char buff[300]; + if (len == 0) + len = strlen(a); + char *instrcution_name = (char *)malloc(len + 1); + memcpy(instrcution_name, a, len); + instrcution_name[len] = 0; + string message = "instruction " + string(instrcution_name) + string(" is not supported yet"); + fatal_error(build_error_message(message.c_str()).c_str()); + free(instrcution_name); } - ++g_numberOfInstructions; - return len; - } - - return 0; + return strlen(a); } @@ -233,7 +282,7 @@ parse_line (const char *p) return n; // ..label or assignment if (n = tryparse_instruction (p)) return n; - fatal_error ("wrong line"); + //fatal_error ("wrong line"); } return 0; } @@ -241,32 +290,49 @@ parse_line (const char *p) #define MAX_LINESIZE 1024 int -parse_file (const char *filename) +parse_file (/*const char *filename*/) { - FILE *fp = fopen (filename, "r"); - if (!fp) - fatal_error ("wrong file"); + if (g_parsing_contexts.size() > 0) + { + string filename = g_parsing_contexts.top().current_directory + g_parsing_contexts.top().current_file_name; - //const int MAX_LINESIZE = 1024; - char line[MAX_LINESIZE]; - int newline_missing = 0; + FILE *fp = fopen(filename.c_str(), "r"); + if (fp) + { - while (fgets (line, MAX_LINESIZE, fp)) - { - if (newline_missing) - fatal_error ("line too long"); + g_currentLineNumber = 0; - int n = strlen (line); - if (!n) - continue; + //const int MAX_LINESIZE = 1024; + char line[MAX_LINESIZE]; + int newline_missing = 0; - parse_line (line); + while (g_stopParsing == 0 && fgets(line, MAX_LINESIZE, fp)) + { + ++g_currentLineNumber; - newline_missing = (line[n - 1] != '\n'); - } + if (newline_missing) + fatal_error(build_error_message("line too long").c_str()); - fclose (fp); - return 0; + int n = strlen(line); + if (!n) + continue; + + parse_line(line); + + newline_missing = (line[n - 1] != '\n'); + } + + fclose(fp); + } + else + { + string message = string("can't open file ") + filename; + fatal_error(message.c_str()); + } + + g_parsing_contexts.pop(); + } + return 0; } /* @@ -298,16 +364,19 @@ if (current_token == DIRECTIVE) { void init_parser () { - g_numberOfInstructions = 0; + g_number_of_instructions = 0; } + + + void relase_parser () { - for (int i = 0; i < g_numberOfInstructions; ++i) + for (int i = 0; i < g_number_of_instructions; ++i) { zmem_destroy (g_codes[i]); } - g_numberOfInstructions = 0; + g_number_of_instructions = 0; } diff --git a/zilasm/parser.h b/zilasm/parser.h index 2ed9ae7..007d2ff 100644 --- a/zilasm/parser.h +++ b/zilasm/parser.h @@ -1,7 +1,7 @@ /* * parser.h -- part of ZilUtils/ZilAsm * - * Copyright (C) 2016, 2019 Jason Self + * Copyright (C) 2016, 2019, 2020 Jason Self * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -31,11 +31,21 @@ struct Instruction void init_parser (); -int parse_file (const char *filename); +int parse_file();// const char *filename); -extern unsigned g_numberOfInstructions; +extern unsigned g_number_of_instructions; #define MAX_NUMBER_OF_INSTRUCTIONS 65536 extern ZMemblock (*g_codes[MAX_NUMBER_OF_INSTRUCTIONS]); +struct Parsing_Context +{ + string current_directory; + string current_file_name; +}; + +extern stack g_parsing_contexts; +extern unsigned g_currentLineNumber; +extern bool g_haveErrors; + #endif /* ifndef ZILASM_PARSER */ diff --git a/zilasm/string_table.cpp b/zilasm/string_table.cpp index a086b87..ed2b6d7 100644 --- a/zilasm/string_table.cpp +++ b/zilasm/string_table.cpp @@ -1,7 +1,7 @@ /* * string_table.cpp -- part of ZilUtils/ZilAsm * - * Copyright (C) 2019 Jason Self + * Copyright (C) 2019, 2020 Jason Self * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -90,7 +90,14 @@ String_table::encrypt_string (const char *str, ZMemblock * zmb) } else if (c == ' ') { - write_one_word_to_string_table (zmb, &w, &numberOfSymbolsInWord, 0); + //if (numberOfSymbolsInWord != 0) + //{ + // zmem_putbyte(zmb, (w >> 8) & 255); + // zmem_putbyte(zmb, w & 255); + // numberOfSymbolsInWord = 0; + // w = 0; + //} + write_one_word_to_string_table (zmb, &w, &numberOfSymbolsInWord, 0); } else { @@ -130,7 +137,14 @@ String_table::encrypt_string (const char *str, ZMemblock * zmb) numberOfSymbolsInWord = 0; w = 0; } - zmb->contents[zmb->used_size - 2] |= 0x80; + + if ((zmb->contents[zmb->used_size - 1] & 31) == 0) // if the last symbol is a space or not filled + zmb->contents[zmb->used_size - 2] |= 0x80; // then we mark this word as the last word + else + { + zmem_putbyte(zmb, 0x80); // otherwise we add one more empty word + zmem_putbyte(zmb, 0x0); // with 15-th bit set + } return zmb; }