Advertisement
Guest User

Untitled

a guest
Feb 1st, 2021
89
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
TCL 6.59 KB | None | 0 0
  1. package require struct
  2. # Исходный текст.
  3. set input_buffer {}
  4. # Память. Список байтов.
  5. set output_buffer {}
  6. # Созданные слова.
  7. array set dict {}
  8. # Адрес последнего определенного слова.
  9. set last_word 0
  10. # Стек для условий и циклов.
  11. ::struct::stack stack
  12. # Слова - единичные инструкции.
  13. lappend istr      DUP    +      AND    OR     =      <      U<     NIP    DROP   NOT    1-
  14. lappend istr_code 0x6081 0x6203 0x6303 0x6403 0x6703 0x6803 0x6F03 0x6003 0x6103 0x6600 0x6A00
  15. lappend istr      1<<    1>>    swap_bytes A@     SWAP   DEPTH  R@     OVER   >R     R>     {;}    EXIT
  16. lappend istr_code 0x6D00 0x6900 0x6500     0x6C00 0x6180 0x6E81 0x6BC1 0x6181 0x6147 0x6BCD 0x700C 0x700C
  17.  
  18. if {$argc != 2} {
  19.     puts {Incorrect command line arguments.}
  20.     exit
  21. }
  22.  
  23. set input_filename [lindex $argv 0]
  24. set output_filename [lindex $argv 1]
  25.  
  26. proc append_byte {byte} {
  27.     global output_buffer
  28.     lappend output_buffer $byte
  29. }
  30.  
  31. proc append_word {word} {
  32.     global output_buffer
  33.     lappend output_buffer [expr {$word & 0xFF}]
  34.     lappend output_buffer [expr {($word >> 8) & 0xFF}]
  35. }
  36.  
  37. # Если слово является инструкцией процессора, то компилирует его и возвращает true,
  38. # если не является инструкцией процессора, то возвращает false.
  39. proc istr_compile {word} {
  40.     global istr istr_code
  41.     if {$word == "A!"} {
  42.         append_word 24867
  43.         append_word 24835
  44.         return true
  45.     } else {
  46.         set pos [lsearch $istr $word]
  47.         if {$pos < 0} {
  48.             return false
  49.         } else {
  50.             append_word [lindex $istr_code $pos]
  51.             return true
  52.         }
  53.     }
  54. }
  55.  
  56. # Компиляция ранее определенного слова или предопределенного слова (инструкции).
  57. # Если слово есть в словаре, возвращает true, иначе возвращает false.
  58. proc compile_word {word} {
  59.     global dict
  60.     if {[istr_compile $word]} {
  61.         return true
  62.     } else {
  63.         if {[info exists dict($word)]} {
  64.             append_word [expr {($dict($word) >> 1) | 0x4000}]
  65.             return true
  66.         } else {
  67.             return false
  68.         }
  69.     }
  70. }
  71.  
  72. proc compile_literal {number} {
  73.     set num [expr {$number & 0xFFFF}]
  74.     if {$num > 0x7FFF} {
  75.         append_word [expr {~$num | 0x8000}]
  76.         compile_word "NOT"
  77.     } else {
  78.         append_word [expr {$num | 0x8000}]
  79.     }
  80. }
  81.  
  82. proc parse_word {} {
  83.     global input_buffer
  84.     set input_buffer [string trimleft $input_buffer " \t\n"]
  85.     regexp {^([^\s\t\n]*)(.*)} $input_buffer buf word input_buffer
  86.     return $word
  87. }
  88.  
  89. # Адрес начала незанятой памяти.
  90. proc here {} {
  91.     global output_buffer
  92.     return [llength $output_buffer]
  93. }
  94.  
  95. # Формат заголовка:
  96. # выравнивающий байт (при нечетной длине имени)
  97. # имя (побайтно)
  98. # терминирующий нулевой байт
  99. # признак immediate - 1байт (по умолчанию 0)
  100. # адрес предыдущего слова - 2 байта.
  101. proc new_word {name} {
  102.     global dict last_word
  103.     append_byte 0
  104.     if {[string bytelength $name] % 2 != 1} {
  105.         # Выравнивающий байт.
  106.         append_byte 0
  107.     }
  108.     foreach char [split $name {}] {
  109.         append_byte [scan $char %c]
  110.     }
  111.     # Терминирующий байт.
  112.     append_byte 0
  113.     # Байт признака.
  114.     append_byte 0
  115.     # Адрес предыдущего слова.
  116.     append_word $last_word
  117.     set last_word [here]
  118.     # Добавление в словарь.
  119.     set dict($name) [here]
  120. }
  121.  
  122. proc read_word {addr} {
  123.     global output_buffer
  124.     set wordL [lindex $output_buffer $addr]
  125.     set wordH [lindex $output_buffer $addr+1]
  126.     return [expr {$wordL | ($wordH << 8)}]
  127. }
  128.  
  129. proc replace_word {addr word} {
  130.     global output_buffer
  131.     set wordL [expr {$word & 0xFF}]
  132.     set wordH [expr {($word >> 8) & 0xFF}]
  133.     set output_buffer [lreplace $output_buffer $addr $addr+1 $wordL $wordH]
  134. }
  135.  
  136. proc compile_string {str} {
  137.     for {set i 0} {$i < [string length $str]} {incr i; incr i} {
  138.         set chr1 [string index $str $i]; set n1 [expr [scan $chr1 %c]+0]
  139.         set chr2 [string index $str $i+1]; set n2 [expr [scan $chr2 %c]+0]
  140.         append_word [expr {$n1 | ($n2 << 8)}]
  141.     }
  142.     if {[string length $str] % 2 != 1} {append_word 0}
  143. }
  144.  
  145. proc compile_input {} {
  146.     global input_buffer last_word output_buffer dict
  147.     while {[string trim $input_buffer " \t\n"] != {}} {
  148.         set word [parse_word]
  149.         switch $word {
  150.             : {
  151.                 new_word [parse_word]
  152.             }
  153.             IMMEDIATE {
  154.                 # Задает последнему слову признак немедленного исплонения.
  155.                 set pos [expr {$last_word - 3}]
  156.                 set output_buffer [lreplace $output_buffer $pos $pos 1]
  157.             }
  158.             IF {
  159.                 stack push [here]
  160.                 # Опкод условного перехода.
  161.                 append_word 0x2000
  162.             }
  163.             ELSE {
  164.                 stack push [here]
  165.                 stack rotate 2 1
  166.                 # Опкод безусловного перехода.
  167.                 append_word 0
  168.                 set addr [stack pop]
  169.                 set word [expr {[read_word $addr] | ([here] >> 1)}]
  170.                 replace_word $addr $word
  171.             }
  172.             THEN {
  173.                 set addr [stack pop]
  174.                 set word [expr {[read_word $addr] | ([here] >> 1)}]
  175.                 replace_word $addr $word
  176.             }
  177.             BEGIN {
  178.                 stack push [expr {[here] >> 1}]
  179.             }
  180.             AGAIN {
  181.                 append_word [stack pop]
  182.             }
  183.             UNTIL {
  184.                 append_word [expr {[stack pop] | 0x2000}]
  185.             }
  186.             WHILE {
  187.                 stack push [here]
  188.                 append_word 0
  189.             }
  190.             REPEAT {
  191.                 stack rotate 2 1
  192.                 append_word [stack pop]
  193.                 replace_word [stack pop] [expr {([here] >> 1) | 0x2000}]
  194.             }
  195.             "\\" {
  196.                 # Комментарий.
  197.                 regexp {[^\n]*(.*)} $input_buffer buf input_buffer
  198.             }
  199.             Tcl" {
  200.                 regexp {^.([^\"]*)\"(.*)} $input_buffer buf str input_buffer
  201.                 eval $str
  202.             }
  203.             default {
  204.                 if {![compile_word $word]} {
  205.                     if {[string is integer $word]} {
  206.                         compile_literal $word
  207.                     } else {
  208.                         puts "Unknown token: $word"
  209.                         exit
  210.                     }
  211.                 }
  212.             }
  213.         }
  214.     }
  215. }
  216.  
  217. if [catch {open forth.f r} forth_file] {
  218.     puts stderr $forth_file
  219.     exit
  220. } else {
  221.     set input_buffer [read $forth_file]
  222.     close $forth_file
  223. }
  224.  
  225. compile_input
  226.  
  227. if [catch {open $output_filename w} out_file] {
  228.     puts stderr $out_file
  229. } else {
  230.     fconfigure $out_file -translation binary -encoding binary
  231.     set addr 0
  232.     foreach byte $output_buffer {
  233.         incr addr
  234.         puts -nonewline $out_file [binary format c $byte]
  235.     }
  236.     for {set i $addr} {$i < 0x4000} {incr i} {
  237.         puts -nonewline $out_file [binary format c 0]
  238.     }
  239.     close $out_file
  240. }
  241. puts "[llength $output_buffer] bytes"
  242. puts "[array size dict] words"
  243. puts {Done.}
  244.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement