View unanswered posts | View active topics It is currently Thu Mar 28, 2024 2:08 pm



Reply to topic  [ 52 posts ]  Go to page Previous  1, 2, 3, 4  Next
 PLASMA virtual machine on the OPC6 
Author Message

Joined: Wed Jan 09, 2013 6:54 pm
Posts: 1780
We don't have an overflow bit, so (I think) the unsigned case is easy: you do the subtraction and check the carry out. But the signed case is difficult, in effect you end up emulating a machine with an overflow bit. This is easy to get wrong in emulators, but surely it can be done correctly. This might help:

Code:
We can repeat the same table for subtraction.  Note that subtracting
a positive number is the same as adding a negative, so the conditions that
trigger the overflow flag are:

      SUBTRACTION SIGN BITS
    num1sign num2sign sumsign
   ---------------------------
        0 0 0
        0 0 1
        0 1 0
 *OVER* 0 1 1 (subtracting a negative is the same as adding a positive)
 *OVER* 1 0 0 (subtracting a positive is the same as adding a negative)
        1 0 1
        1 1 0
        1 1 1

- from here.

That said, Dave can probably help here more than I can.


Tue Aug 08, 2017 9:47 pm
Profile

Joined: Wed Jan 09, 2013 6:54 pm
Posts: 1780
Thinking about this overnight, it's not necessary to emulate the overflow bit, but it is necessary to do a two-stage comparison.

Just checking the case for ARM with gcc, we find this code sequence:
Quote:
cmp r2, r3 @ tmp136, tmp137
movge r3, #0 @, D.1752
movlt r3, #1 @, D.1752

Where the 'ge' predicate is "N=V" (Signed Greater or Equal) - this doesn't help us as we don't have a V.

I'm going to have to rename your registers because r0 is special...
SteveF wrote:
...how would the following C-ish snippets be translated into OPC6 assembler?
Code:
int r1, r2, r3
if (r2 < r3) { r1 = 1; } else { r1 = 0; }

Code:
unsigned int r1, r2, r3
if (r2 < r3) { r1 = 1; } else { r1 = 0; }


Unsigned should be something like this:
Code:
        mov     r1, r0
        cmp     r2, r3
        nc.inc  r1, 1


Signed is a bit more difficult: we need (at least) one bit of sign extension. As the sign extension uses the sign bits, we can just check the sign bits first, and if they are the same, perform an unsigned comparison. (If they are different, we already have our answer.)

This seems to work:
Code:
MACRO nop()
        z.mov   r0,r0
ENDMACRO

MACRO cmpunsigned()
        mov     r1, r0
        cmp     r2, r3
        nc.inc  r1, 1
ENDMACRO

MACRO cmpsigned(__signsdiffer, __done)
        mov     r1, r2
        xor     r1, r3
        mi.inc  pc, __signsdiffer-PC

        cmpunsigned()
        inc     pc, __done-PC

__signsdiffer:
        mov     r1, r0
        mov     r0, r2
        mi.inc  r1, 1

__done:

ENDMACRO

        mov     r3, r0, 3
        mov     r2, r0, 2
        cmpunsigned()
        nop()

        mov     r3, r0, 4
        mov     r2, r0, 4
        cmpunsigned()
        nop()

        mov     r3, r0, 6
        mov     r2, r0, 7
        cmpunsigned()
        nop()

        mov     r3, r0, 0x33
        mov     r2, r0, 0x22
        cmpsigned(L001, L001a)
        nop()

        mov     r3, r0, 0x44
        mov     r2, r0, 0x44
        cmpsigned(L002, L002a)
        nop()

        mov     r3, r0, 0x66
        mov     r2, r0, 0x77
        cmpsigned(L003, L003a)
        nop()

        mov     r3, r0, 3
        mov     r2, r0, -2
        cmpsigned(L004, L004a)
        nop()

        mov     r3, r0, -2
        mov     r2, r0, 3
        cmpsigned(L005, L005a)
        nop()

        mov     r3, r0, -1
        mov     r2, r0, -255
        cmpsigned(L006, L006a)
        nop()

        halt    r0,r0,0x999


I made these notes while thinking about this:

A signed byte would be [-128..127]

We can compare two bytes
[-128..127] <? [-128..127]
presumably by subtraction
[-128..127] - [-128..127] = [-255..255]
which fits in 9 bits as a signed value - but not in 8.

