;************************************************************************** ; FILE: enigma.asm * ; CONTENTS: The Enigma Machine * ; COPYRIGHT: MadLab Ltd. 2004 * ; AUTHOR: James Hutchby * ; UPDATED: 11/08/04 * ;************************************************************************** list p=16F628 include "p16f628.inc" __config _INTRC_OSC_NOCLKOUT & _WDT_ON & _PWRTE_ON & _BODEN_ON & _MCLRE_OFF & _LVP_OFF & _CP_ALL __idlocs h'EC10' errorlevel -207,-302,-305,-306 ;************************************************************************** ; * ; Specification * ; * ;************************************************************************** ; power-up self-test: ; flashes all display LEDs twice (after a few seconds if keyboard missing) ; waits for keyboard assurance test, displays '!' if failure ; otherwise displays "ENIGMA" repeatedly until key pressed or timeout ; M3 Naval Enigma - machine setup characterised by: ; left, centre and right rotor numbers (0 to 7 = I to VIII) ; left, centre and right rotor rings (0 to 25 = A to Z) ; left, centre and right rotor start positions (0 to 25 = A to Z) ; reflector (0 or 1 = B or C) ; plug board - up to 13 wires connecting pairs of letters (0 to 25 = A to Z) ; default settings - rotors 1, 2, 3; rings A, A, A; start A, A, A; reflector B; no plugs ; F1 displays help message ; F2 sets rotors, accepts 3 digits (left-centre-right) '1' to '8' ; resets all rings and start positions to 'A' ; error if invalid key or same digit entered twice ; shift F2 displays current rotors, 3 digits '1' to '8' ; F3 sets rings, accepts 3 letters 'A' to 'Z' ; error if invalid key ; shift F3 displays current ring settings, 3 letters 'A' to 'Z' ; F4 sets rotor start positions, accepts 3 letters 'A' to 'Z' ; error if invalid key ; shift F4 displays rotor start positions, 3 letters 'A' to 'Z' ; shift F5 displays current rotor positions, 3 letters 'A' to 'Z' ; F6 sets reflector, accepts 1 letter 'B' or 'C' ; error if invalid key ; shift F6 displays current reflector, 1 letter 'B' or 'C' ; F7 sets plug board, accepts pairs of letters 'A' to 'Z' ; error if invalid key, letter already used, or link to self ; finish by ENTER, or after 13 pairs entered ; existing plugs not retained ; F7,ENTER will clear all plugs ; shift F7 displays plug board, up to 13 pairs of letters 'A' to 'Z' ; displays '?' when waiting for settings input, displays '!' if error ; letters 'A' to 'Z' encoded and displayed, other keys ignored ; rotors stepped before each letter encoded ; ESC/HOME resets rotors to start positions (i.e. clears message) ; display flashes twice to acknowledge ; BACKSPACE/DELETE deletes last letter entered (steps rotors back one position) ; keyboard LEDs flash twice to acknowledge ; single level of delete only ;************************************************************************** ; * ; Port assignments * ; * ;************************************************************************** PORTA_IO equ b'00110000' ; port A I/O status PORTB_IO equ b'11000000' ; port B I/O status COLUMN1_BIT equ 0 ; LED columns COLUMN2_BIT equ 4 COLUMN3_BIT equ 1 COLUMN4_BIT equ 5 COLUMN5_BIT equ 0 COLUMN6_BIT equ 7 COLUMN7_BIT equ 6 COLUMN1_PORT equ PORTB COLUMN2_PORT equ PORTB COLUMN3_PORT equ PORTA COLUMN4_PORT equ PORTB COLUMN5_PORT equ PORTA COLUMN6_PORT equ PORTA COLUMN7_PORT equ PORTA ROW1_BIT equ 3 ; LED rows ROW2_BIT equ 2 ROW3_BIT equ 2 ROW4_BIT equ 1 ROW5_BIT equ 3 ROW1_PORT equ PORTA ROW2_PORT equ PORTA ROW3_PORT equ PORTB ROW4_PORT equ PORTB ROW5_PORT equ PORTB PIXELS_MASKA equ b'00001100' ; pixel masks PIXELS_MASKB equ b'00001110' #define KBD_CLK PORTB,7 ; keyboard clock #define KBD_DATA PORTB,6 ; keyboard data kbd macro c,d movlw PORTB_IO if c == 0 bcf KBD_CLK andlw ~b'10000000' endif if d == 0 bcf KBD_DATA andlw ~b'01000000' endif tris_B endm ;************************************************************************** ; * ; Constants and timings * ; * ;************************************************************************** CLOCK equ d'4000000' ; processor clock frequency in Hz PRESCALE equ b'00000000' ; prescale TMR0 1:2, weak pull-ups enabled CR equ d'13' ; carriage return ; delays in 1/100 s SHOW_DELAY equ d'60' BLANK_DELAY equ d'3' FLASH_DELAY equ d'20' ROTOR_DELAY equ d'4' ;-------------------------------------------------------------------------- ; keyboard codes ;-------------------------------------------------------------------------- ; keyboard command codes KBD_BAT equ h'AA' ; Basic Assurance Test successful KBD_ERROR equ h'FC' ; keyboard error KBD_EXTENDED equ h'E0' ; extended key code (+h'80' in scan code) KBD_LEDS equ h'ED' ; set/reset LEDs (0 = scroll, 1 = num, 2 = caps) KBD_BREAK equ h'F0' ; key break code KBD_TYPEMATIC equ h'F3' ; set typematic rate and delay (h'7f' = slowest) KBD_ACK equ h'FA' ; acknowledge KBD_RESEND equ h'FE' ; resend last byte KBD_RESET equ h'FF' ; reset keyboard ; keyboard scan codes KBD_A equ h'1C' KBD_B equ h'32' KBD_C equ h'21' KBD_D equ h'23' KBD_E equ h'24' KBD_F equ h'2B' KBD_G equ h'34' KBD_H equ h'33' KBD_I equ h'43' KBD_J equ h'3B' KBD_K equ h'42' KBD_L equ h'4B' KBD_M equ h'3A' KBD_N equ h'31' KBD_O equ h'44' KBD_P equ h'4D' KBD_Q equ h'15' KBD_R equ h'2D' KBD_S equ h'1B' KBD_T equ h'2C' KBD_U equ h'3C' KBD_V equ h'2A' KBD_W equ h'1D' KBD_X equ h'22' KBD_Y equ h'35' KBD_Z equ h'1A' KBD_0 equ h'45' KBD_1 equ h'16' KBD_2 equ h'1E' KBD_3 equ h'26' KBD_4 equ h'25' KBD_5 equ h'2E' KBD_6 equ h'36' KBD_7 equ h'3D' KBD_8 equ h'3E' KBD_9 equ h'46' KBD_QUOTE equ h'0E' KBD_MINUS equ h'4E' KBD_EQUALS equ h'55' KBD_BACKSLASH equ h'5D' KBD_BACKSPACE equ h'66' KBD_SPACE equ h'29' KBD_TAB equ h'0D' KBD_CAPS_LOCK equ h'58' KBD_LEFT_SHFT equ h'12' KBD_LEFT_CTRL equ h'14' KBD_LEFT_GUI equ h'1F'+h'80' KBD_LEFT_ALT equ h'11' KBD_RIGHT_SHFT equ h'59' KBD_RIGHT_CTRL equ h'14'+h'80' KBD_RIGHT_GUI equ h'27'+h'80' KBD_RIGHT_ALT equ h'11'+h'80' KBD_APPS equ h'2F'+h'80' KBD_ENTER equ h'5A' KBD_ESC equ h'76' KBD_F1 equ h'05' KBD_F2 equ h'06' KBD_F3 equ h'04' KBD_F4 equ h'0C' KBD_F5 equ h'03' KBD_F6 equ h'0B' KBD_F7 equ h'83' KBD_F8 equ h'0A' KBD_F9 equ h'01' KBD_F10 equ h'09' KBD_F11 equ h'78' KBD_F12 equ h'07' KBD_PRNT_SCRN1 equ h'12'+h'80' ; make = E0,12,E0,7C; break = E0,F0,7C,E0,F0,12 KBD_PRNT_SCRN2 equ h'7C'+h'80' KBD_SCROLL_LOCK equ h'7E' KBD_PAUSE equ h'E1' ; make = E1,14,77,E1,F0,14,F0,77; break = none KBD_OPEN_SQUARE equ h'54' KBD_INSERT equ h'70'+h'80' KBD_HOME equ h'6C'+h'80' KBD_PAGE_UP equ h'7D'+h'80' KBD_DELETE equ h'71'+h'80' KBD_END equ h'69'+h'80' KBD_PAGE_DOWN equ h'7A'+h'80' KBD_UP_ARROW equ h'75'+h'80' KBD_LEFT_ARROW equ h'6B'+h'80' KBD_DOWN_ARROW equ h'72'+h'80' KBD_RIGHT_ARROW equ h'74'+h'80' KBD_NUM_LOCK equ h'77' KBD_KP_SLASH equ h'4A'+h'80' KBD_KP_ASTERISK equ h'7C' KBD_KP_MINUS equ h'7B' KBD_KP_PLUS equ h'79' KBD_KP_ENTER equ h'5A'+h'80' KBD_KP_DOT equ h'71' KBD_KP_0 equ h'70' KBD_KP_1 equ h'69' KBD_KP_2 equ h'72' KBD_KP_3 equ h'7A' KBD_KP_4 equ h'6B' KBD_KP_5 equ h'73' KBD_KP_6 equ h'74' KBD_KP_7 equ h'6C' KBD_KP_8 equ h'75' KBD_KP_9 equ h'7D' KBD_CLOSE_SQUARE equ h'5B' KBD_SEMICOLON equ h'4C' KBD_APOSTROPHE equ h'52' KBD_COMMA equ h'41' KBD_DOT equ h'49' KBD_SLASH equ h'4A' ; ACPI KDB_POWER equ h'37'+h'80' KBD_SLEEP equ h'3F'+h'80' KBD_WAKE equ h'5E'+h'80' ; Windows multimedia KDB_NEXT_TRACK equ h'4D'+h'80' KDB_PREV_TRACK equ h'15'+h'80' KDB_STOP equ h'3B'+h'80' KDB_PLAY_PAUSE equ h'34'+h'80' KDB_MUTE equ h'23'+h'80' KDB_VOLUME_UP equ h'32'+h'80' KDB_VOLUME_DOWN equ h'21'+h'80' KDB_MEDIA_SELECT equ h'50'+h'80' KDB_EMAIL equ h'48'+h'80' KDB_CALCULATOR equ h'2B'+h'80' KDB_MY_COMPUTER equ h'40'+h'80' KDB_WWW_SEARCH equ h'10'+h'80' KDB_WWW_HOME equ h'3A'+h'80' KDB_WWW_BACK equ h'38'+h'80' KDB_WWW_FORWARD equ h'30'+h'80' KDB_WWW_STOP equ h'28'+h'80' KDB_WWW_REFRESH equ h'20'+h'80' KDB_WWW_FAVOURITES equ h'18'+h'80' ;************************************************************************** ; * ; File register usage * ; * ;************************************************************************** RAM set h'20' MAX set h'80' cblock RAM flags ; various flags pixels:5 ; LED pixels row, col ; row and column count, loop, repeat ; counters bits ; bits count key ; key pressed, 0 if none kbd_code ; keyboard code kbd_command ; keyboard command letter ; current letter, 0 to 25 letter1, letter2 ; plug board letters leds ; keyboard LEDs ignore ; number of scan codes to ignore rotor_L:5 ; left rotor - number, ring, start, position, saved rotor_C:5 ; centre rotor - number, ring, start, position, saved rotor_R:5 ; right rotor - number, ring, start, position, saved reflector ; reflector, 0 or 1 plugs:d'26' ; plug board, 0 to 25 pairs ; number of plug board pairs setting:2 ; setting limits pnt ; pointer timeout ; timeout timer work1, work2 ; work registers RAM_ endc if RAM_ > MAX error "File register usage overflow" endif ; flags RESET equ 0 ; set if keyboard reset INHIBIT equ 1 ; set if keyboard inhibited EXTENDED equ 2 ; set if extended key code received BREAK equ 3 ; set if key break code received SHIFT1 equ 4 ; set if left shift key pressed SHIFT2 equ 5 ; set if right shift key pressed SHIFT_MASK equ (1< d'40' variable n = (i-d'20')/d'12' i -= (n*d'12')+d'20' movlw n ; [4] call ldelay ; [8] else if i >= d'16' variable n = (i-d'16')/d'4' i -= (n*d'4')+d'16' call delay16-n ; [8] endif endif while i > 0 nop ; [4] i -= d'4' endw endm ;-------------------------------------------------------------------------- ; waits, fed with the wait in ms in w reg ;-------------------------------------------------------------------------- routine wait_ms movwf count WAIT equ CLOCK/(d'1000'*d'80') wait1 movlf WAIT,loop wait2 clrwdt ; [4] delay d'64' ; [64] decfsz loop ; [4] goto wait2 ; [8] decfsz count goto wait1 return ;-------------------------------------------------------------------------- ; waits for a clock transition from the keyboard, returns the Z flag set ; if timeout ;-------------------------------------------------------------------------- routine wait_kbd ; timeout period in ms TIMEOUT equ (d'256'*d'80')/(CLOCK/d'1000') clrf timeout ; wait for clock to go high wait3 clrwdt ; [4] btfsc KBD_CLK ; [8] goto wait4 btfsc KBD_CLK ; [8] goto wait4 btfsc KBD_CLK ; [8] goto wait4 btfsc KBD_CLK ; [8] goto wait4 btfsc KBD_CLK ; [8] goto wait4 btfsc KBD_CLK ; [8] goto wait4 btfsc KBD_CLK ; [8] goto wait4 btfsc KBD_CLK ; [8] goto wait4 decfsz timeout ; [4] goto wait3 ; [8] goto wait7 ; timeout wait4 clrf timeout ; wait for clock to go low wait5 clrwdt ; [4] btfss KBD_CLK ; [8] goto wait6 btfss KBD_CLK ; [8] goto wait6 btfss KBD_CLK ; [8] goto wait6 btfss KBD_CLK ; [8] goto wait6 btfss KBD_CLK ; [8] goto wait6 btfss KBD_CLK ; [8] goto wait6 btfss KBD_CLK ; [8] goto wait6 btfss KBD_CLK ; [8] goto wait6 decfsz timeout ; [4] goto wait5 ; [8] goto wait7 ; timeout wait6 delay d'10'*(CLOCK/d'1000000') clrz ; signal ok wait7 return ;-------------------------------------------------------------------------- ; transmits the byte in w reg to the keyboard, returns the Z flag set if ; timeout ;-------------------------------------------------------------------------- routine tx_kbd movwf kbd_code kbd 0,1 ; request to send movlw 1 call wait_ms kbd 0,0 ; start bit nop kbd 1,0 call wait_kbd bz tx6 ; branch if timeout movlf 8,count clrf bits tx1 btfsc kbd_code,0 incf bits rrf kbd_code ; transmit data bits skpnc bsf KBD_DATA skpc bcf KBD_DATA call wait_kbd bz tx6 ; branch if timeout decfsz count goto tx1 btfss bits,0 ; parity bit (odd parity) bsf KBD_DATA btfsc bits,0 bcf KBD_DATA call wait_kbd bz tx6 ; branch if timeout kbd 1,1 ; stop bit clrf timeout ; wait for acknowledge bit tx2 clrwdt decf timeout bz tx6 ; branch if timeout btfsc KBD_DATA goto tx2 clrf timeout tx3 clrwdt decf timeout bz tx6 ; branch if timeout btfsc KBD_CLK goto tx3 clrf timeout tx4 clrwdt decf timeout bz tx6 ; branch if timeout btfss KBD_DATA goto tx4 clrf timeout tx5 clrwdt decf timeout bz tx6 ; branch if timeout btfss KBD_CLK goto tx5 clrz ; signal ok return tx6 kbd 1,1 setz ; signal timeout return ;-------------------------------------------------------------------------- ; polls the keyboard ;-------------------------------------------------------------------------- routine poll_kbd ; delay d'10'*(CLOCK/d'1000000') call rx_kbd ; receive byte bz pollk7 ; branch if timeout bc pollk7 ; branch if error tstf ignore ; ignore scan codes ? bz pollk1 ; branch if not decf ignore goto pollk7 pollk1 movlw KBD_PAUSE ; pause key ? subwf kbd_code,w bnz pollk2 ; branch if not movlf d'7',ignore ; ignore the next scan codes goto pollk7 pollk2 movlw KBD_EXTENDED ; extended key code ? subwf kbd_code,w bnz pollk3 ; branch if not bsf flags,EXTENDED ; signal received goto pollk8 pollk3 movlw KBD_BREAK ; key break code ? subwf kbd_code,w bnz pollk4 ; branch if not bsf flags,BREAK ; signal received goto pollk8 pollk4 btfsc flags,EXTENDED ; extended key ? goto pollk6 ; branch if yes movlw KBD_LEFT_SHFT ; left shift key ? subwf kbd_code,w bnz pollk5 ; branch if not bsf flags,SHIFT1 ; shift status btfsc flags,BREAK bcf flags,SHIFT1 goto pollk7 pollk5 movlw KBD_RIGHT_SHFT ; right shift key ? subwf kbd_code,w bnz pollk6 ; branch if not bsf flags,SHIFT2 ; shift status btfsc flags,BREAK bcf flags,SHIFT2 goto pollk7 pollk6 btfsc flags,BREAK ; key break ? goto pollk7 ; branch if yes movff kbd_code,key ; store key pressed btfsc flags,EXTENDED bsf key,7 ; extended scan code pollk7 bcf flags,EXTENDED bcf flags,BREAK pollk8 return ;-------------------------------------------------------------------------- ; receives a byte from the keyboard into w reg (and kbd_code), returns ; the Z flag set if timeout and the C flag set if error ;-------------------------------------------------------------------------- routine get_kbd kbd 1,1 call wait_kbd bz rx4 ; branch if timeout routine rx_kbd btfsc KBD_DATA ; test start bit goto rx5 ; branch if error movlf 8,count clrf bits rx1 call wait_kbd bz rx4 ; branch if timeout clrc ; receive data bits btfsc KBD_DATA setc rrf kbd_code btfsc kbd_code,7 incf bits decfsz count goto rx1 call wait_kbd bz rx4 ; branch if timeout btfsc KBD_DATA ; test parity bit incf bits btfss bits,0 goto rx5 ; branch if error call wait_kbd bz rx4 ; branch if timeout btfss KBD_DATA ; test stop bit goto rx5 ; branch if error clrf timeout ; wait for clock to go high rx2 clrwdt btfsc KBD_CLK goto rx3 decfsz timeout goto rx2 goto rx4 ; timeout rx3 movfw kbd_code clrz ; signal ok clrc return rx4 clrw clrf kbd_code setz ; signal timeout clrc return rx5 clrw clrf kbd_code clrz ; signal error setc return ;-------------------------------------------------------------------------- ; transmits the command in w reg to the keyboard, returns the Z flag set ; if error ;-------------------------------------------------------------------------- routine tx_command movwf kbd_command txc1 movfw kbd_command ; transmit command call tx_kbd bz txc4 ; branch if timeout call get_kbd ; wait for acknowledge bz txc2 ; branch if timeout bc txc2 ; branch if error xorlw KBD_RESEND ; resend ? bz txc1 ; branch if yes xorlw KBD_RESEND ; acknowledge ? xorlw KBD_ACK bnz txc4 ; branch if not goto txc3 txc2 movlw KBD_RESEND ; transmit command call tx_kbd bz txc4 ; branch if timeout call get_kbd ; wait for acknowledge bz txc4 ; branch if timeout bc txc4 ; branch if error xorlw KBD_RESEND ; resend ? bz txc1 ; branch if yes xorlw KBD_RESEND ; acknowledge ? xorlw KBD_ACK bnz txc4 ; branch if not txc3 clrz ; signal ok return txc4 setz ; signal error return ;-------------------------------------------------------------------------- ; converts a keyboard scan code in w reg into ASCII, returns the ASCII ; code in w reg, or the Z flag set if not found ;-------------------------------------------------------------------------- routine convert_key movwf work1 movlf high keyboard,PCLATH clrf work2 conv1 movfw work2 ; get scan code index keyboard incf work2 tstw ; end of table ? bz conv3 ; branch if yes subwf work1,w ; match ? bnz conv2 ; branch if not movfw work2 ; get ASCII index keyboard clrz ; signal found goto conv3 conv2 incf work2 goto conv1 conv3 return ;-------------------------------------------------------------------------- ; initialises the keyboard, returns the Z flag set if error ;-------------------------------------------------------------------------- routine initialise_keyboard bcf flags,RESET bcf flags,INHIBIT REPEAT equ d'1000'/TIMEOUT initk1 movlf REPEAT,repeat ; wait for keyboard BAT initk2 call get_kbd bz initk3 ; branch if timeout bc initk4 ; branch if error xorlw KBD_BAT bnz initk4 goto initk5 ; branch if ok initk3 decfsz repeat goto initk2 initk4 btfsc flags,RESET ; not received or error goto initk6 bsf flags,RESET movlw d'250' call wait_ms movlw d'250' call wait_ms movlw d'250' call wait_ms movlw d'250' call wait_ms movlw KBD_RESET ; reset keyboard call tx_command bz initk6 ; branch if error goto initk1 initk5 movlw KBD_TYPEMATIC ; set typematic to slowest call tx_command bz initk6 movlw h'7f' call tx_command bz initk6 ; branch if error clrz ; signal ok return initk6 setz ; signal error return ;-------------------------------------------------------------------------- ; inhibits communication from the keyboard ;-------------------------------------------------------------------------- routine inhibit_keyboard kbd 0,1 movlw 1 call wait_ms bsf flags,INHIBIT ; keyboard inhibited return ;-------------------------------------------------------------------------- ; releases communication from the keyboard ;-------------------------------------------------------------------------- routine release_keyboard kbd 1,1 delay d'75'*(CLOCK/d'1000000') bcf flags,INHIBIT ; keyboard not inhibited return ;-------------------------------------------------------------------------- ; flashes the keyboard LEDs ;-------------------------------------------------------------------------- routine flash_keyboard movlf d'4',repeat clrf leds flash1 movlw KBD_LEDS ; set/reset LEDs call tx_command bz flash2 ; branch if error comf leds ; toggle LEDS movfw leds andlw 7 call tx_command bz flash2 ; branch if error decf repeat,w movlw FLASH_DELAY skpnz movlw d'1' call display decfsz repeat goto flash1 flash2 return ;************************************************************************** ; display routines * ;************************************************************************** ;-------------------------------------------------------------------------- ; display delay, fed with the period in 1/100 s in w reg ;-------------------------------------------------------------------------- routine display poll macro local poll1,poll2 btfss flags,INHIBIT ; clock active and keyboard [8/4] goto poll1 ; not inhibited ? [0/8] nop ; [4/0] goto poll2 ; [8/0] poll1 btfss KBD_CLK ; [0/8] call poll_kbd ; poll keyboard if yes poll2 endm movwf loop movlf high row_enable,PCLATH disp1 movlf pixels,FSR clrf row disp2 movlf PIXELS_MASKA,PORTA movlf PIXELS_MASKB,PORTB poll movfw row index row_enable poll movwf work1 andlw ~(1<