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



Reply to topic  [ 108 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6, 7, 8  Next
 One Page Computing - roll your own challenge 
Author Message

Joined: Sat Jun 16, 2018 2:51 am
Posts: 50
Thanks for the responses!

Would it be big endian if the bytes were packed as follows?
Code:
0000: 0100                  GLOBAL1:        BYTE    0x01
0001: 0102                  GLOBAL2:        BYTE    0x01, 0x02
0002: 0102 0300             GLOBAL3:        BYTE    0x01, 0x02, 0x03
0004: 0102 0304             GLOBAL4:        BYTE    0x01, 0x02, 0x03, 0x04


Is there a reason to use one approach over the other? At first glance, it seems little endian is popular only because of legacy.

For the programmer, using the BYTE directive with little endian seems confusing as what they define will be stored in memory swapped... For example suppose they wanted to access the fourth byte (0x04) in GLOBAL4. The programmer would have to realize that the byte is actually stored at GLOBAL4 + 2 bytes instead of GLOBAL4 + 3 bytes.

This is further confusing because for byte strings, the bytes are stored in the order in which they appear... For example:
Code:
BYTE "Hello", 0x0a
is stored as 0x4865 0x6c6c 0x6f0a. ('H'-0x48, 'e'-0x65, 'l'-0x6c, 'o'-0x6f)

Tangentially related, how would a C compiler for OPC6 translate a data type like char (1 byte)? Would it use the BYTE directive of the assembler?


Last edited by quadrant on Wed Mar 20, 2019 9:09 pm, edited 3 times in total.



Wed Mar 20, 2019 8:10 pm
Profile

Joined: Sat Jun 16, 2018 2:51 am
Posts: 50
BigEd wrote:
And we do read and interpret srecords, I think, and had to decide how to handle a bytestream. I expect that was a little-endian approach too.
What are srecord files used for? Also how does the OPC get access to one, do you transmit it over serial (rx/tx) to a physical OPC (ex hosted on an FPGA)?

There seems to be quite a bit of stuff hidden away in the libraries (one, two)...


Wed Mar 20, 2019 8:32 pm
Profile

Joined: Wed Jan 09, 2013 6:54 pm
Posts: 1780
For sure, arguments have raged elsewhere over whether little or big endian is the more natural. Suffice it to say, I find little endian more natural - not a question of legacy!

For some of the OPC machines, hoglet wrote a small monitor program, which allowed us to load code, and IIRC it was in srecord format. It's possible I misremember. And yes, ultimately the srecord data would have come from a host, over a serial line. We have had OPCs connected up as second processor to a BBC Micro (or Master), and we've done that in both FPGA form and in emulated-on-a-Pi form. In that configuration, I often connect a serial cable to the Beeb, and drive it from a laptop. It's quite convenient to paste data in srecord format when I've done that. But I can't be completely sure this is what I did. I now remember we also had an OPC on a BlackICE FPGA board, which has a USB port that acts as a serial device.


Wed Mar 20, 2019 8:37 pm
Profile

Joined: Wed Jan 09, 2013 6:54 pm
Posts: 1780
Ah, just noticed, you have already found and linked to some srecord-reading code for OPC!


Wed Mar 20, 2019 8:38 pm
Profile

Joined: Wed Jan 09, 2013 6:54 pm
Posts: 1780
quadrant wrote:
Tangentially related, how would a C compiler for OPC6 translate a data type like char (1 byte)? Would it use the BYTE directive of the assembler?

I think there are choices here, but making a char 16 bits might be most natural. It does potentially waste space.
This article is a fun read:


Wed Mar 20, 2019 8:44 pm
Profile

Joined: Sat Jun 16, 2018 2:51 am
Posts: 50
Ah I see. I've seen the photo, but must admit didn't really understand what was happening.

Thanks for the article link, will give it a read.


Wed Mar 20, 2019 10:06 pm
Profile

Joined: Wed Jan 09, 2013 6:54 pm
Posts: 1780
Sorry about my vagueness. There are two setups we've used: a standalone OPC computer with a monitor program, and an OPC as a second processor connected to a Beeb. And in the second case, that's been either an FPGA or a Pi running a C model. So, three setups. I'll come in again...


Wed Mar 20, 2019 10:19 pm
Profile

Joined: Tue Apr 25, 2017 7:33 pm
Posts: 32
quadrant wrote:
This is further confusing because for byte strings, the bytes are stored in the order in which they appear... For example:
Code:
BYTE "Hello", 0x0a
is stored as 0x4865 0x6c6c 0x6f0a. ('H'-0x48, 'e'-0x65, 'l'-0x6c, 'o'-0x6f)


I'm not sure there's any confusion here if you run the assembler. The code in your snippet above isn't valid syntax - it doesn't work, and certainly doesn't end up with bytes stored in that order.

This is the output of the assembler.

Code:
0000  0000 0000 0000        BYTE    "HELLO", 0x0A

Assembled 3 words of code with 1 error and 0 warnings.

Symbol Table:

PC                               0x0001 (000001)

Error: illegal or undefined register name or expression in ...
         BYTE    "HELLO", 0x00


I think what you meant to write was

Code:
0000  4548 4c4c 0a4f        BSTRING    "HELLO", 0x0A

Assembled 3 words of code with 0 errors and 0 warnings.


..which as you can see stores the bytes in the same little endian order as if they had been specified using the BYTE directive. So BSTRING and BYTE are consistent with each other.

If you prefer to store characters one per 16-bit word, then use either the STRING or WORD directive. There are also PSTRING and PBSTRING options for Pascal style strings with either word or byte storage respectively.


Wed Mar 20, 2019 10:24 pm
Profile

Joined: Sat Jun 16, 2018 2:51 am
Posts: 50
I guess my confusion arises from the discussion in the link I shared where they are talking about how a compiled C program stores a string in memory.
Quote:
ANSI C standard 6.1.4 specifies that string literals are stored in memory by "concatenating" the characters in the literal.
This can be confirmed by writing a simple C program and viewing the generated binary.* I incorrectly assumed that OPC6 follows this convention when handling strings. I have a feeling I'm getting confused by the byte order used in a high level language vs what the machine actually does at the low level.

*Edit:
Do debuggers swap the bytes accordingly? For example when you are viewing a hexdump? Above when I state viewing the binary, I used the program objdump. But it seems that objdump knows the endianness of the binary it is displaying...
Quote:
--endian={big|little}
Specify the endianness of the object files. This only affects
disassembly. This can be useful when disassembling a file format
which does not describe endianness information, such as
S-records.
Also, I do not want to hijack this thread with my confusion on endianess. I will gladly move it if necessary.


Edit 2:
I came across this post (Does hexdump respect the endianness of its system).

I ran the following on my terminal:
Code:
$ echo Hello > endianTest.txt

$ hexdump endianTest.txt
0000000 6548 6c6c 0a6f
0000006

$ hexdump -C endianTest.txt
00000000  48 65 6c 6c 6f 0a                        |Hello.|
00000006

$ hexdump -c endianTest.txt
0000000   H   e   l   l   o  \n
0000006
And the man pages for hexdump say the following about the -C and -c flags:
Quote:
-C
Canonical hex+ASCII display. Display the input offset in hexadecimal,
followed by sixteen space-separated, two column, hexadecimal bytes,
followed by the same sixteen bytes in %_p format
enclosed in ``|'' characters.
-c
One-byte character display. Display the input offset in hexadecimal,
followed by sixteen space-separated, three column, space-filled,
characters of input data per line.
So I got confused with what I was reading from my debugging tool as it likely uses the hex+ASCII display. Even though my machine is little endian and stored the string bytes accordingly, they were displayed as big endian.

Edit 3 (last):
This directly answered my question. String is stored as it appears. If hexdump reads two bytes integers (default behaviour) it will display the bytes of the pair swapped in accordance with little endian. If hexdump reads one byte integers (for example with -c and -C flags) it will display them in order appear in file.
Quote:
-x
Two-byte hexadecimal display. Display the input offset in hexadecimal,
followed by eight, space separated, four column, zero-filled,
two-byte quantities of input data, in hexadecimal, per line.


Last edited by quadrant on Thu Mar 21, 2019 8:44 pm, edited 1 time in total.



Wed Mar 20, 2019 11:47 pm
Profile

Joined: Wed Jan 09, 2013 6:54 pm
Posts: 1780
I think a thread on endianness might well be the right answer here.

Mostly, a word-oriented machine doesn't even have the concept of endianness - that can only arise in software running on the machine, which can do whatever it likes, or software running off the machine, as in the case of the assembler, which is similarly unconstrained. When machines have both addressable words and addressable bytes, there's then the question of presentation. Every debugger and hex dumper has the option of "helping" by presenting data in some particular way. It's easy to be misled if your only view of memory is coming from a "helpful" tool!


Thu Mar 21, 2019 8:03 am
Profile
User avatar

Joined: Fri Mar 22, 2019 8:03 am
Posts: 328
Location: Girona-Catalonia
Not sure if this is the right thread to post a question on the OPC instruction set, or I need to open a new thread. I suppose it is ok here.

So, my question is particularly about the CMP and CMPC instructions of the OPC6 and OPC5. Are they supposed to be chained to perform longer-than one word comparisons?. For example, to perform 32 bit comparisons on the 16bit OPC6 processor?. I'm confused about what's the intended processor implementation, because I can think on a couple of cases where this will (?) fail if we only take into account the carry resulting of the second comparison.

I have also looked at the emulator opc6emu.py implementation, and although I'm not particularly versed on python, I think that the code for CMPC just subtracts with carry and sets the resulting carry to the status register.

To add more confusion to this, I just discovered that AVR processors have also a CPC (compare with carry) instruction, However, in this case the docs seem to imply that "All conditional branches can be used after this instruction" (regardless of the condition), See this: https://www.microchip.com/webdoc/avrassembler/avrassembler.wb_CPC.html. The example at the bottom of the page is quite significative. A branch "non equal" is performed after the chained CP and CPC, which kind of seem to imply that the CPC does a lot more than just subtracting with carry and setting the resulting carry!.

Any ideas?


Fri Sep 20, 2019 4:02 pm
Profile

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

The CMP and CMPC instructions work in almost exactly the same way as SUB and SBC, except that the compare versions do not store any results in processor registers.

* CMP and SUB ignore the carry input and subtract one operand from the other
* CMPC and SBC include the carry input in the subtraction

In all four cases the Zero, Carry out ('not borrow' in this case) and Sign flags are set/reset appropriately according to the result of the subtraction.

Running the emulator is probably easier than trying to understand how the flags work by just reading the code.

So if you want to compare the register pair (r4,r5) with (r6,r7) you might do something like this:

Write your assembler code in compare.s

Code:
        mov     r4, r0, 0x1234
        mov     r6, r0, 0x1234
        mov     r5, r0, 0x5678
        mov     r7, r0, 0x5678

        cmp     r7, r5                # compare the lower words
      z.cmpc    r6, r4                # only compare the upper words if lower words were equal (and taking borrow into account)
     nz.mov     pc, r0, not_equal     # if either upper or lower compare was non-zero then exit to 'not_equal'
     nc.mov     pc, r0, not_equal     # if carry out is zero then exit to 'not_equal'

equal:  halt    r0,r0,0x000           # exit - pairs are equal

not_equal:
        halt    r0,r0,0x999           # exit pairs are not equal


Assemble it

Code:
python3 opc6asm compare.s compare.hex


.. and then run it to see how the flags are updated

Code:
python3 opc6emu compare.hex


Code:
PC   : Mem       : Instruction            : SWI I S C Z :  r 0  r 1  r 2  r 3  r 4  r 5  r 6  r 7  r 8  r 9  r10  r11  r12  r13  r14  r15
----------------------------------------------------------------------------------------------------------------------------------
0000 : 1004 1234 : mov r4,r0,0x1234       :  0  0 0 0 0 : 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0002
0002 : 1006 1234 : mov r6,r0,0x1234       :  0  0 0 0 0 : 0000 0000 0000 0000 1234 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0004
0004 : 1005 5678 : mov r5,r0,0x5678       :  0  0 0 0 0 : 0000 0000 0000 0000 1234 0000 1234 0000 0000 0000 0000 0000 0000 0000 0000 0006
0006 : 1007 5678 : mov r7,r0,0x5678       :  0  0 0 0 0 : 0000 0000 0000 0000 1234 5678 1234 0000 0000 0000 0000 0000 0000 0000 0000 0008
0008 : 2a57      : cmp r7,r5              :  0  0 0 0 0 : 0000 0000 0000 0000 1234 5678 1234 5678 0000 0000 0000 0000 0000 0000 0000 0009
0009 : 2b46      : cmpc r6,r4             :  0  0 0 1 1 : 0000 0000 0000 0000 1234 5678 1234 5678 0000 0000 0000 0000 0000 0000 0000 000a
000a : 700f 0010 : nz.mov r15,r0,0x0010   :  0  0 0 1 1 : 0000 0000 0000 0000 1234 5678 1234 5678 0000 0000 0000 0000 0000 0000 0000 000c
000c : b00f 0010 : nc.mov r15,r0,0x0010   :  0  0 0 1 1 : 0000 0000 0000 0000 1234 5678 1234 5678 0000 0000 0000 0000 0000 0000 0000 000e
000e : 3000 0000 : halt r0,r0,0x0000      :  0  0 0 1 1 : 0000 0000 0000 0000 1234 5678 1234 5678 0000 0000 0000 0000 0000 0000 0000 0010
Stopped on halt instruction at 000e with halt number 0x0000



Rev.


Sat Sep 21, 2019 5:48 pm
Profile
User avatar

Joined: Fri Mar 22, 2019 8:03 am
Posts: 328
Location: Girona-Catalonia
Hi Revaldinho,

Thanks for that. It's a bit tricky but this is the way I thought it worked (I also understand that C after a subtraction represents an 'inverted' borrow as it's the case of most processors). I think the NC.MOV in your example is not actually required if we are testing only for equality.

