Commit | Line | Data |
---|---|---|
f6e94cb2 AT |
1 | # Overview # |
2 | ||
3 | Although GCC claims to support PDP-11 targets there are some bugs that must be | |
4 | worked around both in GCC and the GNU assembler. | |
5 | ||
6 | # GNU binutils Bugs # | |
7 | ||
8 | ## Problem Description ## | |
9 | ||
10 | One of the addressing modes supported by the PDP-11 is 'index deferred', | |
11 | represented by `@X(Rn)`. This operand indicates that `Rn` contains a pointer | |
12 | which should be dereferenced and the result added to `X` to generate a new | |
13 | pointer to the final location. For example, consider the following four values, | |
14 | one stored in a register and the other three in memory. Then `@2(R1)` is the | |
15 | value `222`. | |
16 | ||
17 | R1: 1000 | |
18 | 1000: 2000 | |
19 | 2000: 111 | |
20 | 2002: 222 | |
21 | ||
22 | Similarly, `@0(R1)` is the value `111`. In most PDP-11 assemblers, including | |
23 | DEC's MACRO-11 assembler, the string `@(Rn)` is an alias to `@0(Rn)`. But when | |
24 | the GNU assembler encounters `@(Rn)` it assembles it as though it were `(Rn)`, | |
25 | a single level of indirection instead of two levels! | |
26 | ||
27 | If we're only writing assembly then we can work around this bug by always using | |
28 | the form `@0(Rn)`. But what if we're writing C and using GCC to compile it? | |
29 | Consider the following C code example, taken directly from some stack-based | |
30 | debugger code written for the PDP-11. | |
31 | ||
32 | uint16_t ** csp = (uint16_t **) 070000; | |
33 | *csp = (uint16_t *) 060000; | |
34 | **csp = 0; | |
35 | ||
36 | When GCC transpiles this to assembly it generates code of the form `@(Rn)` when | |
37 | assigning a value to `**csp` thus causing the value `0` to overwrite the value | |
38 | `060000` at `*csp` if GNU `as` is used to assemble the code. | |
39 | ||
40 | ## Solution Description ## | |
41 | ||
42 | The following patch, tested on GNU binutils 2.28, fixes the bug. Since it | |
43 | overloads the `operand->code` variable to pass unrelated state information to | |
44 | `parse_reg()` I haven't submitted it for inclusion in GNU binutils. Once I'm | |
45 | done bug-hunting in my toolchain I will clean up all the fixes and submit them | |
46 | in one bundle. | |
47 | ||
48 | --- tc-pdp11.c 2017-06-24 22:33:00.260210000 -0700 | |
49 | +++ tc-pdp11.c.fixed 2017-06-24 22:32:12.455205000 -0700 | |
50 | @@ -431,6 +431,9 @@ | |
51 | { | |
52 | LITTLENUM_TYPE literal_float[2]; | |
53 | ||
54 | + /* Store the value (if any) passed by parse_op_noreg() before parse_reg() overwrites it. */ | |
55 | + int deferred = operand->code; | |
56 | + | |
57 | str = skip_whitespace (str); | |
58 | ||
59 | switch (*str) | |
60 | @@ -451,6 +454,15 @@ | |
61 | operand->code |= 020; | |
62 | str++; | |
63 | } | |
64 | + /* | |
65 | + * This catches the case where @(Rn) is interpreted as (Rn) rather than @0(Rn) | |
66 | + */ | |
67 | + else if (deferred) | |
68 | + { | |
69 | + operand->additional = 1; | |
70 | + operand->word = 0; | |
71 | + operand->code |= 060; | |
72 | + } | |
73 | else | |
74 | { | |
75 | operand->code |= 010; | |
76 | @@ -581,6 +593,12 @@ | |
77 | ||
78 | if (*str == '@' || *str == '*') | |
79 | { | |
80 | + /* | |
81 | + * operand->code is overwritten by parse_reg() inside parse_op_no_deferred() | |
82 | + * We use it to temporarily catch the alias @(Rn) -> @0(Rn) since | |
83 | + * parse_op_no_deferred() starts at str+1 and thus misses the '@'. | |
84 | + */ | |
85 | + operand->code |= 010; | |
86 | str = parse_op_no_deferred (str + 1, operand); | |
87 | if (operand->error) | |
88 | return str; |