Sign-extending into two top bytes and then doing a two-byte subtraction would be overkill. However, we don't need a two-byte subtraction: for comparison, we can first compare the top bytes and only if they match do we compare the bottom bytes - the original ones - as unsigned.

As the top byte is just a sign extension of the sign bits, we just can compare the signs first, and then compare the low bytes (original bytes) as unsigned if the signs match.

is -128 < -1? Yes, because 128 < 255
is 128 < 1? No, because 128 !< 1


Wed Aug 09, 2017 6:44 am
Profile

Joined: Sat Aug 05, 2017 6:57 pm
Posts: 26
Thanks Ed, looks good - I'll give that a try!

I've heavily edited the following so apologises if anyone has read it beforehand:

I completely messed up the commands to assemble and then emulate and went down a blind alley as a result, but I still find the below snippet odd. It doesn't directly matter because I am just executing some horrible mangled up verison of my code, not what I thought, but I would expect this emulation trace to make sense anyway and it doesn't, so maybe I can learn something by asking this anyway.
Code:
$ python opc6emu.py plasma.o mem|grep -C10 '^0000'
[...]
0218 : 0daa      : lsr r10,r10            :  0  0 0 0 1 : 0000 effe 03aa f000 f000 11b5 0000 0000 0000 0100 0000 000a 2000 effe 0262 0219
0219 : 17aa 021e : ld r10,r10,0x021e      :  0  0 0 0 1 : 0000 effe 03aa f000 f000 11b5 0000 0000 0000 0100 0000 000a 2000 effe 0262 021b
021b : 09ae      : jsr r14,r10            :  0  0 0 0 0 : 0000 effe 03aa f000 f000 11b5 0000 0000 0000 0100 025e 000a 2000 effe 0262 021c
025e : 28de      : push r14,r13           :  0  0 0 0 0 : 0000 effe 03aa f000 f000 11b5 0000 0000 0000 0100 025e 000a 2000 effe 021c 025f
025f : 2810      : push r0,r1             :  0  0 0 0 0 : 0000 effe 03aa f000 f000 11b5 0000 0000 0000 0100 025e 000a 2000 effd 021c 0260
0260 : 190e 030a : jsr r14,r0,0x030a      :  0  0 0 0 0 : 0000 effd 03aa f000 f000 11b5 0000 0000 0000 0100 025e 000a 2000 effd 021c 0262
030a : 1406 0001 : add r6,r0,0x0001       :  0  0 0 0 0 : 0000 effd 03aa f000 f000 11b5 0000 0000 0000 0100 025e 000a 2000 effd 0262 030c
030c : 3a06 0002 : cmp r6,r0,0x0002       :  0  0 0 0 0 : 0000 effd 03aa f000 f000 11b5 0001 0000 0000 0100 025e 000a 2000 effd 0262 030e
030e : 60ef      : nz.mov r15,r14         :  0  0 1 0 0 : 0000 effd 03aa f000 f000 11b5 0001 0000 0000 0100 025e 000a 2000 effd 0262 030f
0262 : 29df      : pop r15,r13            :  0  0 1 0 0 : 0000 effd 03aa f000 f000 11b5 0001 0000 0000 0100 025e 000a 2000 effd 0262 0263
0000 : 0000      : mov r0,r0              :  0  0 1 0 0 : 0000 effd 03aa f000 f000 11b5 0001 0000 0000 0100 025e 000a 2000 effe 0262 0001
0001 : 0000      : mov r0,r0              :  0  0 0 0 1 : 0000 effd 03aa f000 f000 11b5 0001 0000 0000 0100 025e 000a 2000 effe 0262 0002
0002 : 0000      : mov r0,r0              :  0  0 0 0 1 : 0000 effd 03aa f000 f000 11b5 0001 0000 0000 0100 025e 000a 2000 effe 0262 0003


I don't understand why the pop at instruction 0262 transfers control to address 0. I pushed 021c at instruction 025e and I don't see how any of the intervening instructions can interfere with popping this value.

I'm sure it's something really obvious in hindsight but I just can't see it...

Cheers.

Steve


Wed Aug 09, 2017 8:03 pm
Profile

Joined: Wed Jan 09, 2013 6:54 pm
Posts: 1780
Ah - looks like your r1 stack and your r13 stack are both pointing to the same place - that can't be right.


Wed Aug 09, 2017 9:01 pm
Profile

