-
Notifications
You must be signed in to change notification settings - Fork 1
/
op.py
2153 lines (1614 loc) · 57.5 KB
/
op.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
"""
The definition of all the operations in the HERA language.
Each operation is defined as a class which inherits from `AbstractOperation` or one of
its subclasses. A class for a HERA op typically needs to define only three things:
- A class-level `P` field that is a tuple of the types of the operations arguments.
The types are defined in the TYPES FOR `P` FIELD section of this module.
- A class-level `BITV` field that is a string describing the pattern of the
operation's binary encoding. See the docstring of `match_bitvector` for a
description of the pattern format.
- An `execute` method that takes a `VirtualMachine` object and performs its
operation on it.
Many subclasses of `AbstractOperation` are provided to ease implementation further, such
as `UnaryOp`, `BinaryOp`, `RegisterBranch`, and `RelativeBranch`.
A class for a HERA pseudo-op typically needs to define only two things:
- A `P` field, same as regular ops.
- A `convert` method which converts the pseudo-op into a list of regular operations.
Data operations (e.g., `INTEGER`) should inherit from `DataOperation` and define a `P`
field and `execute` and `assemble` methods. The `assemble` method of a data operation
returns the data, as a `bytes` object, that the operation places into static memory.
Once the operation's class has been defined, make an entry in the `name_to_class`
dictonary in this module. After doing this, the operation should work throughout the
hera-py toolkit (interpreter, debugger, assembler, etc.)!
Author: Ian Fisher (iafisher@protonmail.com)
Version: July 2019
"""
import json
import sys
from contextlib import suppress
from hera import stdlib
from hera.data import Constant, DataLabel, HERAError, Label, Location, Messages, Token
from hera.utils import format_int, from_u16, print_error, print_warning, to_u16, to_u32
from hera.vm import VirtualMachine
class AbstractOperation:
"""
The abstract base class for HERA operations. All operation classes should inherit
from this class.
"""
# Default value supplied to silence mypy's complaints.
BITV = ""
def __init__(self, *args, loc=None):
self.args = [a.value for a in args]
self.tokens = list(args)
# When the preprocessor converts pseudo-ops to real ops, it will set the
# original field of the real op to the corresponding pseudo-op, for use by the
# HERA debugger.
self.original = None
if isinstance(loc, Location):
self.loc = loc
elif hasattr(loc, "location"):
self.loc = loc.location
else:
self.loc = None
def typecheck(self, symbol_table: "Dict[str, int]") -> Messages:
"""
Type-check the operation. Subclasses do not generally need to override this
method, as long as they provide a P class field listing their parameter types.
"""
messages = Messages()
if len(self.P) < len(self.tokens):
msg = "too many args to {} (expected {})".format(self.name, len(self.P))
messages.err(msg, self.loc)
elif len(self.P) > len(self.tokens):
msg = "too few args to {} (expected {})".format(self.name, len(self.P))
messages.err(msg, self.loc)
return messages.extend(check_arglist(self.P, self.tokens, symbol_table))
def convert(self) -> "List[AbstractOperation]":
"""
Convert the pseudo-operation into a list of real operations. Only pseudo-ops
need to override this method.
"""
return [self]
def assemble(self) -> bytes:
"""
Assemble the operation into a 16-bit string. Subclasses do not generally need
to override this method, as long as they provide a BITV class field.
"""
return substitute_bitvector(self.BITV, self.args)
@classmethod
def disassemble(cls, *args):
"""
Disassemble the integer arguments into an operation. Subclasses do not
generally need to override this method; it is provided only for the special
purposes of the INC and DEC classes.
"""
return cls(*args)
def execute(self, vm: VirtualMachine) -> None:
"""
Execute the operation on a virtual machine. Real operations should override
this method; pseudo-operations should not.
"""
raise NotImplementedError
def __getattr__(self, name):
if name == "name":
return self.__class__.__name__
else:
raise AttributeError(name)
def __eq__(self, other):
return (
isinstance(other, self.__class__)
and len(self.tokens) == len(other.tokens)
and all(a1 == a2 for a1, a2 in zip(self.tokens, other.tokens))
)
def __repr__(self):
return "{}({})".format(self.name, ", ".join(repr(a) for a in self.tokens))
def __str__(self):
return "{}({})".format(
self.name, ", ".join(arg_to_string(a) for a in self.tokens)
)
def arg_to_string(arg):
if arg.type == Token.STRING:
return json.dumps(arg.value)
elif arg.type == Token.REGISTER:
return "R" + str(arg.value)
else:
return str(arg.value)
# == TYPES FOR `P` FIELD ==
# Use these variables in the `P` field of classes for HERA ops.
REGISTER = "REGISTER"
REGISTER_OR_LABEL = "REGISTER_OR_LABEL"
STRING = "STRING"
LABEL_TYPE = "LABEL_TYPE"
I16 = range(-2 ** 15, 2 ** 16)
I16_OR_LABEL = "I16_OR_LABEL"
U16 = range(2 ** 16)
I8 = range(-2 ** 7, 2 ** 8)
I8_OR_LABEL = "I8_OR_LABEL"
U5 = range(2 ** 5)
U4 = range(2 ** 4)
class UnaryOp(AbstractOperation):
"""
Abstract class to simplify implementation of unary operations. Child classes only
need to implement the calculate method and set the BITV field.
"""
P = (REGISTER, REGISTER)
def execute(self, vm):
arg = vm.load_register(self.args[1])
result = self.calculate(vm, arg)
vm.set_zero_and_sign(result)
vm.store_register(self.args[0], result)
vm.pc += 1
@staticmethod
def calculate(vm, arg):
"""
Calculate the result of the unary operation, given a concrete argument.
The virtual machine is passed in so that the overflow and carry flags can be
set if necessary.
"""
raise NotImplementedError
class BinaryOp(AbstractOperation):
"""
Abstract class to simplify implementation of binary operations. Child classes only
need to implement the calculate method and set the BITV field.
"""
P = (REGISTER, REGISTER, REGISTER)
def execute(self, vm):
left = vm.load_register(self.args[1])
right = vm.load_register(self.args[2])
result = self.calculate(vm, left, right)
vm.set_zero_and_sign(result)
vm.store_register(self.args[0], result)
vm.pc += 1
@staticmethod
def calculate(vm, left, right):
"""
Calculate the result of the binary operation, given the concrete left and right
arguments.
The virtual machine is passed in so that the overflow and carry flags can be
set if necessary.
"""
raise NotImplementedError
class Branch(AbstractOperation):
pass
class RegisterBranch(Branch):
"""
Abstract class to simplify implementation of register branches. Child classes only
need to implement the should method and set the BITV field.
"""
P = (REGISTER_OR_LABEL,)
def execute(self, vm):
if self.should(vm):
vm.pc = vm.load_register(self.args[0])
else:
vm.pc += 1
@staticmethod
def should(vm):
"""
Return True if branching should occur, based on the virtual machine's state.
"""
raise NotImplementedError
def convert(self):
if self.tokens[0].type == Token.REGISTER:
# When the argument to the branch is a concrete register.
return super().convert()
else:
# When the argument to the branch is a label, which has already been
# substituted for its value.
lbl = self.args[0]
return [
SETLO(Token.R(11), Token.Int(lbl & 0xFF)),
SETHI(Token.R(11), Token.Int(lbl >> 8)),
self.__class__(Token.R(11)),
]
class RelativeBranch(Branch):
"""
Abstract class to simplify implementation of relative branches. Child classes only
need to implement the should method and set the BITV field.
"""
P = (I8_OR_LABEL,)
def execute(self, vm):
if self.should(vm):
vm.pc += self.args[0]
else:
vm.pc += 1
@staticmethod
def should(vm):
"""
Return True if branching should occur, based on the virtual machine's state.
"""
raise NotImplementedError
class DebuggingOperation(AbstractOperation):
pass
class DataOperation(AbstractOperation):
pass
class SETLO(AbstractOperation):
"""
SETLO(Rd, v)
Set register Rd to the sign-extended 8-bit integer v. Sign-extension means that
Rd will contain the value of v, interpreted as a signed integer, regardless of
what was in Rd before. In other words, although the operation is called SETLO, it
affects not just the low 8 bits but also the high 8 bits.
Note that values of v above 127 are interpreted as negative numbers, so that for
instance SETLO(R1, 200) results in R1 having a value of -56 (= 200 - 256).
SETLO does not affect any flags.
HERA programmers typically use the SET pseudo-operation to assign a value to a
register, rather than the low-level SETLO operation.
"""
P = (REGISTER, I8)
BITV = "1110 AAAA bbbbbbbb"
def execute(self, vm):
value = self.args[1]
if value > 127:
value -= 256
vm.store_register(self.args[0], to_u16(value))
vm.pc += 1
class SETHI(AbstractOperation):
"""
SETHI(Rd, v)
Set the high 8 bits of the register Rd to the unsigned 8-bit integer v. SETHI
is typically used just after a SETLO operation on the same register.
SETHI does not affect any flags.
HERA programmers typically use the SET pseudo-operation to assign a value to a
register, rather than the low-level SETHI operation.
"""
P = (REGISTER, I8)
BITV = "1111 AAAA bbbbbbbb"
def execute(self, vm):
target, value = self.args
vm.store_register(target, (value << 8) + (vm.load_register(target) & 0x00FF))
vm.pc += 1
class SET(AbstractOperation):
"""
SET(Rd, v)
Set the register Rd to the signed 16-bit integer v.
SET cannot be used to set a register to the value of another register; use MOVE
for that instead.
SET does not affect any flags.
"""
P = (REGISTER, I16_OR_LABEL)
def convert(self):
dest = self.tokens[0]
value = to_u16(self.args[1])
lo = value & 0xFF
hi = value >> 8
return [
SETLO(dest, Token.Int(lo), loc=self.loc),
SETHI(dest, Token.Int(hi), loc=self.loc),
]
class ADD(BinaryOp):
"""
ADD(Rd, Ra, Rb)
Compute Ra + Rb and store the result in Rd. If the carry block flag is off, then
the carry flag will be added to the sum if it is set, i.e. Ra + Rb + 1 will be
computed instead.
If the sum exceeds 16 bits, the higher bits are lost. For example, if an ADD
results in a sum of 0x10067, then 0x0067 will be stored in the destination
register.
ADD sets the following flags:
sign, if the sum was negative
zero, if the sum was zero
carry, if the sum exceeded 16 bits
overflow, if signed integer overflow occurred (i.e., D != A + B under the signed
interpretation of the bits of A, B and D)
HERA programmers will often want to set the carry block flag (using SETCB())
before performing this operation, to avoid unexpected results.
"""
BITV = "1010 AAAA BBBB CCCC"
@staticmethod
def calculate(vm, left, right):
carry = 1 if not vm.flag_carry_block and vm.flag_carry else 0
result = (left + right + carry) & 0xFFFF
vm.flag_carry = result < (left + right + carry)
vm.flag_overflow = from_u16(result) != from_u16(left) + from_u16(right)
return result
class SUB(BinaryOp):
"""
SUB(Rd, Ra, Rb)
Compute Ra - Rb and store the result in Rd. If the carry block flag is off, then
the carry flag will be subtracted from the difference if it is NOT set, i.e.
Ra - Rb - 1 will be computed instead.
SUB sets the following flags:
sign, if the difference was negative
zero, if the difference was zero
carry, if there was no need to borrow from the 2^16's place (i.e., A > B,
interpreting A and B as unsigned)
overflow, if signed integer overflow occurred (i.e., D != A - B under the signed
interpretation of the bits of A, B and D)
HERA programmers will often want to set the carry block flag (using SETCB())
before performing this operation, to avoid unexpected results.
"""
BITV = "1011 AAAA BBBB CCCC"
@staticmethod
def calculate(vm, left, right):
borrow = 1 if not vm.flag_carry_block and not vm.flag_carry else 0
# to_u16 is necessary because although left and right are necessarily
# uints, left - right - borrow might not be.
result = to_u16((left - right - borrow) & 0xFFFF)
vm.flag_carry = left >= right
vm.flag_overflow = from_u16(result) != from_u16(left) - from_u16(right) - borrow
return result
class MUL(BinaryOp):
"""
MUL(Rd, Ra, Rb)
Compute Ra * Rb and store the result in Rd.
The behavior of the MUL instruction depends on the values of the flags. If the
carry-block is on, or if all the flags are off, then it produces the low 16 bits
of the product. If the carry-block is off and the sign flag is on, then it
produces the high 16 bits of the product, to facilitate multiplication of large
numbers. For all other combinations of flags, the behavior of MUL is undefined by
the HERA specification; in hera-py, MUL will produce the low 16 bits for the other
combinations of flags.
MUL sets the following flags:
sign, if the product was negative
zero, if the product was zero
carry, if the product exceeded 16 bits
carry, if there was no need to borrow from the 2^16's place (i.e., A > B,
interpreting A and B as unsigned)
overflow, if signed integer overflow occurs (i.e., D != A * B under the signed
interpretation of the bits of A, B and D)
HERA programmers will often want to set the carry block flag (using SETCB())
before performing this operation, to avoid unexpected results.
"""
BITV = "1100 AAAA BBBB CCCC"
@staticmethod
def calculate(vm, left, right):
if vm.flag_sign and not vm.flag_carry_block:
# Take the high 16 bits.
left = to_u32(from_u16(left))
right = to_u32(from_u16(right))
result = ((left * right) & 0xFFFF0000) >> 16
else:
# Take the low 16 bits.
result = (left * right) & 0xFFFF
vm.flag_carry = result < left * right
vm.flag_overflow = from_u16(result) != from_u16(left) * from_u16(right)
return result
class AND(BinaryOp):
"""
AND(Rd, Ra, Rb)
Compute the bitwise and of Ra and Rb and store the result in Rd. AND's behavior
does not depend on the flags, but it does set the sign flag when the result is
negative and the zero flag when the result is zero.
"""
BITV = "1000 AAAA BBBB CCCC"
@staticmethod
def calculate(vm, left, right):
return left & right
class OR(BinaryOp):
"""
OR(Rd, Ra, Rb)
Compute the bitwise or of Ra and Rb and store the result in Rd. OR's behavior does
not depend on the flags, but it does set the sign flag when the result is negative
and the zero flag when the result is zero.
"""
BITV = "1001 AAAA BBBB CCCC"
@staticmethod
def calculate(vm, left, right):
return left | right
class XOR(BinaryOp):
"""
XOR(Rd, Ra, Rb)
Compute the bitwise xor of Ra and Rb and store the result in Rd. XOR's behavior
does not depend on the flags, but it does set the sign flag when the result is
negative and the zero flag when the result is zero.
"""
BITV = "1101 AAAA BBBB CCCC"
@staticmethod
def calculate(vm, left, right):
return left ^ right
class INC(AbstractOperation):
"""
INC(Rd, v)
Increment the register by v, an integer between 1 and 64 inclusive. INC sets the
same flags as the equivalent ADD instruction would, but it ignores the carry flag
when computing its result.
"""
P = (REGISTER, range(1, 65))
BITV = "0011 AAAA 10bb bbbb"
def execute(self, vm):
target, value = self.args
original = vm.load_register(target)
result = (value + original) & 0xFFFF
vm.store_register(target, result)
vm.set_zero_and_sign(result)
vm.flag_overflow = from_u16(result) != from_u16(original) + value
vm.flag_carry = value + original >= 2 ** 16
vm.pc += 1
def assemble(self):
# The increment value encoded in the instruction is one less than the actual
# increment, i.e. INC(R1, 1) is assembled as if it were INC(R1, 0) since the
# latter is illegal.
bv = super().assemble()
return bytes([bv[0], bv[1] - 1])
@classmethod
def disassemble(cls, arg0, arg1):
return cls(arg0, Token(Token.INT, arg1.value + 1))
class DEC(AbstractOperation):
"""
DEC(Rd, v)
Decrement the register by v, an integer between 1 and 64 inclusive. DEC sets the
same flags as the equivalent SUB instruction would, but it ignores the carry flag
when computing its result.
"""
P = (REGISTER, range(1, 65))
BITV = "0011 AAAA 11bb bbbb"
def execute(self, vm):
target, value = self.args
original = vm.load_register(target)
result = to_u16((original - value) & 0xFFFF)
vm.store_register(target, result)
vm.set_zero_and_sign(result)
vm.flag_overflow = from_u16(result) != from_u16(original) - value
vm.flag_carry = original < value
vm.pc += 1
def assemble(self):
# The decrement value encoded in the instruction is one less than the actual
# decrement, i.e. DEC(R1, 1) is assembled as if it were DEC(R1, 0) since the
# latter is illegal.
bv = super().assemble()
return bytes([bv[0], bv[1] - 1])
@classmethod
def disassemble(cls, arg0, arg1):
return cls(arg0, Token(Token.INT, arg1.value + 1))
class LSL(UnaryOp):
"""
LSL(Rd, Rb)
Shift the value of Rb one bit to the left, and store the result in Rd. If the
carry-block is off and the carry is set, then the bit 1 is shifted in on the
right; otherwise 0 is shifted in. The bit shifted out becomes the carry flag.
LSL sets the sign flag when the result is negative, and the zero flag when the
result is zero.
"""
BITV = "0011 AAAA 0000 BBBB"
@staticmethod
def calculate(vm, arg):
carry = 1 if vm.flag_carry and not vm.flag_carry_block else 0
result = ((arg << 1) + carry) & 0xFFFF
vm.flag_carry = arg & 0x8000
return result
class LSR(UnaryOp):
"""
LSR(Rd, Rb)
Shift the value of Rb one bit to the right, and store the result in Rd. If the
carry-block is off and the carry is set, then the bit 1 is shifted in on the
left; otherwise 0 is shifted in. The bit shifted out becomes the carry flag.
LSR sets the sign flag when the result is negative, and the zero flag when the
result is zero.
"""
BITV = "0011 AAAA 0001 BBBB"
@staticmethod
def calculate(vm, arg):
carry = 2 ** 15 if vm.flag_carry and not vm.flag_carry_block else 0
result = (arg >> 1) + carry
vm.flag_carry = arg % 2 == 1
return result
class LSL8(UnaryOp):
"""
LSL8(Rd, Rb)
Shift the value of Rb eight bits to the left, and store the result in Rd.
LSL8 sets the sign flag when the result is negative, and the zero flag when the
result is zero.
"""
BITV = "0011 AAAA 0010 BBBB"
@staticmethod
def calculate(vm, arg):
return (arg << 8) & 0xFFFF
class LSR8(UnaryOp):
"""
LSR8(Rd, Rb)
Shift the value of Rb eight bits to the right, and store the result in Rd.
LSR8 sets the sign flag when the result is negative, and the zero flag when the
result is zero.
"""
BITV = "0011 AAAA 0011 BBBB"
@staticmethod
def calculate(vm, arg):
return arg >> 8
class ASL(UnaryOp):
"""
ASL(Rd, Rb)
Shift the value of Rb one bit to the left, and store the result in Rd. If the
carry-block is off and the carry is set, then the bit 1 is shifted in on the
left; otherwise 0 is shifted in. The bit shifted out becomes the carry flag.
ASL sets the sign flag when the result is negative, and the zero flag when the
result is zero.
The only difference between ASL and LSL is that ASL will additionally set the
overflow flag to the same value it would receive after executing ADD(Rd, Rb, Rb).
"""
BITV = "0011 AAAA 0100 BBBB"
@staticmethod
def calculate(vm, arg):
carry = 1 if vm.flag_carry and not vm.flag_carry_block else 0
result = ((arg << 1) + carry) & 0xFFFF
vm.flag_carry = arg & 0x8000
vm.flag_overflow = arg & 0x8000 and not result & 0x8000
return result
class ASR(UnaryOp):
"""
ASL(Rd, Rb)
Shift the value of Rb one bit to the left, and store the result in Rd. If the
carry-block is off and the carry is set, then the bit 1 is shifted in on the
left; otherwise 0 is shifted in. The bit shifted out becomes the carry flag.
ASL sets the sign flag when the result is negative, and the zero flag when the
result is zero.
The only difference between ASL and LSL is that ASL will additionally set the
overflow flag to the same value it would receive after executing ADD(Rd, Rb, Rb).
"""
BITV = "0011 AAAA 0101 BBBB"
@staticmethod
def calculate(vm, arg):
# This is a little messy because right shift in Python rounds towards
# negative infinity (7 >> 1 == -4) but in HERA it rounds towards zero
# (7 >> 1 == -3).
if arg & 0x8000:
if arg & 0x0001:
result = ((arg >> 1) | 0x8000) + 1
else:
result = arg >> 1 | 0x8000
else:
result = arg >> 1
vm.flag_carry = arg & 0x0001
return result
class SAVEF(AbstractOperation):
"""
SAVEF(Rd)
Save the flags to Rd. The flags are stored in the following bits:
0: sign
1: zero
2: overflow
3: carry
4: carry-block
The higher bits of Rd are set to 0.
"""
P = (REGISTER,)
BITV = "0011 AAAA 0111 0000"
def execute(self, vm):
value = (
int(vm.flag_sign)
+ 2 * int(vm.flag_zero)
+ 4 * int(vm.flag_overflow)
+ 8 * int(vm.flag_carry)
+ 16 * int(vm.flag_carry_block)
)
vm.store_register(self.args[0], value)
vm.pc += 1
class RSTRF(AbstractOperation):
"""
RSTRF(Rd)
Restore the flags from Rd.
Normally you should only invoke RSTRF on a register whose contents have previously
been set by a call to SAVEF.
"""
P = (REGISTER,)
BITV = "0011 AAAA 0111 1000"
def execute(self, vm):
value = vm.load_register(self.args[0])
vm.flag_sign = bool(value & 1)
vm.flag_zero = bool(value & 0b10)
vm.flag_overflow = bool(value & 0b100)
vm.flag_carry = bool(value & 0b1000)
vm.flag_carry_block = bool(value & 0b10000)
vm.pc += 1
class FON(AbstractOperation):
"""
FON(v)
Turn on the flags indicated by the bits of the 5-bit integer v.
0: sign
1: zero
2: overflow
3: carry
4: carry-block
"""
P = (U5,)
BITV = "0011 000a 0110 aaaa"
def execute(self, vm):
value = self.args[0]
vm.flag_sign = vm.flag_sign or bool(value & 1)
vm.flag_zero = vm.flag_zero or bool(value & 0b10)
vm.flag_overflow = vm.flag_overflow or bool(value & 0b100)
vm.flag_carry = vm.flag_carry or bool(value & 0b1000)
vm.flag_carry_block = vm.flag_carry_block or bool(value & 0b10000)
vm.pc += 1
class FOFF(AbstractOperation):
"""
FOFF(v)
Turn off the flags indicated by the bits of the 5-bit integer v. See the docs for
FON for a list of which bits of v correspond to which flags.
"""
P = (U5,)
BITV = "0011 100a 0110 aaaa"
def execute(self, vm):
value = self.args[0]
vm.flag_sign = vm.flag_sign and not bool(value & 1)
vm.flag_zero = vm.flag_zero and not bool(value & 0b10)
vm.flag_overflow = vm.flag_overflow and not bool(value & 0b100)
vm.flag_carry = vm.flag_carry and not bool(value & 0b1000)
vm.flag_carry_block = vm.flag_carry_block and not bool(value & 0b10000)
vm.pc += 1
class FSET5(AbstractOperation):
"""
FSET5(v)
Set the flags according to the bits of the 5-bit integer v. See the docs for FON
for a list of which bits of v correspond to which flags.
"""
P = (U5,)
BITV = "0011 010a 0110 aaaa"
def execute(self, vm):
value = self.args[0]
vm.flag_sign = bool(value & 1)
vm.flag_zero = bool(value & 0b10)
vm.flag_overflow = bool(value & 0b100)
vm.flag_carry = bool(value & 0b1000)
vm.flag_carry_block = bool(value & 0b10000)
vm.pc += 1
class FSET4(AbstractOperation):
"""
FSET4(v)
Like FSET5, except it does not affect the carry-block flag.
"""
P = (U4,)
BITV = "0011 110a 0110 aaaa"
def execute(self, vm):
value = self.args[0]
vm.flag_sign = bool(value & 1)
vm.flag_zero = bool(value & 0b10)
vm.flag_overflow = bool(value & 0b100)
vm.flag_carry = bool(value & 0b1000)
vm.pc += 1
class LOAD(AbstractOperation):
"""
LOAD(Rd, o, Rb)
Load into Rd the value at memory location Rb + o, where o is a 5-bit unsigned
integer.
LOAD sets the sign flag when the value loaded into Rd is negative, and the zero
flag when it is zero.
"""
P = (REGISTER, U5, REGISTER)
BITV = "010b AAAA bbbb CCCC"
def execute(self, vm):
target, offset, address = self.args
result = vm.load_memory(vm.load_register(address) + offset)
vm.set_zero_and_sign(result)
vm.store_register(target, result)
vm.pc += 1
class STORE(AbstractOperation):
"""
STORE(Rd, o, Rb)
Store the value in Rd at the memory location Rb + o, where o is a 5-bit unsigned
integer.
STORE does not set any flags.
"""
P = (REGISTER, U5, REGISTER)
BITV = "011b AAAA bbbb CCCC"
def execute(self, vm):
source, offset, address = self.args
vm.store_memory(vm.load_register(address) + offset, vm.load_register(source))
vm.pc += 1
class BR(RegisterBranch):
"""
BR(label)
Jump unconditionally to the given label.
Run `doc branch` for a detailed explanation of branching instructions.
"""
BITV = "0001 0000 0000 AAAA"
@staticmethod
def should(vm):
return True
class BRR(RelativeBranch):
"""
BRR(n)
Jump forward or backward n instructions.
Run `doc branch` for a detailed explanation of branching instructions.
"""
BITV = "0000 0000 aaaa aaaa"
def execute(self, vm):
if self.args[0] != 0:
vm.pc += self.args[0]
else:
vm.halted = True
class BL(RegisterBranch):
"""
BL(label)
Jump to the given label if either the sign flag or the overflow flag are on, but
not if both are.
Run `doc branch` for a detailed explanation of branching instructions.
"""
BITV = "0001 0010 0000 AAAA"
@staticmethod
def should(vm):
return vm.flag_sign ^ vm.flag_overflow
class BLR(RelativeBranch):
"""
BLR(n)
Jump forward or backward n instructions if either the sign flag or the overflow
flag are on, but not if both are.
Run `doc branch` for a detailed explanation of branching instructions.
"""
BITV = "0000 0010 aaaa aaaa"
@staticmethod
def should(vm):
return vm.flag_sign ^ vm.flag_overflow
class BGE(RegisterBranch):
"""
BGE(label)
Jump to the given label if the sign flag or the overflow flag are either both on
or both off.
Run `doc branch` for a detailed explanation of branching instructions.
"""
BITV = "0001 0011 0000 AAAA"
@staticmethod
def should(vm):
return not (vm.flag_sign ^ vm.flag_overflow)
class BGER(RelativeBranch):
"""
BGER(n)
Jump forward or backward n instructions if the sign flag or the overflow flag are
either both on or both off.
Run `doc branch` for a detailed explanation of branching instructions.
"""
BITV = "0000 0011 aaaa aaaa"
@staticmethod
def should(vm):
return not (vm.flag_sign ^ vm.flag_overflow)
class BLE(RegisterBranch):