;;*************************************** ;;***** RS-232 keyboard monitor ;;*************************************** ; ;main program also uses rs232 interface to count up ; .include "d:\bruce\eedocs\atmel\asm src\8515def.inc" .device AT90S8515 .def temp =r16 ;temporary register .def count =r17 ;a counter to use in message .def savSREG =r18 ;save the status register .def TXbusy =r19 ;transmit busy flag .def RXchar =r20 ;a received character .def TXflash =r21 ;text to be sent is in flash if <>0 .def digit =r21 ;ascii format variable .equ baud96 =25 ;9600 baud constant for 4Mhz crystal .equ go ='g' ;0x67 ascii 'g' .equ stop ='s' ;0x73 ascii 's' .equ azero ='0' ;0x30 ascii '0' ;************************************** ;This weird construction calls the ISR as if it is a subroutine ;You can put it your program whenever you want to enter the ;keyboard monitor .macro break cli rcall RXisr .endmacro ;************************************** .dseg ;define variable strings to be tranmitted from RAM cntstr: .byte 3 ;a two digit count + a zero terminate ;************************************** .cseg .org $0000 rjmp RESET ;reset entry vector reti reti reti reti reti reti reti reti rjmp RXisr ;UART receive rjmp TXempty ;UART buffer empty reti reti ;************************** text: .db "the count is:" crlf: .db 0x0d, 0x0a, 0x00 ;carrage return/line feed ;************************** RESET: ldi temp, LOW(RAMEND) ;setup stack pointer out SPL, temp ldi temp, HIGH(RAMEND) out SPH, temp ;initial conditions clr count ; zero the count clr TXbusy ;start out not busy on TX ldi RXchar, go ;start out running ;setup UART -- enable TXempty & RXdone int, and RX, TX pins ldi temp, 0b10111000 out UCR, temp ;set baud rate to 9600 ldi temp, baud96 out UBRR, temp sei ;now print three strings and wait for incoming character interrupt TXloop: cpi RXchar, go ;wait for go signal - a 'g' on the keyboard brne TXloop ;ldi r27,0xaa ;break ;undate counter and form string in RAM inc count ;a counter to append to message cpi count, 100 ;counts to 99 then resets to brlt noreset ;ascii zero clr count noreset:ldi ZL, LOW(cntstr) ;ptr to RAM ldi ZH, HIGH(cntstr) ldi digit, 0 ;init 10's place convert to ascii mov temp, count tens: cpi temp, 10 brlt write10 subi temp, 10 ;subrtact until no more 10's left inc digit ;add one to tens digit rjmp tens write10:subi digit, -azero ;add ascii offset st Z, digit ;the top count byte to write to RAM inc ZL ;store in next position in RAM subi temp, -azero ;temp has lower digit st Z, temp ;bottom count byte to RAM inc ZL ;zero string terminator to RAM clr temp st Z, temp ;set up the transmit pointer for the first message ldi ZL, LOW(text<<1) ;shifted becuase pgm memory is words ldi ZH, HIGH(text<<1) ; lpm ;put the char in r0 out UDR, r0 ;put the character in the UART buffer ser TXflash ;string is in flash memory ser TXbusy ;and set the TX busy flag sbi UCR, UDRIE ;enable the TXempty interrupt rcall TXwait ;chill until done ;now the pointer to the variable message in RAM ldi ZL, LOW(cntstr) ;ptr to RAM ldi ZH, HIGH(cntstr) ld r0,Z out UDR, r0 ;fire off the UART transmit clr TXflash ;the string is in RAM ser TXbusy ;and set the TX busy flag sbi UCR, UDRIE ;enable the TXempty interrupt rcall TXwait ;setup ptr for string ldi ZL, LOW(crlf<<1) ;shifted becuase pgm memory is words ldi ZH, HIGH(crlf<<1) lpm out UDR, r0 ;trigger the UART TX ser TXflash ;text string in flash memory ser TXbusy ;and set the TX busy flag sbi UCR, UDRIE ;enable the TXempty interrupt rcall TXwait rjmp TXloop ;*********************************** ;subroutine to wait for transmit finish ;you could put useful work here TXwait: tst TXbusy ;now wait for the tranmission to finish brne TXwait ret ;*********************************** ;Transmit ISR ; UART needs a character TXempty:in savSREG, SREG ;save processor status tst TXflash ;Is the string in flash memory? breq TXram ;If not, it is in RAM inc ZL ;get the next char from flash lpm ;and put it in r0 rjmp TXfls TXram: inc ZL ;get the next char from RAM ld r0,Z TXfls: tst r0 ;if char is zero then exit breq TXend out UDR, r0 ;otherwise transmit it rjmp TXexit ;exit until next char TXend: clr TXbusy ;no more chars cbi UCR, UDRIE ;clear the TXempty interrupt TXexit: out SREG, savSREG ;restore proc status reti ;back to pgm ; TX done -- buffer is empty -- unused here TXdone: in savSREG, SREG ;save processor status out SREG, savSREG ;restore proc status reti ;back to pgm ;************************************ ; receive ISR and keyboard monitor ;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 RXisr: ;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 mov RXchar, RXparm ;if not then this is a user char 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 breq RXexit ;is it a reg read command? RXreg: cpi RXparm, 'r' ;dump a register brne RXmem rcall RXputc ;echo the 'r' rcall RXget2digit ;find out which reg cpi RXparm, 27 ;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, 27-1 ;regs were stacked in numerical order add ZL, RXparm ;stack offset clr RXparm adc ZH, RXparm ld RXparm, Z rcall RXput2char rjmp RXcmd ;memory read RXmem: cpi RXparm, 'm' brne RXio 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 ;i/o register read RXio: 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 ld RXparm, Z ;now get it rcall RXput2char 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 RXhuh 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 ;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 ;***********************************