; ************************************************************************** ; * * ; * PICAXE Brainf**k (BF) Interpreter PICAXEBF.BAS * ; * * ; ************************************************************************** SYMBOL VERSION_MAJOR = 2 ' 2.0 SYMBOL VERSION_MINOR = 0 ; .------------------------------------------------------------------------. ; | | ; | Version 2.0 | ; | Last Update 2004-08-26 | ; | | ; | Target PICAXE-18X | ; | Compiler Used Revolution Programming Editor 4.1.4 | ; | Memory Used Approx 840 bytes of 2048 | ; | | ; | Author The Happy Hippy | ; | Web Site www.hippy.freeserve.co.uk/picaxebf.htm | ; | Email hippy@psynet.net | ; | | ; | Copyright (C) 2004, The Happy Hippy | ; | | ; `------------------------------------------------------------------------' ; .------------------------------------------------------------------------. ; | | ; | Define variables used within interpreter | ; | | ; `------------------------------------------------------------------------' SYMBOL flags = b0 SYMBOL debugFlag = bit0 ; Debugging Flag ( 1 = On ) SYMBOL codePtr = b1 ; Pointer to instruction SYMBOL codeByte = b2 ; Instruction SYMBOL dataPtr = b3 ; Data Cell Pointer SYMBOL dataByte = b4 ; Data Cell value SYMBOL inputPtr = b5 ; Input Stream Pointer SYMBOL stackPtr = b6 ; Subroutine Stack Pointer SYMBOL loopDepth = b7 SYMBOL number = b8 SYMBOL counter = b9 SYMBOL error = b10 ; .------------------------------------------------------------------------. ; | | ; | Console communications constants | ; | | ; `------------------------------------------------------------------------' SYMBOL RX = 2 ; SERIN pin used SYMBOL RX_BAUD = N4800 ; 4800 baud ; .------------------------------------------------------------------------. ; | | ; | Data Cell definitions | ; | | ; `------------------------------------------------------------------------' SYMBOL SFR_PAGE0_MIN = $50 SYMBOL SFR_PAGE0_MAX = $7F SYMBOL SFR_PAGE1_MIN = $C0 SYMBOL SFR_PAGE1_MAX = $EF SYMBOL SFR_PAGE0_MIN_MINUS_1 = SFR_PAGE0_MIN - 1 SYMBOL SFR_PAGE0_MAX_PLUS_1 = SFR_PAGE0_MAX + 1 SYMBOL SFR_PAGE1_MIN_MINUS_1 = SFR_PAGE1_MIN - 1 SYMBOL SFR_PAGE1_MAX_PLUS_1 = SFR_PAGE1_MAX + 1 SYMBOL SFR_PAGE0_MAX_INDEX = SFR_PAGE0_MAX - SFR_PAGE0_MIN SYMBOL SFR_PAGE1_MAX_INDEX = SFR_PAGE1_MAX - SFR_PAGE1_MIN SYMBOL SFR_PAGE0_SIZE = SFR_PAGE0_MAX_INDEX + 1 SYMBOL SFR_PAGE1_SIZE = SFR_PAGE1_MAX_INDEX + 1 SYMBOL SFR_SIZE = SFR_PAGE0_SIZE + SFR_PAGE1_SIZE SYMBOL SFR_MAX_INDEX = SFR_SIZE - 1 ; .------------------------------------------------------------------------. ; | | ; | Subroutine Jump Table and Stack definitions | ; | | ; `------------------------------------------------------------------------' SYMBOL SFR_MIN_TABLE = $B0 SYMBOL SFR_MAX_TABLE = $B7 SYMBOL SFR_MIN_STACK = $B8 SYMBOL SFR_MAX_STACK = $BF SYMBOL SFR_INIT_STACK = SFR_MIN_STACK - 1 ; ************************************************************************** ; * * ; * Main Program * ; * * ; ************************************************************************** PowerOnReset: FOR dataPtr = SFR_PAGE0_MIN TO SFR_PAGE0_MAX POKE dataPtr,0 NEXT FOR dataPtr = SFR_PAGE1_MIN TO SFR_PAGE1_MAX POKE dataPtr,0 NEXT GOSUB FindInputStream codePtr = 0 dataPtr = SFR_PAGE0_MIN stackPtr = SFR_INIT_STACK GOTO FetchCodeByte ; ************************************************************************** ; * * ; * Instruction Execution Loop * ; * * ; ************************************************************************** Fetch_ShowData: counter = 0 IF debugFlag = 0 THEN Fetch GOSUB ShowDataNumber Fetch: IF codePtr = $00 THEN Finished FetchCodeByte: READ codePtr,codeByte IF debugFlag = 0 THEN IncCodePtr SERTXD ( CR,LF ) number = codePtr GOSUB ShowThreeDigitNumber SERTXD ( " ",codeByte, " ") GOSUB ShowDataNumber IncCodePtr: codePtr = codePtr+1 IF codeByte = ">" THEN IncDataPtr IF codeByte = "<" THEN DecDataPtr IF codeByte = "+" THEN IncCellData IF codeByte = "-" THEN DecCellData IF codeByte = "," THEN InChar IF codeByte = "." THEN OutChar IF codeByte = "[" THEN OpenLoop IF codeByte = "]" THEN CloseLoop IF codeByte = "{" THEN DebugOn IF codeByte = "}" THEN DebugOff IF codeByte = "I" THEN InNumber IF codeByte = "O" THEN OutNumber IF codeByte = "@" THEN SetPins IF codeByte = ":" THEN ResetInStream IF codeByte = "?" THEN InStream IF codeByte = "=" THEN SetConstant IF codeByte = "^" THEN SetDataPtrIndirect IF codeByte = "*" THEN SetDataPtr IF codeByte = "&" THEN CallSubroutine IF codeByte = "!" THEN Finished IF codeByte >= "A" AND codeByte <= "G" THEN DefineSubroutine IF codeByte >= "0" AND codeByte <= "9" THEN SetRepeatCounter GOTO Fetch ; .------------------------------------------------------------------------. ; | | ; | 0 to 9 Set > < + and - repeat counter | ; | | ; `------------------------------------------------------------------------' SetRepeatCounter: counter = counter * 10 + codeByte - "0" IF debugFlag = 0 THEN Fetch number = counter GOSUB ShowThreeDigitNumber GOTO Fetch ; .------------------------------------------------------------------------. ; | | ; | > Move Data Cell Pointer to the right | ; | | ; `------------------------------------------------------------------------' IncDataPtr: dataPtr = dataPtr+1 IF dataPtr = SFR_PAGE1_MAX_PLUS_1 THEN Crashed_IncDataPtr IF dataPtr <> SFR_PAGE0_MAX_PLUS_1 THEN RepeatIncDataPtr dataPtr = SFR_PAGE1_MIN RepeatIncDataPtr: IF counter <= 1 THEN Fetch_ShowData counter = counter-1 GOTO IncDataPtr ; .------------------------------------------------------------------------. ; | | ; | < Move Data Cell Pointer to the left | ; | | ; `------------------------------------------------------------------------' DecDataPtr: dataPtr = dataPtr-1 IF dataPtr = SFR_PAGE0_MIN_MINUS_1 THEN Crashed_DecDataPtr IF dataPtr <> SFR_PAGE1_MIN_MINUS_1 THEN RepeatDecDataPtr dataPtr = SFR_PAGE0_MAX RepeatDecDataPtr: IF counter <= 1 THEN Fetch_ShowData counter = counter-1 GOTO DecDataPtr ; .------------------------------------------------------------------------. ; | | ; | + Increment Data Cell value pointed to by Data Cell Pointer | ; | | ; `------------------------------------------------------------------------' IncCellData: PEEK dataPtr,dataByte dataByte = dataByte+1 POKE dataPtr,dataByte IF counter <= 1 THEN Fetch_ShowData counter = counter-1 GOTO IncCellData ; .------------------------------------------------------------------------. ; | | ; | - Decrement Data Cell value pointed to by Data Cell Pointer | ; | | ; `------------------------------------------------------------------------' DecCellData: PEEK dataPtr,dataByte dataByte = dataByte-1 POKE dataPtr,dataByte IF counter <= 1 THEN Fetch_ShowData counter = counter-1 GOTO DecCellData ; .------------------------------------------------------------------------. ; | | ; | Read an ASCII character from the console and store in Data Cell | ; | | ; `------------------------------------------------------------------------' InChar: SERIN RX,RX_BAUD,dataByte POKE dataPtr,dataByte GOTO Fetch_ShowData ; .------------------------------------------------------------------------. ; | | ; | Send Data Cell contents as to the console as an ASCII character | ; | | ; `------------------------------------------------------------------------' OutChar: PEEK dataPtr,dataByte IF databyte = 10 THEN OutCrLf SERTXD( dataByte ) GOTO Fetch OutCrLf: SERTXD( CR,LF ) GOTO Fetch ; .------------------------------------------------------------------------. ; | | ; | [ If Data Cell zero, skip to after matching ] | ; | | ; `------------------------------------------------------------------------' OpenLoop: PEEK dataPtr,dataByte IF dataByte <> 0 THEN Fetch loopDepth = 1 OpenLoopAgain: IF codePtr = $00 THEN Crashed_OpenLoop READ codePtr,codeByte codePtr = codePtr+1 IF codeByte = "!" THEN Crashed_OpenLoop IF codeByte = "[" THEN OpenLoopInc IF codeByte = "]" THEN OpenLoopDec GOTO OpenLoopAgain OpenLoopInc: loopDepth = loopDepth+1 GOTO OpenLoopAgain OpenLoopDec: loopDepth = loopDepth-1 IF loopDepth <> 0 THEN OpenLoopAgain GOTO Fetch ; .------------------------------------------------------------------------. ; | | ; | ] If Data Cell not zero, skip backwards to matching [ | ; | | ; `------------------------------------------------------------------------' CloseLoop: PEEK dataPtr,dataByte IF dataByte = 0 THEN Fetch loopDepth = 0 CloseLoopAgain: IF codePtr = $00 THEN Crashed_CloseLoop codePtr = codePtr-1 READ codePtr,codeByte IF codeByte = "]" THEN CloseLoopInc IF codeByte = "[" THEN CloseLoopDec GOTO CloseLoopAgain CloseLoopInc: loopDepth = loopDepth+1 GOTO CloseLoopAgain CloseLoopDec: loopDepth = loopDepth-1 IF loopDepth <> 0 THEN CloseLoopAgain GOTO Fetch ; .------------------------------------------------------------------------. ; | | ; | { Turn debug tracing on | ; | | ; `------------------------------------------------------------------------' DebugOn: debugFlag = 1 GOTO Fetch ; .------------------------------------------------------------------------. ; | | ; | } Turn debug tracing off | ; | | ; `------------------------------------------------------------------------' DebugOff: debugFlag = 0 GOTO Fetch ; .------------------------------------------------------------------------. ; | | ; | I Read a number from the console and store in the Data Cell | ; | | ; `------------------------------------------------------------------------' InNumber: SERIN RX,RX_BAUD,#dataByte POKE dataPtr,dataByte GOTO Fetch_ShowData ; .------------------------------------------------------------------------. ; | | ; | O Print Data Cell value as a number on the console | ; | | ; `------------------------------------------------------------------------' OutNumber: PEEK dataPtr,dataByte SERTXD (#dataByte) GOTO Fetch ; .------------------------------------------------------------------------. ; | | ; | @ Set PICAXE pins to value held in Data Cell | ; | | ; `------------------------------------------------------------------------' SetPins: PEEK dataPtr,dataByte pins = dataByte GOTO Fetch ; .------------------------------------------------------------------------. ; | | ; | : Set the Input Stream pointer to start of Input Stream | ; | | ; `------------------------------------------------------------------------' ResetInStream: GOSUB FindInputStream GOTO Fetch ; .------------------------------------------------------------------------. ; | | ; | ? Read an ASCII character from Input Stream to Data Cell | ; | | ; `------------------------------------------------------------------------' InStream: IF inputPtr = $00 THEN Crashed_InStream READ inputPtr,dataByte POKE dataPtr,dataByte inputPtr = inputPtr+1 IF inputPtr <> $00 THEN Fetch_ShowData GOSUB FindInputStream GOTO Fetch_ShowData ; .------------------------------------------------------------------------. ; | | ; | =n; Read number and put in Data Cell | ; | | ; `------------------------------------------------------------------------' SetConstant: GOSUB ReadNumber POKE dataPtr,dataByte GOTO Fetch_showData ; .------------------------------------------------------------------------. ; | | ; | ^ Read Data Cell and set the Data Cell Pointer to that | ; | | ; `------------------------------------------------------------------------' SetDataPtrIndirect: PEEK dataPtr,dataByte GOTO SetDataPtrToDataByteValue ; .------------------------------------------------------------------------. ; | | ; | *n; Read number as set data Cell Pointer to its value | ; | | ; `------------------------------------------------------------------------' SetDataPtr: GOSUB ReadNumber SetDataPtrToDataByteValue: IF dataByte > SFR_MAX_INDEX THEN Crashed_SetDataPtr IF dataByte <= SFR_PAGE0_MAX_INDEX THEN SetDataPtrInPage0 dataByte = dataByte-SFR_PAGE0_SIZE+SFR_PAGE1_MIN-SFR_PAGE0_MIN SetDataPtrInPage0: dataPtr = dataByte+SFR_PAGE0_MIN GOTO Fetch_ShowData ; .------------------------------------------------------------------------. ; | | ; | A-H Define Subroutine | ; | | ; `------------------------------------------------------------------------' DefineSubroutine: codeByte = codeByte - "A" + SFR_MIN_TABLE IF codeByte < SFR_MIN_TABLE THEN Crashed_IllegalSubroutine IF codeByte > SFR_MAX_TABLE THEN Crashed_IllegalSubroutine POKE codeByte,codePtr SkipSubDefinition: PEEK codePtr,codeByte codePtr = codePtr + 1 IF codeByte <> "=" AND codeByte <> "#" THEN NotSubExtended SkipSubEmbedded: PEEK codePtr,codeByte codePtr = codePtr + 1 IF codeByte <> ";" THEN SkipSubEmbedded GOTO SkipSubDefinition NotSubExtended: IF codeByte <> ";" THEN SkipSubDefinition GOTO Fetch ; .------------------------------------------------------------------------. ; | | ; | &A..&H Call Subroutine | ; | | ; `------------------------------------------------------------------------' CallSubroutine: stackPtr = stackPtr+1 IF stackPtr < SFR_MIN_STACK THEN Crashed_StackOverflow IF stackPtr > SFR_MAX_STACK THEN Crashed_StackOverflow POKE stackPtr,codePtr PEEK codePtr,codeByte codeByte = codeByte - "A" + SFR_MIN_TABLE IF codeByte < SFR_MIN_TABLE THEN Crashed_IllegalSubroutine IF codeByte > SFR_MAX_TABLE THEN Crashed_IllegalSubroutine PEEK codeByte,codePtr GOTO Fetch ; .------------------------------------------------------------------------. ; | | ; | ; Return from Subroutine | ; | | ; `------------------------------------------------------------------------' ReturnFromSubroutine: IF stackPtr < SFR_MIN_STACK THEN Crashed_StackUnderflow IF stackPtr > SFR_MAX_STACK THEN Crashed_StackUnderflow PEEK stackPtr,codePtr stackPtr = stackPtr - 1 codePtr = codePtr + 1 GOTO Fetch ; .------------------------------------------------------------------------. ; | | ; | Handle user program errors | ; | | ; `------------------------------------------------------------------------' Crashed_StackUnderflow: error = error+1 ' 10 Crashed_StackOverflow: error = error+1 ' 9 Crashed_IllegalSubroutine: error = error+1 ' 8 Crashed_SetDataPtr: error = error+1 ' 7 Crashed_ReadNumber: error = error+1 ' 6 Crashed_InStream: error = error+1 ' 5 Crashed_CloseLoop: error = error+1 ' 4 Crashed_OpenLoop: error = error+1 ' 3 Crashed_DecDataPtr: error = error+1 ' 2 Crashed_IncDataPtr: error = error+1 ' 1 SERTXD( "Error ",#error ) ; .------------------------------------------------------------------------. ; | | ; | ! terminate user program | ; | | ; `------------------------------------------------------------------------' Finished: GOTO Finished ; ************************************************************************** ; * * ; * Identify end of program and set Input Stream Pointer * ; * * ; ************************************************************************** FindInputStream: inputPtr = $00 FindInputStreamAgain: PEEK inputPtr,codeByte inputPtr = inputPtr+1 IF InputPtr = $00 THEN FoundInputStream IF codeByte <> "!" THEN FindInputStreamAgain FoundInputStream: RETURN ; ************************************************************************** ; * * ; * Formatted number display routines * ; * * ; ************************************************************************** ; [pp]=ddd a where pp is Data Cell Pointer 0..95 ; ddd is Data Cell value 0..255 ; a is Data Cell value as ASCII char ShowDataNumber: SERTXD( 9,"[" ) number = dataPtr IF number <= SFR_PAGE0_MAX THEN ShowDataCellPage0 number=number-SFR_PAGE1_MIN+SFR_PAGE0_SIZE+SFR_PAGE0_MIN ShowDataCellPage0: number = number-SFR_PAGE0_MIN GOSUB ShowTwoDigitNumber SERTXD ( "]=" ) PEEK dataPtr,dataByte number = dataByte ; .------------------------------------------------------------------------. ; | | ; | Print a number in a selection of formats | ; | | ; `------------------------------------------------------------------------' ShowNumberLongFormat: GOSUB ShowThreeDigitNumber IF number <= " " THEN ShowNumberNoAscii IF number <= $7E THEN ShowNumberAscii ShowNumberNoAscii: number = " " ShowNumberAscii: SERTXD( " ",number ) RETURN ; .------------------------------------------------------------------------. ; | | ; | Print a fixed field width decimal number | ; | | ; `------------------------------------------------------------------------' ShowThreeDigitNumber: IF number >= 100 THEN ShowNumberNoPadding SERTXD( "0" ) ShowTwoDigitNumber: IF number >= 10 THEN ShowNumberNoPadding SERTXD( "0" ) ShowNumberNoPadding: SERTXD( #number ) RETURN ; ************************************************************************** ; * * ; * Read a number for the = and * commands * ; * * ; ************************************************************************** ReadNumber: dataByte = 0 ReadNumberLoop: IF codePtr = $00 THEN Crashed_ReadNumber READ codePtr,codeByte IF debugFlag = 0 THEN ReadNumberNoDebug SERTXD( codeByte ) ReadNumberNoDebug: codePtr = codePtr+1 IF codeByte = ";" THEN ReadNumberFinished IF codeByte < "0" THEN Crashed_ReadNumber IF codeByte > "9" THEN Crashed_ReadNumber dataByte = dataByte * 10 + codeByte - "0" GOTO ReadNumberLoop ReadNumberFinished: RETURN ; ************************************************************************** ; * * ; * Example BF program * ; * * ; ************************************************************************** ; This program will print Hello World! on the console EEPROM ("++++++++[->+++++++++<]>.") EEPROM ("<+++++++[->++++<]>+.") EEPROM ("+++++++..") EEPROM ("+++.") EEPROM (">++++++++[->++++<]>.") EEPROM ("<<++++++++.") EEPROM ("--------.") EEPROM ("+++.") EEPROM ("------.") EEPROM ("--------.") EEPROM (">>+.") EEPROM ("---[---<+>]<.") EEPROM ("!") ; ************************************************************************** ; * * ; * End of PICAXE BF Interpreter * ; * * ; **************************************************************************