Software Serial Routine

From NESdev Wiki
Jump to navigationJump to search

This page covers implementation of bit-banged serial read/write routines for use with a Serial Cable. It's meant if you're implementing your own, or want to understand how the library routines work.

Encoding

Serial data is transferred as a series of bits followed by an idle state. Each bit lasts the same amount of time, but the idle state can be any length. An 8-bit byte is transmitted as 10 bits, with the first and last bits serving as markers of the start and end of the byte. The start bit is encoded as a 0, and the stop bit as a 1. Data bit 0 is first.

...--       ----- ----- ----- ----- ----- ----- ----- ----- -------//---
     \     /     X     X     X     X     X     X     X     /            \
      ----------- ----- ----- ----- ----- ----- ----- -----              -----...
           
     start    0     1     2     3     4     5     6     7   stop  idle  start ...

For example, the byte 00110001 is encoded as

...--       -----                   -----------             ------...
     \     /     \                 /           \           /
      -----       -----------------             -----------
           
     start    1     0     0     0     1     1     0     0   stop

The idle state between bytes is also a 1, so it's as if the stop bit lasts until the next start bit. It can be any length. Compressing the time scale, the byte 00110001 sent three times with varying delays between might appear as (S=start s=stop)

...--  --      ----    --  --      ----    -------  --      ----    --...
     --  ------    ----  --  ------    ----       --  ------    ----  
     S 1 0 0 0 1 1 0 0 s S 1 0 0 0 1 1 0 0 s      S 1 0 0 0 1 1 0 0 s 
    |<-- first byte  -->|<-- second byte -->|idle|<-- third byte  -->|
    

Above, the first two bytes are encoded back-to-back with no idle time between. This achieves the highest throughput, since no time is wasted. The last byte is delayed. Note that the idle state can last any amount of time, in particular, it doesn't have to be a multiple of the bit length.

Timing

A serial rate of 57600 bits per second is the most useful on the NES. On NTSC, this comes close to 31 cycles per bit (1789772.7/57600=31.07). On PAL, this comes close to 29 cycles per bit (1662607/57600=28.86).

Sending is simple: just output each of the 10 bits for 31 cycles each (29 for PAL).

Receiving is more involved: wait for the beginning of a start bit, delay 1.5 bit lengths before reading the first bit, then delay one bit length between the remaining bits. This way, the code is reading the bits in the middle so that any slight variations in timing won't cause bits to be read too close to the edges. Below, the * represents the read, and the delays are shown between them.

...--       ----- ----- ----- ----- ----- ----- ----- ----- -----...
     \     /     X     X     X     X     X     X     X     /
      ----------- ----- ----- ----- ----- ----- ----- -----
*******       *     *     *     *     *     *     *     *
         1.5    1.0   1.0   1.0   1.0   1.0   1.0   1.0

Allowing for a +/- 1% variation in NES/serial speeds, a receive routine must meet this timing:

NTSC PAL Notes
Start to bit 0 43-50 39-46 Number of cycles from beginning of start bit to read in middle of bit 0. Range because code to wait for start bit takes 7 cycles per iteration, and may read just after start bit has begun, or just before, in which case it will be another 7 cycles until it reads again. This averages to 46.5 cycles (42.5 on PAL), almost exactly 1.5 bit lengths.
Bit n to bit n+1 31 29 Cycles between data bits.
Bit 7 to next start bit 23-47 20-43 Cycles between read in middle of bit 7 and first read in start bit loop for next byte. This causes the start bit loop's first read to fall within the stop bit

The following routines demonstrate proper timing. Note that they are written entirely for clarity, rather than optimized for practical use.

Initializing

Before sending or receiving for the first time, write $03 to $4016 and delay:

        ldx #$03
        stx $4016
        ldx #350/5      ; delay 350 cycles, more than 10 bit lengths on NTSC
wait:   dex
        bne wait

This puts an idle state on the output lines so that the first byte sent will not be corrupt. It also prevents Famicom controllers from interfering with reads (as long as the A button isn't being pressed).

Sending

To send a byte, write each of the 10 bits, with 31 cycles between (29 for PAL).

        clc             ; start bit
        ldx #10
loop:   tay
        lda #$FF        ; replicate carry in low two bits
        adc #0
        eor #$FF
        and #%11
        sta $4016
        tya
        sec             ; stop bit
        ror a           ; next bit into carry
        nop             ; delay 6 cycles
        nop
        nop             ; remove for PAL timing
        dex
        bne loop

Remove the indicated NOP for PAL timing.

This outputs the start bit (0), 8 data bits, and the stop bit (1), each lasting 31 cycles. Note how it only outputs on bits 0 and 1 of $4016, and clears the others. Some serial connections may be using bit 1 in the future, so a routine should write to both bits if possible. Other devices may be using higher bits, so these should always be clear.

Receiving

The NES inverts serial input, but not output, so when receiving data, things are inverted: a start bit is 1, a stop bit 0, and data is inverted.

To receive a byte, wait for the beginning of the start bit, delay 43 cycles (39 for PAL), read bit 0, delay 31 cycles (29 for PAL), read bit 1, etc. through bit 7:

        lda #%10111 ; wait for start bit
start:  bit $4017
        beq start
        pha         ; delay 17 cycles
        pla
        nop
        nop
        nop
        nop
        nop
        ldx #8      ; read 8 data bits
        nop         ; remove for PAL timing
loop:   nop         ; remove for PAL timing
        nop         ; delay 10 cycles
        nop
        nop
        nop
        nop
        tay
        lda #%10111 ; mask input lines and put into carry, inverted
        and $4017
        cmp #1
        tya
        ror a       ; shift into output byte
        dex
        bne loop
        eor #$FF    ; invert final byte

Remove both indicated NOPs for PAL timing.

A 1.5 bit period delay is 46.5 cycles (31*1.5), but in this code there are only 43 cycles between the time that BIT $4017 reads the start bit and AND $4017 reads bit 0. This is because the start bit loop takes 7 cycles, and if the start bit begins just after the BIT $4017, the loop will take another 7 cycles before it's seen. Since it can start at any point in this 7-cycle loop, the final BIT $4017 sees the start bit an average of 3.5 cycles after it began. Thus the 43-cycle delay in the code gives an average of 46.5 cycles between the beginning of the start bit and where it reads bit 0, exactly as desired. The actual bit 0 read time can vary by +/- 3.5 cycles from the center of bit 0 (7 cycles/2). Since each bit lasts 31 cycles, this variation isn't a problem.