Full Version : RSA Credit-Card Encryption System (AVR ASM)
avr >>COMMUNICATIONS & WEB PROJECTS >>RSA Credit-Card Encryption System (AVR ASM)


<< Prev | Next >>

AVR_Admin- 05-09-2006
Secure RSA Credit Card Transaction System. by Tony Cuadra, Ilyas Elkin

Introduction

In today's world of e-commerce and widespread credit card use, credit card storefront terminals have become common to almost all the stores, and thus big business. They are what allows us to use our credit cards in almost any store in the country. RSA public key encryption has become an industry standard because to crack it, the thief has to factor a number with unknown factorization, and that number can be chosen large enough so that time to factor it would be overwhelmingly large even on the fas-*test*-('") computers. For the final project, we designed a credit card transaction system with RSA encryption that takes credit card input from the terminal and sends it to the server controller, which buffers it, and outputs it to the server PC monitor when necessary or requested. While our system is not as secure as industry strength 56 or 128 bit encryption, because we are limited by the math we can do on this server, it does demonstrate all the important properties and functionality of RSA encryption, and can be scaled up if we were to migrate to a more powerful CPU.

High Level Design

We used two Atmel AT90S4414 microcontrollers in this project. One is the storefront terminal, which takes the account number and the transaction amount from the store clerk through the keypad, displaying them on the LCD as they are being typed in. It then requests a key pair (more about encryption below), from the server using software UART. It waits for the server to respond, then loops through the account and the cash amount data, encrypting and sending it to the server one bit at a time.
The server receives the data, decrypts it, and stores it in it's memory. When the memory overflows, it dumps the contents to the PC hyperterminal through the UART. In addition to these features, the server also allows the "bank operator" to perform some simple functions. These include displaying the table or clearing it without waiting for the buffer to fill up, and also allows the operator to change keys for better security, in which case the server will just go down one entry in it's list of stored keys and use a different pair.
Our encryption strength was limited by the fact that to encrypt a number in RSA, it has to be raised to the power of an encoding coefficient (more details below) , and then reduced mod m (encryption modulus). A simple algorithm called Russian Peasant Arithmetic (I am not lying, that's what it's called in the Math 336 book :) makes it possible to perform this function by simply squaring and reducing mod m several times. (more details below) Still, to guarantee correctness, we need to be able to do division on any number less then the square of the modulus. Since the stock division routine we used does 16bit divides, we chose m to be 8 bits, so technically this is 8 bit encryption.

Program/Hardware Design

