Version 29 of Playing PIC Simulation

Updated 2012-11-15 22:58:30 by Jorge

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]
                  continue
                  }
            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 {
              break
            }
        }
        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
        end
 }

 asm::run
 
 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
 0001 MOVWF OFFSET
 0002 MOVLW H'20'
 0003 MOVWF CHRIX
 0004 MOVLW H'30'
 0005 MOVWF FSR
 0006 MOVLW D'16'
 0007 MOVWF COUNTER
 0008 MOVFW COUNTER
 0009 ADDWF CHRIX W
 0010 MOVWF INDF
 0011 INCF FSR
 0012 DECFSZ COUNTER
 0013 GOTO 8
 0014 INCF OFFSET
 0015 INCF CHRIX

 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