Playing PIC Simulation

See picL aka PICLab

JM 14 Nov 2012 - After "Playing Assembler" a little bit, and looking at the RS comments that says: "Of course this is no real assembler. The memory model is constant-size instructions...", that just sounded like how the PIC microcontroller is arranged [L1 ]
So I took a section of a PIC's working code (a loop that fills a RAM buffer for a later transfer to a LCD) and tweak Playing Assembler with a few of the PIC's instruction set.

the example shows a RAM buffer (from 30H to 3FH) filled with values descending from 30H to 21H. The real thing in hardware shows all the possible charactes a LCD can display.[L2 ]

 console show
 namespace eval asm {
    proc asm body {
        variable mem
        catch {unset mem} ;# good for repeated sourcing
        foreach line [split $body \n] {
            foreach i {label op args} {set $i ""}
            regexp {([^;]*);} $line -> line ;# strip off comments
            regexp {^ *(([A-Z0-9]+):)? *([A-Z]*) +(.*)} [string toupper $line]\
                 ->  -   label           op       args
            # puts label=$label,op=$op,args=$args
            if {$label!=""} {set sym($label) $PC}
            if {$op==""}     continue
            if {$op=="DB"}  {set mem($PC) [convertHex $args]; incr PC; continue}
            if {$op=="EQU"} {set sym($label) [convertHex $args]; continue}
            if {$op=="ORG"} {
                  set PC [convertHex $args]
            regsub -all ", *" $args " " args ;# normalize commas
            set mem($PC) "$op $args"
            incr PC
        substituteSymbols sym
        dump   sym
    proc convertHex s {
        set s [string trim $s]
        #tk_messageBox -message $s
        if [regexp {^H'([0-9A-F]+)'$} $s -> s] {set s [expr 0x$s]}
        if [regexp {^D'([0-9A-F]+)'$} $s -> s] {set s $s}
        set s
    proc substituteSymbols {_sym} {
        variable mem
        upvar $_sym sym
        foreach i [array names mem] {
            set tmp [lindex $mem($i) 0]
            foreach j [lrange $mem($i) 1 end] {
                if {[array names sym $j]==$j} {set j $sym($j)}
                lappend tmp $j
            set mem($i) $tmp
    proc dump {_sym} {
        variable mem
        variable ram
        upvar $_sym sym
        puts "\n dump...prog memory"
        foreach i [lsort -integer [array names mem]] {
            puts [format " %04d %s" $i $mem($i)]
        puts "\n dump...sym names"
        foreach i [lsort [array names sym]] {
            puts [format " %-10s: %04x" $i $sym($i)]
    proc run {{pc 0}} {
        puts "\n running ASM program..."
        #incr pc -1
        variable mem
        foreach i {A B C D E Z W} {set ::$i 0}
        #tk_messageBox -message "pc: $pc: $mem($pc)"
        while {$pc>=0} {
            incr pc
            #tk_messageBox -message "pc: $pc: $mem($pc)"
            if {[info exists mem($pc)]} {
            #puts "$mem($pc)\tA:$::A B:$::B C:$::C D:$::D E:$::E Z:$::Z"
            #tk_messageBox -message "$mem($pc)\tA:$::A B:$::B C:$::C D:$::D E:$::E Z:$::Z"
            eval $mem($pc)
            } else {
        puts " ASM program ended..."
    #----------------- "machine opcodes" implemented as procs
    proc SHOWRAM dummyNr {
      variable ram
      puts "---RAM dump"
      foreach i [lsort [array names ram]] {
        puts "$i: $ram($i)"
    proc ADDWF {adr dest}  {
      variable ram
      if {$dest == "W"} {
        set ::W [expr $ram($adr) + $::W]
      } else {
        set ram($adr) [expr $ram($adr) + $::W]
    proc CALL {name}      {[string tolower $name] $::W}
    proc GOTO adr         {
      #tk_messageBox -message "GOTO $adr"
      uplevel 1 set pc [expr $adr - 1]
    proc INCF  adr        {
      variable ram
      set ram($adr) [expr $ram($adr) + 1]      
    proc DECFSZ adr {
      variable ram
      set ram($adr) [expr $ram($adr) - 1]
      if {$ram($adr) == 0} {uplevel 1 set pc [expr [uplevel 1 set pc] +1]}    
    proc MOV  {reg adr}   {variable mem; set ::$reg $mem($adr)}
    proc MOVWF adr   {
      variable ram
      if {$adr == "INDF"} {
        set fsrC $ram(FSR)
        set ram($fsrC) $::W
      } else {
        set ram($adr) $::W
    proc MOVFW  adr   {
      variable ram
      set ::W $ram($adr)
    proc MVI  {reg value} {set ::$reg $value}
    proc MOVLW  value {set ::W [convertHex $value]}

#-- Now testing:
 asm::asm {
        org  0    ; the canonical start address in PICs
        movlw 0
        movwf offset
        movlw H'20'
        movwf chrix        
        movlw H'30'
        movwf FSR
        movlw D'16'
        movwf counter
 WEER:  movfw counter
        addwf chrix,w
        movwf INDF
        ;call puts
        incf FSR
        decfsz counter
        goto weer
        incf offset
        incf chrix

 puts "\n RAM after run:"
 puts " addr contents"
 puts " ----  --"  
 foreach i [lsort [array names asm::ram]] {
    #puts "$i [string is integer $i]"
    if [string is integer $i] {
      puts [format " %04x: %02x (%c)" $i $asm::ram($i) $asm::ram($i)]
    } else {
      puts [format " %-10s: %02x" $i $asm::ram($i)]    

When running the example, the outputs is:

 dump...prog memory
 0000 MOVLW 0
 0002 MOVLW H'20'
 0004 MOVLW H'30'
 0006 MOVLW D'16'
 0011 INCF FSR
 0013 GOTO 8

 dump...sym names
 WEER      : 0008

 running ASM program...
 ASM program ended...

 RAM after run:
 addr contents
 ----  --
 0030: 30 (0)
 0031: 2f (/)
 0032: 2e (.)
 0033: 2d (-)
 0034: 2c (,)
 0035: 2b (+)
 0036: 2a (*)
 0037: 29 ())
 0038: 28 (()
 0039: 27 (')
 003a: 26 (&)
 003b: 25 (%)
 003c: 24 ($)
 003d: 23 (#)
 003e: 22 (")
 003f: 21 (!)
 CHRIX     : 21
 COUNTER   : 00
 FSR       : 40
 OFFSET    : 01

(code) 1 %

Jorge - 2012-11-15 16:57:13

  • changed the ORG address to the canonical start address for PICs
  • prepared the hex pattern for conversion to be H'nn'
  • cleaned up the output to show RAM addresses in hex format also

Jorge - 2012-11-15 22:18:41

  • the command addwf was fixed to properly use "d" as a switch to define if the result is to be stored in the file register itselt or the W register.
 addwf chrix,w
  • hex preffix was changed to H'nn' to follow MPLAB syntax (the well known 0x is also valid though not implemented here yet)
  • default ORG address in run proc was set to 0
  • the corresponding char is shown to illustrate which set of symbols are going to be displayed on LCD

Jorge - 2014-01-30 23:01:01

I was just looking at simavr (for AVR devices, not PIC though) and thought it could be great if calling the simulation could be done from Tcl (Replacing Glut)...we could then construct nice GUI around that simulator.

Jorge - 2014-11-10 02:23:09

I think I will try gluttk

Jorge - 2016-1-4

See also