[ I mentioned the example of the AVR CPC (compare with carry) instruction, because for it to work as advertised (although I didn't find it documented) I think that other than using the input carry for the subtraction, the instruction should combine the incoming Z flag with the operation Z flag by means of an AND. This way the resulting Z flag after the CP, CPC pair is the correct representation of the double-word comparison. I believe that nothing special must be done with the C and V flags resulting from the CPC because I think they already correctly represent the result of the double-word comparison ]

For the case of the OPC processor, if I understand it correctly, depending on the condition that needs to be tested, a different predicate must be used on the CMPC instruction. For example,

- To test for 'equality' (Z==0) or 'non equality' (Z==1), the CMPC must be predicated with Z as in your example above in order to obtain the correct combined Z flag

- To test for 'unsigned higher or same' (C==1), or 'Unsigned lower' (C==0), the CMPC instruction would not be predicated in order to get the correct C flag after the instruction pair. Is this right?

But what to do to test conditions such as "unsigned higher" (C==1 && Z==0) or the opposite "unsigned lower or same" (C==0 || Z==1) ?

Also, do the OPC processors support signed comparison in some way?

Thanks.

John


Sat Sep 21, 2019 9:17 pm
Profile

Joined: Tue Apr 25, 2017 7:33 pm
Posts: 32
The OPC6 is working with 2's complement arithmetic, so signed comparison is largely a given except that as you can see there is no overflow flag and check. You would need to create a macro to detect and handle this condition.

To check your various cases, I'd still say just tweak the code in the previous example for the different comparisons and run the emulator to see the flag behaviour. The simple predication is quite cheap (in code and hardware) and powerful for the OPC machines, but it does become a bit unwieldy when you need to check several flags. Remember that the key goal here was verilog/assembler/emulator all in a page of code - the machines can all easily be extended outside of this constraint if you need to.

R


Sun Sep 22, 2019 10:59 am
Profile

Joined: Wed Jan 09, 2013 6:54 pm
Posts: 1780
joanlluch wrote:
... my question is particularly about the CMP and CMPC instructions of the OPC6 and OPC5. Are they supposed to be chained to perform longer-than one word comparisons?

I think there might be some subtlety, or some clever tricks, so I posted a related query over on 6502.org. (Bytes on 6502 will behave very like words on the OPC machines. But the 6502 lacks predication, and only has CMP with no carry in. The 6502 does have a V flag. So, not exactly the same situation!)


Sun Sep 22, 2019 11:35 am
Profile
Display posts from previous:  Sort by  
Reply to topic   [ 108 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6, 7, 8  Next

Who is online

Users browsing this forum: AhrefsBot and 6 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