Skip to content

Commit

Permalink
Replace USART_Transmit_Byte with bit-banging code
Browse files Browse the repository at this point in the history
The bit-banging implementation is designed to work with
the Atmel clocked by a 14.318MHz crystal and allows SIO
transfer rates up to POKEY divisor 0 (~126kbit/sec).

Signed-off-by: Matthias Reichl <hias@horus.com>
  • Loading branch information
HiassofT committed Jan 28, 2018
1 parent 78e1ab4 commit 7a81985
Show file tree
Hide file tree
Showing 2 changed files with 193 additions and 15 deletions.
62 changes: 47 additions & 15 deletions SDrive.c
Expand Up @@ -3,6 +3,14 @@
// Bob!k & Raster, C.P.U., 2008
//*****************************************************************************

// Changes:
// 2009-06-01 Matthias Reichl <hias@horus.com>
// - USART_Transmit_Byte waits for end of transmission at Pokey divisors 0 - 3.
//
// 2009-06-02 Matthias Reichl <hias@horus.com>
// - replaced USART_Transmit_Byte with bit-banging implementation to work around
// POKEY awkwardness and to closely match PAL speed with the 14.318MHz crystal

#include <avr/io.h> // include I/O definitions (port names, pin names, etc)
#include <avr/interrupt.h> // include interrupt support
#include <avr/pgmspace.h>
Expand All @@ -26,6 +34,8 @@ extern void Delay200us();
extern void Delay800us();
extern void Delay1000us();

extern void DelayAtariX(unsigned short ataricycles);

//F_CPU = 14318180
//UBRR = (F_CPU/16/BAUD)-1
//BAUD = (F_CPU/16/(UBRR+1)
Expand All @@ -37,7 +47,11 @@ extern void Delay1000us();

#define US_POKEY_DIV_DEFAULT 0x06 //#6 => 68838 bps

#define US_POKEY_DIV_MAX (255-6) //pokeydiv 249 => avrspeed 255 (vic nemuze)
// Hias: bit-banging serial code limits maximum divisor to 119:
// avrspeed = divisor + 6 = 125
// bit-bang-delay = 2*avrspeed = 250
// start-bit-delay = bit-bang-delay + 5 = 255
#define US_POKEY_DIV_MAX 119

/*
#define SIOSPEED_MODES 9 //pocet fastsio_mode=0..8
Expand Down Expand Up @@ -125,6 +139,10 @@ extern u32 debug_endofvariables;

unsigned char last_key;

// tx_checksum is automatically updated by the bit-banging
// USART_Transmit_Byte routine
unsigned char tx_checksum;

unsigned char get_checksum(unsigned char* buffer, u16 len)
{
u16 i;
Expand Down Expand Up @@ -159,12 +177,29 @@ void USART_Init( u08 value )
UBRRH = 0; //HB =0
UBRRL = value;

/* parameters for bit-banging serial transmission: */
/* total bit duration is 17+bit_delay*8 Atmel cycles */
/* value is pokey divisor plus 6, so we have to multiply it */
/* by 2 to get the correct delay: */
//bit_delay = 2*value;

/* stretch start bit by 5 Atari cycles (40 Atmel cycles) */
/* to account for awkward pokey sampling (shifted by 5 cycles) */
//start_bit_delay = bit_delay + 5;

/* Set double speed flag */
// UCSRA = (1<<UDRE)|(1<<U2X); //double speed
UCSRA = (1<<UDRE);

/* Enable Receiver and Transmitter */
UCSRB = (1<<RXEN)|(1<<TXEN);
/* UCSRB = (1<<RXEN)|(1<<TXEN); */

/* Enable Receiver */
UCSRB = (1<<RXEN);

/* set TxD high (idle) */
sbi(PORTD, PIND1);

/* Set frame format: 8data, 1stop bit */
UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);

Expand All @@ -175,14 +210,8 @@ void USART_Init( u08 value )
}
}

void USART_Transmit_Byte( unsigned char data )
{
/* Wait for empty transmit buffer */
while ( !( UCSRA & (1<<UDRE)) ) ;

/* Put data into buffer, sends the data */
UDR = data;
}
/* use bit-banging implementation */
extern void USART_Transmit_Byte( unsigned char data );

