-
Notifications
You must be signed in to change notification settings - Fork 2
/
assembler.mi
841 lines (751 loc) · 31 KB
/
assembler.mi
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
dnl $ Id: $
dnl Copyright{2000,2001}: Albert van der Horst, HCC FIG Holland by GNU Public License
@section Introduction
Via forthurl({http://home.hccnet.nl/a.w.m.van.der.horst/ciforth.html}) you
can find here a couple of assemblers, to complement the generic
ciforth system. The assemblers are not part of the thisforth
package, and must be fetched separately. They are based on the
postit/fixup principle. The assembler that is present in the
blocks, is based on the same principle, but is less
sophisticated, especially regards error detection. If you use
that one, you can still benefit from this section by the
background information it gives.
_BOOTED_({{On this stand alone version of ciforth, you only
have the assembler in blocks, but they are not documented
separately. So you have to keep up with this description. These
files are not part of this distribution, maybe you but they are
available at my site, see chapter 3.) }})
forthbreak
prototype : still present in the forth.lab ("blocks"), usable and cleaned up
forthbreak
forthfile({ass.frt}) : the 80-line 8086 assembler (no error detection)
forthbreak
forthfile({asgen.frt}) : generic part of postit/fixup assembler
forthbreak
forthfile({as80.frt}) : 8080 assembler, requires forthfile({asgen.frt})
forthbreak
forthfile({asi86.frt}) : 8086 assembler, requires forthfile({asgen.frt})
forthbreak
forthfile({asi586.frt}) : 80386 assembler, requires forthfile({asgen.frt})
forthbreak
forthfile({ps.frt}) : generate opcode sheets
forthbreak
forthfile({p0.asi586.ps}) : first byte opcode for asi586 assembler
forthbreak
forthfile({p0F.asi586.ps}) : two byte opcode for same that start with 0F.
forthbreak
forthfile({test.mak}) : makefile, i.e. with targets for opcode sheets.
forthbreak
De forthfile({asi586.frt}) (containing the full 80386 instruction set) is in
many respects non-compliant to Intel syntax. The instruction
mnemonics are redesigned in behalf of reverse engineering.
There is a one to one correspondence between mnemonics and
machine instructions. In principle this would require a
monumental amount of documentation, comparable to parts of
Intel's architecture manuals. Not to mention the amount of work
to check this. I circumvent this. Opcode sheets for this
assembler are generated by tools automatically, and you can ask
interactively how a particular instructions can be completed.
This is a viable alternative to using manuals, if not more
practical. (Of course someone has to write up the descriptions,
I am happy Intel has done that.).
So look at my opcode sheets. If you think an instruction would be
what you want, type it in and ask for completion. If you are at all
a bit familiar, most of the time you can understand what your options are.
If not compare with an Intel opcode sheet, and look up the instruction
that sits on the same place. If you don't understand them, you can still
experiment in a Forth to find out.
Now for the bad news. The assembler in the Library Addressable
by Blocks (block file) hasn't any of those feature. It is
intended for incidental use, to speed up a crucial word. Worse
yet, the opcodes are not always the same as used here, and the
comma-ers are even mostly different. You have to resort to that
old game, reading the source.
@section Reliability
I skimped on write up. I didn't skimp on testing, at least not for forthfile({asi586.frt}).
It is tested in this way:
forthenumerate
forthitem All instructions are generated. (Because this uses the same
mechanism as checking during entry, it is most unlikely that you will get an
instruction assembled that is not in this set.)
forthitem They are assembled.
forthitem They are disassembled again, must come out the same.
forthitem They are disassembled by a different tool (GNU's objdump),
and the output is compared with 3. This has been done manually,
just once. Bugs where revealed, yes... in the other tool.
forthendenumerate
This leaves room for a defect of the following type:
A valid instruction is rejected or has been totally overlooked.
But opcode maps reveal their Terra incognita relentlessly. So I
am quite confident to promise a bottle of good Irish whiskey to
the first one to come up with a defect in this assembler.
The full set of instructions, with all operand combinations sit
in a file for reference. This is all barring the 256-way forthsamp({SIB})
construction and prefixes, or combinations thereof. This would
explode this approach to beyond Terabytes. It is also not
practical for the Alpha with 32K register combinations per
instruction.
@section Principle of operation
In making an assembler for the Pentium it turns out that
the in-between-step of creation defining words for each type
of assembly gets in the way. There are just too many of them.
MASM heavily overloads the instruction, in particular forthsamp({MOV}) .
Once I used to criticise Intel because they had an unpleasant to use
instruction set with forthsamp({MOV}) forthsamp({MVR}) and forthsamp({MVI}) for move instructions. In
hindsight that was not too bad and I am returning to that.
(I mean they are really different instructions, it might have been
better if they weren't. But an assembler must live up to the truth.)
Where the Intel folks really go overboard is with the disambiguation of
essentially ambiguous constructs, by things as forthsamp({OFFSET}) forthsamp({BYTE POINTER})
forthsamp({ASSUME}) . You can no longer find out what the instruction means by itself.
forthbreak
The simplest example is
forthexample({ INC [BX]})
Are we to increment the byte or the word at BX? ( Intel's solution : forthsamp({INC BYTE POINTER BX}))
Contrarily here we adapt the rule : if an instruction doesn't determine the operand
size (some do, like forthcode({LEA,}) ), then a size fixup is needed (
forthsamp({X|}) or forthsamp({B|}) ). forthbreak
In this assembler this looks like
forthexample({ INC, B| D0 [BX] })
This is completely unambiguous.
These are the phases in which this assembler handles an instruction:
forthitemize
forthitem
POSTIT phase:
forthcode({MOV,}) assembles a two byte instruction with holes.
forthitem
FIXUP phase:
forthcode({X|}) or forthcode({B|}) fits in one of the holes left.
forthitem
COMMA phase:
forthcode({IX,}) or forthcode({X,}) add addresses and immediate data.
forthenditemize
Doesn't that lay a burden on the programmer? Yes.
He has to know exactly what he is doing.
But assembly programming is dancing on a rope. The Intel syntax tries
to hide from you were the rope is. A bad idea. There is no such thing as
assembly programming for dummies.
An advantage is that you are more
aware of what instructions are there.
Because you see the duplicates.
Now if you are serious, you have to study the forthfile({asgen.frt}) and
forthfile({as80.frt}) sources. You better get your feet wet with forthfile({as80.frt})
before you attack the Pentium. The way forthsamp({SIB}) is handled is so
clever, that sometimes I don't understand it myself.
Another invention in this assembler is the forthdefi({family of instructions}).
Assembler instructions are grouped into families with identical fixups, and
a increment for the opcodes. These are defined as a group by a single execution of a defining
word. For each group there is one opportunity to get the opcode wrong; formerly that
was for each opcode.
@section The 8080 assembler
The 8080 assembler doesn't take less place than Cassady's . (You bet that the postit-fixup principle pays off for
the Pentium, but not for the 8080.) But... The regularities are much
more apparent. It is much more difficult to make a mistake with the
code for the forthsamp({ADD}) and forthsamp({ADI}) instructions. And there is information there
to the point that it allows to make a disassembler that is independant
of the instruction information, one that will work for the 8086, look
at the pop family.
First I had
forthexample({ 38 C1 02 4 1FAMILY, POP -- PUSH RST ( B'|) })
(cause I started from an existing assembler.)
But of course forthcode({RST}) (the restart instruction) has nothing
to
do
with registers, so it gets a separated out. Then the exception, represented
by the hole forthsamp({--}) disappears.
The bottom line is : the assembler proper now takes 22 lines of code.
Furthermore the ``call conditional'' and ``return conditional''
instructions where missing. This became apparent as soon as I printed the
opcode sheets.
For me this means turning ``jump conditional'' into a family.
@section Opcode sheets
Using forthfile({test.mak}) (on a linux computer in lina) you can generate opcode sheets
by "make asi586.ps". For the opcode sheets featuring a n-byte prefix you must
pass the forthsamp({PREFIX}) to make and a forthsamp({MASK}) that covers the prefix and the byte opcode, e.g.
forthsamp({make asi586.ps MASK=FFFF PREFIX=0F})
The opcode sheets forthfile({p0.asi586.ps}) and forthfile({p0F.asi586.ps}) are already made and can be
printed on a PostScript printer or viewed with e.g. forthsamp({gv}).
Compare the opcode sheets with Intel's to get an overview of what I have done
to the instruction set. In essence I have re-engineered it to make it reverse
assemblable, i.e. from a disassembly you can regenerate the machine code.
This is forthemph({not}) true for Intel's instruction set, e.g. Intel has the same opcode for
forthsamp({MOV, X| T| AX'| R| BX| }) and forthsamp({MOV, X| F| BX'| R| AX|})
and, as of this writing,
GNU's objdump gives the same disassembly for both forthsamp({IMUL})s despite the difference
between a 32*32 > 64 and a 32*32 > 32 operation:
forthsamp({IMUL|AD, X| R| BX|}) or forthsamp({IMUL, AX'| R| BX|})
To get a reminder of what instructions there are type
forthcode({SHOW-OPCODES}) . If you are a bit familiar with the
opcodes you are almost there. For if you want to know what the
precise instruction format of e.g. forthcode({IMUL|AD,}) just
type forthsamp({SHOW: IMUL|AD,}) You can also type
forthcode({SHOW-ALL,}) but that takes a lot of time and is more
intended for test purposes.
_VERBOSE_({
@section Details about the 80386 instructions
Read the introductory comment of forthfile({asgen.frt}) for how the assembler
keeps track of the state, using the forthcode({BI}) forthcode({BY}) forthcode({BA}) tallies.
forthenumerate
forthitem
A word ending in forthkey({,}) reserves place in the dictionary.
It stand for one assembler instruction.
The start of the instruction is kept and there is a bitfield (the tally) for
all bits that belong to the instruction, if only mentally. These bits are
put as comment in front of the instruction and they are considered filled
in. They also imply the instruction length.
forthitem
A word ending in forthkey({|}) is a fixup, it forthcode({OR})s in some bits
in an already assembled instruction. Again there is a mask in front
of fixups and in using the fixup these bits are considered to be filled
in. A fixup cannot touch data before the start of the latest instruction.
forthitem
Families can be constructed from instructions or fixups with the
same tally bit field, provided they differ by a fixed increment. If
data or addresses following differ this is unwise.
forthitem
The part before a possible forthkey({|}) in an instruction -- but excluding an
optional trailing I -- is the opcode. Opcodes define indeed a same action.
forthitem
The part after forthkey({|}) in an instruction may be
considered a built in fixup where irregularity forbids to use a
real fixup. A X stands for xell or natural data width. This is
16 bit for a 16 bit assembler and 32 bit for a 32 bit
assembler. These can be overruled with forthcode({ AS:, }) dnl
applying to forthcode({DX|}) and forthcode({MEM|}) and with
forthcode({ OS:, }) applying to tdata required where there is
an I suffix.
forthitem
Width fixups determine the data width : forthcode({X|})
(xell or natural data width 16/32 ) or forthcode({B|}) ( 8 bit) unless
implied.
forthitem
Instruction ending in forthkey({I}) have an immediate data field after all
fixups. This can be either forthcode({X,}) (xell or natural data
width)
or
forthcode({B,}) forthcode({W,}) forthcode({L,}) ( 8 16 32 bit).
If there are width fixups they should correspond with the data.
forthitem
Instructions ending in forthsamp({|SEG}) builtin fixup
(segments) require forthcode({SEG,}) (which is always 16 bits).
If forthcode({X,}) cannot be used caused by width overrules,
the programmer should carefully insert forthcode({W,}) or
forthcode({L,}) whatever appropriate.
forthitem
With r/m you can
have offsets (for forthcode({DB|}) and forthcode({DX|}) ) that
must be assembled using forthcode({B,}) or forthcode({X,}) but
mind the previous point.
forthitem
Instruction with r/m can have a register instead of memory indicated
by the normal fixups forthcode({AX|}) etc.
forthitem
If instructions with r/m have another register, that one is indicated
by a prime such as forthcode({AX'|}). Or if an instruction can handle two general
registers, the one that cannot be replaced by a memory reference gets a prime.
forthitem
Unless forthcode({T|}) forthcode({F|}) (to/from) are present, a primed register is the modifiable one,
else forthcode({T|}) forthcode({F|}) refer to the primed register. The primed register is the one
that cannot be replaced by a memory reference.
forthitem
At the start of an instruction the mask of the previous instruction
plus fixup should add up non-overlappingly to a full field.
Offsets and immediate data should have been comma-ed in in that order.
forthitem
A fixup or instruction
is mightier than an other one
if its mask contains all the bits of that other one.
The second fixup or instruction
shall then not be used.
forthitem
Instructions ending in forthsamp({ :, }) are prefixes and are considered in their own
right. They have no fixups.
forthitem
The Scaled Index Byte is handled in the following way:
The fixup forthcode({SIB|}) closes the previous instruction (i.e.
fill up its bit field), but possible immediate data and offsets are kept.
Then forthcode({SIB,}) starts a new instruction.
forthitem The forthcode({SET,}) instruction unfortunately requires a duplicate of the
O| etc. fixups of the forthcode({J,}) and forthcode({J|X,}) instructions.
forthitem
Some single byte instructions require forthcode({X'|}) and
forthcode({B'|}) instead of forthcode({X|}) and forthcode({B|}) dnl
that are used for the ubiquitous instructions with r/m.
forthendenumerate
Hand disassembling can be done as follows.
forthenumerate
forthitem
Find the mightiest instruction that agrees with the data at the
program counter. Tally the bits. The instructions length follows from
the instruction. As does the presence of address offsets and immediate
data. The dictionary may be organized such that the mightiest
instruction is always found first.
forthitem
Find the mightiest fixup that agrees with untallied bits.
forthitem
If not all bits have been tallied go to 2
forthitem
Disassemble the address offsets and immediate data, in accordance with
the instruction. Length is determined from fixups and prefix bytes.
The result must agree with the instruction in the first place.
forthendenumerate
@section Using 16 bits code in the 32 bit assembler
In general X refers to Xell. So in 16 bit mode or with a 16 bit prefix
AX is to mean the Intel forthcode({AX}) instead of what is normal:
forthcode({EAX}) .
It is thus possible to insert a patch of 16 bit code in 32 bit code
all with the 32 bit assembler. This can be necessary in system
programming. Just use forthsamp({MOV, AX'| R| BX|}) and in 16 bit mode it refers
to 16 bit registers.
If an address overwrite suffix applies, the indexing fixups ending in
a prime must be used, e.g. forthsamp({[BX+SI]'}) instead of
forthcode({[AX]}) for code running in a default 32 bit environment.
(Otherwise use the 8086 assembler) But during system programming only
the programmer knows what is going on, so some error messages are
suppressed.
While using 16 bits code, whenever you get error messages and
you are sure you know better than the assembler, put
forthcode({!TALLY}) before the word that gives the error
messages, and they will be suppressed.
@section This assembler is not yet integrated in the generic Forth
In the generic Forth automatically a 32 bit assembler is loaded
if the Forth itself is 32 bits and a 16 bit assembler for the
16 bit forths. Adding the extra complexity to run the 16 bit
assembler on a 32 bit system would be the drip that overflows
the bucket. This is no restriction for what code can be
generated. forthemph({The built in assembler has no error
checking and may have bugs the very extensively tested})
forthfile({asi586.frt}) forthemph({has not.})
});_END_({_VERBOSE_})
_VERBOSE_({
@section A rant about redundancy
You could complain about redundancy in postit-fixup assemblers.
But there is an advantage to that, it helps detect invalid
combinations of instructions parts. They look bad at first
sight. What about forthbreak forthsamp({MOV, B| T| [BX+SI] R|
AX|}) forthbreak forthsamp({MOV,}) needs two operands but there
is no primary operand in sight. forthcode({[BX+SI]}) would not
qualify. and not even forthcode({BX|}) because the primary
operand should be marked with a prime. forthbreak
forthsamp({MOV, X| T| BX| AX|}) looks bad because you know
forthcode({BX|}) and forthcode({AX|}) work on the same bit
fields, so it easy to remember you need the prime.
forthcode({T|}) and forthcode({F|}) refer to the primary
operands, so gone is the endless confusion about what is the
destination of the move. forthbreak forthsamp({MOV, X| T| BX'|
R| AL})| looks bad , because forthcode({AL|}) could not
possibly qualify as an X register. forthbreak forthsamp({MOV,
X| T| BX'| AX|}) looks bad , because soon you will adopt the
habit that one of the 8 main register always must be preceeded
with forthfile({T|}) forthcode({F|}) or forthcode({R|}) .
forthbreak forthsamp({MOV, X| T| BX'| R| AX|}) looks right but
you still can code forthsamp({MOV, AX| BX'| R| T| X|}) if you
prefer your fixups in alphabetic order. (A nice rule for those
Code Standard Police out there?).
And yes forthsamp({ES: OS:
MOV, X| T| DI'| SIB|, DX| [BP +8* AX] FFFFF800 X,}) though
being correct, and in a logical order, looks still bad, because
it forthemph({is}) bad in the sense that the Pentium design got
overboard in complication. (This example is from the built-in assembler,
the one in forthfile({asi586.frt}) redefines forthcode({[BP}) c.s. to get rid of the forthcode({SIB|,}) instruction.)
forthbreak
First remark: lets assume this is
32 bit code,(because otherwise there
would not be a forthcode({SIB,}) sure?)
forthbreak
There are 3 sizes involved :
forthitemize
forthitem
The size of the data transported this is always the forthsamp({X}) as
in forthcode({X|}) .
Then the first forthcode({X|}) changes its meaning to 16 bit, because
of the forthcode({OS:}) prefix.
forthitem
The forthcode({X}) in forthcode({DX|}) and in forthcode({X,}) must
agree and are 32 bits because you are in a 32 bits segment and this
cannot be overridden.
forthitem
The offset (in forthsamp({+AX]}) ) is counted in 64 bits, a strange
array for fetching the forthsamp({DI}) but anyway.
forthenditemize
And .. by the way the data is placed in the extra segment.
Add a bit of awareness of the cost of the instructions in execution time
and take care of the difference between the Pentium processors MMX en III
and what not and you will see that assembly program is not for the faint
of heart. The forthsamp({ASSUME}) of the MASM assembler buys you
nothing,
but
inconvenience.
});_END_({_VERBOSE_})
_VERBOSE_({
@section Reference opcodes
Table one contains all the opcodes used in forthfile({asi586.frt}) in alphabetic order,
with forthkey({|}) sorted before any letter.
All opcodes on the first position are the same as Intel opcodes.
You can use it in two ways.
You want the opcode for some known Intel opcode.
forthbreak
Look it up in the first column. One of the opcodes on that
line is what you want. To
pick the right one, consider the extension that are explained
in table 2. Exception: forthsamp({PUSHI}) is not on the line with forthsamp({PUSH}) .
Some times you have to trim built in size designators, e.g. you
look up forthsamp({LODSW}) but you are stuck at forthcode({LODS}) , so that's it.
With forthsamp({ SHOW: LODS, }) you can see what the operands look like.
forthbreak
You want to know what a POSIT/FIXUP code does. Look it up in the table,
on the first word on the line you should recognize an Intel opcode. For example you have
forthcode({ CALLFAROI, })
That is at the line with forthcode({CALL,}) . So the
combination of operands for forthcode({CALLFAROI,}) are to be
found in the description for forthsamp({CALL}) in the Intel
manuals.
Note. Some things are ugly. forthcode({LDS,}) should be
forthcode({L|DS,}) . I would replace forthcode({MOV|FA,}) by
forthcode({STA,}) and forthcode({MOV|TA,}) by forthcode({LDA, }) . But
that would make the cross referencing more problematic. Note. The
meaning of the operands for forthsamp({JMP}) and forthsamp({JMPFAR})
are totally different. So my suffices are different.
Table 1. Opcode cross reference.
@table @var
forthitem AAA,
forthitem AAD,
forthitem AAM,
forthitem AAS,
forthitem ADC, ADCI, ADCI|A, ADCSI,
forthitem ADD, ADDI, ADDI|A, ADDSI,
forthitem AND, ANDI, ANDI|A,
forthitem ARPL,
forthitem AS:,
forthitem BOUND,
forthitem BSF,
forthitem BSR,
forthitem BT, BTI,
forthitem BTC, BTCI,
forthitem BTR, BTRI,
forthitem BTS, BTSI,
forthitem CALL, CALLFAR, CALLFAROI, CALLO,
forthitem CBW,
forthitem CLC,
forthitem CLD,
forthitem CLI,
forthitem CLTS,
forthitem CMC,
forthitem CMP, CMPI, CMPI|A,
forthitem CMPS, CMPSI,
forthitem CPUID,
forthitem CS:,
forthitem CWD,
forthitem DAA,
forthitem DAS,
forthitem DEC, DEC|X,
forthitem DIV|AD,
forthitem DS:,
forthitem ENTER,
forthitem ES:,
forthitem FS:,
forthitem GS:,
forthitem HLT,
forthitem IDIV|AD,
forthitem IMUL, IMUL|AD, IMULI, IMULSI,
forthitem INC, INC|X,
forthitem INS,
forthitem INT, INT3, INTO,
forthitem IN|D, IN|P,
forthitem IRET,
forthitem J, J|X, (Intel Jcc)
forthitem JCXZ,
forthitem JMP, {JMPFAR,} JMPFAROI, JMPO, JMPS,
forthitem LAHF,
forthitem LAR,
forthitem LDS,
forthitem LEA,
forthitem LEAVE,
forthitem LES,
forthitem LFS,
forthitem LGDT,
forthitem LGS,
forthitem LIDT,
forthitem LLDT,
forthitem LMSW,
forthitem LOCK,
forthitem LODS,
forthitem LOOP, LOOPNZ, LOOPZ,
forthitem LSL,
forthitem LSS,
forthitem LTR,
forthitem MOV, MOV|CD, MOV|FA, MOV|SG, MOV|TA,
forthitem MOVI, MOVI|BR, MOVI|XR,
forthitem MOVS,
forthitem MOVSX|B, MOVSX|W,
forthitem MOVZX|B, MOVZX|W,
forthitem MUL|AD,
forthitem NEG,
forthitem NOT,
forthitem OR, ORI, ORI|A,
forthitem OS:,
forthitem OUTS,
forthitem OUT|D, OUT|P,
forthitem POP, POP|ALL, POP|DS, POP|ES, POP|FS, POP|GS, POP|SS, POP|X,
forthitem POPF,
forthitem PUSH, PUSH|ALL, PUSH|CS, PUSH|DS, PUSH|ES, PUSH|FS, PUSH|GS, PUSH|SS, PUSH|X,
forthitem PUSHF,
forthitem PUSHI|B, PUSHI|X,
forthitem RCL,
forthitem RCR,
forthitem REPNZ,
forthitem REPZ,
forthitem RET+, RET, RETFAR+, RETFAR,
forthitem ROL,
forthitem ROR,
forthitem SAHF,
forthitem SAR,
forthitem SBB, SBBI, SBBI|A, SBBSI,
forthitem SCAS,
forthitem SET, (Intel SETcc)
forthitem SGDT,
forthitem SHL,
forthitem SHLD|C, SHLDI,
forthitem SHR,
forthitem SHRD|C, SHRDI,
forthitem SIDT,
forthitem SLDT,
forthitem SMSW,
forthitem SS:,
forthitem STC,
forthitem STD,
forthitem STI,
forthitem STOS,
forthitem STR,
forthitem SUB, SUBI, SUBI|A, SUBSI,
forthitem TEST, TESTI, TESTI|A,
forthitem VERR,
forthitem VERW,
forthitem WAIT,
forthitem XCHG,
forthitem XCHG|AX,
forthitem XLAT,
forthitem XOR, XORI, XORI|A,
forthitem ~SIB,
@end table
Table 2 Suffixes
@table @var
forthitem I : Immediate operand
forthitem SI : Sign extended immediate operand
forthitem FAR : Far (sometimes combined with OI)
forthitem O : Operand
forthitem OI : Operand indirect
@end table
});_END_({_VERBOSE_})
@section The dreaded SIB byte
If you ask for the operands of a memory instruction (one of the
simple one is LGDT, ) instead of all the sib (forthdefi({scaled index byte}))
possibilities you see.
forthsamp({LGDT, DB| ~SIB| 14 SIB,, 18, B,})
This loads the general description table from an address
described by a sib-byte of 14.
The forthsamp({~SIB| 14 SIB,,}) may be replaced by any sib-specification of
the kind forthsamp({[AX +2* SI]}).
You can ask for a reminder of the 256 possibilities by
forthsamp({SHOW: ~SIB,})
For the curious:
Explanation of
forthsamp({LGDT, DB| ~SIB| 10 SIB,, 14, B,})
This way of specifying a sib-byte
would be perfectly legal, had I not hidden those words.
It shows what is going on: the instruction is completed by ~SIB|
telling the assembler that a comma-er forthcode({SIB,,}) is required.
Instead of the comma-er we use a forthcode({~SIB,}) instruction.
This specifies in fact a one byte opcode with three fields
examplified by forthsamp({[AX +2* SI})] (and
again you might say forthsamp({+2* SI] [AX}) with the same meaning.)
@section A last caveat
There is no way to communicate to the assembler whether the current
instructions are supposed to be executed in 16 or 32 bit mode. This
means that if you use the address overwrite prefix forthcode({AS:,}) dnl
and/or primed fixups forthcode({ [BX]' }) and/or run your code in 16
bit mode, you must be very careful.
As long as you stay away from the above, you can be sure that
valid instructions are correctly assembled and executed as you
specified and invalid instructions are rejected.
_VERBOSE_({
@section An incomplete and irregular guide to the instruction mnemonics.
The following is an attempted overview of the suffixes and fixup's
used. It may be of some help for using the assembler because it gives
some idea of some of the names.
It is not checked in a long time and was inaccurate and incomplete in the first place.
You may also find names that are only used in the block files and
not explained in table 1.
So beware!
Note that some of the instruction are Pentium and as yet
not present in the forthfile(asi586.frt) (which should still be called forthfile(asi386.frt)).
Never use an instruction that end in a ' (such as forthcode({ [BP+IS]'
}) except in case of address size overwrites forthemph({and}) you know
what you are doing.
Some instructions
forthbreak
SET : Byte Set on Condition
forthbreak
BT : Bit Test
forthbreak
BTR: Bit Test and Reset
forthbreak
BTS: Bit Test and Set
forthbreak
BTC: Bit Test and Complement
forthbreak
CPUID: CPU Identification
forthbreak
CLTS:
forthbreak
L : Load Full Pointer
forthbreak
LAR : Load Access Rights Byte
forthbreak
LLDT: Load Local Descriptor Table Register
forthbreak
LGDT: Load General Descriptor Table Register
forthbreak
LIDT: Load Interrupt Descriptor Table Register
forthbreak
LTR: Load Task Register
forthbreak
LMSW: Load Machine Status Word
forthbreak
MOV : Move
forthbreak
RSM:
forthbreak
RDTSC: Read from Time Stamp Counter
forthbreak
RDMSR: Read from Model Specific Register
forthbreak
SHLD: Double Precision Shift Left
forthbreak
SHRD: Double Precision Shift Right
forthbreak
SLDT: Store Local Descriptor Table Register
forthbreak
SMSW: Store Machine Status Word
forthbreak
VERR: Verify a Segment for Reading or Writing
forthbreak
WRMSR: Write to Model Specific Register
forthbreak
forthbreak
Suffices of the opcode
forthbreak
|ALL : All
forthbreak
|CD : Control/Debug register
forthbreak
|FS : Replaces FS| in irregular opcodes.
forthbreak
|GS : Replaces GS| in irregular opcodes.
forthbreak
|AD : Implicit A and Double result.
forthbreak
|C : Implicit C (count)
forthbreak
forthbreak
Items in Fixups.
forthbreak
Y| : Yes, Use the condition straight
forthbreak
N| : No, Use the condition inverted
forthbreak
O| : Overflow
forthbreak
C| : Carry
forthbreak
Z| : Zero
forthbreak
CZ| : C || Z (unsigned <= )
forthbreak
S| : Sign ( <0 )
forthbreak
P| : Parity (even)
forthbreak
L| : S != O (signed < )
forthbreak
LE| : L || Z (signed <= )
forthbreak
<AH| : As a second register is a source,
different in size from the destination.
T| : To (primed or special register)
forthbreak
F| : From (primed or special register)
forthbreak
V| : Variable number
forthbreak
forthbreak
OB : Obligatory byte
forthbreak
OW : Obligatory word (=16bits)
forthbreak
});_END_({_VERBOSE_})
@section Assembler Errors
Errors are identified by a number. They are globally unique, so
assembler error numbers do not overlap with other ciforth error numbers,
or errors returned from operating system calls.
_VERBOSE_({Of course the error numbers are given in decimal, always.})
The errors whose message starts with forthsamp({AS:}) are used by the PostIt FixUp assembler
in the file forthfile({asgen.frt}). forthxref({Errors}) for other errors.
forthitemize
forthitem
forthsamp({ciforth ERROR # 26 : AS: PREVIOUS INSTRUCTION INCOMPLETE})
You left holes in the instruction before the current one, i.e.
one or more fixups like forthcode({X|}) are missing. Or you forget
to supply data required by the opcode like forthcode({OW,}) .
_VERBOSE_({With forthcode({SHOW:}) you can see what completions of your opcode
are legal.})
forthitem
forthsamp({ciforth ERROR # 27 : AS: INSTRUCTION PROHIBITED IRREGULARLY})
The instruction you try to assemble would have been legal, if Intel
had not made an exception just for this combination. This situation
is handled by special code, to issue just this error.
forthitem
forthsamp({ciforth ERROR # 28 : AS: UNEXPECTED FIXUP/COMMAER})
You try to complete an opcode by fixup's (like forthcode({X|}))
or comma-ers (like forthcode({OW,}) ) in a way that conflicts
with what you specified earlier. So the fixup/comma-er word at
which this error is detected conflicts with either the opcode,
or one of the other fixups/comma-ers.
dnl FIXME This explanation is the same as the following.
_VERBOSE_({For example forthcode({B|}) (byte size) with a forthcode({LEA,}) opcode
or with a forthcode({DI|}) operand.})
forthitem
forthsamp({ciforth ERROR # 29 : AS: DUPLICATE FIXUP/UNEXPECTED COMMAER})
You try to complete an opcode by fixup's (like forthcode({X|}) dnl
) or comma-ers (like forthcode({OW,}) ) in a way that conflicts
with what you specified earlier. So the fixup/comma-er word at
which this error is detected conflicts with either the opcode,
or one of other fixups/comma-ers.
dnl FIXME This explanation is the same as the previous.
_VERBOSE_({For example forthcode({B|}) (byte size) with a forthcode({LEA,}) opcode
or with a DI| operand.})
forthitem
forthsamp({ciforth ERROR # 30 : AS: COMMAERS IN WRONG ORDER})
The opcode requires more than one data item to be comma-ed in, such as
immediate data and an address. However you put them in the wrong order.
Use forthcode({SHOW:}) .
forthitem
forthsamp({ciforth ERROR # 31 : AS: DESIGN ERROR, INCOMPATIBLE MASK})
This signals an internal inconsistency in the assembler itself.
If you are using an assembler supplied with ciforth, you can report
this as a defect (``bug'').
The remainder of this explanation is intended for the writers
of assemblers.
The bits that are filled in by an assembler word are outside
of the area were it is supposed to fill bits in. The latter
are specified separately by a mask.
forthitem
forthsamp({ciforth ERROR # 32 : AS: PREVIOUS OPCODE PLUS FIXUPS INCONSISTENT})
The total instruction with opcode, fixups and data is ``bad''.
Somewhere there are parts that are conflicting. This may be another
one of the irregularities of the Intel instruction set. Or the
forthcode({BAD}) data was preset with bits to indicate that you
want to prohibit this instruction on this processor, because it
is not implemented. Investigate forthcode({BAD}) for two consecutive bits
that are up, and inspect the meaning of each of the two bits.
forthenditemize