Pseudocode
Client side:
Start:
Prompt for an account number. (with a #)
Wait
Get account number input followed by a return (A)
Store in memory one digit per byte.
Prompt for transaction amount. (with a $)
Wait.
Get the amount input followed by a return (A)
Store in memory one digit per byte.
Send transaction request to the server using software UART.
Get the public key pair (e,m) from the server.
Encrypt loop (for all the bytes of acct# and cash$)
Encrypt individual bytes by raising to the e power modulo m using Russian peasant arithmetic algorithm (see the encryption appendix) and send the encrypted value to server.
Wait for server to decrypt (delay).
End Encrypt loop.
Start Over


Server Side.
Start:
Wait for transaction request from client or keyboard input from operator
If there is keyboard input from operator (hardware UART) execute the Keyboard Input routine.
If there is a transaction request from client:
Clear Interrupts, so that operator can't interrupt the critical transfer routine, or change keys in the middle of it.
Check if the transaction table is full.
If full, dump the transaction table to the hyperterminal using Display Table routine, and clear the table using Clear Table routine.
Send the public key pair to the client with software UART
Wait for transmission from client.
One by one, receive and decrypt the bits by encrypting them again with the decoding exponent and store the data in the transaction memory.
Set Interrupts
Start Over

Keyboard Input:
Get the keyboard input from hardware UART.
If “D”call Display Table.
If “C”(Clear Table) set the table index to 0.
If“K” call Change Keys.
return.

Display Table (tblflush):
Print Header to the UART
Loop through all the transactions until the index pointer and print them
return.

Change Keys:
Move the key index to the next one in key table (or go back to 0 if already at the end)
return.

Hardware Design

We don't really have much hardware here, aside from the two Atmel AT90S4414 controllers , Keypad and Hitachi LCD. The two controllers have connected grounds, and pins D6 and D7 for Duplex Software UART. The server controller has Pins D0, D1 connected to the hardware RS-232 port.
TOC

Results of the Design

Our credit card transaction system works. We can successfully enter the account number, encrypt send it to the server controller, decrypt, and store it in the "database". We can also view the table, change keys, and clear the table. On table overflow, the server dumps the table to the UART and clears it as proposed. So the functionality we set out to implement is there. The encryption in only 8 bits strong, because we need to be able to divide the square of any number less then the modulus, so the number itself has to be 8 bits if we are to use the standard 16/16 divide and 8x8 multiply from the Atmel website (we need the remainder). However, our encrypt routine does demonstrate all the concepts of public key encryption, which is what we were aiming for, even if it's not industry strength.
Sample Hyperterminal capture
QUOTE


Receiving Transaction:    757265     $0075.27
© clear table, (d) display table, (k) change key >

Receiving Transaction:    957276     $7557.27

© clear table, (d) display table, (k) change key >

Receiving Transaction:    727646     $0772.72

© clear table, (d) display table, (k) change key > d

Account    Amount
537757     $5768.31
757265     $0075.27
957276     $7557.27
727646     $0772.72

© clear table, (d) display table, (k) change key > k

RSA key changed

© clear table, (d) display table, (k) change key >

Receiving Transaction:    054542     $0005.75

© clear table, (d) display table, (k) change key > c

Data table cleared

© clear table, (d) display table, (k) change key > d

Account    Amount

© clear table, (d) display table, (k) change key >



What Would We Do Different Next Time

Connect the ground on both chips right away - took us 4 hrs to figure out why we were sending random things.

Possibly implement a 24/16 divide with working remainder function, and 16x16 multiply se we can make encryption stronger.

Maybe do some sort of interface in visual basic to actually store the UART output in an actual database.

Not even think of using SPI - wasted quite some time getting it to work only to scrap it for software UART. Not enough documentation and examples on that feature

Possibly use an external chip for encryption and interface it with the Atmel controller.


Much of the encryption math comes straight out of the Math 336 book.
Some definitions and important properties of numbers that apply to RSA encryption:

QUOTE
Given a modulus m,  a  is a unit of m iff 0<a<m and GCD(a,m)=1.  The number of units  m has is F(m) (actually it's Phi(m), but that's not important here)
If m is a prime, then F(m) = m-1.  Also for any unit a of m, a^(F(m))=1 (mod m).  Consequently, a^(k*F(m)+1)=a (mod m), for any k.  This is the key behind RSA encryption.  If you choose integers e and d, so that e*d =1 (mod (F(m)), then a^(e*d)=a(mod m).  So to encrypt a number, RSA raises it to the e power, and to decrypt, it raises it again to the d  power mod m.  The Russian peasant arithmetic algorithm raises to large powers by squaring the result and reducing the exponent by half, and multiplying the result by the source when exponent is odd, everytime reducing mod m.  because the number is reduced every iteration, there is no need to store a^d number anywhere and then reducing it mod m.  instead, the biggest number stored is a^2, and it is reduced mod m. I wrote this algorithm in C, and then translated it to assembly manually.  Here is the listing of the C code. Notice how the computational load is reduced by using this algorithm.

CODE

include <stdio.h>
int main(int argc, char *argv[])
{
int result = 1;
int s=atoi(argv[1]); //the number being encrypted
int m=atoi(argv[2]); //the base
int e=atoi(argv[3]); //the encoding key
while(e>0)
{
if((e%2)==1){
result=(result*s)%m;
e=e-1;
}
s=(s*s)%m;
e=e/2;
}
printf("%d ",result);
}


Link: http://vfx.freeweb.hu/avr/index.html

AVR_Admin- 05-09-2006
CODE

;***** Final Project - Credit card transaction system with RSA encryption ********
;
; Tony Cuadra, Ilyas Elkin
;

;************************************************
.nolist
.include "c:\avrtools\appnotes\4414def.inc"
.list


.def char =r0
.def savSREG =r1;save the status register
.def reload =r2;timer 0 interval
.def lcdstat =r3

.def  val0 =r4
.def val1 =r5
.def val2 =r6
.def val3 =r7
.def val4 =r8
.def val5 =r9

.def RSAe =r11
.def RSAd =r11;the encrypt method uses RSAd
.def RSAm =r12
.def RSAs =r13

.def bitcnt =R16  ;bit counter
.def temp =R17  ;temporary storage register
.def Txbyte =R18  ;Data to be transmitted
.def Rxbyte =R19  ;Received data

.def mode =r20;mode of the machine (getting #=0001, getting$=0010, processing=0100)
.def state =r21;state of the debouncer state machine
.def time1 =r22;keeps track of time left for 30ms interrupt
.def timeout =r23;Timeout value in mSec passed to subroutine
.def butnum =r24;the button pressed on the keypad
.def press =r25;the button detected previosly (used by debouncer)
.def key =r26;encoding for the button pressed on the keypad

.equ nopress =1;state when no button has been pressed
.equ maybe =2;detected a button once so far
.equ press =3;detected a button pressed at least twice in a row
.equ bounce =4;detected a button release once


.equ lcdrs =PA6;LCD rs pin connected to PD6
.equ lcdrw =PA5;LCD r/w pin connected to PD5
.equ lcde =PA4;LCD e pin connected to PD4

.equ key0 =0b01111101
.equ key1 =0b11101110
.equ key2 =0b11101101
.equ key3 =0b11101011
.equ key4 =0b11011110
.equ key5 =0b11011101
.equ key6 =0b11011011
.equ key7 =0b10111110
.equ key8 =0b10111101
.equ key9 =0b10111011
.equ keyA =0b11100111
.equ keyB =0b11010111
.equ keyILL =0xff;no button (or multiple buttons) pressed on keypad

.equ getacct =0;bits for the mode
.equ getcash =1
.equ process =2

.macro break
   cli
   rcall Monitor
.endmacro


;*******************************
; Data segment
.dseg
acct: .byte 6;stores the account number
cash: .byte 6;stores the transaction amount


;*******************************
; Initialization
.cseg
.org $0000
rjmp  RESET;reset entry vector
reti  
reti
reti
reti
reti
reti
rjmp  t0int
reti
rjmp Monitor
reti
rjmp Monitor
reti



RESET: ldi Temp, LOW(RAMEND);setup stack pointer
out  SPL, Temp
ldi Temp, HIGH(RAMEND)
out SPH, Temp

 
;PORTC is used for the keypad
;PORTA is used for the LCD


;setup timer 0 for overflow once every mSec
ldi  temp, 3 ;prescale timer by 64
out  TCCR0, temp
ldi temp,256-62;preload timer since
mov reload, temp
out  TCNT0, Reload;62.5 x (64x.25) microSec = 1.0 mSec.

;turn on timer0 overflow interrupt
ldi temp, exp2(TOIE0)
out TIMSK, temp
 
      ;setup hardware UART -- enable RX, TX pins without ISRs
       
ldi     temp, 0b00011000
       out     UCR, temp
       
;set baud rate to 9600
       
ldi     temp, 25
       out     UBRR, temp

      ;enable receive ISR ONLY for keyboard monitor
       sbi     UCR, RXCIE

ldi  mode, EXP2(getacct)

;setup software UART
cbi PORTD,RxD
cbi DDRD,RxD
sbi PORTD,TxD
sbi DDRD,TxD


;initial conditions
ldi time1, 30;set time1 for 30 mSec
ldi state, nopress;set initial state to nopress

clr val0
clr val1
clr val2
clr val3
clr val4
clr val5

sei

ldi temp, 0xff
out DDRA, temp
out PORTA, temp

rcall lcdinit ;Initialize LCD module
rcall lcdclr ;Clear LCD screen

rcall getinput

;***************************
Sched:  
tst time1 ;-*test*-('") for first task ready
brne Sched ;if not then loop
rcall debounce

tskend: rjmp Sched


;***************************
debounce:
ldi time1, 30;reset for another 30 mSec
cpi state, nopress
breq  _nopress
cpi state, maybe
breq  _maybe
cpi state, press
breq  _press
cpi state, bounce
breq  _bounce
error:
rjmp error


_nopress:
rcall keybd
cpi butnum, keyILL;check if nothing is pressed
breq _nopressY
mov press, butnum
ldi state, maybe
_nopressY:
ret


_maybe: rcall keybd
cp press, butnum
breq _maybeY
ldi  state, nopress
ret
_maybeY:ldi state, press
rcall gotkey
ret  


_press: rcall keybd
cp press, butnum
breq _pressY
ldi  state, bounce
_pressY:ret


_bounce:rcall keybd
cp  press, butnum
breq _bounceY
ldi  state, nopress
ret
_bounceY:
ldi  state, press
ret

;***************************
getinput:

ldi temp, '0'
mov val0, temp
mov val1, temp
mov val2, temp
mov val3, temp
mov val4, temp
mov val5, temp

rcall display
ret

;***************************
gotkey:
sbrc  mode, process
ret

cpi press, 'A';-*test*-('") for go command
breq Mstart ;done with this input

mov val5,val4;shift all the frequency digits left
mov val4,val3
mov val3,val2
mov val2,val1
mov val1,val0
mov val0,press;shift in the newly read digit

display:
rcall lcdclr

 sbrc mode, getacct
ldi temp, '#'

sbrc mode, getcash
ldi temp, '$'

rcall lcdput

mov temp, val5
rcall lcdput
mov temp, val4
rcall lcdput
mov temp, val3
rcall lcdput
mov temp, val2
rcall lcdput

ldi temp, '.'
sbrc mode, getcash
rcall  lcdput

mov temp, val1
rcall lcdput
mov temp, val0
rcall lcdput

ret

;***************************
Mstart:

cpi mode, EXP2(getacct)
breq stacct

ldi ZL, LOW(cash)
ldi ZH, HIGH(cash)
rjmp stcash

stacct: ldi ZL, LOW(acct)
ldi ZH, HIGH(acct)

stcash: st Z, val5
std Z+1, val4
std Z+2, val3
std Z+3, val2
std Z+4, val1
std Z+5, val0

cpi mode, EXP2(getcash)
breq proc

ldi mode, EXP2(getcash)
rcall getinput
ret

proc:
ldi mode, EXP2(process)
ldi timeout, 10
rcall delay ;delay 10 ms

;**************************
cli;disable interrupts so we can receive the key

rcall putchar

rcall getchar
mov RSAm, Rxbyte

rcall getchar
mov RSAe, Rxbyte


sei;enable interrupts after receiving key
;**************************

 
ldi ZL, LOW(acct)
ldi ZH, HIGH(acct)

encrloop:

ld RSAs, Z
ldi temp, '0'
sub RSAs, temp
rcall encrypt

ldi timeout, 20
rcall delay ;delay 20 ms

mov Txbyte, RSAs
rcall putchar

ldi timeout, 1
rcall delay ;delay 1 ms

adiw ZL,1
cpi ZL, LOW(acct+12)
brne encrloop

ldi mode, EXP2(getacct)
rcall getinput

ret


keypressed: ret;ignore keys received on the hardware UART


;***************************
keybd: ldi temp, 0x0f;set lower four lines to output
out  DDRC, temp
ldi temp, 0xf0;and turn on the pullups on the inputs
out PORTC, temp
nop  ;Need some time for the pullups to
nop  ;charge the port pins
nop
nop
in temp, PINC;read the high nibble
mov key, temp;and store it (with zeros in the low nibble)


 ldi temp, 0xf0;set upper four lines to outputs
out  DDRC, temp
ldi temp, 0x0f;and turn on pullups on the inputs
out PORTC, temp

nop  ;As before wait for the pin to charge
nop
nop
nop
in temp, PINC;read the low nibble
or key, temp;combine to make key code

;At the point the raw key code should have exactly one zero each in
;the lower and upper nibbles. Any other number of zeros indicates
;either no-button pressed or multiple-button pressed.

ldi butnum, '0'
cpi key, key0
breq tbldone

ldi butnum, '1'
cpi key, key1
breq tbldone

ldi butnum, '2'
cpi key, key2
breq tbldone

ldi butnum, '3'
cpi key, key3
breq tbldone

ldi butnum, '4'
cpi key, key4
breq tbldone

ldi butnum, '5'
cpi key, key5
breq tbldone

ldi butnum, '6'
cpi key, key6
breq tbldone

ldi butnum, '7'
cpi key, key7
breq tbldone

ldi butnum, '8'
cpi key, key8
breq tbldone

ldi butnum, '9'
cpi key, key9
breq tbldone

ldi butnum, 'A'
cpi key, keyA
breq tbldone

ldi butnum, keyILL

tbldone:ret



;*****************************
;interrupt routines


;*****************************
;timer 0 ISR (timer-zero overflow)
;Enters every 1.0 mSec

t0int: in savSREG, SREG
out  TCNT0, reload; keeps clock ticking at 1 mSec

dec  timeout ;subtract another mSec

tst time1
breq t0exit
dec time1

t0exit: out SREG, savSREG
reti  ;back to backgound tasks


.include ".\lcd.inc"
.include ".\keymon3.inc"
.include ".\uart.inc"
.include ".\rsa.inc"



CODE

;***** Final Project - Credit card transaction system with RSA encryption ********
;
; Tony Cuadra, Ilyas Elkin
;

;************************************************
.nolist
.include "c:\avrtools\appnotes\4414def.inc"
.list

.def char =r0
.def savSREG =r1;save the status register


.def RSAd =r11
.def RSAm =r12
.def RSAs =r13
.def RSAe =r14


.def bitcnt =R16  ;bit counter
.def temp =R17  ;temporary storage register
.def Txbyte =R18  ;Data to be transmitted
.def Rxbyte =R19  ;Received data

.def mode =r20;mode of the machine (getting #=0001, getting$=0010, processing=0100)
.def temp =r21;temporary register
.def timeout =r22;Timeout value in mSec passed to subroutine

.def keyindex=r23;index for the key table
.def tblindex=r24;index for the data table
.def count =r25;count how many entries we have received

.def TXflash =r26;text to be sent is in flash if <>0
.def first =r27;flag that is set to 1 if we are receiving the account num
.def RXchar =r28;char received by the hardware UART


.equ keymax =3*3
.equ tblmax =15*10


;*******************************
; Data segment
.dseg
table: .byte 15*10;enough room for 10 entries
 ;6 byte account num + 0-term + 6 byte transaction amt + 0-term


;**************************************
;print and read string macros

.macro PrintFlashStr;use: PrintFlashStr flashaddress
ldi TXflash, 1
ldi ZL, low(@0<<1)
ldi ZH, high(@0<<1)
rcall putString
.endmacro

.macro PrintRamStr;use: PrintRamStr ramaddress
ldi TXflash, 0
ldi ZL, low(@0)
ldi ZH, high(@0)
rcall putString
.endmacro

.macro PrintRamStr2;use: PrintRamStr2 ramaddress, offset_register
ldi TXflash, 0
ldi ZL, low(@0)
ldi ZH, high(@0)
add ZL, @1
adc ZH, TXflash
rcall putString
.endmacro


.macro LED ;use: LED register_to_output
push temp
mov temp, @0
com temp
out PORTB, temp
pop temp
.endmacro

.macro LED2 ;use: LED constant
push temp
ldi temp, @0
com temp
out PORTB, temp
pop temp
.endmacro


.macro delaymac;use: delaymac time (mSec)
ldi timeout, @0
rcall delay2
.endmacro

;*******************************
; Initialization
.cseg
.org $0000
rjmp  RESET;reset entry vector
reti  
reti
reti
reti
reti
reti
reti
reti
rjmp Monitor
reti
rjmp Monitor
reti


;define fixed strings to be transmitted from flash- zero terminated
prompt: .db "(c) clear table, (d) display table, (k) change key > ",0x00
heading:.db "Account    Amount",0x00
spacer: .db "     $",0x00
newline:.db 0x0d, 0x0a, 0x00;carrage return/line feed

Receiving: .db "Receiving Transaction:    ", 0x00
TableCleared: .db "Data table cleared", 0x00
KeyChanged: .db "RSA key changed", 0x00

;define RSA keys:
keys: .db 241, 83, 107, 251, 127, 63, 221, 23, 167
 

RESET: ldi Temp, LOW(RAMEND);setup stack pointer
out  SPL, Temp
ldi Temp, HIGH(RAMEND)
out SPH, Temp


      ;setup UART -- enable RX, TX pins without ISRs
       
ldi     temp, 0b00011000
       out     UCR, temp
       
;set baud rate to 9600
       
ldi     temp, 25
       out     UBRR, temp

      ;enable receive ISR ONLY for keyboard monitor
       sbi     UCR, RXCIE


;set up the PORTs
ser temp ;set PORTB to be
out DDRB,temp;all outputs (for LEDs)

;software UART
cbi PORTD,RxD
cbi DDRD,RxD
sbi PORTD,TxD
sbi DDRD,TxD

;initial conditions
clr tblindex
clr keyindex
rcall newkey
printFlashStr newline
printFlashStr prompt

sei


spin: rcall getchar

cli;disable interrupts so we can receive data

cpi tblindex, tblmax
brne notfull

printFlashStr newline
printFlashStr newline
rcall tblflush;output the table contents to the terminal
clr tblindex;clear the table

notfull:

printFlashStr newline
printFlashStr newline
printFlashStr Receiving

delaymac 10

mov Txbyte, RSAm
rcall putchar

delaymac 10

mov Txbyte, RSAe
rcall putchar

ser first;set first when we are receiving the account num
ldi count, 6
ldi ZL, LOW(table)
ldi ZH, HIGH(table)
clr temp
add ZL, tblindex
adc ZH, temp


getentry:

rcall getchar
mov RSAs, Rxbyte

rcall encrypt
ldi temp, '0'
add RSAs, temp

st Z, RSAs

mov char, RSAs
rcall putc

adiw ZL, 1

cpi count, 3
breq decimal

get_1: dec count
brne getentry


ldi count, 6
clr temp
st Z, temp;write terminating 0

adiw ZL, 1

tst first ;if first time through (got account num)
breq notfirst
clr first

push ZL
push ZH
printFlashStr spacer
pop ZH
pop ZL

rjmp getentry;go through again to get transaction amount

notfirst:
subi tblindex, -15 ;point to next entry in the table

printFlashStr newline
printFlashStr newline
printFlashStr prompt

sei;enable interrupts (done receiving data)

rjmp spin

decimal:
tst first
brne get_1

ldi temp, '.'
mov char, temp
rcall putc
st Z, char
adiw ZL, 1
rjmp get_1


;***************************
;output the entire contents of the table to the terminal

tblflush:
printFlashStr heading
printFlashStr newline

clr count

tblflush_1:
cp count, tblindex
breq tblflush_2

printRamStr2 table, count
subi count, -7

printFlashStr spacer

printRamStr2 table, count
subi count, -8

printFlashStr newline

rjmp tblflush_1

tblflush_2:
printFlashStr newline
ret


;***************************
;change RSA key
newkey:
clr temp
ldi ZL, LOW(keys*2)
ldi ZH, HIGH(keys*2)
add ZL, keyindex
adc ZH, temp

lpm
mov RSAm, r0

adiw ZL, 1
lpm
mov RSAe, r0

adiw ZL, 1
lpm
mov RSAd, r0

subi keyindex, -3
cpi keyindex, keymax
brne newkey_1
clr keyindex

newkey_1:
ret


;***************************
;handle input from hardware UART
keypressed:
cpi RXchar, 'd'
breq key_disp

cpi RXchar, 'D'
breq key_disp

cpi RXchar, 'c'
breq key_clr

cpi RXchar, 'C'
breq key_clr

cpi RXchar, 'k'
breq key_newkey

cpi RXchar, 'K'
breq key_newkey

ret

key_disp:
mov char, RXchar
rcall putc
printFlashStr newline
printFlashStr newline
rcall tblflush
printFlashStr prompt
ret

key_clr:
mov char, RXchar
rcall putc
clr tblindex
printFlashStr newline
printFlashStr newline
printFlashStr TableCleared
printFlashStr newline
printFlashStr newline
printFlashStr prompt
ret

key_newkey:
mov char, RXchar
rcall putc
rcall newkey
printFlashStr newline
printFlashStr newline
printFlashStr KeyChanged
printFlashStr newline
printFlashStr newline
printFlashStr prompt
ret




;*********************************************
;string soubroutines
;*********************************************
;routine to output one char
;enter with char in char
putc: sbis USR, UDRE;wait until clear then send one char
rjmp putC
out  UDR, char
ret

;routine to input one char
;exit with char in char
getc:  sbis USR, RXC;wait until ready then get one char
rjmp getc
in char, UDR
ret

;routine to send a string
putString:
tst TXflash ;is it flash?
breq _txram
lpm;char ;if so get a char
rjmp _tstend
_txram: ld char, Z ;if mem get a char
_tstend:tst char ;is it string end marker?
breq _TXend
rcall putc ;emit a char
adiw ZL, 1 ;set up next pointer
rjmp putString
_TXend: ret



delay2:  push temp

delay2_1: ldi temp, 250

delay2_2: nop
 nop
 nop
 nop
 nop
 nop
 nop
 nop  
 nop
 nop
 nop
 nop
 nop
 dec temp
 brne delay2_2

 dec timeout
 brne delay2_1

 pop temp
 ret


.include ".\uart.inc"
.include ".\keymon3.inc"
.include ".\rsa.inc"

CODE

;*******************RSA encryption Routine************
;**Encrypts Numbers using 8 bit RSA encryption********
;def statements:

;.def RSAd =r21
;.def RSAm =r22
;.def RSAs =r13
.def RSAr =r24


.def drem16uL=r14
.def drem16uH=r15
.def dres16uL=r16
.def dres16uH=r17
.def dd16uL =r16
.def dd16uH =r17
.def dv16uL =r18
.def dv16uH =r19
.def dcnt16u =r20


;changed these around a little
.def mc8u =r18 ;multiplicand
.def mp8u =r16 ;multiplier
.def m8uL =r16 ;result Low byte
.def m8uH =r17 ;result High byte
.def mcnt8u =r19 ;loop counter


;***********end def statements***********



encrypt:

push RSAr;push all the used registers except the source
push RSAd
push RSAm
push r14
push r15
push r16
push r17
push r18
push r19
push r20


ldi RSAr, 1 ; result=1

encloop:
sbrs RSAd, 0;if(e%2=1) do, else skip
rjmp endencif

mov mp8u, RSAr;multiply result by source
mov mc8u, RSAs;source is s
rcall mpy8u

;mov dd16uH, m8uH;divide the product from above
;mov dd16uL, m8uL
mov dv16uL, RSAm;by the modulus
clr dv16uH; 16/8 divide
rcall div16u

mov RSAr, drem16uL; result is the remainder


endencif:
mov mp8u, RSAs
mov mc8u, RSAs; source is s
rcall mpy8u
;mov dd16uH, m8uH
;mov dd16uL, m8uL
mov dv16uL, RSAm
clr dv16uH; 16/8 divide
rcall div16u

mov RSAs, drem16uL; s is the remainder
lsr RSAd; integer divide e by 2

clr r14
cp  RSAd, r14
brne encloop





endencloop:
mov RSAs,RSAr;store the result in the register where source is.
pop r20
pop r19
pop r18
pop r17
pop r16
pop r15
pop r14
pop RSAm
pop RSAd
pop RSAr
ret






;***************************************************************************
;*
;* "mpy8u" - 8x8 Bit Unsigned Multiplication
;*
;* This subroutine multiplies the two register variables mp8u and mc8u.
;* The result is placed in registers m8uH, m8uL
;*  
;* Number of words :9 + return
;* Number of cycles :58 + return
;* Low registers used :None
;* High registers used  :4 (mp8u,mc8u/m8uL,m8uH,mcnt8u)
;*
;* Note: Result Low byte and the multiplier share the same register.
;* This causes the multiplier to be overwritten by the result.
;*
;***************************************************************************

;***** Subroutine Register Variables
;***** Code


mpy8u: clr m8uH ;clear result High byte
ldi mcnt8u,8;init loop counter
lsr mp8u ;rotate multiplier

m8u_1: brcc m8u_2 ;carry set
add  m8uH,mc8u;   add multiplicand to result High byte
m8u_2: ror m8uH ;rotate right result High byte
ror m8uL ;rotate right result L byte and multiplier
dec mcnt8u ;decrement loop counter
brne m8u_1 ;if not done, loop more
ret



;***************************************************************************
;*
;* "div16u" - 16/16 Bit Unsigned Division
;*
;* This subroutine divides the two 16-bit numbers
;* "dd8uH:dd8uL" (dividend) and "dv16uH:dv16uL" (divisor).
;* The result is placed in "dres16uH:dres16uL" and the remainder in
;* "drem16uH:drem16uL".
;*  
;* Number of words :19
;* Number of cycles :235/251 (Min/Max)
;* Low registers used :2 (drem16uL,drem16uH)
;* High registers used  :5 (dres16uL/dd16uL,dres16uH/dd16uH,dv16uL,dv16uH,
;*       dcnt16u)
;*
;***************************************************************************

;***** Subroutine Register Variables
;***** Code

div16u: clr drem16uL;clear remainder Low byte
sub drem16uH,drem16uH;clear remainder High byte and carry
ldi dcnt16u,17;init loop counter
d16u_1: rol dd16uL ;shift left dividend
rol dd16uH
dec dcnt16u ;decrement counter
brne d16u_2 ;if done
ret  ;    return
d16u_2: rol drem16uL;shift dividend into remainder
rol drem16uH
sub drem16uL,dv16uL;remainder = remainder - divisor
sbc drem16uH,dv16uH;
brcc d16u_3 ;if result negative
add drem16uL,dv16uL;    restore remainder
adc drem16uH,dv16uH
clc  ;    clear carry to be shifted into result
rjmp d16u_1 ;else
d16u_3: sec  ;    set carry to be shifted into result
rjmp d16u_1

CODE

;LCD include file

;================================================

; Clear entire LCD and delay for a bit

lcdclr:

ldi temp,1 ;Clear LCD command

rcall lcdcmd


ldi timeout,3;Delay 3 mS for clear command

rcall delay


ret


;================================================

; Initialize LCD module

lcdinit:

ldi temp,0x80;Setup port pins

out PORTA,temp;Pull all pins low (except D7)

ldi temp,0xff;All pins are outputs

out DDRA,temp

ldi timeout,15;Wait at least 15 mS at power up

rcall delay

; LCD specs call for 3 repetitions as follows:
;first rep

ldi temp,0x83;Function set

out PORTA,temp;to 8-bit mode
nop  ;nop is data setup time

sbi PORTA,lcde;Toggle enable line
nop

cbi PORTA,lcde


ldi timeout,15;Wait at least 15 mS

rcall delay

;second rep

ldi temp,0x83;Function set

out PORTA,temp
nop

sbi PORTA,lcde;Toggle enable line
nop

cbi PORTA,lcde


ldi timeout,15;Wait at least 15 ms

rcall delay

;third rep

ldi temp,0x83;Function set

out PORTA,temp
nop

sbi PORTA,lcde;Toggle enable line
nop

cbi PORTA,lcde

ldi timeout,15;Wait at least 15 ms

rcall delay

;Now change to 4-wire interface mode

ldi temp,0x82;Function set, 4 wire databus

out PORTA,temp
nop

sbi PORTA,lcde;Toggle enable line
nop

cbi PORTA,lcde


; Finally, at this point,

; the normal 4 wire command routine (lcdcmd) can be used


ldi temp,0b00100000;Function set, 4 wire, 1 line, 5x7 font

rcall lcdcmd


ldi temp,0b00001100;Display on, no cursor, no blink

rcall lcdcmd


ldi temp,0b00000110;Address increment, no scrolling

rcall lcdcmd

ret


;============================================

; Wait for LCD to go unbusy

lcdwait:

ldi temp,0xF0;Make 4 data lines inputs

out DDRA,temp

sbi PORTA,lcdrw;Set r/w pin to read

cbi PORTA,lcdrs;Set register select to command

waitloop:

sbi PORTA,lcde;Toggle enable line
nop

cbi PORTA,lcde

in lcdstat,PINA;Read busy flag

;Read, and ignore lower nibble

sbi PORTA,lcde;Toggle enable line
nop

cbi PORTA,lcde


sbrc lcdstat,3;Loop until done

rjmp waitloop

ret


;=============================================

; Send command in temp to LCD

lcdcmd:

push temp ;Save character

rcall lcdwait


ldi temp,0xFF;Make all port D pins outputs

out DDRA,temp

pop temp ;Get character back

push temp ;Save another copy

swap temp ;Get upper nibble

andi temp,0x0F;Strip off upper bits
ori temp,0x80

out PORTA,temp;Put on port
nop  ;wait for data setup time

sbi PORTA,lcde;Toggle enable line
nop

cbi PORTA,lcde


pop temp ;Recall character

andi temp,0x0F;Strip off upper bits
ori temp,0x80

out PORTA,temp;Put on port
nop

sbi PORTA,lcde;Toggle enable line
nop

cbi PORTA,lcde


ret


;=============================================

; Send character data in temp to LCD

lcdput:

push temp ;Save character

rcall lcdwait



ldi temp,0xFF;Make all port D pins outputs

out DDRA,temp


pop temp ;Get character back

push temp ;Save another copy

swap temp ;Get upper nibble

andi temp,0x0F;Strip off upper bits
ori temp,0x80

out PORTA,temp;Put on port

sbi PORTA,lcdrs;Register select set for data
nop

sbi PORTA,lcde;Toggle enable line
nop

cbi PORTA,lcde


pop temp ;Recall character

andi temp,0x0F;Strip off upper bits
ori temp,0x80

out PORTA,temp;Put on port

sbi PORTA,lcdrs;Register select set for data
nop

sbi PORTA,lcde;Toggle enable line
nop

cbi PORTA,lcde


ret


;=============================================
; subroutine waits for time equal to value in register timeout
;the register 'timeout' should be loaded before the call


delay: tst timeout
brne delay

ret

;=============================================


CODE


;;*******************************
;;***** RS-232 keyboard monitor
;;*******************************

;To be included into a program under -*test*-('").

;************************************
; keyboard monitor and optional receive ISR
;returns a user char UNLESS the char is "esc" or entry was via rcall

.def RXtemp=r29;a temp register
.def RXparm=r28;subroutine parameter
.def RXsave=r27;save the SREG

.equ npush=5 ;the number of saved registers in RXisr

Monitor:
;save processor status
push ZH
push ZL
push RXtemp
push RXparm
push RXsave
in RXsave, SREG

sbis USR, RXC;is a char available
rjmp RXcmd ;if not go to cmd handler

in RXparm, UDR;get the character
cpi RXparm, 0x1b;is the char an "esc"
breq RXcmd ;if so go to cmd handler

;######
;Uncomment the next MOV if your program expects to have the receive-done
;ISR return a character. You can also put other code here, perhaps
;a pointer increment or <cr> detection.

rcall keypressed
;######

RXexit: out SREG, RXsave;restore proc status
pop RXsave
pop RXparm ;restore program state
pop RXtemp
pop ZL
pop ZH

reti  ;back to pgm
;
;**************************************
; monitor command interperter
RXcmd: ldi RXparm, 0x0d;emit "cr" for a prompt
rcall RXputc
ldi RXparm, 0x0a;emit "lf" for a prompt
rcall RXputc
ldi RXparm, '>';emit ">" for a prompt
rcall RXputc

;wait for a command char
rcall  RXgetc  

;is it a go command?
RXgo: cpi RXparm, 'g';if so return to user code
brne RXreg
rcall RXputc ;echo the 'g'
cbi UCR, TXCIE;clear single-step isr
sbi USR, TXC;and its flag
breq RXexit

;is it a reg read command?
RXreg: cpi RXparm, 'r';dump a register
brne RXregW
rcall RXputc  ;echo the 'r'
rcall RXget2digit ;find out which reg
cpi RXparm, 32-npush;is the reg stored on the stack
brge _stkreg
mov ZL, RXparm ;if not, just read it back
clr ZH
ld RXparm, Z ;remember that 0-1f is reg
rcall RXput2char
rjmp RXcmd
_stkreg:in ZL, SPL  ;if the reg is stacked compute the address
in ZH, SPH  ;on the stack
subi RXparm, 32-npush-1;regs were stacked in numerical order
add ZL, RXparm ;stack offset
clr RXparm
adc ZH, RXparm
ld RXparm, Z
rcall RXput2char
rjmp RXcmd

;is is a register write command?
RXregW: cpi RXparm, 'R' ;write a register
brne RXmem
rcall RXputc  ;echo the 'R'
rcall RXget2digit ;find out which reg
mov ZL, RXparm ;store register number in ZL
ldi RXparm, '=' ;prompt with a equal sign
rcall RXputc
rcall RXget2digit ;get the new reg value

cpi ZL, 32-npush ;is the reg stored on the stack?
brge _stkrgw
;if not, just write it
clr ZH  ;ZL already has reg number
st Z, RXparm ;remember that 0-1f is reg
rjmp RXcmd

_stkrgw:mov RXtemp, RXparm ;RXtemp has new reg value
mov RXparm, ZL ;RXparm now has reg number
in ZL, SPL  ;if the reg is stacked compute the address
in ZH, SPH  ;on the stack
subi RXparm, 32-npush-1;regs were stacked in numerical order
add ZL, RXparm ;stack offset
clr RXparm
adc ZH, RXparm
st Z, RXtemp
rjmp RXcmd


;memory read
RXmem: cpi RXparm, 'm'
brne RXmemw
rcall RXputc ;echo
rcall RXget2digit;get a 4 digit address
mov ZH, RXparm
rcall RXget2digit
mov ZL, RXparm
ld RXparm, Z;load mem data
rcall RXput2char
rjmp RXcmd

;memory write
RXmemw: cpi RXparm, 'M'
brne RXio
rcall RXputc ;echo
rcall RXget2digit;get a 4 digit address
mov ZH, RXparm
rcall RXget2digit
mov ZL, RXparm
ldi RXparm, '=' ;prompt with a equal sign
rcall RXputc
rcall RXget2digit ;get the new memory value
st Z, RXparm ;now store it
rjmp RXcmd

;i/o register read
RXio: cpi RXparm, 'i'
brne RXiow
rcall RXputc ;echo
rcall RXget2digit
subi RXparm, -0x20;i/o register offset
mov ZL, RXparm;pointer to i/o reg
clr ZH
ld RXparm, Z;now get it
rcall RXput2char
rjmp RXcmd

;i/o register write
RXiow: cpi RXparm, 'I'
brne RXsreg
rcall RXputc ;echo
rcall RXget2digit
subi RXparm, -0x20;i/o register offset
mov ZL, RXparm;pointer to i/o reg
clr ZH
ldi RXparm, '=' ;prompt with a equal sign
rcall RXputc
rcall RXget2digit ;get the new reg value
st Z, RXparm;now store it
rjmp RXcmd

;get SREG
RXsreg: cpi RXparm, 's'
brne RXpc
rcall RXputc ;echo
mov Rxparm, RXsave;get the saved SREG
rcall RXput2char
rjmp RXcmd

;get the program counter
RXpc: cpi RXparm, 'p'
brne RXpcw
rcall RXputc ;echo
in ZL, SPL ;get prog counter from the stack
in ZH, SPH
ldd RXparm, Z+npush+1;stack offset to high byte of PC
rcall  RXput2char
ldd RXparm, Z+npush+2;stack offset to low byte of PC
rcall RXput2char2
rjmp RXcmd


;write the program counter
RXpcw: cpi RXparm, 'P'
brne RXstep
rcall RXputc ;echo the P
ldi RXparm, '=';prompt with a equal sign
rcall RXputc
rcall RXget2digit;high order PC
in ZL, SPL ;get prog counter from the stack
in ZH, SPH
std Z+npush+1, RXparm;stack offset to high byte of PC
rcall  RXget2digit
std Z+npush+2, RXparm;stack offset to low byte of PC
rjmp RXcmd

RXstep: cpi RXparm, 't'
brne RXz
rcall RXputc ;echo
in ZL, SPL ;get prog counter from the stack
in ZH, SPH
ldd RXparm, Z+npush+1;stack offset to high byte of PC
rcall  RXput2char
ldd RXparm, Z+npush+2;stack offset to low byte of PC
rcall RXput2char2
ldi RXparm, ' '  ;ascii space
rcall RXputc
sbi UCR, TXCIE  ;kick-start TX complete interrupt
_twait: sbis USR, TXC  ;and wait for the TX complete
rjmp _twait   ;bit to be true
rjmp RXexit

RXz: cpi RXparm, 'z'
brne RXhuh
rcall RXputc ;echo z
in ZL, SPL ;get z-register from the stack
in ZH, SPH
ldd RXparm, Z+npush ;stack offset to ZH
rcall  RXput2char
ldd RXparm, Z+npush-1;stack offset to ZL
rcall RXput2char2
ldd RXparm, Z+npush  ;now get Z again
ldd RXtemp, Z+npush-1
mov ZH, RXparm
mov ZL, RXtemp
;push r0
;lpm
;mov RXparm, r0
;pop r0
;rcall RXput2char
ld RXparm, Z;print the memory pointed to by z
rcall RXput2char
rjmp RXcmd

;unknown command trap
RXhuh: ldi RXparm, '?'
rcall RXputc
rjmp RXcmd
;
;*********************************************
;utility routine to output one char
;enter with char in RXparm
RXputc: sbis USR, UDRE;wait until clear then send one char
rjmp RXputC
out  UDR, RXparm
ret

;utility routine to input one char
;exit with char in RXparm
RXgetc: sbis USR, RXC;wait until ready then get one char
rjmp RXgetc
in RXparm, UDR
ret

;utility routine to make a ascii char into a hex value
;enter with char in RXparm
RXcharTohex:
cpi RXparm, 'a'
brge _aboveA
cpi RXparm, '0';error check-- is digit <'0'?
brge _n1
rjmp RXcmd
_n1: cpi RXparm, ':';error check -- is digit <':'
brlt _n2
rjmp RXcmd
;if here then digit is 0 to 9
_n2: subi RXparm, '0'
ret
_aboveA:
cpi RXparm, 'g';error check -- is digit>'f'
brlt _ab1
rjmp RXcmd
_ab1: subi RXparm, 'a'-10
ret

;utility routine to input rwo ascii chars and
; convert into a hex value returned in RXparm
RXget2digit:
rcall RXgetc ;get the highorder reg # nibble
rcall RXputc ;echo it
rcall RXcharTohex;convert to hex
swap RXparm ;make it the highorder bits
mov RXtemp, RXparm;and save it
rcall RXgetc ;get the loworder reg # nibble
rcall RXputc
rcall RXcharTohex
or RXparm, RXtemp;combine high and low nibbles
ret

;utility routine to output two ascii chars given a
;hex value in RXparm
RXput2char:
mov RXtemp, RXparm;a copy
ldi RXparm, '=';a delimiter
rcall RXputc ;print the delimiter
mov RXparm, RXtemp;restore input

RXput2char2:;entry used only for the second byte of PC dump
mov RXtemp, RXparm
andi RXparm, 0xf0;mask high digits
swap RXparm ;and make them loworder
cpi RXparm, 10;numbers or letters?
brge _f8f1
subi RXparm, -'0';convert a numeric digit to ascii
rjmp _f8tx1
_f8f1: subi RXparm, -'a'+10;convert a letter to ascii
_f8tx1: rcall RXputc
mov RXparm, RXtemp;now repeat for the other digit
andi RXparm, 0x0f
cpi RXparm, 10
brge _f8f2
subi RXparm, -'0'
rjmp _f8tx2
_f8f2: subi RXparm, -'a'+10
_f8tx2: rcall RXputc
ret
;***********************************

CODE

;**** A P P L I C A T I O N   N O T E   A V R 3 0 5 ************************
;*
;* Title  : Half Duplex Interrupt Driven Software UART
;* Version  : 1.1
;* Last updated  : 97.08.27
;* Target  : AT90Sxxxx (All AVR Device)
;*
;* Support email : avr@atmel.com
;*
;* Code Size  : 32 Words
;* Low Register Usage : 0
;* High Register Usage : 4
;* Interrupt Usage : None
;*
;* DESCRIPTION
;* This Application note contains a very code efficient software UART.
;* The example program receives one character and echoes it back.
;***************************************************************************

;***** Pin definitions

.equ RxD =6  ;Receive pin is PD6
.equ TxD =7  ;Transmit pin is PD7

;***** Global register variables

.def bitcnt =R16  ;bit counter
.def temp =R17  ;temporary storage register

.def Txbyte =R18  ;Data to be transmitted
.def RXbyte =R19  ;Received data

.cseg

;***************************************************************************
;*
;* "putchar"
;*
;* This subroutine transmits the byte stored in the "Txbyte" register
;* The number of stop bits used is set with the sb constant
;*
;* Number of words :14 including return
;* Number of cycles :Depens on bit rate
;* Low registers used :None
;* High registers used :2 (bitcnt,Txbyte)
;* Pointers used :None
;*
;***************************************************************************
.equ  sb =1 ;Number of stop bits (1, 2, ...)

putchar: ldi bitcnt,9+sb;1+8+sb (sb is # of stop bits)
 com Txbyte ;Inverte everything
 sec  ;Start bit

putchar0: brcc putchar1;If carry set
 cbi PORTD,TxD;    send a '0'
 rjmp putchar2;else

putchar1: sbi PORTD,TxD;    send a '1'
 nop

putchar2: rcall UART_delay;One bit delay
 rcall UART_delay

 lsr Txbyte ;Get next bit
 dec bitcnt ;If not all bit sent
 brne putchar0;   send next
   ;else
 ret  ;   return


;***************************************************************************
;*
;* "getchar"
;*
;* This subroutine receives one byte and returns it in the "Rxbyte" register
;*
;* Number of words :14 including return
;* Number of cycles :Depens on when data arrives
;* Low registers used :None
;* High registers used :2 (bitcnt,Rxbyte)
;* Pointers used :None
;*
;***************************************************************************

getchar: ldi  bitcnt,9;8 data bit + 1 stop bit

getchar1: sbic  PIND,RxD;Wait for start bit
 rjmp  getchar1

 rcall UART_delay;0.5 bit delay

getchar2: rcall UART_delay;1 bit delay
 rcall UART_delay  

 clc  ;clear carry
 sbic  PIND,RxD;if RX pin high
 sec  ;

 dec  bitcnt ;If bit is stop bit
 breq  getchar3;   return
   ;else
 ror  Rxbyte ;   shift bit into Rxbyte
 rjmp  getchar2;   go get next

getchar3: ret


;***************************************************************************
;*
;* "UART_delay"
;*
;* This delay subroutine generates the required delay between the bits when
;* transmitting and receiving bytes. The total execution time is set by the
;* constant "b":
;*
;* 3·b + 7 cycles (including rcall and ret)
;*
;* Number of words :4 including return
;* Low registers used :None
;* High registers used :1 (temp)
;* Pointers used :None
;*
;***************************************************************************
; Some b values:  (See also table in Appnote documentation)
;
; 1 MHz crystal:
;   9600 bps - b=14
;  19200 bps - b=5
;  28800 bps - b=2
;
; 2 MHz crystal:
;  19200 bps - b=14
;  28800 bps - b=8
;  57600 bps - b=2

; 4 MHz crystal:
;  19200 bps - b=31
;  28800 bps - b=19
;  57600 bps - b=8
; 115200 bps - b=2


.equ b =250
;.equ b =31;19200 bps @ 4 MHz crystal


UART_delay: ldi temp,b
UART_delay1: dec temp
 brne UART_delay1

 ret





Free Forum Hosting by Forumer.comTM!