Joined: Sat Aug 05, 2017 6:57 pm
Posts: 26
Thank you, that makes sense!

My code actually had a bug earlier and the machine was in a funny state by this point anyway, but I wanted to understand what was going on in that trace. Cheers!


Wed Aug 09, 2017 9:03 pm
Profile

Joined: Tue Apr 25, 2017 7:33 pm
Posts: 32
Steve,

Is it this bit

Code:
025e : 28de      : push r14,r13           :  0  0 0 0 0 : 0000 effe 03aa f000 f000 11b5 0000 0000 0000 0100 025e 000a 2000 effe 021c 025f
025f : 2810      : push r0,r1             :  0  0 0 0 0 : 0000 effe 03aa f000 f000 11b5 0000 0000 0000 0100 025e 000a 2000 effd 021c 0260
0260 : 190e 030a : jsr r14,r0,0x030a      :  0  0 0 0 0 : 0000 effd 03aa f000 f000 11b5 0000 0000 0000 0100 025e 000a 2000 effd 021c 0262


You push r14 onto the stack pointed at by r13, but then immediately push 0 onto the same location - r13 and r1 both have the same address at the start of each push instruction. Later you expect to pop off the value you pushed onto r13, but actually get back the value from the second push.

EDIT - oops. Just seen Ed's post while I was typing that:)
R


Wed Aug 09, 2017 9:10 pm
Profile

Joined: Sat Aug 05, 2017 6:57 pm
Posts: 26
Thanks anyway, I appreciate the help! And now I feel twice as stupid. :-)


Wed Aug 09, 2017 9:21 pm
Profile

Joined: Wed Jan 09, 2013 6:54 pm
Posts: 1780
Don't worry - it took me ages to see it! And this machine is just big enough to confuse, sometimes. (If we didn't have the self-imposed limit of one page on the emulator, it would be nice to log the data reads and writes.)


Wed Aug 09, 2017 9:25 pm
Profile

Joined: Tue Apr 25, 2017 7:33 pm
Posts: 32
Well, since I'm here, I'll let you know that I've checked in a bit of a work in progress which is opc6byteasm.py, specifically for the PLASMA VM work.

The new assembler builds up the data a byte at a time rather than a word at a time. All the original syntax (including the BYTE additions) works as described previously, generating word aligned data and padding where necessary. So, although the listing output is different from the original, the final hex files produced are exactly the same when run on the existing benchmarks. All of those assemble and run fine on the emulator.

For the VM work then there is some new syntax.

Code:
UBYTE   expr, expr, expr...
UWORD   expr, expr, expr...
UBSTRING "string", "string",...

All have the same syntax as the BYTE, WORD and BSTRING but this time do not create padded data, and do not need to be on word aligned boundaries. e.g.

Code:
UBYTE   0x01
UWORD   0x02,0x03
UBYTE   0x04
UBYTE   0x05


...will generate a run of bytes 0x01, 0x02, 0x03, 0x04, 0x05

Of course this means that you can now generate sequences that will cause the next OPC instruction to be on an unaligned byte. That has to be illegal and will be reported as an error. To fix that you need to use the new directive.

Code:
ALIGN


And then there's the thorny issue of labels. Really there are two separate spaces here: byte space for the VM and word space for the OPC code. I think you said that they don't need to mix. Now, I may not have the right solution here, but what I've done is make two types of label so deciding which space you're in is up to the user.

Code:
label:B
label:[W]


