v2.5 with updates and bugfix

master
Joachim Stolberg 2022-02-22 20:32:32 +01:00
parent aa11810f69
commit 3252d66174
10 changed files with 192 additions and 56 deletions

1
.gitignore vendored
View File

@ -6,4 +6,5 @@
test/bin/
test/test.lua
test/test_asm.lua
test/test_masm.lua
del_eol_blanks.py

View File

@ -11,7 +11,7 @@
]]--
local vm16lib = ...
assert(vm16lib.version() == "2.4")
assert(vm16lib.version() == "2.5")
local VMList = {}
local storage = minetest.get_mod_storage()

View File

@ -64,11 +64,40 @@ Labels can be used in two different ways:
- `jump loop` is translated into an instruction with an absolute memory address
- `jump +loop` is translated into an instruction with a relative address (+/- some addresses), so that the code can be relocated to a different memory address
### Namespaces with local and global labels
The complete assembler program represents a namespace in which all labels are known. However, to divide the program into separate namespaces, the keyword `namespace` can be used. Labels now are `namespace` section local. To make a label globally available again, the keyword `global` is used.
The following example should explain the usage:
```asm
global func1
global func2
; Function 1 with a local `loop` label
func1:
...
loop:
....
djnz A, loop
ret
namespace ;--------------------------------------------------
; Function 2 with the same local `loop` label
func2:
...
loop:
....
djnz A, loop
ret
```
## Assembler Directives
Assembler directives are used to distinguish between code, data, and text segments, or to specify a memory address for following code blocks.
Assembler directives are used to distinguish between code, data, and text sections, or to specify a memory address for following code section.
Here a (not useful) example:
@ -90,10 +119,10 @@ text1: "Hello World\0"
```
- `.org` defines the memory start address for the locater. In the example above, the code will start at address 100 (hex).
- `.code` marks the start of a code block and is optional at the beginning of a program (code is default).
- `.data` marks the start of a data/variables block. Variables have a name and a start value. Variables have always the size of one word.
- `.text` marks the start of a text block with "..." strings. `\0` is equal to the value zero and has always be used to terminate the string.
- `.ctext` marks the start of a compressed text block (two characters in one word). This is not used in the example above but allows a better packaging of constant strings. It depends on your output device, if compressed strings are supported.
- `.code` marks the start of a code section and is optional at the beginning of a program (code is default).
- `.data` marks the start of a data/variables section. Variables have a name and a start value. Variables have always the size of one word.
- `.text` marks the start of a text section with "..." strings. `\0` is equal to the value zero and has always be used to terminate the string.
- `.ctext` marks the start of a compressed text section (two characters in one word). This is not used in the example above but allows a better packaging of constant strings. It depends on your output device, if compressed strings are supported.
@ -113,4 +142,3 @@ For symbols the characters 'A' - 'Z', 'a' - 'z', '_' and '0' - '9' are allowed
Of course, symbols must be defined before they can be used.

View File

@ -99,7 +99,7 @@ The instruction supports absolute and relative addressing.
```
jump $1234 ; absolute
jump +4 ; relative
jump +4 ; relative (skip 2 two-word instructions)
```
### call - Call a subroutin
@ -109,7 +109,7 @@ and the stack pointer is decremented. The instruction supports absolute and rela
```
call $1234 ; absolute
call +4
call +4 ; relative (skip 2 two-word instructions)
```
### ret - Return from subroutine
@ -289,8 +289,8 @@ operand2 can be any memory location/register, operand2 an absolute and relative
```
bnze A, #$100 ; jump to absolute address
bnze A, -4 ; jump to the prior address
bnze [X], +2 ; skip the next 2 word instruction
bnze A, -2 ; jump to the prior address
bnze [X], +4 ; skip the next 2 word instruction
```
### bze - Branch if zero
@ -300,8 +300,8 @@ operand2 can be any memory location/register, operand2 an absolute and relative
```
bze A, #$100 ; jump to absolute address
bze A, -4 ; jump to the prior address
bze [X], +2 ; skip the next 2 word instruction
bze A, -2 ; jump to the prior address
bze [X], +4 ; skip the next 2 word instruction
```
### bpos - Branch if positive
@ -311,20 +311,19 @@ operand2 can be any memory location/register, operand2 an absolute and relative
```
bpos A, #$100 ; jump to absolute address
bpos A, -4 ; jump to the prior address
bpos [X], +2 ; skip the next 2 word instruction
bpos A, -2 ; jump to the prior address
bpos [X], +4 ; skip the next 2 word instruction
```
### bneg - Branch if negative
Branch to the operand2 address if operand1 is negative (value >= $8000).
operand2 can be any memory location/register, operand2 an absolute and relative address.
(`+2` means: skip the next 2 word instruction)
```
bneg A, #$100 ; jump to absolute address
bneg A, -4 ; jump to the prior address
bneg [X], +2 ; skip the next 2 word instruction
bneg A, -2 ; jump to the prior address
bneg [X], +4 ; skip the next 2 word instruction
```
### dbnz - Decrement and branch if zero
@ -334,8 +333,8 @@ operand1 can be any memory location/register, operand2 an absolute and relative
```
dbnz A, #$100 ; jump to absolute address
dbnz A, -4 ; jump to the prior address
dbnz [X], +2 ; skip the next 2 word instruction
dbnz A, -2 ; jump to the prior address
dbnz [X], +4 ; skip the next 2 word instruction
```
### in - IN operation

View File

@ -90,9 +90,9 @@ The Instruction Set table below uses mainly the following two addressing groups:
|------|------|------|------|------|------|------|------|
| 0100 | 0120 | 0140 | 0160 | 0180 | 01A0 | 01C0 | 01E0 |
|CONST | MEM | REL |[SP+n]|
|------|------|------|------|
| 0200 | 0220 | 0240 | 0260 |
| CONST | MEM | REL*) | [SP+n] | REL2 |
| ----- | ---- | ----- | ------ | ---- |
| 0200 | 0220 | 0240 | 0260 | 0280 |
#### Operand 2 (Opnd2)
@ -104,9 +104,11 @@ The Instruction Set table below uses mainly the following two addressing groups:
|------|------|------|------|------|------|------|------|
| 0008 | 0009 | 000A | 000B | 000C | 000D | 000E | 000F |
|CONST | MEM | REL |[SP+n]|
|------|------|------|------|
| 0010 | 0011 | 0012 | 0013 |
| CONST | MEM | REL*) | [SP+n] | REL2 |
| ----- | ---- | ----- | ------ | ---- |
| 0010 | 0011 | 0012 | 0013 | 0014 |
*) REL instructions are deprecated. Use REL2 instead!
@ -168,13 +170,13 @@ The Instruction Set table below uses mainly the following two addressing groups:
4810, 0008 xor A, #8
4811, 0100 xor A, $100
4C00 not A
5012, 0002 bnze A, +2
5014, 0002 bnze A, +2
5010, 0100 bnze A, $100
5412, FFFE bze A, -2
5414, FFFE bze A, -2
5410, 0100 bze A, $100
5812, FFFC bpos A, -4
5814, FFFC bpos A, -4
5810, 0100 bpos A, $100
5C12, FFFC bneg A, -4
5C14, FFFC bneg A, -4
5C10, 0100 bneg A, $100
6010, 0002 in A, #2
6600, 0003 out #3, A

View File

@ -9,6 +9,8 @@
See LICENSE.txt for more information
]]--
local version = "2.0"
-- Tok Elems {1, "add A, 1", "add A, 1 ; start value", CODESEC, 10, {0x1234, 0x001}}
local LINENO = 1
local CODESTR = 2
@ -25,7 +27,8 @@ local CTEXTSEC = 4
local tOpcodes = {}
local tOperands = {}
local IDENT = "^[A-Za-z_][A-Za-z_0-9%.]+"
local IDENT = "^[A-Za-z_][A-Za-z_0-9%.]+"
local RIPLBL = "^PC%+[A-Za-z_][A-Za-z_0-9%.]+"
--
-- OP-codes
@ -49,7 +52,7 @@ local Opcodes = {[0] =
local Operands = {[0] =
"A", "B", "C", "D", "X", "Y", "PC", "SP",
"[X]", "[Y]", "[X]+", "[Y]+", "#0", "#1", "-", "-",
"IMM", "IND", "REL", "[SP+n]",
"IMM", "IND", "REL", "[SP+n]", "REL2",
}
--
@ -137,6 +140,20 @@ local function value(s, is_hex)
end
end
local function pos_value(s, is_hex)
if s:match(IDENT) then
return "PC+" .. s
end
return value(s, is_hex)
end
local function neg_value(s, is_hex)
if s:match(IDENT) then
return "PC+" .. s
end
return 0x10000 - value(s, is_hex)
end
local function word_val(s, idx)
if s:byte(idx) == 0 then
return 0
@ -168,13 +185,14 @@ end
-------------------------------------------------------------------------------
local Asm = {}
function Asm:new(attr)
local o = {
section = attr.section or CODESEC,
address = attr.address or 0,
symbols = {},
errors = {},
}
function Asm:new(o)
o = o or {}
o.section = o.section or CODESEC
o.address = o.address or 0
o.globals = {}
o.symbols = {}
o.errors = {}
o.namespace_cnt = 1
setmetatable(o, self)
self.__index = self
return o
@ -206,11 +224,35 @@ function Asm:scanner(text)
return lOut
end
function Asm:posfix(label)
return label .. string.format("$%03d", self.namespace_cnt)
end
function Asm:address_label(tok)
local codestr = tok[CODESTR]
local _, pos, label = codestr:find("^([A-Za-z_][A-Za-z_0-9]+):( *)")
if label then
self.symbols[label] = self.address
if self.globals[label] == -1 then
self.globals[label] = self.address
else
if self.symbols[self:posfix(label)] then
self:err_msg("Redefinition of label " .. label)
end
self.symbols[self:posfix(label)] = self.address
end
tok[CODESTR] = codestr:sub(pos+1, -1)
end
return tok
end
function Asm:global_def(tok)
local codestr = tok[CODESTR]
local _, pos, label = codestr:find("^ *global +([A-Za-z_][A-Za-z_0-9]+)( *)")
if label then
if self.globals[label] then
self:err_msg("Redefinition of global " .. label)
end
self.globals[label] = -1
tok[CODESTR] = codestr:sub(pos+1, -1)
end
return tok
@ -248,8 +290,8 @@ function Asm:operand(s)
if c == "$" then return tOperands["IND"], value(s) end
-- value without '#' and '$'
if string.byte(c) >= 48 and string.byte(c) <= 57 then return tOperands["IND"], value(s) end
if c == "+" then return tOperands["REL"], value(string.sub(s, 2, -1)) end
if c == "-" then return tOperands["REL"], 0x10000 - value(string.sub(s, 2, -1)) end
if c == "+" then return tOperands["REL2"], pos_value(string.sub(s, 2, -1)) end
if c == "-" then return tOperands["REL2"], neg_value(string.sub(s, 2, -1)) end
if string.sub(s, 1, 4) == "[SP+" then return tOperands["[SP+n]"], value(string.sub(s, 5, -2)) end
-- valid label keyword
if s:match(IDENT) then
@ -268,11 +310,18 @@ function Asm:decode_code(tok)
if codestr == "" then
return self:no_code(tok)
end
if codestr == "namespace" then
self.namespace_cnt = self.namespace_cnt + 1
return self:no_code(tok)
end
-- Aliases
if words[2] == "=" then
if words[1]:match(IDENT) then
local ident = words[1]
self.symbols[ident] = value(words[3])
local label = words[1]
if self.symbols[self:posfix(label)] then
self:err_msg("Redefinition of symbol " .. label)
end
self.symbols[self:posfix(label)] = value(words[3])
else
self:err_msg("Invalid left value")
end
@ -392,14 +441,39 @@ function Asm:decode_ctext(tok)
end
end
function Asm:handle_rip_label(tok, i, opc)
if opc:match(RIPLBL) then
local label = string.sub(opc, 4, -1)
if self.symbols[self:posfix(label)] then
tok[OPCODES][i] = self.symbols[self:posfix(label)] - (tok[ADDRESS] or 0)
elseif self.globals[label] then
tok[OPCODES][i] = self.globals[label] - (tok[ADDRESS] or 0)
else
self:err_msg("Unknown label " .. label)
end
return true
end
end
function Asm:handle_label(tok, i, label)
if self.symbols[self:posfix(label)] then
tok[OPCODES][i] = self.symbols[self:posfix(label)]
elseif self.globals[label] then
tok[OPCODES][i] = self.globals[label]
else
self:err_msg("Unknown label " .. label)
end
end
function Asm:assembler(lToken)
local lOut = {}
-- pass 1
self.namespace_cnt = 1
for _,tok in ipairs(lToken or {}) do
self.lineno = tok[LINENO]
tok = self:address_label(tok)
tok = self:section_def(tok)
tok = self:org_directive(tok)
tok = self:address_label(tok)
if self.section == CODESEC then
append(lOut, self:decode_code(tok))
@ -410,17 +484,19 @@ function Asm:assembler(lToken)
elseif self.section == CTEXTSEC then
extend(lOut, self:decode_ctext(tok))
end
end
-- pass 2
self.namespace_cnt = 1
for _,tok in ipairs(lOut) do
self.lineno = tok[LINENO]
if tok[CODESTR] == "namespace" then
self.namespace_cnt = self.namespace_cnt + 1
end
for i, opc in ipairs(tok[OPCODES] or {}) do
if type(opc) == "string" then
if self.symbols[opc] then
tok[OPCODES][i] = self.symbols[opc]
else
self:err_msg("Unknown label " .. opc)
if not self:handle_rip_label(tok, i, opc) then
self:handle_label(tok, i, opc)
end
end
end
@ -447,13 +523,20 @@ function Asm:listing(lToken)
local out = {}
for _,tok in ipairs(lToken) do
append(out, string.format("%04X: %-10s %s", tok[ADDRESS], mydump(tok[OPCODES]), tok[TXTLINE]))
if #tok[OPCODES] > 2 then
append(out, string.format(" %-10s %s\n%04X: %s", "", tok[TXTLINE], tok[ADDRESS], mydump(tok[OPCODES])))
elseif #tok[OPCODES] > 0 then
append(out, string.format("%04X: %-10s %s", tok[ADDRESS], mydump(tok[OPCODES]), tok[TXTLINE]))
else
append(out, string.format(" %-10s %s", "", tok[TXTLINE]))
end
end
return table.concat(out, "\n")
end
vm16.Asm = Asm
vm16.Asm.version = version
vm16.Asm.LINENO = LINENO
vm16.Asm.CODESTR = CODESTR
vm16.Asm.TXTLINE = TXTLINE

View File

@ -45,11 +45,11 @@ sudo luarocks make
The program output should look like this:
```
vm16 2.4-0 depends on lua 5.1 (5.1-1 provided by VM)
vm16 2.x-y depends on lua 5.1 (5.1-1 provided by VM)
gcc -O2 -fPIC -I/usr/include/lua5.1 -c src/vm16core.c -o src/vm16core.o
gcc -O2 -fPIC -I/usr/include/lua5.1 -c src/vm16lua.c -o src/vm16lua.o
gcc -shared -o vm16lib.so src/vm16core.o src/vm16lua.o
vm16 2.4-0 is now installed in /usr/local (license: GPLv3)
vm16 2.x-y is now installed in /usr/local (license: GPLv3)
```
For the installation of 'luarocks' (if not already available), see [luarocks](https://luarocks.org/)
@ -63,6 +63,11 @@ secure.trusted_mods = vm16
```
## Configuration
Open the tab Settings -> All Settings -> Mods -> vm16
to enable the CPU/demo blocks or check settingtypes.txt.
## Dependencies
@ -79,6 +84,17 @@ Licensed under the GNU GPLv3 (See LICENSE.txt)
## History
### v2.5 (2022-02-22)
- C-core: Add REL2 addressing mode (as replacement for wrong REL implementation)
- Asm: Fix RIP addressing bug (reported by DS)
- Asm: Add 'namespace' and 'global' keyword to enable local and global labels
- Docu: Updates
### Older Versions
- 2020-11-21 v1.1 * First commit as LuaRocks project
- 2020-11-29 v1.2 * Complete rework, Add test nodes
- 2020-11-30 v1.3 * Add functions read_h16/write_h16

