v2.5 with updates and bugfix
parent
aa11810f69
commit
3252d66174
|
@ -6,4 +6,5 @@
|
|||
test/bin/
|
||||
test/test.lua
|
||||
test/test_asm.lua
|
||||
test/test_masm.lua
|
||||
del_eol_blanks.py
|
||||
|
|
2
api.lua
2
api.lua
|
@ -11,7 +11,7 @@
|
|||
]]--
|
||||
|
||||
local vm16lib = ...
|
||||
assert(vm16lib.version() == "2.4")
|
||||
assert(vm16lib.version() == "2.5")
|
||||
|
||||
local VMList = {}
|
||||
local storage = minetest.get_mod_storage()
|
||||
|
|
40
doc/asm.md
40
doc/asm.md
|
@ -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.
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
125
mod/asm.lua
125
mod/asm.lua
|
@ -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
|
||||
|
|
20
readme.md
20
readme.md
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
package = "vm16"
|
||||
version = "2.4-0"
|
||||
version = "2.5-0"
|
||||
source = {
|
||||
url = "git+https://github.com/joe7575/vm16.git"
|
||||
}
|
Loading…
Reference in New Issue