The :B form will generate a label pointing at a byte address. The :W form (you can omit the W because that's the original label syntax) will generate a label pointing at a word address. Again you can't put a word label on an unaligned word, so you will need to use that ALIGN directive if that might happen. If you want to label an aligned location with both a byte and word label for some reason, then you must make sure that the labels have different names to avoid a clash - i.e. the :B, :W suffix are not part of the label name.

So, there it is. It's definitely not finished but it does assemble all the existing word-oriented test cases, so please give it a try. Hopefully this is more along the lines of what you need for the VM, but let me know (maybe PM me) if it needs more changes - the labels perhaps - or other additions. I think you might need some kind of 'current-PC' for example as the OPC has in the word-space.

R


Wed Aug 09, 2017 9:28 pm
Profile

Joined: Sat Aug 05, 2017 6:57 pm
Posts: 26
Thanks, that looks really good - but I think you're right, I do need some kind of 'current byte space address' in order to be able to calculate relative distances (the '*' in the code snippets I posted the other day. If you can add that I think this will be exactly what I want and I can give it a try.

Edited to add: That would certainly be nice, but I could always add dummy extra labels where I want to use this hypothetical 'current byte space address', i.e. write:
Code:
    UBYTE 0x05
branch_target:B
    UBYTE 0x10, 0x20
fake_star:B
    UWORD branch_target-fake_star

So it's not essential if this is difficult to add.

(Perfect timing too as I've been using a couple of branch instructions in my latest VM tests tonight and calculating the byte displacement by hand is subject to hand-waving. "It doesn't work, oh, but wait, I put displacement 17 and it should be 18, yeah, I counted wrong, I'm not just fiddling it because it might start working then, honest". :-) )


Last edited by SteveF on Wed Aug 09, 2017 9:45 pm, edited 1 time in total.



Wed Aug 09, 2017 9:32 pm
Profile

Joined: Sat Aug 05, 2017 6:57 pm
Posts: 26
I've just updated the plasma.s in my repo to use the new UBYTE/UWORD directives and they're working a treat! Thanks very much - as I say, the 'current byte offset' aka '*' would be helpful, but the workaround of adding extra labels works fine and it's still a huge improvement on doing it manually via the BYTE directive.


Wed Aug 09, 2017 9:43 pm
Profile

Joined: Tue Apr 25, 2017 7:33 pm
Posts: 32
Steve,

Just pushed another update with the 'current byte location' addition: _BPC_

Here's an assembled fragment using this new token:

Code:
2479                       L11:B
2479 11 22                         UBYTE 0x11, 0x22
247b                       L12:B
247b 33 44 55                      UBYTE 0x33, 0x44, 0x55
247e                       L13:B
247e 66 77 88 99                   UBYTE 0x66, 0x77, 0x88, 0x99
2482 82 24                         UWORD _BPC_
2484 84 24                         UWORD _BPC_
2486 0b                            UBYTE _BPC_ - L12


In the two UWORD statements you can see that _BPC_ is correctly updated to point at the location of each statement.

The expression in the final UBYTE evaluates the difference between current location and label L12, and counting backwards that looks right to me (i.e. 11) .

_BPC_ can be used like any other symbol with any expression that would be valid in Python.

R


Thu Aug 10, 2017 7:22 pm
Profile

Joined: Tue Apr 25, 2017 7:33 pm
Posts: 32
and by the way, Ed wrote

Quote:
(If we didn't have the self-imposed limit of one page on the emulator, it would be nice to log the data reads and writes.)


so I added the logging of all memory and IO activity to the emulator trace in the same release. And why not, as the late Barry Norman might have said.

R


Thu Aug 10, 2017 7:52 pm
Profile

Joined: Sat Aug 05, 2017 6:57 pm
Posts: 26
Thanks Revaldinho, that's great stuff. Behold...
Code:
steven@riemann:~/src/PLASMA-opc6$ ./doit.sh test5.pla
[...]
steven@riemann:~/src/PLASMA-opc6$ python annotate.py mem | grep a000:
a000: 0100 0302 0504 0706 0908 000a 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

I'm sure it will break on compiled code I haven't tested, but it does seem to compile and run test[1-5].pla OK. (I deliberately haven't done hackery to add :B suffixes to all the label types I suspect will need it, to force me to think about them in context.) This should make it a lot more fun/quick to work through test code to implement more VM opcodes.

I do have a couple of requests/bits of feedback though:
  • EQU gives an error if the current position isn't word-aligned - is this necessary? Isn't EQU just a way of setting a label to a constant value? This isn't causing me any big problems, but I thought I'd mention it.
  • Would it be possible to have the assembler return with a non-0 exit code if assembly fails?

Thanks again!

Steve


Thu Aug 10, 2017 9:21 pm
Profile

Joined: Tue Apr 25, 2017 7:33 pm
Posts: 32
Steve

EQU is a bit limited. In fact I'm not sure it works at all with labels, or at least not labels which are as yet undefined. Have you got some example use of it which I can see ?

Exiting with non-zero status on errors ? Yes, I'll add that.

R


Thu Aug 10, 2017 9:25 pm
Profile
Display posts from previous:  Sort by  
Reply to topic   [ 52 posts ]  Go to page Previous  1, 2, 3, 4  Next

Who is online

Users browsing this forum: No registered users and 10 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
cron
Powered by phpBB® Forum Software © phpBB Group
Designed by ST Software