View File

@ -37,7 +37,7 @@ LUALIB_API int luaopen_vm16(lua_State *L);
#define IDENT (0x36314D56)
#define VERSION (2) // VM compatibility
#define SVERSION "2.4" // see history in readme.md
#define SVERSION "2.5" // see history in readme.md
#define VM16_WORD_SIZE (16)
#define MEM_BLOCK_SIZE (4096)
#define MAX_MEM_BLOCKS (16) // = 64 KW

View File

@ -57,8 +57,9 @@ along with VM16. If not, see <https://www.gnu.org/licenses/>.
#define CNST (0x10) // constant: move a, #1234; jump 0
#define ABS (0x11) // absolute: move a, 100
#define REL (0x12) // relative: jump -10
#define REL (0x12) // relative: jump (deprecated)
#define SREL (0x13) // stack relative: inc [SP+1]
#define REL2 (0x14) // relative: jump -10
/* OP codes */
@ -171,6 +172,7 @@ static uint16_t *getaddr(vm16_t *C, uint8_t addr_mod) {
C->pcnt++;
return ADDR_DST(C, C->sptr + offs);
}
case REL2: return ADDR_DST(C, 0); // invalid
default: return ADDR_DST(C, 0);
}
}
@ -222,6 +224,11 @@ static uint16_t getoprnd(vm16_t *C, uint8_t addr_mod) {
C->pcnt++;
return C->sptr + offs;
}
case REL2: {
uint16_t offs = *ADDR_SRC(C, C->pcnt);
C->pcnt--;
return C->pcnt + offs;
}
default: return 0;
}
}

View File

@ -1,5 +1,5 @@
package = "vm16"
version = "2.4-0"
version = "2.5-0"
source = {
url = "git+https://github.com/joe7575/vm16.git"
}