paulb@273 | 1 | #!/usr/bin/env python |
paulb@273 | 2 | |
paulb@273 | 3 | """ |
paulb@273 | 4 | A really simple virtual processor employing a simple set of instructions which |
paulb@273 | 5 | ignore low-level operations and merely concentrate on variable access, structure |
paulb@273 | 6 | access, structure allocation and function invocations. |
paulb@273 | 7 | |
paul@286 | 8 | Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk> |
paul@286 | 9 | |
paul@286 | 10 | This program is free software; you can redistribute it and/or modify it under |
paul@286 | 11 | the terms of the GNU General Public License as published by the Free Software |
paul@286 | 12 | Foundation; either version 3 of the License, or (at your option) any later |
paul@286 | 13 | version. |
paul@286 | 14 | |
paul@286 | 15 | This program is distributed in the hope that it will be useful, but WITHOUT |
paul@286 | 16 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
paul@286 | 17 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
paul@286 | 18 | details. |
paul@286 | 19 | |
paul@286 | 20 | You should have received a copy of the GNU General Public License along with |
paul@286 | 21 | this program. If not, see <http://www.gnu.org/licenses/>. |
paul@286 | 22 | |
paul@286 | 23 | -------- |
paul@286 | 24 | |
paulb@273 | 25 | The execution model involves the following things: |
paulb@273 | 26 | |
paulb@273 | 27 | * Memory |
paulb@273 | 28 | * PC (program counter) stack |
paulb@273 | 29 | * Value stack |
paulb@276 | 30 | * Metadata stack (containing pointers to the value stack) |
paulb@276 | 31 | * Current frame and arguments pointers |
paulb@273 | 32 | |
paulb@273 | 33 | The memory contains constants, global variable references and program code. |
paulb@273 | 34 | |
paulb@273 | 35 | The PC stack contains the return address associated with each function |
paulb@273 | 36 | invocation. |
paulb@273 | 37 | |
paulb@273 | 38 | The value stack contains references to objects that are being manipulated, along |
paulb@273 | 39 | with pointers to previous stack frames. |
paulb@273 | 40 | |
paulb@273 | 41 | The current frame pointer indicates the position of the current stack frame |
paulb@273 | 42 | within the value stack. |
paulb@273 | 43 | |
paulb@273 | 44 | The PC and value stacks have their own pointers which remember the top of the |
paulb@273 | 45 | stack: this is, in fact, the next position to be used by the stack when pushing |
paulb@273 | 46 | a value onto it. |
paulb@273 | 47 | """ |
paulb@273 | 48 | |
paulb@273 | 49 | class IllegalInstruction(Exception): |
paulb@273 | 50 | pass |
paulb@273 | 51 | |
paulb@273 | 52 | class IllegalAddress(Exception): |
paulb@273 | 53 | pass |
paulb@273 | 54 | |
paulb@273 | 55 | class EmptyPCStack(Exception): |
paulb@273 | 56 | pass |
paulb@273 | 57 | |
paulb@276 | 58 | class EmptyMetadataStack(Exception): |
paulb@276 | 59 | pass |
paulb@276 | 60 | |
paulb@273 | 61 | class RSVPMachine: |
paulb@273 | 62 | |
paulb@273 | 63 | "A really simple virtual processor." |
paulb@273 | 64 | |
paulb@273 | 65 | def __init__(self, memory, pc=None, debug=0): |
paulb@273 | 66 | |
paulb@273 | 67 | """ |
paulb@273 | 68 | Initialise the processor with a 'memory' (a list of values containing |
paulb@273 | 69 | instructions and data) and the optional program counter 'pc'. |
paulb@273 | 70 | """ |
paulb@273 | 71 | |
paulb@273 | 72 | self.memory = memory |
paulb@273 | 73 | self.pc = pc or 0 |
paulb@273 | 74 | self.debug = debug |
paulb@273 | 75 | self.pc_stack = [] |
paulb@273 | 76 | self.value_stack = [] |
paulb@276 | 77 | self.metadata_stack = [] |
paulb@273 | 78 | self.current_frame = 0 |
paulb@276 | 79 | self.argument_frame = 0 |
paulb@273 | 80 | |
paulb@273 | 81 | def load(self, address): |
paulb@273 | 82 | |
paulb@273 | 83 | "Return the value at the given 'address'." |
paulb@273 | 84 | |
paulb@273 | 85 | try: |
paulb@273 | 86 | return self.memory[address] |
paulb@273 | 87 | except IndexError: |
paulb@273 | 88 | raise IllegalAddress, address |
paulb@273 | 89 | |
paulb@273 | 90 | def save(self, address, value): |
paulb@273 | 91 | |
paulb@273 | 92 | "Save to the given 'address' the specified 'value'." |
paulb@273 | 93 | |
paulb@273 | 94 | try: |
paulb@273 | 95 | self.memory[address] = value |
paulb@273 | 96 | except IndexError: |
paulb@273 | 97 | raise IllegalAddress, address |
paulb@273 | 98 | |
paulb@273 | 99 | def new(self, size): |
paulb@273 | 100 | |
paulb@273 | 101 | """ |
paulb@273 | 102 | Allocate space of the given 'size', returning the address of the space. |
paulb@273 | 103 | """ |
paulb@273 | 104 | |
paulb@273 | 105 | addr = len(self.memory) |
paulb@273 | 106 | for i in range(0, size): |
paulb@273 | 107 | self.memory.append(None) |
paulb@273 | 108 | return addr |
paulb@273 | 109 | |
paulb@273 | 110 | def push(self, data): |
paulb@273 | 111 | |
paulb@273 | 112 | "Push 'data' onto the value stack." |
paulb@273 | 113 | |
paulb@273 | 114 | self.value_stack.append(data) |
paulb@273 | 115 | |
paulb@273 | 116 | def pull(self): |
paulb@273 | 117 | |
paulb@273 | 118 | "Pull a value from the value stack and return it." |
paulb@273 | 119 | |
paulb@273 | 120 | return self.value_stack.pop() |
paulb@273 | 121 | |
paulb@273 | 122 | def push_pc(self, data): |
paulb@273 | 123 | |
paulb@273 | 124 | "Push 'data' onto the PC stack." |
paulb@273 | 125 | |
paulb@273 | 126 | self.pc_stack.append(data) |
paulb@273 | 127 | |
paulb@273 | 128 | def pull_pc(self): |
paulb@273 | 129 | |
paulb@273 | 130 | "Pull a value from the PC stack and return it." |
paulb@273 | 131 | |
paulb@273 | 132 | try: |
paulb@273 | 133 | return self.pc_stack.pop() |
paulb@273 | 134 | except IndexError: |
paulb@273 | 135 | raise EmptyPCStack |
paulb@276 | 136 | |
paulb@276 | 137 | def push_metadata(self, data): |
paulb@276 | 138 | |
paulb@276 | 139 | "Push 'data' onto the metadata stack." |
paulb@276 | 140 | |
paulb@276 | 141 | self.metadata_stack.append(data) |
paulb@276 | 142 | |
paulb@276 | 143 | def pull_metadata(self): |
paulb@276 | 144 | |
paulb@276 | 145 | "Pull a value from the metadata stack and return it." |
paulb@276 | 146 | |
paulb@276 | 147 | try: |
paulb@276 | 148 | return self.metadata_stack.pop() |
paulb@276 | 149 | except IndexError: |
paulb@276 | 150 | raise EmptyMetadataStack |
paulb@276 | 151 | |
paulb@273 | 152 | def execute(self): |
paulb@273 | 153 | |
paulb@273 | 154 | "Execute code in the memory, starting from the current PC address." |
paulb@273 | 155 | |
paulb@273 | 156 | try: |
paulb@273 | 157 | while 1: |
paulb@273 | 158 | instruction = self.load(self.pc) |
paulb@273 | 159 | if self.debug: |
paulb@273 | 160 | print "%8d %s" % (self.pc, instruction) |
paulb@273 | 161 | method = getattr(self, instruction, None) |
paulb@273 | 162 | if method is None: |
paulb@273 | 163 | raise IllegalInstruction, self.pc |
paulb@273 | 164 | else: |
paulb@273 | 165 | method() |
paulb@273 | 166 | except EmptyPCStack: |
paulb@273 | 167 | pass |
paulb@273 | 168 | |
paulb@273 | 169 | def jump(self, addr, next): |
paulb@273 | 170 | |
paulb@273 | 171 | """ |
paulb@273 | 172 | Jump to the subroutine at (or identified by) 'addr'. If 'addr' |
paulb@273 | 173 | identifies a library function then invoke the library function and set |
paulb@273 | 174 | PC to 'next' afterwards; otherwise, set PC to 'addr'. |
paulb@273 | 175 | """ |
paulb@273 | 176 | |
paulb@273 | 177 | if isinstance(addr, str): |
paulb@273 | 178 | getattr(self, addr)() |
paulb@273 | 179 | self.PSF() |
paulb@273 | 180 | self.pc = next |
paulb@273 | 181 | else: |
paulb@273 | 182 | self.push_pc(self.pc + 2) |
paulb@273 | 183 | self.pc = addr |
paulb@273 | 184 | |
paulb@273 | 185 | # Instructions. |
paulb@273 | 186 | |
paulb@273 | 187 | def SCF(self): |
paulb@273 | 188 | |
paulb@273 | 189 | """ |
paulb@273 | 190 | SCF |
paulb@276 | 191 | Save current stack and argument frames: record the current stack and |
paulb@276 | 192 | argument frame pointers on the value stack; then set the argument frame |
paulb@276 | 193 | pointer to the top of the stack (similar to the next frame produced by |
paulb@276 | 194 | NSF). |
paulb@273 | 195 | """ |
paulb@273 | 196 | |
paulb@276 | 197 | self.push_metadata(self.argument_frame) |
paulb@276 | 198 | self.push_metadata(self.current_frame) |
paulb@276 | 199 | top = len(self.value_stack) |
paulb@276 | 200 | self.argument_frame = top |
paulb@273 | 201 | self.pc += 1 |
paulb@273 | 202 | |
paulb@273 | 203 | def NSF(self): |
paulb@273 | 204 | |
paulb@273 | 205 | """ |
paulb@273 | 206 | NSF #n |
paulb@273 | 207 | Next stack frame of size n: set the current stack frame pointer to the |
paulb@276 | 208 | current top of stack value minus n positions, used for parameter values. |
paulb@273 | 209 | """ |
paulb@273 | 210 | |
paulb@273 | 211 | top = len(self.value_stack) |
paulb@273 | 212 | n = self.load(self.pc + 1) |
paulb@276 | 213 | self.current_frame = top - n |
paulb@276 | 214 | self.argument_frame = 0 |
paulb@273 | 215 | self.pc += 2 |
paulb@273 | 216 | |
paulb@273 | 217 | def PSF(self): |
paulb@273 | 218 | |
paulb@273 | 219 | """ |
paulb@273 | 220 | PSF |
paulb@273 | 221 | Previous stack frame: restore the previous stack frame by reading the |
paulb@273 | 222 | stored stack frame pointer, setting the current stack frame pointer to |
paulb@273 | 223 | that value, and by setting the top of stack to the previous stack frame |
paulb@273 | 224 | pointer. In order to propagate return values, the top of the stack from |
paulb@273 | 225 | before this instruction is restored afterwards. |
paulb@273 | 226 | """ |
paulb@273 | 227 | |
paulb@273 | 228 | result = self.pull() |
paulb@273 | 229 | top = self.current_frame |
paulb@276 | 230 | self.current_frame = self.pull_metadata() |
paulb@276 | 231 | self.argument_frame = self.pull_metadata() |
paulb@276 | 232 | self.value_stack = self.value_stack[:top] # reset stack before SCF |
paulb@273 | 233 | self.push(result) |
paulb@273 | 234 | self.pc += 1 |
paulb@273 | 235 | |
paulb@273 | 236 | def ESF(self): |
paulb@273 | 237 | |
paulb@273 | 238 | """ |
paulb@276 | 239 | ESF #n |
paulb@273 | 240 | Extend stack frame by n values: allocate n positions on the stack, |
paulb@273 | 241 | typically so that local variables can be stored. |
paulb@273 | 242 | """ |
paulb@273 | 243 | |
paulb@273 | 244 | n = self.load(self.pc + 1) |
paulb@273 | 245 | for i in range(0, n): |
paulb@273 | 246 | self.value_stack.append(None) |
paulb@273 | 247 | self.pc += 2 |
paulb@273 | 248 | |
paulb@273 | 249 | def LPF(self): |
paulb@273 | 250 | |
paulb@273 | 251 | """ |
paulb@273 | 252 | LPF #n |
paulb@273 | 253 | Load from position n in frame: get position n from the current stack |
paulb@273 | 254 | frame, push the retrieved value onto the stack. |
paulb@273 | 255 | """ |
paulb@273 | 256 | |
paulb@273 | 257 | n = self.load(self.pc + 1) |
paulb@273 | 258 | self.push(self.value_stack[self.current_frame + n]) |
paulb@273 | 259 | self.pc += 2 |
paulb@273 | 260 | |
paulb@273 | 261 | def SPF(self): |
paulb@273 | 262 | |
paulb@273 | 263 | """ |
paulb@273 | 264 | SPF #n |
paulb@273 | 265 | Save to position n in frame: pull a value from the stack and set it as |
paulb@273 | 266 | position n in the current stack frame. |
paulb@273 | 267 | """ |
paulb@273 | 268 | |
paulb@273 | 269 | n = self.load(self.pc + 1) |
paulb@273 | 270 | self.value_stack[self.current_frame + n] = self.pull() |
paulb@273 | 271 | self.pc += 2 |
paulb@273 | 272 | |
paulb@273 | 273 | def LRM(self): |
paulb@273 | 274 | |
paulb@273 | 275 | """ |
paulb@273 | 276 | LRM #addr |
paulb@273 | 277 | Load the reference to memory: get the address addr, push the value onto |
paulb@273 | 278 | the stack. |
paulb@273 | 279 | |
paulb@273 | 280 | This is useful for loading constants. |
paulb@273 | 281 | """ |
paulb@273 | 282 | |
paulb@273 | 283 | addr = self.load(self.pc + 1) |
paulb@273 | 284 | self.push(addr) |
paulb@273 | 285 | self.pc += 2 |
paulb@273 | 286 | |
paulb@273 | 287 | def LAM(self): |
paulb@273 | 288 | |
paulb@273 | 289 | """ |
paulb@273 | 290 | LAM addr |
paulb@273 | 291 | Load from address addr in memory: get the contents of address addr from |
paulb@273 | 292 | memory, push the retrieved value onto the stack. |
paulb@273 | 293 | |
paulb@273 | 294 | This is useful for loading globals. |
paulb@273 | 295 | """ |
paulb@273 | 296 | |
paulb@273 | 297 | addr = self.load(self.pc + 1) |
paulb@273 | 298 | self.push(self.load(addr)) |
paulb@273 | 299 | self.pc += 2 |
paulb@273 | 300 | |
paulb@273 | 301 | def SAM(self): |
paulb@273 | 302 | |
paulb@273 | 303 | """ |
paulb@273 | 304 | SAM addr |
paulb@273 | 305 | Save to address addr in memory: pull a value from the stack and save it |
paulb@273 | 306 | to address addr in memory. |
paulb@273 | 307 | |
paulb@273 | 308 | This is useful for saving globals. |
paulb@273 | 309 | """ |
paulb@273 | 310 | |
paulb@273 | 311 | addr = self.load(self.pc + 1) |
paulb@273 | 312 | self.save(addr, self.pull()) |
paulb@273 | 313 | self.pc += 2 |
paulb@273 | 314 | |
paulb@273 | 315 | def LPR(self): |
paulb@273 | 316 | |
paulb@273 | 317 | """ |
paulb@273 | 318 | LPR #n |
paulb@273 | 319 | Load from position n in reference: get the contents of position n in the |
paulb@273 | 320 | memory referenced by the value on the top of the stack, replacing the |
paulb@273 | 321 | value on the top of the stack with the retrieved value. |
paulb@273 | 322 | """ |
paulb@273 | 323 | |
paulb@273 | 324 | n = self.load(self.pc + 1) |
paulb@273 | 325 | ref = self.pull() |
paulb@273 | 326 | self.push(self.load(ref + n)) |
paulb@273 | 327 | self.pc += 2 |
paulb@273 | 328 | |
paulb@273 | 329 | def SPR(self): |
paulb@273 | 330 | |
paulb@273 | 331 | """ |
paulb@273 | 332 | SPR #n |
paulb@273 | 333 | Save to position n in reference: pull a value from the stack and save it |
paulb@273 | 334 | to position n in the memory referenced by the next value on the stack, |
paulb@273 | 335 | also removing the next value on the stack. |
paulb@273 | 336 | """ |
paulb@273 | 337 | |
paulb@273 | 338 | n = self.load(self.pc + 1) |
paulb@273 | 339 | value = self.pull() |
paulb@273 | 340 | self.save(self.pull() + n, value) |
paulb@273 | 341 | self.pc += 2 |
paulb@273 | 342 | |
paulb@273 | 343 | def JAS(self): |
paulb@273 | 344 | |
paulb@273 | 345 | """ |
paulb@273 | 346 | JAS addr |
paulb@273 | 347 | Jump to address addr to invoke a subroutine: store the location of the |
paulb@273 | 348 | next instruction on the PC stack, set the address addr in the PC. |
paulb@273 | 349 | """ |
paulb@273 | 350 | |
paulb@273 | 351 | addr = self.load(self.pc + 1) |
paulb@273 | 352 | self.jump(addr, self.pc + 2) |
paulb@273 | 353 | |
paulb@273 | 354 | def RAC(self): |
paulb@273 | 355 | |
paulb@273 | 356 | """ |
paulb@273 | 357 | RAC |
paulb@273 | 358 | Return to the address of the caller: pull a value from the PC stack and |
paulb@273 | 359 | set it in the PC. |
paulb@273 | 360 | """ |
paulb@273 | 361 | |
paulb@273 | 362 | self.pc = self.pull_pc() |
paulb@273 | 363 | |
paulb@273 | 364 | def JAT(self): |
paulb@273 | 365 | |
paulb@273 | 366 | """ |
paulb@273 | 367 | JAT addr |
paulb@273 | 368 | Jump to address addr if true: pull a value from the stack and if it |
paulb@273 | 369 | represents a true value, jump to address addr. |
paulb@273 | 370 | """ |
paulb@273 | 371 | |
paulb@273 | 372 | addr = self.load(self.pc + 1) |
paulb@273 | 373 | value = self.pull() |
paulb@273 | 374 | if value: |
paulb@273 | 375 | self.pc = addr |
paulb@273 | 376 | else: |
paulb@273 | 377 | self.pc += 2 |
paulb@273 | 378 | |
paulb@273 | 379 | def JAF(self): |
paulb@273 | 380 | |
paulb@273 | 381 | """ |
paulb@273 | 382 | JAF addr |
paulb@273 | 383 | Jump to address addr if false: pull a value from the stack and if it |
paulb@273 | 384 | represents a false value, jump to address addr. |
paulb@273 | 385 | """ |
paulb@273 | 386 | |
paulb@273 | 387 | addr = self.load(self.pc + 1) |
paulb@273 | 388 | value = self.pull() |
paulb@273 | 389 | if not value: |
paulb@273 | 390 | self.pc = addr |
paulb@273 | 391 | else: |
paulb@273 | 392 | self.pc += 2 |
paulb@273 | 393 | |
paulb@276 | 394 | def MVA(self): |
paulb@276 | 395 | |
paulb@276 | 396 | """ |
paulb@276 | 397 | MVA #n |
paulb@276 | 398 | Move the value from the top of the stack to the argument in position n: |
paulb@276 | 399 | pull a value from the stack and store it in the argument frame at the |
paulb@276 | 400 | given position. |
paulb@276 | 401 | """ |
paulb@276 | 402 | |
paulb@276 | 403 | pos = self.load(self.pc + 1) |
paulb@276 | 404 | value = self.pull() |
paulb@276 | 405 | self.value_stack[self.argument_frame + pos] = value |
paulb@276 | 406 | self.pc += 2 |
paulb@276 | 407 | |
paulb@273 | 408 | # Library functions. |
paulb@273 | 409 | |
paulb@273 | 410 | def rsvp___printnl(self): |
paulb@273 | 411 | print self.load(self.value_stack[-1]) |
paulb@273 | 412 | |
paulb@273 | 413 | def rsvp___print(self): |
paulb@273 | 414 | print self.load(self.value_stack[-1]), |
paulb@273 | 415 | |
paulb@273 | 416 | def int_____gt__(self): |
paulb@273 | 417 | self.push(self.load(self.value_stack[-2]) > self.load(self.value_stack[-1])) |
paulb@273 | 418 | |
paulb@273 | 419 | def int_____sub__(self): |
paulb@273 | 420 | result = self.load(self.value_stack[-2]) - self.load(self.value_stack[-1]) |
paulb@273 | 421 | addr = self.new(1) |
paulb@273 | 422 | self.push(addr) |
paulb@273 | 423 | self.save(addr, result) |
paulb@273 | 424 | |
paulb@273 | 425 | def int_____mul__(self): |
paulb@273 | 426 | result = self.load(self.value_stack[-2]) * self.load(self.value_stack[-1]) |
paulb@273 | 427 | addr = self.new(1) |
paulb@273 | 428 | self.push(addr) |
paulb@273 | 429 | self.save(addr, result) |
paulb@273 | 430 | |
paulb@273 | 431 | class RSVPAssembler: |
paulb@273 | 432 | |
paulb@273 | 433 | "An assembler for RSVP code." |
paulb@273 | 434 | |
paulb@273 | 435 | def __init__(self, debug=0): |
paulb@273 | 436 | |
paulb@273 | 437 | "Initialise the assembler." |
paulb@273 | 438 | |
paulb@273 | 439 | self.memory = [] |
paulb@273 | 440 | self.label_users = {} |
paulb@273 | 441 | self.labels = {} |
paulb@273 | 442 | self.debug = debug |
paulb@273 | 443 | |
paulb@273 | 444 | def add(self, value, data=None): |
paulb@273 | 445 | |
paulb@273 | 446 | "Add the given 'value' and optional 'data' to the program." |
paulb@273 | 447 | |
paulb@273 | 448 | if self.debug: |
paulb@273 | 449 | if data is None: |
paulb@273 | 450 | print value |
paulb@273 | 451 | else: |
paulb@273 | 452 | print value, data |
paulb@273 | 453 | |
paulb@273 | 454 | self.memory.append(value) |
paulb@273 | 455 | if data is not None: |
paulb@273 | 456 | if isinstance(data, str) and data.startswith("$"): |
paulb@273 | 457 | label_user = len(self.memory) |
paulb@273 | 458 | |
paulb@273 | 459 | # Where a label exists, write an absolute address. |
paulb@273 | 460 | |
paulb@273 | 461 | if self.labels.has_key(data): |
paulb@273 | 462 | self.memory.append(self.labels[data]) |
paulb@273 | 463 | |
paulb@273 | 464 | # Otherwise, add a placeholder value. |
paulb@273 | 465 | |
paulb@273 | 466 | else: |
paulb@273 | 467 | self.memory.append(None) |
paulb@273 | 468 | |
paulb@273 | 469 | # Add the label "user" for relocation purposes. |
paulb@273 | 470 | |
paulb@273 | 471 | if not self.label_users.has_key(data): |
paulb@273 | 472 | self.label_users[data] = [] |
paulb@273 | 473 | self.label_users[data].append(label_user) |
paulb@273 | 474 | |
paulb@273 | 475 | else: |
paulb@273 | 476 | self.memory.append(data) |
paulb@273 | 477 | |
paulb@273 | 478 | def add_many(self, *instructions): |
paulb@273 | 479 | for instruction in instructions: |
paulb@273 | 480 | self.add(*instruction) |
paulb@273 | 481 | |
paulb@273 | 482 | def label(self, name, value=None): |
paulb@273 | 483 | |
paulb@273 | 484 | """ |
paulb@273 | 485 | Define the label with the given 'name'. If the optional 'value' is |
paulb@273 | 486 | specified, associate the label with 'value'; otherwise, associate it |
paulb@273 | 487 | with the next position in the program. |
paulb@273 | 488 | """ |
paulb@273 | 489 | |
paulb@273 | 490 | if self.debug: |
paulb@273 | 491 | if value is None: |
paulb@273 | 492 | print name |
paulb@273 | 493 | else: |
paulb@273 | 494 | print name, "at", value |
paulb@273 | 495 | |
paulb@273 | 496 | if value is None: |
paulb@273 | 497 | value = len(self.memory) |
paulb@273 | 498 | |
paulb@273 | 499 | self.labels[name] = value |
paulb@273 | 500 | for user in self.label_users.get(name, []): |
paulb@273 | 501 | self.memory[user] = value |
paulb@273 | 502 | |
paulb@273 | 503 | def add_labels(self, labels, label_users, offset): |
paulb@273 | 504 | |
paulb@273 | 505 | """ |
paulb@273 | 506 | Add the given 'labels' and 'label_users' to the program, with the |
paulb@273 | 507 | label definitions and users adjusted by the given 'offset'. |
paulb@273 | 508 | """ |
paulb@273 | 509 | |
paulb@273 | 510 | for name, users in label_users.items(): |
paulb@273 | 511 | if not self.label_users.has_key(name): |
paulb@273 | 512 | self.label_users[name] = [] |
paulb@273 | 513 | for user in users: |
paulb@273 | 514 | self.label_users[name].append(user + offset) |
paulb@273 | 515 | |
paulb@273 | 516 | for name, value in labels.items(): |
paulb@273 | 517 | self.label(name, value + offset) |
paulb@273 | 518 | |
paulb@273 | 519 | def merge(self, assembler): |
paulb@273 | 520 | |
paulb@273 | 521 | "Merge the memory and labels of the given 'assembler' with this one." |
paulb@273 | 522 | |
paulb@273 | 523 | label_base = len(self.memory) |
paulb@273 | 524 | self.memory += assembler.memory |
paulb@273 | 525 | self.add_labels(assembler.labels, assembler.label_users, label_base) |
paulb@273 | 526 | |
paul@277 | 527 | def get_reverse_labels(self): |
paul@277 | 528 | return dict([(value, key) for (key, value) in self.labels.items()]) |
paul@277 | 529 | |
paul@277 | 530 | def dump(self): |
paul@277 | 531 | dump(self.memory, self.get_reverse_labels()) |
paul@277 | 532 | |
paul@277 | 533 | def dump(memory, reverse_labels=None): |
paul@277 | 534 | reverse_labels = reverse_labels or {} |
paulb@273 | 535 | for i in range(0, len(memory)): |
paul@277 | 536 | print "%50s %8d %s" % (reverse_labels.get(i, ""), i, memory[i]) |
paulb@273 | 537 | |
paul@277 | 538 | def get_merged(*assemblers): |
paulb@273 | 539 | merged_assembler = RSVPAssembler() |
paulb@273 | 540 | for assembler in assemblers: |
paulb@273 | 541 | merged_assembler.merge(assembler) |
paul@277 | 542 | return merged_assembler |
paulb@273 | 543 | |
paulb@273 | 544 | def test_fact(n, debug=0): |
paulb@273 | 545 | |
paulb@273 | 546 | """ |
paulb@273 | 547 | Assemble the classic factorial function: |
paulb@273 | 548 | def fact(n): |
paulb@273 | 549 | if n > 1: |
paulb@273 | 550 | return n * fact(n - 1) |
paulb@273 | 551 | else: |
paulb@273 | 552 | return 1 |
paulb@273 | 553 | """ |
paulb@273 | 554 | |
paulb@273 | 555 | i_fact = 13 |
paulb@273 | 556 | i_else = 47 |
paulb@273 | 557 | i_n = 51 |
paulb@273 | 558 | i_1 = 52 |
paulb@273 | 559 | memory = ["SCF", "SCF", "LRM", i_n, "NSF", 1, "JAS", i_fact, "NSF", 1, "JAS", "rsvp___print", "RAC"] |
paulb@276 | 560 | memory += ["SCF", "LPF", 0, "LRM", i_1, "NSF", 2, "JAS", "int_____gt__", "JAF", i_else] |
paulb@276 | 561 | memory += ["SCF", "LPF", 0, |
paulb@273 | 562 | "SCF", |
paulb@276 | 563 | "SCF", "LPF", 0, "LRM", i_1, "NSF", 2, "JAS", "int_____sub__", |
paulb@273 | 564 | "NSF", 1, "JAS", i_fact, |
paulb@273 | 565 | "NSF", 2, "JAS", "int_____mul__", |
paulb@273 | 566 | "PSF", "RAC"] |
paulb@273 | 567 | memory += ["LRM", i_1, "PSF", "RAC"] |
paulb@273 | 568 | memory += [n, 1] |
paulb@273 | 569 | |
paulb@273 | 570 | dump(memory) |
paulb@273 | 571 | |
paulb@273 | 572 | proc = RSVPMachine(memory, debug=debug) |
paulb@273 | 573 | proc.execute() |
paulb@273 | 574 | |
paulb@273 | 575 | def test_fact2(n, debug=0): |
paulb@273 | 576 | main_assembler = RSVPAssembler() |
paulb@273 | 577 | main_assembler.add_many( |
paulb@273 | 578 | ("SCF",), ("SCF",), ("LRM", "$n"), ("NSF", 1), ("JAS", "$fact"), ("NSF", 1), ("JAS", "rsvp___print"), ("RAC",) |
paulb@273 | 579 | ) |
paulb@273 | 580 | |
paulb@273 | 581 | fact_assembler = RSVPAssembler() |
paulb@273 | 582 | fact_assembler.label("$fact") |
paulb@273 | 583 | fact_assembler.add_many( |
paulb@276 | 584 | ("SCF",), ("LPF", 0), ("LRM", "$1"), ("NSF", 2), ("JAS", "int_____gt__"), ("JAF", "$else"), # if n > 1: |
paulb@276 | 585 | ("SCF",), ("LPF", 0), # n |
paulb@276 | 586 | ("SCF",), |
paulb@276 | 587 | ("SCF",), ("LPF", 0), ("LRM", "$1"), ("NSF", 2), ("JAS", "int_____sub__"), # n - 1 |
paulb@276 | 588 | ("NSF", 1), ("JAS", "$fact"), # fact(...) |
paulb@276 | 589 | ("NSF", 2), ("JAS", "int_____mul__"), # ... * ... |
paulb@276 | 590 | ("PSF",), ("RAC",) # return ... |
paulb@273 | 591 | ) |
paulb@276 | 592 | fact_assembler.label("$else") # else: |
paulb@273 | 593 | fact_assembler.add_many( |
paulb@276 | 594 | ("LRM", "$1"), ("PSF",), ("RAC",) # return 1 |
paulb@273 | 595 | ) |
paulb@273 | 596 | fact_assembler.label("$n") |
paulb@273 | 597 | fact_assembler.add(n) |
paulb@273 | 598 | fact_assembler.label("$1") |
paulb@273 | 599 | fact_assembler.add(1) |
paulb@273 | 600 | |
paul@277 | 601 | merged = get_merged(main_assembler, fact_assembler) |
paul@277 | 602 | merged.dump() |
paulb@273 | 603 | |
paul@277 | 604 | proc = RSVPMachine(merged.memory, debug=debug) |
paulb@273 | 605 | proc.execute() |
paulb@273 | 606 | |
paulb@273 | 607 | def test_empty(debug=0): |
paulb@273 | 608 | |
paulb@273 | 609 | "Test an empty memory." |
paulb@273 | 610 | |
paulb@273 | 611 | try: |
paulb@273 | 612 | proc = RSVPMachine([], debug=debug) |
paulb@273 | 613 | proc.execute() |
paulb@273 | 614 | except IllegalAddress, exc: |
paulb@273 | 615 | return exc.args[0] == 0 |
paulb@273 | 616 | |
paulb@273 | 617 | if __name__ == "__main__": |
paulb@273 | 618 | print test_empty() |
paulb@273 | 619 | test_fact(3) |
paulb@273 | 620 | test_fact2(3) |
paulb@273 | 621 | |
paulb@273 | 622 | # vim: tabstop=4 expandtab shiftwidth=4 |