Skip to content

Commit f62a011

Browse files
committed
Update readme and examples
1 parent c979e1b commit f62a011

File tree

12 files changed

+1195
-119
lines changed

12 files changed

+1195
-119
lines changed

README.md

Lines changed: 15 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ void main() {
4141
}
4242
```
4343

44+
# Command IR
45+
46+
A new intermediate representation has been developed that the assembly language builds on top of.
47+
To find out more see [Command IR](https://github.com/simon816/Command-Block-Assembly/wiki/Command-IR).
48+
4449
# The Assembly Language
4550

4651
It is a simple language with instructions similar to that of x86.
@@ -230,10 +235,10 @@ The assembler is invoked by calling `main.py`.
230235

231236
Command line parameters:
232237
```
233-
usage: main.py [-h] [--world-dir WORLD_DIR] [--as_zip] [--namespace NAMESPACE]
234-
[--rem-existing] [--debug] [--stack STACK] [--arg ARG]
235-
[--jump JUMP] [--place-location PLACE_LOCATION] [--enable-sync]
236-
[--setup-on-load] [--spawn-location SPAWN_LOCATION]
238+
usage: main.py [-h] [--world-dir WORLD_DIR] [--as-zip] [--namespace NAMESPACE]
239+
[--rem-existing] [--debug] [--dump-ir] [--gen-cleanup]
240+
[--jump JUMP] --place-location PLACE_LOCATION [--setup-on-load]
241+
[--spawn-location SPAWN_LOCATION]
237242
[--pack-description PACK_DESCRIPTION]
238243
file
239244
@@ -244,17 +249,16 @@ optional arguments:
244249
-h, --help show this help message and exit
245250
--world-dir WORLD_DIR
246251
World Directory
247-
--as_zip Write datapack as zip file
252+
--as-zip Write datapack as zip file
248253
--namespace NAMESPACE
249254
Function namespace
250255
--rem-existing Remove existing functions in namespace
251256
--debug Enable debug output
252-
--stack STACK Stack size
253-
--arg ARG ASM file arguments
257+
--dump-ir Dump Command IR output
258+
--gen-cleanup Generate cleanup function
254259
--jump JUMP Output subroutine jump instruction
255260
--place-location PLACE_LOCATION
256261
Location to place command blocks
257-
--enable-sync Enable SYNC opcode
258262
--setup-on-load Run setup on minecraft:load
259263
--spawn-location SPAWN_LOCATION
260264
Location to spawn hidden armor stand
@@ -267,21 +271,9 @@ Notes:
267271
If `--world-dir` is not provided, no functions are written.
268272
This can be useful in combination with `--debug`.
269273

270-
`--place-location` is where to start laying out command blocks, should they be needed.
271-
Defaults to `~1,~,~1`
274+
`--place-location` is where to place a utility command block. It has to be an absolute position e.g. '0,56,0'
272275

273-
`--arg` is used to pass values in to a program that get replaced in the output. They are currently
274-
only applicable in CMD and TEST instructions.
275-
e.g.
276-
```asm
277-
main: CMD say Hello $arg:name$! This was generated on $arg:date$
278-
```
279-
Running `python main.py test.asm --debug --arg "name=Simon" --arg "date=24/10/2017"`
280-
produces:
281-
```
282-
Function sub_main
283-
say Hello Simon! This was generated on 24/10/2017
284-
```
276+
You may want `--gen-cleanup` which creates a function to remove all scoreboard objectives and the global entity.
285277

286278
### Running a program
287279

@@ -316,45 +308,14 @@ The value is then WORD_SIZE bits in the y axis. i.e. for an 8-bit word, y=0 is t
316308
`hdd_driver.asm` is a library file, and exports the `read_mem`, `write_mem` subroutines along with
317309
its constants.
318310

319-
The location where the memory region is defined must be passed in to the assembler:
320-
`--arg "mem_loc=0 0 0"`
311+
The location where the memory region is hardcoded to be `100 60 100`.
321312

322313
#### `mem_test.asm`
323314

324315
A simple test of the hdd_driver library. See description at the top of the file.
325316

326317
# Issues and nuances
327318

328-
## The SYNC instruction (and how it affects CALL and RET)
329-
330-
Command Block Assembly is designed to produce fast and efficient functions,
331-
avoiding expensive operations wherever possible.
332-
As such, some features are optimized unless it is not possible.
333-
334-
By default, a CALL instruction will add a `/function` command to run the subroutine.
335-
This means that once the function finishes, execution continues at the next command.
336-
This behaviour is the anticipated use of CALL, however the implication is that RET has no effect.
337-
338-
Originally, there was going to be an _implied_ return after a subroutine. i.e. always return to caller if CALL
339-
is ran.
340-
But the RET instruction is required for SYNC to work, so it was added.
341-
342-
As stated in the instruction description, SYNC effectively "pauses" the current execution until one tick later.
343-
344-
The current calling stack (nested `/function` calls) will always return to the caller and continue on the next line.
345-
SYNC must nullify the next call so the calling stack returns and the game runs a tick.
346-
However, this is not possible with CALL's guarantee to return to caller.
347-
348-
Therefore, CALL must push the address of the next instruction onto the stack, letting RET pop it off later.
349-
With that, it is now possible to cause an interrupt between CALL and the subsequent RET.
350-
This is how you would expect CALL and RET to function, but doing so is less efficient for the common use-case.
351-
352-
By default, SYNC is disabled. The use of RET prints a warning to stderr saying how it doesn't have any effect.
353-
If the program is anticipated to use SYNC, you should defensively use RET anyway. Just keep in mind
354-
how the optimization works.
355-
356-
To use SYNC, enable it with `--enable-sync`.
357-
358319
## CMP and jumping
359320

360321
Due to there being no concept of a "status" register, jump instructions don't check flags of any sort.

README_C.md

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,10 @@ Command line parameters:
1616
```
1717
usage: compiler_main.py [-h] [-E] [-S] [--world-dir WORLD_DIR] [--as-zip]
1818
[--namespace NAMESPACE] [--rem-existing] [--debug]
19-
[--stack STACK] [--arg ARG]
20-
[--place-location PLACE_LOCATION] [--enable-sync]
21-
[--page-size PAGE_SIZE] [--setup-on-load]
19+
[--dump-ir] [--gen-cleanup] --place-location
20+
PLACE_LOCATION [--page-size PAGE_SIZE]
2221
[--spawn-location SPAWN_LOCATION]
2322
[--pack-description PACK_DESCRIPTION]
24-
[--extern EXTERN]
2523
file
2624
2725
positional arguments:
@@ -38,19 +36,16 @@ optional arguments:
3836
Function namespace
3937
--rem-existing Remove existing functions in namespace
4038
--debug Enable debug output
41-
--stack STACK Stack size
42-
--arg ARG ASM file arguments
39+
--dump-ir Dump Command IR output
40+
--gen-cleanup Generate cleanup function
4341
--place-location PLACE_LOCATION
4442
Location to place command blocks
45-
--enable-sync Enable SYNC opcode
4643
--page-size PAGE_SIZE
4744
Memory page size
48-
--setup-on-load Run setup on minecraft:load
4945
--spawn-location SPAWN_LOCATION
5046
Location to spawn hidden armor stand
5147
--pack-description PACK_DESCRIPTION
5248
Datapack description
53-
--extern EXTERN Specify external symbol
5449
```
5550

5651
You will need to generate the standalone parser (from [Lark](https://github.com/lark-parser/lark)) using the `./compiler/rebuild-grammar.sh` script.

cmd_ir/core.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,6 @@ def end(self):
481481
assert not self.finished
482482
for block in self.blocks:
483483
block.end()
484-
print("%s is closed: %s" % (self, self.is_closed()))
485484
self._finished = True
486485

487486
def get_func_table(self):

cmd_ir/optimizers.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def visit_pre_insn(self, insn):
6464
if isinstance(insn, DefineGlobal):
6565
var = insn._value
6666
if not var.is_referenced:
67-
print("Unreferenced global", var)
67+
#print("Unreferenced global", var)
6868
return None
6969
return insn
7070

@@ -75,7 +75,7 @@ def visit_global(self, name, var):
7575

7676
def visit_function(self, name, func):
7777
if not func.extern_visibility and not func.has_usage():
78-
print("Dead function", func)
78+
#print("Dead function", func)
7979
self.changed = True
8080
return None, None
8181
elim = DeadCodeFuncEliminator()
@@ -98,7 +98,7 @@ def visit_pre_insn(self, insn):
9898
if isinstance(insn, DefineVariable):
9999
var = insn._value
100100
if not var.is_referenced:
101-
print("Unreferenced local", var)
101+
#print("Unreferenced local", var)
102102
return None
103103
return insn
104104

@@ -109,7 +109,7 @@ def visit_local_var(self, name, var):
109109

110110
def visit_block(self, name, block):
111111
if block.use_count() == 0:
112-
print("Dead block", block)
112+
#print("Dead block", block)
113113
return None, None
114114
self.changed |= DeadCodeBlockEliminator().visit(block)
115115
return name, block
@@ -169,7 +169,7 @@ def scan(self, insn):
169169

170170
def kill(self, insn):
171171
if insn in self.kills:
172-
print("Kill", insn)
172+
#print("Kill", insn)
173173
return None
174174
return insn
175175

@@ -189,7 +189,7 @@ class BranchInliner(BlockVisitor):
189189
def visit_insn(self, insn):
190190
if isinstance(insn, Branch):
191191
if insn.label.use_count() == 1:
192-
print("inline", insn.label, "into", self._block)
192+
#print("inline", insn.label, "into", self._block)
193193
return insn.label.insns
194194
return insn
195195

@@ -198,7 +198,7 @@ class BranchEliminator(BlockVisitor):
198198
def visit_insn(self, insn):
199199
if isinstance(insn, Branch):
200200
if insn.label.is_empty():
201-
print("Empty", insn.label)
201+
#print("Empty", insn.label)
202202
return None
203203
if isinstance(insn, (CmpBr, RangeBr)):
204204
copy = insn.copy()
@@ -208,7 +208,7 @@ def visit_insn(self, insn):
208208
changed = True
209209
arg.val = None
210210
if changed:
211-
print("Elim br", copy)
211+
#print("Elim br", copy)
212212
# eliminated whole insn
213213
if not copy.if_true and not copy.if_false:
214214
return None
@@ -240,22 +240,22 @@ def visit_block(self, name, block):
240240
self.changed |= block not in self.data.aliases \
241241
or self.data.aliases[block] != insn.label
242242
self.data.aliases[block] = insn.label
243-
print("Alias", block, "to", insn.label)
243+
#print("Alias", block, "to", insn.label)
244244
# Attempt to inline invoke
245245
if self.func._varsfinalized and isinstance(insn, Invoke) \
246246
and not self.func.get_registers():
247247
if not insn.func.params and not insn.func.returns:
248248
self.changed |= block not in self.data.aliases \
249249
or self.data.aliases[block] != insn.func
250250
self.data.aliases[block] = insn.func
251-
print("Alias", block, "to", insn.func)
251+
#print("Alias", block, "to", insn.func)
252252

253253
# Blocks that have one instruction that generates one command
254254
# are recorded for exec_run substitution
255255
if insn.single_command():
256256
self.changed |= block not in self.data.cmd_aliases
257257
self.data.cmd_aliases.add(block)
258-
print("Alias", block, "to insn", insn)
258+
#print("Alias", block, "to insn", insn)
259259
return name, block
260260

261261
class AliasRewriter(BlockVisitor):
@@ -273,13 +273,13 @@ def visit_insn(self, insn):
273273
for arg in new.query(FunctionLike):
274274
if arg.val in self.data.aliases:
275275
changed = True
276-
print("Alias re", arg.val, "to", self.data.aliases[arg.val])
276+
#print("Alias re", arg.val, "to", self.data.aliases[arg.val])
277277
arg.val = self.data.aliases[arg.val]
278278
# Things like exec_run
279279
elif arg.val in self.data.cmd_aliases and arg.accepts(CmdFunction):
280280
changed = True
281281
cmdvar = BlockAsCommand(arg.val)
282-
print("Alias cmd", arg.val, "to", cmdvar)
282+
#print("Alias cmd", arg.val, "to", cmdvar)
283283
arg.val = cmdvar
284284

285285
# Need to update success tracker to new branch
@@ -302,8 +302,8 @@ def visit_insn(self, insn):
302302
for arg in new.query(Variable):
303303
if arg.access is READ and arg.val in self.varvals:
304304
if arg.accepts(int):
305-
print("Const replace", self.n(arg.val),
306-
"with", self.varvals[arg.val], "in", new)
305+
#print("Const replace", self.n(arg.val),
306+
# "with", self.varvals[arg.val], "in", new)
307307
arg.val = self.varvals[arg.val]
308308
changed = True
309309
# probably wrong place for this, but can't replace var
@@ -427,8 +427,8 @@ def visit_insn(self, insn):
427427
# Replace READs
428428
for arg in new.query(Variable):
429429
if arg.access is READ and arg.val in self.aliases:
430-
print("Alias replace", self.n(arg.val),
431-
self.n(self.aliases[arg.val]))
430+
#print("Alias replace", self.n(arg.val),
431+
# self.n(self.aliases[arg.val]))
432432
arg.val = self.aliases[arg.val]
433433
changed = True
434434
# Subtracting own alias equivalent to setting to 0

compiler_main.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,9 @@
2222
parser.add_argument('--rem-existing', help="Remove existing functions in namespace",
2323
action='store_true')
2424
parser.add_argument('--debug', action='store_true', help="Enable debug output")
25-
parser.add_argument('--dump-ir', action='store_true', help="Dump CMD IR output")
25+
parser.add_argument('--dump-ir', action='store_true', help="Dump Command IR output")
2626
parser.add_argument('--gen-cleanup', action='store_true', help="Generate cleanup function")
27-
parser.add_argument('--place-location', default="~1,~,~1",
28-
help="Location to place command blocks")
27+
parser.add_argument('--place-location', help="Location to place command blocks", required=True)
2928
parser.add_argument('--page-size', type=int, default=64, help="Memory page size")
3029
parser.add_argument('--spawn-location', default='~ ~2 ~',
3130
help="Location to spawn hidden armor stand")

examples/hdd_driver.asm

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
; Set mar to the address to read
2020
; mbr will contain the value once read
2121
read_mem:
22-
CMD summon armor_stand $arg:mem_loc$ {Tags:["$tag:_mem_ptr$"], NoGravity:1b, Marker:1b}
22+
CMD summon armor_stand 100 60 100 {Tags:["hdd_ptr"], NoGravity:1b, Marker:1b}
2323
MOV mar, hdd_addr
2424
MOV #0, mbr
2525
DIV MEM_SIZE_X, hdd_addr
@@ -31,23 +31,23 @@ read_mem:
3131
_read_loop:
3232
CMP hdd_addr, #0
3333
JE _finish
34-
TEST execute at @e[tag=$tag:_mem_ptr$] if block ~ ~ ~ stone
34+
TEST execute at @e[tag=hdd_ptr] if block ~ ~ ~ stone
3535
ADD hdd_mul, mbr
3636
SUB #1, hdd_addr
3737
MUL #2, hdd_mul
38-
CMD execute as @e[tag=$tag:_mem_ptr$] at @s run tp @s ~ ~1 ~
38+
CMD execute as @e[tag=hdd_ptr] at @s run tp @s ~ ~1 ~
3939
JMP _read_loop
4040

4141
_finish:
42-
CMD kill @e[tag=$tag:_mem_ptr$]
42+
CMD kill @e[tag=hdd_ptr]
4343
RET
4444

4545

4646
; Writes a word (WORD_SIZE) to memory
4747
; Set mar to the address to write
4848
; set mbr to the value that will be written
4949
write_mem:
50-
CMD summon armor_stand $arg:mem_loc$ {Tags:["$tag:_mem_ptr$"], NoGravity:1b, Marker:1b}
50+
CMD summon armor_stand 100 60 100 {Tags:["hdd_ptr"], NoGravity:1b, Marker:1b}
5151
MOV mar, hdd_addr
5252
DIV MEM_SIZE_X, hdd_addr
5353
CALL memory_seek
@@ -65,14 +65,14 @@ write_mem:
6565
CMP _mem_temp, #0
6666
JE _write_zero
6767
; If not zero, write 1. note: this captures -1 and 1
68-
CMD execute at @e[tag=$tag:_mem_ptr$] run setblock ~ ~ ~ stone
68+
CMD execute at @e[tag=hdd_ptr] run setblock ~ ~ ~ stone
6969
JMP _continue
70-
_write_zero: CMD execute at @e[tag=$tag:_mem_ptr$] run setblock ~ ~ ~ air
71-
_continue: CMD execute as @e[tag=$tag:_mem_ptr$] at @s run tp @s ~ ~1 ~
70+
_write_zero: CMD execute at @e[tag=hdd_ptr] run setblock ~ ~ ~ air
71+
_continue: CMD execute as @e[tag=hdd_ptr] at @s run tp @s ~ ~1 ~
7272
JMP _write_loop
7373

7474
_finish:
75-
CMD kill @e[tag=$tag:_mem_ptr$]
75+
CMD kill @e[tag=hdd_ptr]
7676
RET
7777

7878

@@ -82,7 +82,7 @@ memory_seek:
8282
CMP hdd_addr, #0
8383
JE _seek_z
8484

85-
CMD execute as @e[tag=$tag:_mem_ptr$] at @s run tp @s ~1 ~ ~
85+
CMD execute as @e[tag=hdd_ptr] at @s run tp @s ~1 ~ ~
8686
SUB #1, hdd_addr
8787
JMP memory_seek
8888

@@ -95,7 +95,7 @@ memory_seek:
9595
JE _end
9696

9797
SUB #1, hdd_addr
98-
CMD execute as @e[tag=$tag:_mem_ptr$] at @s run tp @s ~ ~ ~1
98+
CMD execute as @e[tag=hdd_ptr] at @s run tp @s ~ ~ ~1
9999
JMP _seek_z_loop
100100

101101
_end: RET

0 commit comments

Comments
 (0)