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