Version 5 of Playing PIC Simulation

Updated 2012-11-15 01:14:04 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.
Again, this is not quite ready yet, for example, the command

 addwf chrix,w

was now written as

 addwf chrix

implying that the destiny is the W register

the example shows a RAM buffer (from 48d to 63d) filled with values descending from 30H to 21H. The real thing in hardware shows all the possible charactes a LCD can display.

 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]
        if [regexp {^([0-9A-F]+)H$} [string trim $s] -> s] {set s [expr 0x$s]}
        set 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 "dump..."
        foreach i [lsort -integer [array names mem]] {
            puts [format "%04d %s" $i $mem($i)]
        }
        foreach i [lsort [array names sym]] {
            puts [format "%-10s: %04x" $i $sym($i)]
        }

    }
    proc run {{pc 256}} {
        puts "run..."
        incr pc -1
        variable mem
        foreach i {A B C D E Z W} {set ::$i 0}
        while {$pc>=0} {
            incr 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)
            #incr pc
        }
    }
    #----------------- "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  {
      variable ram
      set ::W [expr $ram($adr) + $::W]
      }
    proc CALL {name}      {[string tolower $name] $::W}
    proc 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 $value}
 }

#-- Now testing:

 asm::asm {
        org  100    ; the canonical start address in CP/M
        goto  START   ; idiomatic: get over the initial variable(s)
 DONE:  equ  0       ; warm start in CP/M ;-)
 MAX:   equ  5
 INCR:  db   2       ; a variable (though we won't vary it)
 ;; here we go...
 START: movlw 0
        movwf offset
        movlw 32
        movwf chrix        
        movlw 48
        movwf FSR
        movlw 16
        movwf counter
 WEER:  movfw counter
        addwf chrix
        movwf INDF
        ;call puts
        incf FSR
        decfsz counter
        goto weer
        incf offset
        incf chrix
        goto DONE
        end
 }
 asm::run 100

puts "RAM after run:" foreach i [lsort [array names asm::ram] {

    puts [format "%-10s: %04x" $i $asm::ram($i)]

}

When running the example, the outputs is:

 label=,op=,args=
 label=,op=ORG,args=100    
 label=,op=GOTO,args=START   
 label=DONE,op=EQU,args=0       
 label=MAX,op=EQU,args=5
 label=INCR,op=DB,args=2       
 label=,op=,args=
 label=START,op=MOVLW,args=0
 label=,op=MOVWF,args=OFFSET
 label=,op=MOVLW,args=32
 label=,op=MOVWF,args=CHRIX        
 label=,op=MOVLW,args=48
 label=,op=MOVWF,args=FSR
 label=,op=MOVLW,args=16
 label=,op=MOVWF,args=COUNTER
 label=WEER,op=MOVFW,args=COUNTER
 label=,op=ADDWF,args=CHRIX
 label=,op=MOVWF,args=INDF
 label=,op=,args=
 label=,op=INCF,args=FSR
 label=,op=DECFSZ,args=COUNTER
 label=,op=GOTO,args=WEER
 label=,op=INCF,args=OFFSET
 label=,op=INCF,args=CHRIX
 label=,op=GOTO,args=DONE
 label=,op=,args=END
 label=,op=,args=
 dump...
 0100 GOTO 102
 0101 2
 0102 MOVLW 0
 0103 MOVWF OFFSET
 0104 MOVLW 32
 0105 MOVWF CHRIX
 0106 MOVLW 48
 0107 MOVWF FSR
 0108 MOVLW 16
 0109 MOVWF COUNTER
 0110 MOVFW COUNTER
 0111 ADDWF CHRIX
 0112 MOVWF INDF
 0113 INCF FSR
 0114 DECFSZ COUNTER
 0115 GOTO 110
 0116 INCF OFFSET
 0117 INCF CHRIX
 0118 GOTO 0
 DONE      : 0000
 INCR      : 0065
 MAX       : 0005
 START     : 0066
 WEER      : 006e
 run...
 RAM after run:
 48        : 0030
 49        : 002f
 50        : 002e
 51        : 002d
 52        : 002c
 53        : 002b
 54        : 002a
 55        : 0029
 56        : 0028
 57        : 0027
 58        : 0026
 59        : 0025
 60        : 0024
 61        : 0023
 62        : 0022
 63        : 0021
 CHRIX     : 0021
 COUNTER   : 0000
 FSR       : 0040
 OFFSET    : 0001
 (code) 1 %