;--------------------------------------------------------------------------- ; Atarilcd.asm ; ============ ; ; Rev 1.00 : Gary Morton (14-Jul-00) ; - First write, adapted from my frequency counter code and updated ; to suit the new hardware. ; Code was able to print 5 hex bytes, but it may have been ; possible that whilst printing the next byte arrived, so ; now use a buffer. However this upsets the SIO bus for some ; unknown reason. ; Rev 1.01 : Gary Morton (15-Jul-00) ; - Need to check for the start bit and stop bit!! ; Rev 1.02 : Gary Morton (18-Jul-00) ; - Still not working so count the number of clocks while the ; command line is low and print (should be 50). ; Used the scope to better understand SIO waveforms. ; Found the bug! - was rotating AtariByte into W and not F! ; Code is now working. ; ;--------------------------------------------------------------------------- TITLE "Atari SIO Monitor" list P=16C84, F=INHX8M __config 0x3FFA include "P16c84.inc" include "wait.mac" Put macro char movlw char call LCD_Send_Char endm ;--------------------------------------------------------------------------- ; Hardware Description ; ==================== ; ; The LCD display is connected in the following manner ; RB5 - R/S ; RB4 - R/W ; RB3 - D7 ; RB2 - D6 ; RB1 - D5 ; RB0 - D4 ; RA3 - E <- note PORT A ; ; The ATARI I/O connector is connected in the following manner ; RA0 - input - CLOCK OUT (from Atari) ; RA1 - input - DATA OUT (from Atari) ; RA2 - input - COMMAND (from Atari) ; RA4 - output - DATA IN (to Atari) - Note this pin is open collector/drain. ;--------------------------------------------------------------------------- ; LCD Display Commands and Control Signal names. ;--------------------------------------------------------------------------- LCD_DATA_PORT EQU PORTB LCD_DATA_TRIS EQU TRISB LCD_READ_MODE EQU b'00001111' LCD_WRITE_MODE EQU b'00000000' LCD_DATA_MASK EQU b'00001111' LCD_BUSY_BIT EQU 7 ; #define LCD_E PORTA,3 ; LCD Enable control line #define LCD_R_W PORTB,4 ; LCD Read/Write control line #define LCD_RS PORTB,5 ; LCD Register Select control line #define LCD_COMMAND bcf LCD_RS #define LCD_DATAIO bsf LCD_RS #define LCD_WRITE bcf LCD_R_W #define LCD_READ bsf LCD_R_W ; Clock_LCD macro bsf LCD_E nop nop bcf LCD_E endm ;CLR_DISP - clears all display, and cursor home to addr 0 CLR_DISP EQU 0x001 ; Clear the Display ;RTN_HOME - cursor home and display unshifted, DDRAM unchanged RTN_HOME EQU 0x002 ;ENTRY_... - sets entry mode, S = display shift on, INC / DEC cursor move dir ENTRY_DEC EQU 0x004 ENTRY_DEC_S EQU 0x005 ENTRY_INC EQU 0x006 ENTRY_INC_S EQU 0x007 ;DISP_... - Display on / off control, C = cursor ON, B = chr pos blink DISP_OFF EQU 0x008 ; Display off DISP_ON EQU 0x00C ; Display on DISP_ON_C EQU 0x00E ; Display on, Cursor on DISP_ON_BC EQU 0x00F ; Display on, Cursor on, Blink cursor ;SHIFT_... - Cursor & Display shift, S=Display, C=cursor, L=Left, R=Right SHIFT_C_L EQU 0x010 SHIFT_C_R EQU 0x014 SHIFT_S_L EQU 0x018 SHIFT_S_R EQU 0x01C ;FUNC_... - Function set, ( 4 bit, 2 lines, 5 x 7 dots) FUNC_SET EQU 0x028 ; 4 bits/2 line/ CURS_RGT EQU 0x093 DD_RAM_LINE1 EQU 0x080 ; Least Significant 7-bit are for address DD_RAM_LINE2 EQU 0x0C0 ;--------------------------------------------------------------------------- ; LCD Display Commands and Control Signal names. ;--------------------------------------------------------------------------- #define ATARI_CLOCKOUT PORTA,0 ; RA0 - input - CLOCK OUT (from Atari) #define ATARI_DATAOUT PORTA,1 ; RA1 - input - DATA OUT (from Atari) #define ATARI_COMMAND PORTA,2 ; RA2 - input - COMMAND (from Atari) #define ATARI_DATAIN PORTA,4 ; RA4 - output - DATA IN (to Atari) ;--------------------------------------------------------------------------- ; Internal RAM assignment ;--------------------------------------------------------------------------- Clock_Freq EQU d'4000000' ; 4MHz CommandByteCount EQU 5 Temp EQU h'10' LcdChar EQU h'11' Temp1 EQU h'12' Temp2 EQU h'13' AtariByte EQU h'15' bitcount EQU h'16' bytecount EQU h'17' hextmp EQU h'18' blocks EQU h'19' buffer EQU h'20' ;--------------------------------------------------------------------------- ; The program code... ;--------------------------------------------------------------------------- org 000h bsf STATUS, RP0 movlw b'11110111' ; LCD-E and Atari DataIn are outputs movwf TRISA clrwdt movlw b'00100111' ; Prescaler -> Timer0, 1:256, rising edge movwf OPTION_REG bcf STATUS,RP0 bcf LCD_E bsf ATARI_DATAIN call Display_Reset call Init_Display movlw DD_RAM_LINE2 call LCD_Send_Cmd Put "A" Put "t" Put "a" Put "r" Put "i" Put " " Put "S" Put "I" Put "O" Put " " Put "M" Put "o" Put "n" Put "i" Put "t" Put "o" Put "r" movlw 0 movwf blocks mainloop WaitForCommandLineHigh1 btfss ATARI_COMMAND goto WaitForCommandLineHigh1 WaitForCommandLineLow btfsc ATARI_COMMAND goto WaitForCommandLineLow movlw buffer movwf FSR ; expect 5 bytes movlw CommandByteCount movwf bytecount ByteLoop ; expect 1 low start bit, 8 data bits, and 1 high stop bit per byte clrf AtariByte movlw 8 movwf bitcount ; ____ 1 2 3 4 5 6 7 8 ; \_________________________________________________________________ ; ___________________ _ _ _ _ _ _ _ _ _ _ _ _ ; |_/ |_/ |_/ |_/ |_/ |_/ |_/ |_/ |_/ |_/ |_/ |_/ |__ ; _________________________ ___ ___ ___ ___ ___ ___ ___ ___ ________ ; \___X___X___X___X___X___X___X___X___/ ; ; NOTES: ; 1) Command starts high ; 2) Clock starts high ; 3) There are several clocks while data is still high ; 4) There are extra clocks after the last bit has been transmitted ; 5) High to low edge rate is fast, but low to high is exponential StartBit ; look for first falling edge of clock with data low - this is the start bit WaitForClockHighStart btfss ATARI_CLOCKOUT goto WaitForClockHighStart WaitForClockLowStart btfsc ATARI_CLOCKOUT goto WaitForClockLowStart ; the start bit should be low btfsc ATARI_DATAOUT goto WaitForClockHighStart ; DATAOUT is still high ; start bit found BitLoop ; there are 8 data bits, least significant bit first WaitForClockHigh btfss ATARI_CLOCKOUT goto WaitForClockHigh WaitForClockLow btfsc ATARI_CLOCKOUT goto WaitForClockLow ; clock line has gone from high to low rrf AtariByte,F ; rotate current byte right bcf AtariByte,7 ; clear top bit in preparation btfsc ATARI_DATAOUT ; test DATAOUT line and skip if 0 bsf AtariByte,7 ; DATAOUT is 1 so set the top bit decfsz bitcount,F goto BitLoop ; look for the falling edge of clock - this should be the stop bit WaitForClockHighStop btfss ATARI_CLOCKOUT goto WaitForClockHighStop WaitForClockLowStop btfsc ATARI_CLOCKOUT goto WaitForClockLowStop ; the stop bit should be high btfss ATARI_DATAOUT goto StopError ; stop line is low - this is wrong btfsc ATARI_COMMAND goto CommandError ; command has gone high, but it should be low movfw AtariByte movwf INDF incf FSR,f decfsz bytecount,F goto ByteLoop incf blocks,f WaitForCommandLineHigh2 btfss ATARI_COMMAND goto WaitForCommandLineHigh2 movlw DD_RAM_LINE1 call LCD_Send_Cmd Put ">" movlw buffer movwf FSR movlw CommandByteCount movwf bytecount BytePrintLoop movfw INDF call PrintWinHex Put " " incf FSR,f decfsz bytecount,F goto BytePrintLoop ; Put "$" movfw blocks call PrintWinHex goto mainloop StopError movlw DD_RAM_LINE2 call LCD_Send_Cmd Put "S" Put "t" Put "o" Put "p" Put " " Put "e" Put "r" Put "r" Put "o" Put "r" Put " " Put " " Put " " movfw bytecount call PrintWinHex movfw bitcount call PrintWinHex goto mainloop CommandError Put "C" Put "o" Put "m" Put "m" Put "a" Put "n" Put "d" Put " " Put "e" Put "r" Put "r" Put "o" Put "r" movfw bytecount call PrintWinHex movfw bitcount call PrintWinHex goto mainloop PrintWinHex movwf hextmp swapf hextmp,w andlw 0xf sublw 9 btfsc STATUS,C goto digithilessthan10 swapf hextmp,w andlw 0xf addlw "A"-10 call LCD_Send_Char goto nexthex1 digithilessthan10 swapf hextmp,w andlw 0xf addlw "0" call LCD_Send_Char nexthex1 movf hextmp,w andlw 0xf sublw 9 btfsc STATUS,C goto digitlolessthan10 movf hextmp,w andlw 0xf addlw "A"-10 call LCD_Send_Char goto nexthex2 digitlolessthan10 movf hextmp,w andlw 0xf addlw "0" call LCD_Send_Char nexthex2 return ;--------------------------------------------------------------------------- ; Liquid Crystal Character Display Routines ;--------------------------------------------------------------------------- Display_Reset bsf STATUS, RP0 ; Bank 1 movlw LCD_WRITE_MODE movwf LCD_DATA_TRIS ; set all except PB0 to output bcf STATUS, RP0 ; Bank 0 clrf LCD_DATA_PORT ; set all port low ;have to wait 15ms here Wait 15 Millisec, 0 movlw b'0011' ; Command for 8-bit interface high nibble movwf LCD_DATA_PORT ; ie 0011xxxx R/S=0, R/W=0 Clock_LCD ;have to wait 4.1ms here Wait 4100 Microsec, 0 Clock_LCD ;have to wait 100us here Wait 100 Microsec, 0 Clock_LCD ;have to wait 100us here Wait 100 Microsec, 0 movlw b'0010' ; Command for 4-bit interface high nibble movwf LCD_DATA_PORT ; ie 0010xxxx Clock_LCD ;from here interface is 4 bit and busy can be checked movlw FUNC_SET ; has to be 0010 10XX call LCD_Send_Cmd movlw DISP_OFF ;0000 1000 call LCD_Send_Cmd return ;**************************** ;* * ;* INITIALIZE DISPLAY * ;* * ;**************************** Init_Display movlw DISP_ON_BC ; Display On, Blink Cursor On call LCD_Send_Cmd movlw CLR_DISP ; Clear the Display call LCD_Send_Cmd movlw ENTRY_INC call LCD_Send_Cmd movlw DD_RAM_LINE1 call LCD_Send_Cmd return ;******************************************************************* ;*SendChar - Sends character to LCD * ;*This routine splits the character into the upper and lower * ;*nibbles and sends them to the LCD, upper nibble first. * ;******************************************************************* ; LCD_Send_Char movwf LcdChar ;Character to be sent is in W so put in local var call Busy_Check ;Wait for LCD to be ready swapf LcdChar, w andlw LCD_DATA_MASK ;Get upper nibble into upper half port movwf LCD_DATA_PORT ;Send data to LCD LCD_DATAIO LCD_WRITE Clock_LCD movf LcdChar, w andlw LCD_DATA_MASK ;Get lower nibble into upper half port movwf LCD_DATA_PORT ;Send data to LCD LCD_DATAIO LCD_WRITE Clock_LCD return ;******************************************************************* ;* SEND_CMD - Sends command to LCD * ;* This routine splits the command into the upper and lower * ;* nibbles and sends them to the LCD, upper nibble first. * ;******************************************************************* LCD_Send_Cmd movwf LcdChar ; Character to be sent is in W so put in local var call Busy_Check ; Wait for LCD to be ready swapf LcdChar,w andlw LCD_DATA_MASK ; Get upper nibble into lower half port movwf LCD_DATA_PORT ; Send data to LCD LCD_COMMAND LCD_WRITE Clock_LCD movf LcdChar,w andlw LCD_DATA_MASK ; Get lower nibble into lower half port movwf LCD_DATA_PORT ; Send data to LCD LCD_COMMAND LCD_WRITE Clock_LCD RETURN ;******************************************************************* ;* This routine checks the busy flag, returns when not busy * ;* Affects: * ;* Temp - Returned with busy/address * ;******************************************************************* ; Busy_Check bsf STATUS, RP0 ; Select Register page 1 movlw LCD_READ_MODE ; read mode to read busy bit movwf LCD_DATA_TRIS bcf STATUS, RP0 ; Select Register page 0 LCD_COMMAND LCD_READ bsf LCD_E ; Set E high nop nop movf LCD_DATA_PORT, W ; Read upper nibble busy flag, DDRam address bcf LCD_E ; Set E low andlw LCD_DATA_MASK ; Mask out higher nibble movwf Temp swapf Temp, F ; move higher nibble into place bsf LCD_E ; Toggle E to get lower nibble nop nop movf LCD_DATA_PORT, W ; Read lower nibble DDRam address bcf LCD_E andlw LCD_DATA_MASK ; Mask out valid data iorwf Temp, F ; Combine nibbles btfsc Temp, LCD_BUSY_BIT ; Check busy flag, high = busy goto Busy_Check ; If busy, check again bsf STATUS, RP0 ; Select Register page 1 movlw LCD_WRITE_MODE ; back to write mode movwf LCD_DATA_TRIS bcf STATUS, RP0 ; Select Register page 0 return ifdef INCLONGDELAY longdelay movwf Temp1 ldloop1 clrf Temp2 ldloop2 decfsz Temp2, F goto ldloop2 decfsz Temp1, F goto ldloop1 return endif ifdef INCSHORTDELAY shortdelay movwf Temp1 sdloop decfsz Temp1, F goto sdloop return endif end