The ROM is now done. It spans from hub byte address $00000 to $00EFF, with $F00 to $1FFFF being RAM. The ROM contains the booter (SPI Flash or Serial) with SHA-256/HMAC authentication to facilitate code protection, and a serial (RX/TX) monitor which starts up if there's no authenticated loader to execute and no security key programmed into the first 128 fuses - you just hit <space> and it auto-bauds and gives you a prompt. The monitor could be used as a very simple development interface, where you just send text scripts to load code and start cogs.
Below is the code for the monitor which uses the new hardware multi-threading to run concurrent auto-baud detection, serial input, and main monitor code:
'********************************************************
'* *
'* Propeller II ROM Monitor *
'* *
'* Version 0.1 *
'* *
'* 11/01/2012 *
'* *
'********************************************************
CON
branch1_ = 0
branch2_ = branch1_ + 31
branch3_ = branch2_ + 35
hello_ = branch3_ + 15
error_ = hello_ + 33
hitspace_ = error_ + 11
spacesq_ = hitspace_ + 10
quotecr_ = spacesq_ + 4
help_ = quotecr_ + 3
DAT
'********
'* Data *
'********
branch1 byte cmd_new, 0
byte cmd_byte, "Y"
byte cmd_word, "W"
byte cmd_long, "N"
byte cmd_viewp, "."
byte cmd_search, "/"
byte cmd_enter, ":"
byte cmd_map, "M"
byte cmd_clrp, "L"
byte cmd_setp, "H"
byte cmd_notp, "T"
byte cmd_offp, "Z"
byte cmd_getp, "R"
byte cmd_quit, "Q"
byte cmd_help, "?"
byte 0 '31 bytes
branch2 byte cmd_view2, 0
byte cmd_view2, " "
byte cmd_range, "."
byte cmd_search2, "/"
byte cmd_enter2, ":"
byte cmd_watch, "@"
byte cmd_clkset, "*"
byte cmd_coginit, "+"
byte cmd_cogstop, "-"
byte cmd_clrp, "L"
byte cmd_setp, "H"
byte cmd_notp, "T"
byte cmd_offp, "Z"
byte cmd_getp, "R"
byte cmd_watchp, "#"
byte cmd_cfgp, "|"
byte cmd_setdacs, "\"
byte 0 '35 bytes
branch3 byte cmd_view3, 0
byte cmd_view3, " "
byte cmd_search3, "/"
byte cmd_fill, ":"
byte cmd_move, ">"
byte cmd_move, "<"
byte cmd_checksum, "^"
byte 0 '15 bytes
hello byte 13,13,"=== Propeller II Monitor ===",13,13
byte 0 '33 bytes
error byte "? - Help"
byte 13,7,0 '11 bytes
hitspace byte "Hit SPACE",0 '10 bytes
spacesq byte " '",0 '4 bytes
quotecr byte "'",13,0 '3 bytes
help byte 13, "~HUB -",13
byte "{adr{.adr}}", "`View",13
byte "{adr{.adr}}/{dat{ dat}}", "`Search",13
byte "{adr{.adr}}:dat{ dat}", "`Enter",13
byte "adr.adr[</>]adr", "`Move",13
byte "adr.adr^", "`Checksum",13
byte "adr@", "`Watch",13
byte "[Y/W/N]", "`Byte/word/long",13
byte "~COGS -",13
byte "cog+pgm{+ptr}", "`Start",13
byte "cog-", "`Stop",13
byte "M", "`Map",13
byte "~PINS -",13
byte "{pin}[H/L/T/Z/R]", "`High/low/toggle/off/read",13
byte "pin#", "`Watch",13
byte "pin|cfg", "`Configure",13
byte "dat\", "`Set DACs",13
byte "~MISC -",13
byte "dat*", "`Set clock",13
byte "'", "`Repeat",13
byte "Q", "`Quit",13
byte 0
longs long
'*********
'* Entry *
'*********
monitor org
rx_pin reps #$1F6-reserves,#1 'clear reserves (first 5 registers get reused)
tx_pin setinda reserves
tx_time mov inda++,#0
v1 getptrb base 'get data base pointer
v2 sub base,#longs << 2
s0 long 0 'start of search data = 0/nop (s0..s1 get reused)
getptra rx_pin 'get rx pin
shr rx_pin,#8
getptra tx_pin 'get tx pin
setp tx_pin
jmptask #baud_task,#%0010 'enable baud detector task
settask #%%0101
tjz period,#$ 'wait for <space> to set period
jmptask #rx_task,#%0100 'enable serial receiver task
settask #%%0121
mov wsize,#1 'init word size to byte
call #set_size
pusha #0 'init input line to <enter>
setptra #hello_ 'print hello message
s1 'end of search data
'*************
'* Main Task *
'*************
message call #tx_string 'print hello/error message
cmd_new call #rx_line 'get input line
call #parse 'parse first term
if_z tjz x,#cmd_viewl 'if no hex and eol, view data
jmp #cmd_go 'else, process command
cmd_next_crlf call #tx_crlf 'print cr/lf
cmd_next addspa #1 'skip chr
cmd_loop call #parse 'parse next term
cmd_go if_nz jmp #cmd_hex 'if hex, branch
movd pinx,#z 'pin update redirected to z
setptra #branch1_ 'not hex, vector by chr
call #vector 'if returns, no match
cmd_error setptra #error_ 'print error message
jmp #message
cmd_hex mov v1,value 'hex, save v1
movd pinx,#pin 'pin update okay
setptra #branch2_ 'vector by chr
call #vector 'if returns, no match
jmp #cmd_view2 'view data
cmd_range call #parse_next 'hex., get hex
if_z jmp #cmd_viewp2 'if no hex, view data
mov v2,value 'hex.hex, save v2
setptra #branch3_ 'vector by chr
call #vector 'if returns, no match
jmp #cmd_view3 'view data
'
'
' Byte/word/long data
'
cmd_byte mov wsize,#1 wz 'set byte mode, z=0
cmd_word if_z mov wsize,#2 wz 'set word mode, z=0
cmd_long if_z mov wsize,#4 'set long mode
call #set_size
jmp #cmd_next 'next command
'
'
' View data
'
cmd_viewl mov v2,#$F '<enter> (eol), show line of data
call #tx_range1
jmp #cmd_new
cmd_viewp mov v2,#$FF '. (more), show page of data
call #tx_range1
jmp #cmd_next
cmd_viewp2 mov v2,#$FF 'addr. (more), show page of data
call #tx_range2
jmp #cmd_loop
cmd_view2 mov v2,v1 'addr, show unit of data
cmd_view3 call #tx_range 'addr.addr, show range of data
jmp #cmd_loop
'
'
' Search
'
cmd_search mov v1,view '/, search from view to end
cmd_search2 mov v2,amask 'addr/, search from address to end
cmd_search3 call #check_range 'addr.addr/, search range
mov y,#0 'reset search size
setinda s0 'point to search data
movs enter_str,#enter_search 'set search mode
call #parse_str 'get search string
sub y,#1 wc 'get search size
if_nc mov ssize,y 'if 0, search old data again
:start setptra v1 'start search, point to search address
setinda s0 'point to search data
mov x,#0 'reset word match counter
:word call #rdxxxx 'get memory word
cmp value,inda++ wz 'compare against search data word
if_z jmp #:match 'if word match, check if string match
add v1,wsize 'word mismatch, advance search address
cmp v1,v2 wz,wc 'at end of range?
if_be jmp #:start 'if not, start next search
mov view,v1 'else, update view
and view,amask
jmp #cmd_loop 'next command
:match incmod x,ssize wc 'word match, increment match counter
if_nc jmp #:word 'if more words to match, compare next word
mov v2,ssize 'got string match
shl v2,shift 'v1 = start of found data
add v2,v1 'v2 = end of found data
jmp #cmd_view3 'show found data
'
'
' Enter data
'
cmd_enter2 mov enter,v1 'addr:, set enter address
and enter,amask 'trim enter address
cmd_enter mov z,#0 ':, reset fill count
call #parse_enter 'parse and enter data
jmp #cmd_loop 'next command
'
'
' Fill data
'
cmd_fill call #check_range 'addr.addr:, check range, z=words to fill
mov enter,v1 'set initial enter address
call #parse_enter 'parse and enter fill-pattern data (jumps to cmd_loop if fill complete)
cmp enter,v1 wz 'any data entered?
if_z jmp #cmd_error 'if no data entered, error
mov y,enter 'get end-of-fill-pattern address (current enter)
setptra v1 'set start-of-fill-pattern address (initial enter)
:loop getptra x 'get current-fill-pattern address
cmp x,y wz 'compare against end-of-fill-pattern address
if_z setptra v1 'if same, reset to start-of-fill-pattern address
call #rdxxxx 'get fill-pattern word
call #enter_data 'enter word (jumps to cmd_loop when fill complete)
jmp #:loop 'loop to fill next word
'
'
' Move data
'
cmd_move mov y,x 'save ">"/"<"
call #check_range 'check 1st address range, get number of words
call #parse_hex 'get 2nd address
max value,amask 'v1=1st, value=2nd, z=words, y=">"/"<"
and value,amask
cmp y,#"<" wz 'if "<", swap v1 and value
if_z mov x,v1
if_z mov v1,value
if_z mov value,x 'v1=from, value=to, z=words
cmp v1,value wc 'if from < to, downward move
if_c mov x,z
if_c shl x,shift
if_c add v1,x
if_c add value,x
if_c xor rdxxxx,#%001_111110 'modify 'rdxxxx value,--ptra'
if_c xor wrxxxx,#%001_111110 'modify 'wrxxxx value,--ptrb'
setptra v1 'set pointers
setptrb value
:loop call #rdxxxx 'move data
call #wrxxxx
djnz z,#:loop
if_c xor rdxxxx,#%001_111110 'restore 'rdxxxx value,ptra++'
if_c xor wrxxxx,#%001_111110 'restore 'wrxxxx value,ptrb++'
jmp #cmd_loop 'next command
'
'
' Checksum
'
cmd_checksum call #check_range 'check range
setptra v1 'sum bytes
:loop call #rdxxxx
add y,value
djnz z,#:loop
mov value,y 'print sum
mov hsize,#8
call #tx_hex
jmp #cmd_next_crlf 'next command
'
'
' Watch
'
cmd_watchp movs rdxxxj,#rdxxxx_ret wz 'set pin mode, z=0
mov hsize,#1
cmd_watch if_z movs rdxxxj,#rdxxxm 'set mem mode
if_z mov hsize,wsize 'set hex size by word size
if_z shl hsize,#1
call #rdxxxp 'get initial value
:loop mov z,value 'preserve value
call #tx_hex 'print value
call #tx_space 'print space
:wait call #rx_check 'if key hit, exit
if_nz jmp #cmd_next_crlf
call #rdxxxp 'get current value
cmp value,z wz 'if same, check again
if_z jmp #:wait
jmp #:loop 'new value, loop
'
'
' Clkset
'
cmd_clkset setptra #hitspace_ 'print hit-space message
call #tx_string
clkset v1 'set clk
:wait call #rx 'wait for space
cmp x,#" " wz
if_nz jmp #:wait
jmp #cmd_next_crlf 'next command
'
'
' Coginit
'
cmd_coginit setcog v1 'set cog
call #parse_hex 'get program address
mov y,value 'save program address
mov value,#0 'clear pointer address
cmp x,#"+" wz 'if '+', get pointer address
if_z call #parse_hex
coginit y,value 'do 'coginit program,pointer'
jmp #cmd_loop 'next command
'
'
' Cogstop
' Quit
'
cmd_quit cogid v1 'quit
cmd_cogstop cogstop v1 'stop cog
jmp #cmd_next 'next command
'
'
' Map
'
cmd_map mov y,#7 'ready for 7..0
cmd_map_loop call #tx_space 'print space
mov x,y 'get cog status
cogid x wc
cmd_map_c cmp x,y wz
if_nc mov x,#"0" 'get 0/1/M chr
if_c mov x,#"1"
if_z mov x,#"M"
call #tx 'print chr
sub y,#1 wc
if_nc jmp #cmd_map_loop 'loop until done
jmp #cmd_next_crlf 'next command
'
'
' Pin writes clrp/setp/notp/offp
' Pin read
'
cmd_clrp movs pinop,#$DA wz 'clrp, z=0
cmd_setp if_z movs pinop,#$DB wz 'setp, z=0
cmd_notp if_z movs pinop,#$D9 wz 'notp, z=0
cmd_offp if_z movs pinop,#$D8 wz 'offp, z=0
cmd_getp if_z movs pinop,#$D6 'getp, z=1
pinx mov pin,v1 'if hex, get pin (d = pin/z)
pinop getp pin wc 'becomes clrp/setp/notp/offp/getp
if_z jmp #cmd_map_c 'if getp, show pin value
jmp #cmd_next 'next command
'
'
' Pin configuration
'
cmd_cfgp call #parse_hex 'get configuration
setport v1 'set pin port
decod5 v1 'get pin mask
cfgpins v1,value 'configure pin
jmp #cmd_loop 'next command
'
'
' Setdacs
'
cmd_setdacs setdacs v1 'set all four dacs with 8-bit values
jmp #cmd_next 'next command
'
'
' Help
'
cmd_help setptra #help_ 'print help message
call #tx_string
jmp #cmd_next_crlf 'next command
'*************************
'* Main Task Subroutines *
'*************************
'
'
' Vector branch
'
vector addptra base 'add data base pointer
vector_loop rdbyte z,ptra++ 'get jump address
vector_ret tjz z,#0 'if 0, no match found, return
rdbyte y,ptra++ 'get target
xor y,x wz 'compare to x
if_nz jmp #vector_loop 'if no match, loop
jmp z 'match found, jump, y=0, z=1
'
'
' Check address range (v1..v2)
'
check_range max v1,amask 'trim v1
and v1,amask
max v2,amask 'trim v2
and v2,amask
cmp v2,v1 wc 'make sure v2 => v1
if_c jmp #cmd_error
mov z,v2 'get number of words
sub z,v1
shr z,shift
add z,#1
check_range_ret ret
'
'
' Set rdxxxx/wrxxxx and others by word size
'
set_size test wsize,#%010 wc 'set rdxxxx/wrxxxx by word size
setbc rdxxxx,#26
setbc wrxxxx,#26
test wsize,#%100 wc
setbc rdxxxx,#27
setbc wrxxxx,#27
mov shift,wsize 'set shift by word size
shr shift,#1
mov amask,wsize 'set amask by word size
sub amask,#1
xor amask,h0001FFFF
and view,amask 'trim view
and enter,amask 'trim enter
set_size_ret ret
rdxxxp getp v1 wc 'read pin as "0" or "1"
if_nc mov value,#0
if_c mov value,#1
rdxxxj jmp #rdxxxx_ret 'd = rdxxxx_ret/rdxxxm
rdxxxm setptra v1 'read mem
rdxxxx rdbyte value,ptra++ 'rdbyte/rdword/rdlong
rdxxxp_ret
rdxxxx_ret ret
wrxxxx wrbyte value,ptrb++ 'wrbyte/wrword/wrlong
wrxxxx_ret ret
h0001FFFF long $0001FFFF
'
'
' Input line
'
rx_line setspa #0 'point to start of line
mov x,#">" 'show prompt
call #tx
call #rx 'get first chr
cmp x,#"'" wz 'check for repeat
if_nz jmp #:first 'if not repeat, first chr
:show popar x wz 'repeat, show line
if_nz call #tx
if_nz jmp #:show
jmp #:done
:loop call #rx 'get next chr
:first cmp x,#13 wz 'cr?
if_z jmp #:cr
cmp x,#8 wz 'backspace?
if_nz cmp x,#127 wz
if_z jmp #:bs
cmp x,#" " wc 'visible chr?
if_nc cmpr x,#"~" wc
if_c jmp #:loop
pusha x 'visible chr, append to line
chkspa wc 'overflow?
if_c subspa #1 'if overflow, back up
if_nc call #tx 'if not overflow, print chr
jmp #:loop
:bs chkspa wz 'backspace, line empty?
if_nz pushar x 'if not empty,
if_nz call #tx '..print backspace
if_nz call #tx_space '..print space
if_nz popar x '..print backspace
if_nz call #tx
if_nz subspa #1 '..back up
jmp #:loop
:cr pusha #0 'cr, end line with 0
:done setspa #0 'point to start of line
tx_crlf mov x,crlf 'print cr/lf
call #tx
tx_crlf_ret
rx_line_ret ret
crlf long 1 << 18 + $0A << 10 + $0D
'
'
' Parse string of hex/text for enter/fill or search
'
parse_enter movs enter_str,#enter_data
parse_str call #parse_next 'hex loop, check hex
if_nz call #enter_str 'if hex, enter value
cmp x,#" " wz 'check for space (more hex)
if_z jmp #parse_str 'if more hex, loop
cmp x,#"'" wz 'not hex, "'"?
if_nz jmp #parse_str_ret 'if not "'", done
:text addspa #2 'text loop
popa x 'get and point to next chr
cmp x,#"'" wz 'check for "'"
if_z jmp #parse_str 'if "'", back to hex
tjz x,#parse_str_ret 'if eol, done
mov value,x 'text chr
call #enter_str 'enter chr
jmp #:text 'loop
parse_enter_ret
parse_str_ret ret
enter_str jmp #enter_data 'jump to enter_data or enter_search
enter_search incmod y,#s1-s0 wc 'search, check if search limit exceeded
if_c jmp #cmd_error 'if search limit exceeded, error
mov inda++,value 'store value in search data
jmp #enter_str_ret 'return
enter_data setptrb enter 'enter/fill, get pointer
call #wrxxxx 'store value in memory
getptrb enter 'update pointer
djz z,#cmd_loop 'if fill done, abort (don't return)
enter_data_ret
enter_str_ret ret
'
'
' Parse hex
'
parse_hex call #parse_next 'try to parse hex
if_z jmp #cmd_error 'if no hex, error
parse_hex_ret ret
'
'
' Parse line (@spa), z=0 if hex (value)
'
parse_next addspa #1 'advance to next chr
parse mov value,#0 wz 'z=1
call #skip_spaces wz 'skip any spaces (preserve z)
:loop popar x 'get chr
call #check_hex 'check hex
if_c shl value,#4 'if hex, append nibble and loop
if_c or value,x
if_c jmp #:loop wz 'z=0
subspa #1 'repoint to non-hex chr
call #skip_spaces wz 'skip any post-hex spaces (preserve z)
call #check_hex 'check hex
if_c popa x 'if hex, back up to space chr
cmpr x,#"a"-1 wc 'make non-hex chr uppercase
if_c cmp x,#"z"+1 wc
if_c sub x,#"a"-"A"
parse_next_ret
parse_ret ret
'
'
' Skip spaces (@spa)
'
skip_spaces popar x 'skip space chr(s)
cmp x,#" " wz
if_z jmp #skip_spaces
subspa #1 'back up to non-space chr
skip_spaces_ret ret wz 'restore z
'
'
' Check hex (x), c=1 if hex (x)
'
check_hex cmpr x,#"0"-1 wc '"0".."9" -> $0..$9
if_c cmp x,#"9"+1 wc
if_c add x,#"A"-"9"-1
if_nc cmpr x,#"A"-1 wc '"A".."F" -> $A..$F
if_c cmp x,#"F"+1 wc
if_c add x,#"a"-"A"
if_nc cmpr x,#"a"-1 wc '"a".."f" -> $A..$F
if_c cmp x,#"f"+1 wc
if_c sub x,#"a"-10
check_hex_ret ret
'
'
' Print range (v1..v2)
'
tx_range1 mov v1,view 'view..view + v2
tx_range2 and v2,amask 'v1..v1 + v2
add v2,v1
tx_range call #check_range 'check range
mov view,v1 'set address
:line mov value,view 'print 5-digit address
mov hsize,#5
call #tx_hex
call #tx_dspace 'print "- "
mov x,wsize 'get number of words on line
rev x,#32-5
mov v1,z
max v1,x
mov v2,v1 'get number of ascii bytes on line
shl v2,shift
sub z,v1 'update number of words left
setptra view 'print hex words
:hex call #rdxxxx
mov hsize,wsize
shl hsize,#1
call #tx_hex
call #tx_space
djnz v1,#:hex
setptra #spacesq_ 'print " '"
call #tx_string
setptra view 'print ascii bytes
:ascii rdbyte x,ptra++
cmp x,#" " wc 'visible chr?
if_nc cmpr x,#"~" wc
if_c mov x,#"." 'substitute "." for non-visible chrs
call #tx
djnz v2,#:ascii
getptra view 'update address
setptra #quotecr_ 'print "'" + cr
call #tx_string
call #rx_check 'check key hit
if_z tjnz z,#:line 'if no key hit and more words left, print another line
tx_range1_ret
tx_range2_ret
tx_range_ret ret
'
'
' Print string (@ptra)
'
tx_string addptra base 'add data base pointer
tx_string_loop rdbyte x,ptra++ 'get chr
tx_string_ret tjz x,#0 'if 0, done
cmp x,#"`" wz 'long tab?
if_z subr y,#32-16
if_nz cmp x,#"~" wz 'short tab?
if_z add y,#16
:tab if_z call #tx_space
if_z djnz y,#:tab
if_z call #tx_dspace
if_z jmp #tx_string_loop
cmp x,#13 wz 'cr?
if_z call #tx_crlf
if_z mov y,#0
if_nz call #tx 'other?
if_nz add y,#1
jmp #tx_string_loop
'
'
' Print hex (value)
'
tx_hex mov y,hsize 'pre-rotate to get 1st nibble in top
shl y,#2
ror value,y
mov y,hsize 'print nibbles
:loop rol value,#4
mov x,value
call #tx_nib
djnz y,#:loop
tx_hex_ret ret
'
'
' Print "- "
'
tx_dspace mov x,dspace
jmp #tx
dspace long 1 << 18 + " " << 10 + "-"
'
'
' Print space
'
tx_space mov x,#" "
jmp #tx
'
'
' Print nibble (x)
'
tx_nib and x,#$F 'isolate nibble
cmp x,#$A wc 'alpha or numeric?
if_c add x,#"0" 'numeric
if_nc add x,#"A"-$A 'alpha
'
'
' Transmit chr (x)
'
tx shl x,#1 'insert start bit
setb x,#9 'set stop bit
getcnt tx_time 'get initial time
:loop add tx_time,period 'add bit period to time
passcnt tx_time 'loop until bit period elapsed
shr x,#1 wc 'get next bit into c
setpc tx_pin 'write c to tx pin
tjnz x,#:loop 'loop until bits done
tx_dspace_ret
tx_space_ret
tx_nib_ret
tx_ret ret
'
'
' Receive chr (x)
'
rx call #rx_check 'wait for rx chr
if_z jmp #rx
rx_ret ret
'
'
' Check receiver, z=0 if chr (x)
'
rx_check or rx_tail,#$80 'if start or rollover, reset tail
getspb rx_temp wz 'if head uninitialized, z=1
if_nz cmp rx_temp,rx_tail wz 'if head-tail mismatch, byte ready, z=0
if_nz getspa rx_temp 'preserve spa
if_nz setspa rx_tail 'get tail
if_nz popar x 'get byte at tail
if_nz getspa rx_tail 'update tail
if_nz setspa rx_temp 'restore spa
rx_check_ret ret
'************************
'* Serial Receiver Task *
'************************
rx_task chkspb wz 'if start or rollover, reset head
if_z setspb #$80
mov rx_bits,#9 'ready for 8 data bits + 1 stop bit
neg rx_time,period 'get -0.5 period
sar rx_time,#1
jp rx_pin,#$ 'wait for start bit
subcnt rx_time 'get time + 0.5 period for initial 1.5 period delay
:bit rcr rx_data,#1 'rotate c into byte
add rx_time,period 'add 1 period
passcnt rx_time 'wait for center of next bit
getp rx_pin wc 'read rx pin into c
djnz rx_bits,#:bit 'loop until 8 data bits + 1 stop bit received
shr rx_data,#32-8 'align byte
pushb rx_data 'store byte at head, inc head
jmp #rx_task 'wait for next byte
'**********************
'* Baud Detector Task *
'**********************
baud_task movd ctr,rx_pin 'set ctra to time rx pin states
:loop notb ctr,#5 wc 'if 1,0 sample set, c=0
setctra ctr '($20 -> 10000001001 -> 1, 6x 0, 1x 1, 2x 0, 1)
if_nc mov limh,buff0 'if 1,0 sample set,
if_nc shr limh,#4 '..make window from 1st 0 (6x if $20)
if_nc neg liml,limh
if_nc add limh,buff0
if_nc add liml,buff0
if_nc mov comp,buff1 'if 1,0 sample set,
if_nc mul comp,#6 '..normalize 2nd 1 (1x if $20) to 6x
if_nc cmpr comp,limh wc '..check if within window
if_nc cmp comp,liml wc
if_nc mov comp,buff2 'if 1,0 sample set,
if_nc mul comp,#3 '..normalize 2nd 0 (2x if $20) to 6x
if_nc cmpr comp,limh wc '..check if within window
if_nc cmp comp,liml wc
if_nc add buff0,buff2 'if $20,
if_nc shr buff0,#3 '..compute period from 6x 0 and 2x 0
if_nc mov period,buff0 '..update period
mov buff0,buff1 'scroll sample buffer
mov buff1,buff2
:wait getcosa buff2 'wait for next sample
tjnz buff2,#:loop
jmp #:wait
ctr long %100_01001 'ctr configuration for timing low on rx pin
'*************
'* Variables *
'*************
reserves
base res 1 'main task
x res 1
y res 1
z res 1
value res 1
view res 1
enter res 1
pin res 1
ssize res 1
hsize res 1
wsize res 1
shift res 1
amask res 1
rx_tail res 1 'serial receiver task
rx_temp res 1
rx_time res 1
rx_data res 1
rx_bits res 1
buff0 res 1 'baud detector task
buff1 res 1
buff2 res 1
limh res 1
liml res 1
comp res 1
period res 1
I never got around to adding the SDRAM boot, as it was a lot of work just to get what's in there ironed out. Of the 3,840 ROM bytes, the monitor takes 2,384 bytes, which is a lot, but I think it's a really nice thing to have. Both the monitor and the SHA-256/HMAC programs can be executed independently from user application code and are very simple to operate.
The ROM is now done. It spans from hub byte address $00000 to $00EFF, with $F00 to $1FFFF being RAM. The ROM contains the booter (SPI Flash or Serial) with SHA-256/HMAC authentication to facilitate code protection, and a serial (RX/TX) monitor which starts up if there's no authenticated loader to execute and no security key programmed into the first 128 fuses - you just hit <space> and it auto-bauds and gives you a prompt. The monitor could be used as a very simple development interface, where you just send text scripts to load code and start cogs.
Below is the code for the monitor which uses the new hardware multi-threading to run concurrent auto-baud detection, serial input, and main monitor code:
'********************************************************
'* *
'* Propeller II ROM Monitor *
'* *
'* Version 0.1 *
'* *
'* 11/01/2012 *
'* *
'********************************************************
CON
branch1_ = 0
branch2_ = branch1_ + 31
branch3_ = branch2_ + 35
hello_ = branch3_ + 15
error_ = hello_ + 33
hitspace_ = error_ + 11
spacesq_ = hitspace_ + 10
quotecr_ = spacesq_ + 4
help_ = quotecr_ + 3
DAT
'********
'* Data *
'********
branch1 byte cmd_new, 0
byte cmd_byte, "Y"
byte cmd_word, "W"
byte cmd_long, "N"
byte cmd_viewp, "."
byte cmd_search, "/"
byte cmd_enter, ":"
byte cmd_map, "M"
byte cmd_clrp, "L"
byte cmd_setp, "H"
byte cmd_notp, "T"
byte cmd_offp, "Z"
byte cmd_getp, "R"
byte cmd_quit, "Q"
byte cmd_help, "?"
byte 0 '31 bytes
branch2 byte cmd_view2, 0
byte cmd_view2, " "
byte cmd_range, "."
byte cmd_search2, "/"
byte cmd_enter2, ":"
byte cmd_watch, "@"
byte cmd_clkset, "*"
byte cmd_coginit, "+"
byte cmd_cogstop, "-"
byte cmd_clrp, "L"
byte cmd_setp, "H"
byte cmd_notp, "T"
byte cmd_offp, "Z"
byte cmd_getp, "R"
byte cmd_watchp, "#"
byte cmd_cfgp, "|"
byte cmd_setdacs, "\"
byte 0 '35 bytes
branch3 byte cmd_view3, 0
byte cmd_view3, " "
byte cmd_search3, "/"
byte cmd_fill, ":"
byte cmd_move, ">"
byte cmd_move, "<"
byte cmd_checksum, "^"
byte 0 '15 bytes
hello byte 13,13,"=== Propeller II Monitor ===",13,13
byte 0 '33 bytes
error byte "? - Help"
byte 13,7,0 '11 bytes
hitspace byte "Hit SPACE",0 '10 bytes
spacesq byte " '",0 '4 bytes
quotecr byte "'",13,0 '3 bytes
help byte 13, "~HUB -",13
byte "{adr{.adr}}", "`View",13
byte "{adr{.adr}}/{dat{ dat}}", "`Search",13
byte "{adr{.adr}}:dat{ dat}", "`Enter",13
byte "adr.adr[</>]adr", "`Move",13
byte "adr.adr^", "`Checksum",13
byte "adr@", "`Watch",13
byte "[Y/W/N]", "`Byte/word/long",13
byte "~COGS -",13
byte "cog+pgm{+ptr}", "`Start",13
byte "cog-", "`Stop",13
byte "M", "`Map",13
byte "~PINS -",13
byte "{pin}[H/L/T/Z/R]", "`High/low/toggle/off/read",13
byte "pin#", "`Watch",13
byte "pin|cfg", "`Configure",13
byte "dat\", "`Set DACs",13
byte "~MISC -",13
byte "dat*", "`Set clock",13
byte "'", "`Repeat",13
byte "Q", "`Quit",13
byte 0
longs long
'*********
'* Entry *
'*********
monitor org
rx_pin reps #$1F6-reserves,#1 'clear reserves (first 5 registers get reused)
tx_pin setinda reserves
tx_time mov inda++,#0
v1 getptrb base 'get data base pointer
v2 sub base,#longs << 2
s0 long 0 'start of search data = 0/nop (s0..s1 get reused)
getptra rx_pin 'get rx pin
shr rx_pin,#8
getptra tx_pin 'get tx pin
setp tx_pin
jmptask #baud_task,#%0010 'enable baud detector task
settask #%%0101
tjz period,#$ 'wait for <space> to set period
jmptask #rx_task,#%0100 'enable serial receiver task
settask #%%0121
mov wsize,#1 'init word size to byte
call #set_size
pusha #0 'init input line to <enter>
setptra #hello_ 'print hello message
s1 'end of search data
'*************
'* Main Task *
'*************
message call #tx_string 'print hello/error message
cmd_new call #rx_line 'get input line
call #parse 'parse first term
if_z tjz x,#cmd_viewl 'if no hex and eol, view data
jmp #cmd_go 'else, process command
cmd_next_crlf call #tx_crlf 'print cr/lf
cmd_next addspa #1 'skip chr
cmd_loop call #parse 'parse next term
cmd_go if_nz jmp #cmd_hex 'if hex, branch
movd pinx,#z 'pin update redirected to z
setptra #branch1_ 'not hex, vector by chr
call #vector 'if returns, no match
cmd_error setptra #error_ 'print error message
jmp #message
cmd_hex mov v1,value 'hex, save v1
movd pinx,#pin 'pin update okay
setptra #branch2_ 'vector by chr
call #vector 'if returns, no match
jmp #cmd_view2 'view data
cmd_range call #parse_next 'hex., get hex
if_z jmp #cmd_viewp2 'if no hex, view data
mov v2,value 'hex.hex, save v2
setptra #branch3_ 'vector by chr
call #vector 'if returns, no match
jmp #cmd_view3 'view data
'
'
' Byte/word/long data
'
cmd_byte mov wsize,#1 wz 'set byte mode, z=0
cmd_word if_z mov wsize,#2 wz 'set word mode, z=0
cmd_long if_z mov wsize,#4 'set long mode
call #set_size
jmp #cmd_next 'next command
'
'
' View data
'
cmd_viewl mov v2,#$F '<enter> (eol), show line of data
call #tx_range1
jmp #cmd_new
cmd_viewp mov v2,#$FF '. (more), show page of data
call #tx_range1
jmp #cmd_next
cmd_viewp2 mov v2,#$FF 'addr. (more), show page of data
call #tx_range2
jmp #cmd_loop
cmd_view2 mov v2,v1 'addr, show unit of data
cmd_view3 call #tx_range 'addr.addr, show range of data
jmp #cmd_loop
'
'
' Search
'
cmd_search mov v1,view '/, search from view to end
cmd_search2 mov v2,amask 'addr/, search from address to end
cmd_search3 call #check_range 'addr.addr/, search range
mov y,#0 'reset search size
setinda s0 'point to search data
movs enter_str,#enter_search 'set search mode
call #parse_str 'get search string
sub y,#1 wc 'get search size
if_nc mov ssize,y 'if 0, search old data again
:start setptra v1 'start search, point to search address
setinda s0 'point to search data
mov x,#0 'reset word match counter
:word call #rdxxxx 'get memory word
cmp value,inda++ wz 'compare against search data word
if_z jmp #:match 'if word match, check if string match
add v1,wsize 'word mismatch, advance search address
cmp v1,v2 wz,wc 'at end of range?
if_be jmp #:start 'if not, start next search
mov view,v1 'else, update view
and view,amask
jmp #cmd_loop 'next command
:match incmod x,ssize wc 'word match, increment match counter
if_nc jmp #:word 'if more words to match, compare next word
mov v2,ssize 'got string match
shl v2,shift 'v1 = start of found data
add v2,v1 'v2 = end of found data
jmp #cmd_view3 'show found data
'
'
' Enter data
'
cmd_enter2 mov enter,v1 'addr:, set enter address
and enter,amask 'trim enter address
cmd_enter mov z,#0 ':, reset fill count
call #parse_enter 'parse and enter data
jmp #cmd_loop 'next command
'
'
' Fill data
'
cmd_fill call #check_range 'addr.addr:, check range, z=words to fill
mov enter,v1 'set initial enter address
call #parse_enter 'parse and enter fill-pattern data (jumps to cmd_loop if fill complete)
cmp enter,v1 wz 'any data entered?
if_z jmp #cmd_error 'if no data entered, error
mov y,enter 'get end-of-fill-pattern address (current enter)
setptra v1 'set start-of-fill-pattern address (initial enter)
:loop getptra x 'get current-fill-pattern address
cmp x,y wz 'compare against end-of-fill-pattern address
if_z setptra v1 'if same, reset to start-of-fill-pattern address
call #rdxxxx 'get fill-pattern word
call #enter_data 'enter word (jumps to cmd_loop when fill complete)
jmp #:loop 'loop to fill next word
'
'
' Move data
'
cmd_move mov y,x 'save ">"/"<"
call #check_range 'check 1st address range, get number of words
call #parse_hex 'get 2nd address
max value,amask 'v1=1st, value=2nd, z=words, y=">"/"<"
and value,amask
cmp y,#"<" wz 'if "<", swap v1 and value
if_z mov x,v1
if_z mov v1,value
if_z mov value,x 'v1=from, value=to, z=words
cmp v1,value wc 'if from < to, downward move
if_c mov x,z
if_c shl x,shift
if_c add v1,x
if_c add value,x
if_c xor rdxxxx,#%001_111110 'modify 'rdxxxx value,--ptra'
if_c xor wrxxxx,#%001_111110 'modify 'wrxxxx value,--ptrb'
setptra v1 'set pointers
setptrb value
:loop call #rdxxxx 'move data
call #wrxxxx
djnz z,#:loop
if_c xor rdxxxx,#%001_111110 'restore 'rdxxxx value,ptra++'
if_c xor wrxxxx,#%001_111110 'restore 'wrxxxx value,ptrb++'
jmp #cmd_loop 'next command
'
'
' Checksum
'
cmd_checksum call #check_range 'check range
setptra v1 'sum bytes
:loop call #rdxxxx
add y,value
djnz z,#:loop
mov value,y 'print sum
mov hsize,#8
call #tx_hex
jmp #cmd_next_crlf 'next command
'
'
' Watch
'
cmd_watchp movs rdxxxj,#rdxxxx_ret wz 'set pin mode, z=0
mov hsize,#1
cmd_watch if_z movs rdxxxj,#rdxxxm 'set mem mode
if_z mov hsize,wsize 'set hex size by word size
if_z shl hsize,#1
call #rdxxxp 'get initial value
:loop mov z,value 'preserve value
call #tx_hex 'print value
call #tx_space 'print space
:wait call #rx_check 'if key hit, exit
if_nz jmp #cmd_next_crlf
call #rdxxxp 'get current value
cmp value,z wz 'if same, check again
if_z jmp #:wait
jmp #:loop 'new value, loop
'
'
' Clkset
'
cmd_clkset setptra #hitspace_ 'print hit-space message
call #tx_string
clkset v1 'set clk
:wait call #rx 'wait for space
cmp x,#" " wz
if_nz jmp #:wait
jmp #cmd_next_crlf 'next command
'
'
' Coginit
'
cmd_coginit setcog v1 'set cog
call #parse_hex 'get program address
mov y,value 'save program address
mov value,#0 'clear pointer address
cmp x,#"+" wz 'if '+', get pointer address
if_z call #parse_hex
coginit y,value 'do 'coginit program,pointer'
jmp #cmd_loop 'next command
'
'
' Cogstop
' Quit
'
cmd_quit cogid v1 'quit
cmd_cogstop cogstop v1 'stop cog
jmp #cmd_next 'next command
'
'
' Map
'
cmd_map mov y,#7 'ready for 7..0
cmd_map_loop call #tx_space 'print space
mov x,y 'get cog status
cogid x wc
cmd_map_c cmp x,y wz
if_nc mov x,#"0" 'get 0/1/M chr
if_c mov x,#"1"
if_z mov x,#"M"
call #tx 'print chr
sub y,#1 wc
if_nc jmp #cmd_map_loop 'loop until done
jmp #cmd_next_crlf 'next command
'
'
' Pin writes clrp/setp/notp/offp
' Pin read
'
cmd_clrp movs pinop,#$DA wz 'clrp, z=0
cmd_setp if_z movs pinop,#$DB wz 'setp, z=0
cmd_notp if_z movs pinop,#$D9 wz 'notp, z=0
cmd_offp if_z movs pinop,#$D8 wz 'offp, z=0
cmd_getp if_z movs pinop,#$D6 'getp, z=1
pinx mov pin,v1 'if hex, get pin (d = pin/z)
pinop getp pin wc 'becomes clrp/setp/notp/offp/getp
if_z jmp #cmd_map_c 'if getp, show pin value
jmp #cmd_next 'next command
'
'
' Pin configuration
'
cmd_cfgp call #parse_hex 'get configuration
setport v1 'set pin port
decod5 v1 'get pin mask
cfgpins v1,value 'configure pin
jmp #cmd_loop 'next command
'
'
' Setdacs
'
cmd_setdacs setdacs v1 'set all four dacs with 8-bit values
jmp #cmd_next 'next command
'
'
' Help
'
cmd_help setptra #help_ 'print help message
call #tx_string
jmp #cmd_next_crlf 'next command
'*************************
'* Main Task Subroutines *
'*************************
'
'
' Vector branch
'
vector addptra base 'add data base pointer
vector_loop rdbyte z,ptra++ 'get jump address
vector_ret tjz z,#0 'if 0, no match found, return
rdbyte y,ptra++ 'get target
xor y,x wz 'compare to x
if_nz jmp #vector_loop 'if no match, loop
jmp z 'match found, jump, y=0, z=1
'
'
' Check address range (v1..v2)
'
check_range max v1,amask 'trim v1
and v1,amask
max v2,amask 'trim v2
and v2,amask
cmp v2,v1 wc 'make sure v2 => v1
if_c jmp #cmd_error
mov z,v2 'get number of words
sub z,v1
shr z,shift
add z,#1
check_range_ret ret
'
'
' Set rdxxxx/wrxxxx and others by word size
'
set_size test wsize,#%010 wc 'set rdxxxx/wrxxxx by word size
setbc rdxxxx,#26
setbc wrxxxx,#26
test wsize,#%100 wc
setbc rdxxxx,#27
setbc wrxxxx,#27
mov shift,wsize 'set shift by word size
shr shift,#1
mov amask,wsize 'set amask by word size
sub amask,#1
xor amask,h0001FFFF
and view,amask 'trim view
and enter,amask 'trim enter
set_size_ret ret
rdxxxp getp v1 wc 'read pin as "0" or "1"
if_nc mov value,#0
if_c mov value,#1
rdxxxj jmp #rdxxxx_ret 'd = rdxxxx_ret/rdxxxm
rdxxxm setptra v1 'read mem
rdxxxx rdbyte value,ptra++ 'rdbyte/rdword/rdlong
rdxxxp_ret
rdxxxx_ret ret
wrxxxx wrbyte value,ptrb++ 'wrbyte/wrword/wrlong
wrxxxx_ret ret
h0001FFFF long $0001FFFF
'
'
' Input line
'
rx_line setspa #0 'point to start of line
mov x,#">" 'show prompt
call #tx
call #rx 'get first chr
cmp x,#"'" wz 'check for repeat
if_nz jmp #:first 'if not repeat, first chr
:show popar x wz 'repeat, show line
if_nz call #tx
if_nz jmp #:show
jmp #:done
:loop call #rx 'get next chr
:first cmp x,#13 wz 'cr?
if_z jmp #:cr
cmp x,#8 wz 'backspace?
if_nz cmp x,#127 wz
if_z jmp #:bs
cmp x,#" " wc 'visible chr?
if_nc cmpr x,#"~" wc
if_c jmp #:loop
pusha x 'visible chr, append to line
chkspa wc 'overflow?
if_c subspa #1 'if overflow, back up
if_nc call #tx 'if not overflow, print chr
jmp #:loop
:bs chkspa wz 'backspace, line empty?
if_nz pushar x 'if not empty,
if_nz call #tx '..print backspace
if_nz call #tx_space '..print space
if_nz popar x '..print backspace
if_nz call #tx
if_nz subspa #1 '..back up
jmp #:loop
:cr pusha #0 'cr, end line with 0
:done setspa #0 'point to start of line
tx_crlf mov x,crlf 'print cr/lf
call #tx
tx_crlf_ret
rx_line_ret ret
crlf long 1 << 18 + $0A << 10 + $0D
'
'
' Parse string of hex/text for enter/fill or search
'
parse_enter movs enter_str,#enter_data
parse_str call #parse_next 'hex loop, check hex
if_nz call #enter_str 'if hex, enter value
cmp x,#" " wz 'check for space (more hex)
if_z jmp #parse_str 'if more hex, loop
cmp x,#"'" wz 'not hex, "'"?
if_nz jmp #parse_str_ret 'if not "'", done
:text addspa #2 'text loop
popa x 'get and point to next chr
cmp x,#"'" wz 'check for "'"
if_z jmp #parse_str 'if "'", back to hex
tjz x,#parse_str_ret 'if eol, done
mov value,x 'text chr
call #enter_str 'enter chr
jmp #:text 'loop
parse_enter_ret
parse_str_ret ret
enter_str jmp #enter_data 'jump to enter_data or enter_search
enter_search incmod y,#s1-s0 wc 'search, check if search limit exceeded
if_c jmp #cmd_error 'if search limit exceeded, error
mov inda++,value 'store value in search data
jmp #enter_str_ret 'return
enter_data setptrb enter 'enter/fill, get pointer
call #wrxxxx 'store value in memory
getptrb enter 'update pointer
djz z,#cmd_loop 'if fill done, abort (don't return)
enter_data_ret
enter_str_ret ret
'
'
' Parse hex
'
parse_hex call #parse_next 'try to parse hex
if_z jmp #cmd_error 'if no hex, error
parse_hex_ret ret
'
'
' Parse line (@spa), z=0 if hex (value)
'
parse_next addspa #1 'advance to next chr
parse mov value,#0 wz 'z=1
call #skip_spaces wz 'skip any spaces (preserve z)
:loop popar x 'get chr
call #check_hex 'check hex
if_c shl value,#4 'if hex, append nibble and loop
if_c or value,x
if_c jmp #:loop wz 'z=0
subspa #1 'repoint to non-hex chr
call #skip_spaces wz 'skip any post-hex spaces (preserve z)
call #check_hex 'check hex
if_c popa x 'if hex, back up to space chr
cmpr x,#"a"-1 wc 'make non-hex chr uppercase
if_c cmp x,#"z"+1 wc
if_c sub x,#"a"-"A"
parse_next_ret
parse_ret ret
'
'
' Skip spaces (@spa)
'
skip_spaces popar x 'skip space chr(s)
cmp x,#" " wz
if_z jmp #skip_spaces
subspa #1 'back up to non-space chr
skip_spaces_ret ret wz 'restore z
'
'
' Check hex (x), c=1 if hex (x)
'
check_hex cmpr x,#"0"-1 wc '"0".."9" -> $0..$9
if_c cmp x,#"9"+1 wc
if_c add x,#"A"-"9"-1
if_nc cmpr x,#"A"-1 wc '"A".."F" -> $A..$F
if_c cmp x,#"F"+1 wc
if_c add x,#"a"-"A"
if_nc cmpr x,#"a"-1 wc '"a".."f" -> $A..$F
if_c cmp x,#"f"+1 wc
if_c sub x,#"a"-10
check_hex_ret ret
'
'
' Print range (v1..v2)
'
tx_range1 mov v1,view 'view..view + v2
tx_range2 and v2,amask 'v1..v1 + v2
add v2,v1
tx_range call #check_range 'check range
mov view,v1 'set address
:line mov value,view 'print 5-digit address
mov hsize,#5
call #tx_hex
call #tx_dspace 'print "- "
mov x,wsize 'get number of words on line
rev x,#32-5
mov v1,z
max v1,x
mov v2,v1 'get number of ascii bytes on line
shl v2,shift
sub z,v1 'update number of words left
setptra view 'print hex words
:hex call #rdxxxx
mov hsize,wsize
shl hsize,#1
call #tx_hex
call #tx_space
djnz v1,#:hex
setptra #spacesq_ 'print " '"
call #tx_string
setptra view 'print ascii bytes
:ascii rdbyte x,ptra++
cmp x,#" " wc 'visible chr?
if_nc cmpr x,#"~" wc
if_c mov x,#"." 'substitute "." for non-visible chrs
call #tx
djnz v2,#:ascii
getptra view 'update address
setptra #quotecr_ 'print "'" + cr
call #tx_string
call #rx_check 'check key hit
if_z tjnz z,#:line 'if no key hit and more words left, print another line
tx_range1_ret
tx_range2_ret
tx_range_ret ret
'
'
' Print string (@ptra)
'
tx_string addptra base 'add data base pointer
tx_string_loop rdbyte x,ptra++ 'get chr
tx_string_ret tjz x,#0 'if 0, done
cmp x,#"`" wz 'long tab?
if_z subr y,#32-16
if_nz cmp x,#"~" wz 'short tab?
if_z add y,#16
:tab if_z call #tx_space
if_z djnz y,#:tab
if_z call #tx_dspace
if_z jmp #tx_string_loop
cmp x,#13 wz 'cr?
if_z call #tx_crlf
if_z mov y,#0
if_nz call #tx 'other?
if_nz add y,#1
jmp #tx_string_loop
'
'
' Print hex (value)
'
tx_hex mov y,hsize 'pre-rotate to get 1st nibble in top
shl y,#2
ror value,y
mov y,hsize 'print nibbles
:loop rol value,#4
mov x,value
call #tx_nib
djnz y,#:loop
tx_hex_ret ret
'
'
' Print "- "
'
tx_dspace mov x,dspace
jmp #tx
dspace long 1 << 18 + " " << 10 + "-"
'
'
' Print space
'
tx_space mov x,#" "
jmp #tx
'
'
' Print nibble (x)
'
tx_nib and x,#$F 'isolate nibble
cmp x,#$A wc 'alpha or numeric?
if_c add x,#"0" 'numeric
if_nc add x,#"A"-$A 'alpha
'
'
' Transmit chr (x)
'
tx shl x,#1 'insert start bit
setb x,#9 'set stop bit
getcnt tx_time 'get initial time
:loop add tx_time,period 'add bit period to time
passcnt tx_time 'loop until bit period elapsed
shr x,#1 wc 'get next bit into c
setpc tx_pin 'write c to tx pin
tjnz x,#:loop 'loop until bits done
tx_dspace_ret
tx_space_ret
tx_nib_ret
tx_ret ret
'
'
' Receive chr (x)
'
rx call #rx_check 'wait for rx chr
if_z jmp #rx
rx_ret ret
'
'
' Check receiver, z=0 if chr (x)
'
rx_check or rx_tail,#$80 'if start or rollover, reset tail
getspb rx_temp wz 'if head uninitialized, z=1
if_nz cmp rx_temp,rx_tail wz 'if head-tail mismatch, byte ready, z=0
if_nz getspa rx_temp 'preserve spa
if_nz setspa rx_tail 'get tail
if_nz popar x 'get byte at tail
if_nz getspa rx_tail 'update tail
if_nz setspa rx_temp 'restore spa
rx_check_ret ret
'************************
'* Serial Receiver Task *
'************************
rx_task chkspb wz 'if start or rollover, reset head
if_z setspb #$80
mov rx_bits,#9 'ready for 8 data bits + 1 stop bit
neg rx_time,period 'get -0.5 period
sar rx_time,#1
jp rx_pin,#$ 'wait for start bit
subcnt rx_time 'get time + 0.5 period for initial 1.5 period delay
:bit rcr rx_data,#1 'rotate c into byte
add rx_time,period 'add 1 period
passcnt rx_time 'wait for center of next bit
getp rx_pin wc 'read rx pin into c
djnz rx_bits,#:bit 'loop until 8 data bits + 1 stop bit received
shr rx_data,#32-8 'align byte
pushb rx_data 'store byte at head, inc head
jmp #rx_task 'wait for next byte
'**********************
'* Baud Detector Task *
'**********************
baud_task movd ctr,rx_pin 'set ctra to time rx pin states
:loop notb ctr,#5 wc 'if 1,0 sample set, c=0
setctra ctr '($20 -> 10000001001 -> 1, 6x 0, 1x 1, 2x 0, 1)
if_nc mov limh,buff0 'if 1,0 sample set,
if_nc shr limh,#4 '..make window from 1st 0 (6x if $20)
if_nc neg liml,limh
if_nc add limh,buff0
if_nc add liml,buff0
if_nc mov comp,buff1 'if 1,0 sample set,
if_nc mul comp,#6 '..normalize 2nd 1 (1x if $20) to 6x
if_nc cmpr comp,limh wc '..check if within window
if_nc cmp comp,liml wc
if_nc mov comp,buff2 'if 1,0 sample set,
if_nc mul comp,#3 '..normalize 2nd 0 (2x if $20) to 6x
if_nc cmpr comp,limh wc '..check if within window
if_nc cmp comp,liml wc
if_nc add buff0,buff2 'if $20,
if_nc shr buff0,#3 '..compute period from 6x 0 and 2x 0
if_nc mov period,buff0 '..update period
mov buff0,buff1 'scroll sample buffer
mov buff1,buff2
:wait getcosa buff2 'wait for next sample
tjnz buff2,#:loop
jmp #:wait
ctr long %100_01001 'ctr configuration for timing low on rx pin
'*************
'* Variables *
'*************
reserves
base res 1 'main task
x res 1
y res 1
z res 1
value res 1
view res 1
enter res 1
pin res 1
ssize res 1
hsize res 1
wsize res 1
shift res 1
amask res 1
rx_tail res 1 'serial receiver task
rx_temp res 1
rx_time res 1
rx_data res 1
rx_bits res 1
buff0 res 1 'baud detector task
buff1 res 1
buff2 res 1
limh res 1
liml res 1
comp res 1
period res 1
I never got around to adding the SDRAM boot, as it was a lot of work just to get what's in there ironed out. Of the 3,840 ROM bytes, the monitor takes 2,384 bytes, which is a lot, but I think it's a really nice thing to have. Both the monitor and the SHA-256/HMAC programs can be executed independently from user application code and are very simple to operate.
No flipping or hurt feelings here. I was just pointing out that such an OS choice would not light the fires of community support.
I'm sure QNX is quite fine, I have used many such embedded operating systems and would always advise against trying to use Linux for fast real-time work. Linux was never intended for that use. Try flipping a GPIO pin on a Raspi with any hard timing constaints and you'll see what I mean (See I'm not such a "fanboi").
In the context of my little dream system a hard real-time OS is not required anyway. As I said the Prop takes care of the hard real-time, multi-threaded, real world interfacing and the ARM can slouch along doing the heavy lifting of file system, networking, GUI, and other such tasks.
I might add that one reason I am a fan of the Propeller, and dare I say it XMOS, is that it does not require an OS to get that multi-tasking, real-time work done. Now that the Prop II has the abilty to run multiple threads in a COG that situation is made even better.
The ROM contains the booter (SPI Flash or Serial) with SHA-256/HMAC authentication to facilitate code protection, and a serial (RX/TX)
What is the smallest/cheapest SPI device supported ?
Will this boot from a QuadSPI part (even if first load is in 1 bit SPI mode ? )
I can see SOT23-6 SPI parts, but they seem to cost more than the 13c/3K that a SO8 SPI part costs.
Unlike i2c, where SOT23 are among the lowest cost parts available
The ROM is now done. It spans from hub byte address $00000 to $00EFF, with $F00 to $1FFFF being RAM. The ROM contains the booter (SPI Flash or Serial) with SHA-256/HMAC authentication to facilitate code protection, and a serial (RX/TX) monitor which starts up if there's no authenticated loader to execute and no security key programmed into the first 128 fuses - you just hit <space> and it auto-bauds and gives you a prompt. The monitor could be used as a very simple development interface, where you just send text scripts to load code and start cogs.
...
I never got around to adding the SDRAM boot, as it was a lot of work just to get what's in there ironed out. Of the 3,840 ROM bytes, the monitor takes 2,384 bytes, which is a lot, but I think it's a really nice thing to have. Both the monitor and the SHA-256/HMAC programs can be executed independently from user application code and are very simple to operate.
Of the 3,840 ROM bytes, the monitor takes 2,384 bytes, which is a lot, but I think it's a really nice thing to have. Both the monitor and the SHA-256/HMAC programs can be executed independently from user application code and are very simple to operate.
The monitor looks really cool but are you sure we're not going be sorry we lost the extra 2K hub RAM bytes? I know we have a lot more hub RAM space in P2 but competing microcontrollers often have twice the program space and a fair amount of SRAM as well.
IMHO, the real question is whether or not that RAM would make the difference. I don't think it would, and we've got some better means of employing external storage in P2.
The monitor looks really cool but are you sure we're not going be sorry we lost the extra 2K hub RAM bytes? I know we have a lot more hub RAM space in P2 but competing microcontrollers often have twice the program space and a fair amount of SRAM as well.
If the Monitor was Top-level single mask stuff, then that would allow some choice, (and cheaper bug fixes) but it sounds like it is compile-time ROM, which is really cast-in-stone stuff. You have to be very sure there are no bugs, and no drop-dead's.
At what size, does true-rom ( or even OTP ?) become more area efficient than Ram-As-Rom ?
With no tables anymore, this ROM does not need to be RAM-stealing, from an operational viewpoint.
It has to load into COG-RAM anyway, and does not have to be in the time-critical address decode/mux path ?
Is 2K worth an additional plan, synthesize, test, cycle? That is the cost of changing it to some copy to COG on boot option...
Basically, this makes the monitor a freebie. High value add, little to no downside. (Of course, I'm a fan too, so the high value is in the eyes of all of us.)
I can say I approve this ROM. Things are looking easy from on - it would also come in handy for the debugging purpose - at least for the host microcontroller (like MPC5200 or ARM9/M4 MCUs in clustered system), even when dealing with a bad flash.
And it could be possible that you might be able to run QNX RTOS. Same for FreeRTOS if you want open source. (uCLinux is already a RTOS anyhow.)
Nevertheless, I am waiting for when it is released so I can buy Quickstart-like board to start doing prototype software - maybe a port of uCLinux. And, afterwards start designing the hardware.
I'm a big fan of bootstrapping through a serial link. It is exactly how I want to deploy my first Prop2. Also very exciting is how quickly you've implemented and exploited the hardware multi-threading hook. Great stuff!
Would it be possible to get the information at the following links (and referenced PDFs) updated? I'm particularly interested in more detailed documentation of the new instructions, but can probably fill in some of the details by looking at their usage in the posted monitor code.
I may be doing too much reading between the lines, but does this mean that the product is going to officially be called "Propeller II"?
Good question. Their preliminary feature list calls it "Propeller 2" so they've used both names. I call it "P2" myself because I'm too lazy to type the longer name! :-)
I'm in two minds about this monitor idea.
Chip has said "Of the 3,840 ROM bytes, the monitor takes 2,384 bytes,"
What does this mean?. I thought there was leeway to swap RAM and ROM around now it seems 3,840 ROM bytes are fixed already.
A monitor is nice but how often would it be used in production? In many apps people run up aganst a RAM limit first.
I would rather have the RAM space for apps and data than consumed by a monitor that will rarely be used in finished products.
You can always load a monitor if you like for tinkering about, as the Forth guys will tell you.
What big SDRAMS? This is a micro-contoller, it should stand alone in most applications. Those external RAMs are just for us freaks who want to turn an MCU into a CP/M machine or other such off the wall stuff for fun.
2K out of a possible 128 K is almost 2% or RAM wasted. That's huge given that it will rarely be used.
Early on, I didn't think there was a 1:1 exchange rate for RAM and ROM. I thought RAM was more expensive space-wise. I almost thought it was like 1:6, if so then 2K of ROM would be only 350 bytes of RAM. But to your point, the poor guy that never uses the monitor but has a program 300 bytes too big! Then there will always be the "if only I had 20 more bytes" scenario at some upper boundary.
If I understand the situation correcty RAM cells are much bigger than ROM cells. In the Prop II we have space for 128K of RAM.
BUT it is possible to butcher some of the RAM cells to make ROM, in the same address space. Still taking the same physical space as what would have been RAM.
So, the more code you make in ROM the less RAM you have. Ergo the ROM monitor is eating valuable RAM for little benifit in general usage.
Point taken! I can't speak to the value of having an in-ROM monitor in an embedded device that won't boot. I'm just a hobbyist so my failure and recovery scenarios are different than a production user.
I can see Chip's vision being drowned out by us yokels always seeing a feature or something different as a problem. Someone will always find and hit a wall and want to knock it down. Someone will always want more of A than B. Someone will always want more of B then A. Someone will always want an MCU to be an MPU. Someone will always want you to do it their way even though it's your baby. Someone is us.
I've been pretty much just sitting back just waiting for that apple pie to come out of the oven. Knowing the cook I'm sure that even though he messes the recipe it's going to extremely tasty. I just want to sit down and enjoy, and make the most of it.
Yes, sorry, being a Brit I'm prone to the affliction observed by Charles Babbage:
"Propose to an Englishman any principle, or any instrument, however admirable, and you will observe that the whole effort of the English mind is directed to find a difficulty, a defect, or an impossibility in it. If you speak to him of a machine for peeling a potato, he will pronounce it impossible: if you peel a potato with it before his eyes, he will declare it useless, because it will not slice a pineapple. Impart the same principle or show the same machine to an American or to one of our Colonists, and you will observe that the whole effort of his mind is to find some new application of the principle, some new use for the instrument. "
Yes, sorry, being a Brit I'm prone to the affliction observed by Charles Babbage:
"Propose to an Englishman any principle, or any instrument, however admirable, and you will observe that the whole effort of the English mind is directed to find a difficulty, a defect, or an impossibility in it. If you speak to him of a machine for peeling a potato, he will pronounce it impossible: if you peel a potato with it before his eyes, he will declare it useless, because it will not slice a pineapple. Impart the same principle or show the same machine to an American or to one of our Colonists, and you will observe that the whole effort of his mind is to find some new application of the principle, some new use for the instrument. "
This might be true for your comment about large SDRAMs?
While I confess being part of that bunch that wants to turn the MCU in MPU, I thought bandwidth is going to be adequate for a graphics display and many more "serious" applications too.
Without initializing, RAM bits come up in a random state. Instead of ROM, would it be possible simply to bias those corresponding bits of RAM on power-up to contain the "ROM" code? Then they could be reused by the app program.
Without initializing, RAM bits come up in a random state. Instead of ROM, would it be possible simply to bias those corresponding bits of RAM on power-up to contain the "ROM" code? Then they could be reused by the app program.
-Phil
I asked exactly that a while back, but it was determined that 7t SRAM cells won't fit where 6t SRAM cells are designed to go.
I wonder, would it be possible to swap on the 6T-SRAM for 7T-SRAM with reset? Could "ROM" be done by having the reset mode of some cells be different than others, in order to make the ROM code? That way we wouldn't lose the RAM space for ROM, but would still be able to boot easily.
If we had more area within the bit cell and more wiring space, we could do a 7T cell with initialization. Too late for that now, though. 6T or less is all that will fit in those bit cell spaces.
Comments
The ROM is now done. It spans from hub byte address $00000 to $00EFF, with $F00 to $1FFFF being RAM. The ROM contains the booter (SPI Flash or Serial) with SHA-256/HMAC authentication to facilitate code protection, and a serial (RX/TX) monitor which starts up if there's no authenticated loader to execute and no security key programmed into the first 128 fuses - you just hit <space> and it auto-bauds and gives you a prompt. The monitor could be used as a very simple development interface, where you just send text scripts to load code and start cogs.
Below is the code for the monitor which uses the new hardware multi-threading to run concurrent auto-baud detection, serial input, and main monitor code:
Here is a conversation with it, from start-up:
I never got around to adding the SDRAM boot, as it was a lot of work just to get what's in there ironed out. Of the 3,840 ROM bytes, the monitor takes 2,384 bytes, which is a lot, but I think it's a really nice thing to have. Both the monitor and the SHA-256/HMAC programs can be executed independently from user application code and are very simple to operate.
From that I assume You have now info on dedicated PIN's for -- Serial and SPI.
Can Ypu post that so it be possible to made experimenting PCB for Propeller II.
Ps. In my opinion -- bad decision on SD -- But that is life.
Pure Genius indeed.
No flipping or hurt feelings here. I was just pointing out that such an OS choice would not light the fires of community support.
I'm sure QNX is quite fine, I have used many such embedded operating systems and would always advise against trying to use Linux for fast real-time work. Linux was never intended for that use. Try flipping a GPIO pin on a Raspi with any hard timing constaints and you'll see what I mean (See I'm not such a "fanboi").
In the context of my little dream system a hard real-time OS is not required anyway. As I said the Prop takes care of the hard real-time, multi-threaded, real world interfacing and the ARM can slouch along doing the heavy lifting of file system, networking, GUI, and other such tasks.
I might add that one reason I am a fan of the Propeller, and dare I say it XMOS, is that it does not require an OS to get that multi-tasking, real-time work done. Now that the Prop II has the abilty to run multiple threads in a COG that situation is made even better.
What is the smallest/cheapest SPI device supported ?
Will this boot from a QuadSPI part (even if first load is in 1 bit SPI mode ? )
I can see SOT23-6 SPI parts, but they seem to cost more than the 13c/3K that a SO8 SPI part costs.
Unlike i2c, where SOT23 are among the lowest cost parts available
my thoughts:
- The monitor looks great!
- Lack of SD boot is not a big deal, SPI flash is dirt cheap now.
- the rom now fully occupies the RDxxxx/WRxxxx direct address range
(Unless of course you've already changed the "upper" address bits for the direct 8 bit address to correspond to the last 256 bytes of ram...)
Did I mention that the monitor looks great???? :-)
If the Monitor was Top-level single mask stuff, then that would allow some choice, (and cheaper bug fixes) but it sounds like it is compile-time ROM, which is really cast-in-stone stuff. You have to be very sure there are no bugs, and no drop-dead's.
At what size, does true-rom ( or even OTP ?) become more area efficient than Ram-As-Rom ?
With no tables anymore, this ROM does not need to be RAM-stealing, from an operational viewpoint.
It has to load into COG-RAM anyway, and does not have to be in the time-critical address decode/mux path ?
I'm not convinced it would.
Is 2K worth an additional plan, synthesize, test, cycle? That is the cost of changing it to some copy to COG on boot option...
Basically, this makes the monitor a freebie. High value add, little to no downside. (Of course, I'm a fan too, so the high value is in the eyes of all of us.)
And it could be possible that you might be able to run QNX RTOS. Same for FreeRTOS if you want open source. (uCLinux is already a RTOS anyhow.)
Nevertheless, I am waiting for when it is released so I can buy Quickstart-like board to start doing prototype software - maybe a port of uCLinux. And, afterwards start designing the hardware.
setdacs -
http://www.parallaxsemiconductor.com/Products/propeller2specs
http://www.parallax.com/Propeller2FeatureList/tabid/898/Default.aspx
I may be doing too much reading between the lines, but does this mean that the product is going to officially be called "Propeller II"?
Chip has said "Of the 3,840 ROM bytes, the monitor takes 2,384 bytes,"
What does this mean?. I thought there was leeway to swap RAM and ROM around now it seems 3,840 ROM bytes are fixed already.
A monitor is nice but how often would it be used in production? In many apps people run up aganst a RAM limit first.
I would rather have the RAM space for apps and data than consumed by a monitor that will rarely be used in finished products.
You can always load a monitor if you like for tinkering about, as the Forth guys will tell you.
Anybody remember how much RAM P2 will have?
I believe it's 128k minus the size of the ROM.
What big SDRAMS? This is a micro-contoller, it should stand alone in most applications. Those external RAMs are just for us freaks who want to turn an MCU into a CP/M machine or other such off the wall stuff for fun.
2K out of a possible 128 K is almost 2% or RAM wasted. That's huge given that it will rarely be used.
If I understand the situation correcty RAM cells are much bigger than ROM cells. In the Prop II we have space for 128K of RAM.
BUT it is possible to butcher some of the RAM cells to make ROM, in the same address space. Still taking the same physical space as what would have been RAM.
So, the more code you make in ROM the less RAM you have. Ergo the ROM monitor is eating valuable RAM for little benifit in general usage.
I've been pretty much just sitting back just waiting for that apple pie to come out of the oven. Knowing the cook I'm sure that even though he messes the recipe it's going to extremely tasty. I just want to sit down and enjoy, and make the most of it.
EDIT: I missed Peter's post while I was posting my message. So now I understand. The extra code is needed to monitor the apple pie while it's baking.
Yes, sorry, being a Brit I'm prone to the affliction observed by Charles Babbage:
"Propose to an Englishman any principle, or any instrument, however admirable, and you will observe that the whole effort of the English mind is directed to find a difficulty, a defect, or an impossibility in it. If you speak to him of a machine for peeling a potato, he will pronounce it impossible: if you peel a potato with it before his eyes, he will declare it useless, because it will not slice a pineapple. Impart the same principle or show the same machine to an American or to one of our Colonists, and you will observe that the whole effort of his mind is to find some new application of the principle, some new use for the instrument. "
This might be true for your comment about large SDRAMs?
While I confess being part of that bunch that wants to turn the MCU in MPU, I thought bandwidth is going to be adequate for a graphics display and many more "serious" applications too.
-Phil
Edit: Found it!