unsigned char USART_Receive_Byte( void )
{
Expand Down Expand Up @@ -280,8 +309,6 @@ u08 USART_Get_atari_sector_buffer_and_check_and_send_ACK_or_NACK(u16 len)

void USART_Send_cmpl_and_atari_sector_buffer_and_check_sum(unsigned short len)
{
u08 check_sum;

// Delay300us(); //po ACKu pred CMPL pauza 250us - 255sec
// Delay300us(); //po ACKu pred CMPL pauza 250us - 255sec
//Kdyz bylo jen 300us tak nefungovalo
Expand All @@ -294,9 +321,11 @@ void USART_Send_cmpl_and_atari_sector_buffer_and_check_sum(unsigned short len)
//Delay800us(); //t6
Delay200us(); //<--pouziva se i u commandu 3F

tx_checksum = 0;
USART_Send_Buffer(atari_sector_buffer,len);
check_sum = get_checksum(atari_sector_buffer,len);
USART_Transmit_Byte(check_sum);
// tx_checksum is updated by bit-banging USART_Transmit_Byte,
// so we can skip separate calculation
USART_Transmit_Byte(tx_checksum);
}

u08 mmcWriteCached(unsigned char force);
Expand Down Expand Up @@ -459,7 +488,7 @@ void set_display(unsigned char n)
//uint8_t EEMEM system_atr_name[]={"SDRIVE ATR"};
// =eeprom_read_byte(&system_atr_name[idx]);
//
uint8_t EEMEM system_info[]="SDrive01 20081012 Bob!k & Raster, C.P.U."; //SDriveVersion info
uint8_t EEMEM system_info[]="SDrive01 20090605H Bob!k & Raster,C.P.U."; //SDriveVersion info
// VVYYYYMMDD
// VV cislo nove oficialne vydane verze, meni se jen pri vydani noveho oficialniho firmware
// s rozsirenymi/zmenenymi funkcemi zpetne nekompatibilni
Expand Down Expand Up @@ -579,6 +608,9 @@ int main(void)
cbi(DDRB,0); // KEY_pin input
cbi(DDRB,1); // KEY_pin input

sbi(DDRD,1); // Bit-Banging TxD output
sbi(PORTD,1); // set high/idle

cbi(DDRD,2); // KEY_pin input
cbi(DDRD,3); // KEY_pin input
cbi(DDRD,4); // KEY_pin input (karta)
Expand Down
146 changes: 146 additions & 0 deletions bitbang.s
@@ -0,0 +1,146 @@
; bitbang.s - serial output using bit-banging
;
; Copyright (c) 2009 by Matthias Reichl <hias@horus.com>
;
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation; either version 2 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

#include <avr/io.h>

.global USART_Transmit_Byte

.global DelayAtariX

.func USART_Transmit_Byte

USART_Transmit_Byte:

; register usage:
; r27 = transmit bit counter
; r26 = transmit byte
; r25 = bit-delay value

; transmit low start bit
cbi _SFR_IO_ADDR(PORTD), PIND1 ; 2

; update tx_checksum
lds r25, tx_checksum ; 2
ldi r26, 0 ; 1
; tx_checksum = tx_checksum + byte
add r25, r24 ; 1
; tx_checksum = tx_checksum + carry
adc r25, r26 ; 1
sts tx_checksum, r25 ; 2
; 7 cycles since start bit

; r27 is used as a bit counter
; note: it's initialized to 9, as the stop-bit will be transmitted
; by the main bit loop
ldi r27,9 ; 1

; move byte from input (r24) to r26
mov r26, r24 ; 1

; read current baudrate register
in r25, _SFR_IO_ADDR(UBRRL) ; 1

; calculate delay value:
; baudrate is clock_frequency / 16 / (UBRR+1)
; DelayAtariX delays for X*8 cycles, so we have to multiply
; the delay by 2. The remaining 16 cycles are spent in "bitlp"
;
; Note: total bit duration time was changed from 16+bit_delay*8
; to 17+bit_delay*8 to compensate for the slightly slower
; clock speeds in PAL Ataris.

lsl r25 ; 1

; stretch start-bit by 5 atari clock cycles to compensate for
; Pokey's too-late data sampling

ldi r24, 4 ; 1
add r24, r25 ; 1
; 13 cycles since start bit

rcall DelayAtariX ; 8*(r24+4) = 32 + 8*r24
; 45 cycles since start bit

; next bit transition will be in 6 Atmel cycles after this
; delay, target is 17+40+bit_delay*8 = 57 + bit_delay*8
; so waste 57-45-6 = 6 more cycles

ldi r24,2 ; 1
bitdel1:
dec r24 ; 1
brne bitdel1 ; 1/2

; bitlp executed 9 times, 8 data bits + 1 stop bit
; carry is set to 1 so that the stopbit will be 1
bitlp: sec ; 1
ror r26 ; 1

brcc bit0 ; 1/2

; transmit high bit
; 3 cycles since bitlp
sbi _SFR_IO_ADDR(PORTD), PIND1 ; 2
rjmp bitdel ; 2

; transmit low bit

bit0: ; 4 cycles since bitlp
cbi _SFR_IO_ADDR(PORTD), PIND1 ; 2
nop ; 1

bitdel: ; 7 cycles per bit so far

mov r24, r25 ; 1
rcall DelayAtariX ; 8*r24

; delay for 6 Atmel cycles so that the total bitlp takes 113
; cycles at pokey divisor 0. This compensates for the slightly slower
; crystal on PAL Ataris.

ldi r24, 2 ; 1
bitdel2:
dec r24 ; 1
brne bitdel2 ; 2/1
; this delay: 1 + (2*3 - 1) = 6 cycles

dec r27 ; 1
brne bitlp ; 1/2
nop ; 1

; total: 17 + r26*8 cycles per data bit
ret

.endfunc

.func DelayAtariX

; delay for r24 Atari clock cycles (8*r24 Atmel cycles)
; note: r24 must be >=2
DelayAtariX: ; 3 cycles for "rcall DelayAtariX"
dec r24 ; 1
delata1: nop ; 1
nop ; 1
nop ; 1
nop ; 1
nop ; 1
dec r24 ; 1
brne delata1 ; 1/2
nop ; 1
ret ; 4

.endfunc

0 comments on commit 7a81985

Please sign in to comment.