Page 1 of 4
1Mhz 6502 bitbang 57kbaud
Posted: Sat Feb 11, 2012 11:28 pm
by syscall
Hey,
I'm doing a software serial routines for a 1Mhz 65xx device that needs to communicate at 57600 bit/s. But my receive code does not work.
57600 gives 17 cycles per bit.
Data comes in negated and it comes in on portb's D6. Protocol is
Startbit-Databits-Stopbit (8N1)
#cycle is a macro that inserts n cycles of code (not touching registers, flags may be touched) and .rept is a repeating macro.
Here's the code. Resulting byte should be in A. Bit sampling is always at 17 cycle interval (or so I believe)
Anyone can see where I've gone wrong?
Code: Select all
readbyte bit portb ; data comes in on portb.6
bvc readbyte ; startbit?
#cycle #25 ; wait ~ 1,5 bit time
.rept 8 ;read the bits
bit portb
bvc ?bt1
clc
ror @
bvs ?nx
?bt1 sec
ror @
#cycle #3
?nx
#cycle #4
.endr
rts
Any hints would be appreciated.
Cheers
Posted: Sat Feb 11, 2012 11:54 pm
by 8BIT
Can you give some samples, i.e.:
expect $41 but get $28
Try sending A,B,D,H which are $41,$42,$44,$48.
That will help decode the bit manipulation that's going on.
Daryl
Posted: Sun Feb 12, 2012 12:00 am
by syscall
Hi Daryl,
Thanks for reply. Here's the bytes.
sent: 0x41
rcvd: 0x00
sent: 0x42
rcvd: 0x80
sent: 0x44
rcvd: 0x00
sent: 0x48
rcvd: 0x80
Posted: Sun Feb 12, 2012 1:21 am
by GARTHWILSON
Do you get the same result every time? Are there interrupts that could be cutting in on your cycle count? What is being done with each byte as it comes in, and is that action quick enough to get back to being ready for the next byte coming in? I see you're doing a JSR-RTS for each byte. That alone takes most of the time of a stop bit, leaving you with almost no time to store one received byte in an array and get back to watching for the start bit of the next byte. 17 cycles is a little off, but not really enough to cause problems. If it were, then since you're straightlining it, you could vary the delays a little bit from one bit to the next so the error would not accumulate. Edit: I meant to add: Do any branches cross page boundaries? That adds another cycle too.
A 6551 adds parts but sure makes it easier for the computer to do other things in the background at the same time. Although it won't do that bit rate from the internal clock generated from the 1.8432MHz crystal, it is able to go much faster than that with an external clock source, like a 6522's PB7 set up to toggle automatically on each T1 timeout with a free-running T1.
Posted: Sun Feb 12, 2012 1:34 am
by Dr Jefyll
It looks to me as if
should be
-- Jeff
Posted: Sun Feb 12, 2012 1:35 am
by leeeeee
I make the two paths through the macro one cycle different.
Also, depending on exactly when the start bit starts, you could already be 3 to 7 cycles into the bit before you get to #cycle #25 and then another 3 or 4 cycles before the bit instruction in the macro tests the port again.
Lee.
Posted: Sun Feb 12, 2012 11:23 am
by syscall
Thanks for the replies...
Garth: There's no concern about jsr/rts because this does not even happen. I send the bytes through the terminal from the keyboard. One at a time.
So the routine waits for my byte forever, reads it bit by bit when it comes and exits. then again waits for the byte, I send the byte etc. This isn't real stream of bytes at the moment. Just one byte. then another.
Also I made sure bvc and bvs branches do not cross pages.
No interrupts as well.
DrJefyll: Did you count the loops right? bxx branches take 2 or 3 cycles depending on taken/not taken condition. #3 cycle seems right to me. But please elaborate,perhaps I'm missing something.
leeeeee: that's a good point. I'll try to account for that.
Posted: Sun Feb 12, 2012 11:29 am
by BitWise
You could replace the bit setting code with a straight sequence, like
Code: Select all
lda portb ; Fetch inverted signal into A
eor #$40
asl a ; Shift bit into C
asl a
ror @ ; And into result byte
Might make the timing easier to calculate.
Re: 1Mhz 6502 bitbang 57kbaud
Posted: Sun Feb 12, 2012 3:48 pm
by Dr Jefyll
These are all good points that've been made. There are a few details I'd like to clarify.
Regarding the instruction
Code: Select all
bit portb ; data comes in on portb.6
...I assume Absolute addressing mode is used -- that the IO port is not located in Zero Page. In other words the instruction consumes 4 cycles (not 3) -- is that right?
Also, I'm a little uncertain about
Are you RORing the
Accumulator or a
memory location? If memory, is the addressing mode Absolute or Zero-Page? I will use the assumption it's the Accumulator. (I think
Bitwise assumes it's memory, which also would work. (And I like his branch-less approach!)
As for timing with your original routine, I've marked below the cycle counts for both paths:
Code: Select all
readbyte bit portb ; data comes in on portb.6
bvc readbyte ; startbit?
#cycle #25 ; wait ~ 1,5 bit time
.rept 8 ;read the bits
4 4 bit portb
3 2 bvc ?bt1
- 2 clc
- 2 ror @
- 3 bvs ?nx
?bt1 2 - sec
2 - ror @
3 - #cycle #3 ;<---- ???
?nx
4 4 #cycle #4
.endr
rts
If you add up the totals you'll see they differ by one cycle -- hence my suggestion to change
#cycle #3 to
#cycle #2.
Following up on
leeeeee's second point, I'd suggest that
be changed to
Code: Select all
#cycle #16 ; wait ~ 1,5 bit time minus 9~
Waiting for the Start Bit is an uncertain business. By the time the code notices it's arrived, up to 6 cycles may already have elapsed. I don't know any perfect way to deal with that -- I would simply average that uncertainty for a "typical" figure of 3. Then add the final (successful) test for the Start Bit, which consumes 4~ for
bit portb and 2~ for
bvc readbyte. That brings us up to 9 cycles. Add
#cycle #16 and you get 25 -- the 1.5 bit-times you wanted.
cheers,
Jeff
Posted: Sun Feb 12, 2012 4:01 pm
by Arlet
Code: Select all
lda portb ; Fetch inverted signal into A
eor #$40
asl a ; Shift bit into C
asl a
ror @ ; And into result byte
You could also leave out the eor #$40, and invert the byte at the end.
Posted: Sun Feb 12, 2012 4:32 pm
by syscall
Thank you all...
Dr Jefyll: ror @ is accumulator. portb is absolute on non-zero page. your assumptions were ok. The missing cycle is also correct.
Thanks.
BTW, i've managed to do it with branchless approach as BitWise suggested, and used the Alert's tip. It works now for single bytes.
Now I need to throw it into a tight loop of reading N bytes.
But that's easier.
Code: Select all
; pom is a zpage 1 byte variable
?r bit portb
bvc ?r
#cycle #15
.rept 8
lda portb ;4
asl @ ;2
asl @ ;2
ror pom ;5
#cycle #4
.endr
lda pom
eor #$ff
rts
cheers
Re: 1Mhz 6502 bitbang 57kbaud
Posted: Sun Feb 12, 2012 4:40 pm
by bogax
Waiting for the Start Bit is an uncertain business. By the time the code notices it's arrived, up to 6 cycles may already have elapsed.
Shouldn't that be 7 cycles (the branch would have been taken on the previous pass)?
Re: 1Mhz 6502 bitbang 57kbaud
Posted: Sun Feb 12, 2012 6:48 pm
by Dr Jefyll
Shouldn't that be 7 cycles (the branch would have been taken on the previous pass)?
Thanks, Bogax. Yes, certainly each unsuccessful iteration takes 7 cycles total -- I don't have an issue with that. But for the maximum resulting delay I got six by thinking, "there's the bus cycle during which the port reads false -- and we count the six cycles it takes to loop around and try again." Silly me -- that's incorrect. The deadline or decision point is of infinitesimal duration; it doesn't occupy a complete bus cycle.
Therefore the time before the Start Bit is recognized ranges from virtually 0~ to virtually 7~, for a typical delay of 3.5. For our calculations we can round that
up to 4~ or
down to 3~. If we're being fussy, the choice to round up or down should be aimed at canceling whatever other fractional-cycle timing error we can identify. For example we're using 25~ to represent 1.5 bit-times, when in fact 1.5 * 17 = 25.
5 cycles. Since 25.5 got rounded down to 25 (an error of -0.5), 3.5 should get rounded up to 4 (for a complementary error of +0.5).
-- Jeff
Posted: Sun Feb 12, 2012 7:45 pm
by leeeeee
I would suggest the following changes ..
Code: Select all
; pom is a zpage 1 byte variable
?r bit portb
bvc ?r
#cycle #11
.rept 8
#cycle #4
lda portb ;4
asl @ ;2
asl @ ;2
ror pom ;5
.endr
lda pom
eor #$ff
rts
That will give you four extra cycles to do something with the byte once you have it.
Lee.
Posted: Mon Feb 13, 2012 3:55 am
by bogax
Following on Leeeeee's suggestion.
IFF bit 6 is the only thing changing on the port you might be able to
do this:
Code: Select all
?r bit portb
bvc ?r
#cycle #8
.rept 7
#cycle #5
eor portb ;4
ora #$40 ;2
eor portb ;4
ror @ ;2
.endr
#cycle #5
eor portb ;4
ora #$40 ;2
eor portb ;4
rol @ ;2
rts
However it spreads the port read out over 6 cycles and reduces
the margin for jitter/misalignment