├── .gitignore ├── 6502-emu.smt2 ├── LICENSE-z3.txt ├── Makefile ├── README.md ├── abstract_machine.h ├── cycles.cpp ├── deps.sh ├── emulator.h ├── emulator2.h ├── enumerator.cpp ├── enumerator2.cpp ├── files.cpp ├── fnv.h ├── gperf.sh ├── ideas.txt ├── include ├── z3++.h ├── z3.h ├── z3_algebraic.h ├── z3_api.h ├── z3_ast_containers.h ├── z3_fixedpoint.h ├── z3_fpa.h ├── z3_interp.h ├── z3_macros.h ├── z3_optimization.h ├── z3_polynomial.h ├── z3_rcf.h ├── z3_spacer.h └── z3_v1.h ├── instructions2.h ├── libz3.so ├── opcode.h ├── operations.h ├── optimize.cpp ├── queue.h ├── radix-sort.h └── random_machine.h /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | enumerator 3 | *.swp 4 | *.swo 5 | *.swn 6 | out 7 | *.tmp 8 | enumerator2 9 | radix-sort 10 | cycles 11 | -------------------------------------------------------------------------------- /6502-emu.smt2: -------------------------------------------------------------------------------- 1 | ; OBSOLETE: this file was meant to be an implementation of 2 | ; a 6502 emulator, but then I discovered that functions 3 | ; defined in a .smt2 file cannot be called using the API. 4 | ; 5 | ; Emulator for 6502 in SMT2 format. 6 | ; This can be used for verifying compiler optimizations. 7 | ; It assumes that the sequence of instructions has one entry point 8 | ; at the first instruction. It stops executing if the sequence 9 | ; exits early because of a branch, jmp, rts, or rti. 10 | ; The only instructions not supported are jsr and brk, because they are 11 | ; implicit entry points. 12 | ; 13 | ; The equivalence checking can ignore variables are not live at the 14 | ; end of the sequence. The assertions assertALive etc. control this 15 | ; behavior. 16 | ; 17 | ; To prove two instruction sequences equivalent, write two functions 18 | ; which apply the instructions to the const someState. Then write 19 | ; (assert (not (sameState (candidate1 someState) (candidate2 someState)))) 20 | ; This says -- there exists some initial machine state someState 21 | ; where candidate1 and candidate2 have different results. If the result 22 | ; is "unsat", then the solver has proven that the two sequences are 23 | ; equivalent for all possible machine states, given the set of registers 24 | ; and flags we care about at the end. 25 | ; If it returns "sat", then use (get-model) to see the initial state which 26 | ; is a counter-example, and use e.g. (eval (candidate1 someState)) to see 27 | ; what the function results in. 28 | 29 | (define-sort Byte () (_ BitVec 8)) 30 | (define-sort Word () (_ BitVec 16)) 31 | 32 | (define-fun ext ((val Byte)) Word 33 | ((_ zero_extend 8) val) 34 | ) 35 | 36 | (define-fun lobyte ((val Word)) Byte 37 | ((_ extract 7 0) val) 38 | ) 39 | 40 | (define-fun hibyte ((val Word)) Byte 41 | ((_ extract 15 8) val) 42 | ) 43 | 44 | (declare-datatypes (U T) ((Either (Left (left U)) 45 | (Right (right T))))) 46 | 47 | (declare-datatypes () ((SpecialExit End Rts Rti))) 48 | 49 | (define-sort Exit () (Either SpecialExit Word)) 50 | 51 | (declare-datatypes () 52 | ((Machine (mk-machine-private 53 | (a Byte) 54 | (x Byte) 55 | (y Byte) 56 | (sp Byte) 57 | (ccS Bool) 58 | (ccV Bool) 59 | (ccI Bool) 60 | (ccD Bool) 61 | (ccC Bool) 62 | (ccZ Bool) 63 | (mem (Array Word Byte)) 64 | (exitLoc Exit)))) 65 | ) 66 | 67 | (define-fun mk-machine2 ((m Machine) 68 | (a_ Byte) 69 | (x_ Byte) 70 | (y_ Byte) 71 | (sp_ Byte) 72 | (s_ Bool) 73 | (v_ Bool) 74 | (i_ Bool) 75 | (d_ Bool) 76 | (c_ Bool) 77 | (z_ Bool) 78 | (mem_ (Array Word Byte)) 79 | (exitLoc_ Exit)) Machine 80 | (ite (= (Left End) (exitLoc m)) 81 | (mk-machine-private a_ x_ y_ sp_ s_ v_ i_ d_ c_ z_ mem_ exitLoc_) 82 | m)) 83 | 84 | (define-fun set-exit ((e Exit) (m Machine)) Machine 85 | (mk-machine2 m (a m) (x m) (y m) (sp m) 86 | (ccS m) (ccV m) (ccI m) (ccD m) (ccC m) (ccZ m) (mem m) e 87 | ) 88 | ) 89 | 90 | (define-fun set-SZ ((val Byte) (m Machine)) Machine 91 | (mk-machine2 m (a m) (x m) (y m) (sp m) 92 | (bvuge val #x80) (ccV m) (ccI m) (ccD m) (ccC m) (= #x00 val) (mem m) (exitLoc m) 93 | ) 94 | ) 95 | 96 | (define-fun set-x ((newX Byte) (m Machine)) Machine 97 | (mk-machine2 m (a m) newX (y m) (sp m) 98 | (ccS m) (ccV m) (ccI m) (ccD m) (ccC m) (ccZ m) (mem m) (exitLoc m) 99 | ) 100 | ) 101 | 102 | (define-fun set-xSZ ((newX Byte) (m Machine)) Machine 103 | (mk-machine2 m (a m) newX (y m) (sp m) 104 | (bvuge newX #x80) (ccV m) (ccI m) (ccD m) (ccC m) (= #x00 newX) (mem m) (exitLoc m) 105 | ) 106 | ) 107 | 108 | (define-fun set-y ((newY Byte) (m Machine)) Machine 109 | (mk-machine2 m (a m) (x m) newY (sp m) 110 | (ccS m) (ccV m) (ccI m) (ccD m) (ccC m) (ccZ m) (mem m) (exitLoc m) 111 | ) 112 | ) 113 | 114 | (define-fun set-ySZ ((newY Byte) (m Machine)) Machine 115 | (mk-machine2 m (a m) (x m) newY (sp m) 116 | (bvuge newY #x80) (ccV m) (ccI m) (ccD m) (ccC m) (= #x00 newY) (mem m) (exitLoc m) 117 | ) 118 | ) 119 | 120 | 121 | (define-fun set-a ((newA Byte) (m Machine)) Machine 122 | (mk-machine2 m newA (x m) (y m) (sp m) 123 | (ccS m) (ccV m) (ccI m) (ccD m) (ccC m) (ccZ m) (mem m) (exitLoc m) 124 | ) 125 | ) 126 | 127 | (define-fun set-aSZ ((newA Byte) (m Machine)) Machine 128 | (mk-machine2 m newA (x m) (y m) (sp m) 129 | (bvuge newA #x80) (ccV m) (ccI m) (ccD m) (ccC m) (= #x00 newA) (mem m) (exitLoc m) 130 | ) 131 | ) 132 | 133 | (define-fun set-sp ((new Byte) (m Machine)) Machine 134 | (mk-machine2 m (a m) (x m) (y m) new 135 | (ccS m) (ccV m) (ccI m) (ccD m) (ccC m) (ccZ m) (mem m) (exitLoc m) 136 | ) 137 | ) 138 | 139 | (define-fun set-ccS ((new Bool) (m Machine)) Machine 140 | (mk-machine2 m (a m) (x m) (y m) (sp m) 141 | new (ccV m) (ccI m) (ccD m) (ccC m) (ccZ m) (mem m) (exitLoc m) 142 | ) 143 | ) 144 | 145 | (define-fun set-ccV ((new Bool) (m Machine)) Machine 146 | (mk-machine2 m (a m) (x m) (y m) (sp m) 147 | (ccS m) new (ccI m) (ccD m) (ccC m) (ccZ m) (mem m) (exitLoc m) 148 | ) 149 | ) 150 | 151 | (define-fun set-ccI ((new Bool) (m Machine)) Machine 152 | (mk-machine2 m (a m) (x m) (y m) (sp m) 153 | (ccS m) (ccV m) new (ccD m) (ccC m) (ccZ m) (mem m) (exitLoc m) 154 | ) 155 | ) 156 | 157 | (define-fun set-ccD ((new Bool) (m Machine)) Machine 158 | (mk-machine2 m (a m) (x m) (y m) (sp m) 159 | (ccS m) (ccV m) (ccI m) new (ccC m) (ccZ m) (mem m) (exitLoc m) 160 | ) 161 | ) 162 | 163 | (define-fun set-ccC ((new Bool) (m Machine)) Machine 164 | (mk-machine2 m (a m) (x m) (y m) (sp m) 165 | (ccS m) (ccV m) (ccI m) (ccD m) new (ccZ m) (mem m) (exitLoc m) 166 | ) 167 | ) 168 | 169 | (define-fun set-ccZ ((new Bool) (m Machine)) Machine 170 | (mk-machine2 m (a m) (x m) (y m) (sp m) 171 | (ccS m) (ccV m) (ccI m) (ccD m) (ccC m) new (mem m) (exitLoc m) 172 | ) 173 | ) 174 | 175 | (define-fun read-byte ((addr Word) (m Machine)) Byte 176 | (select (mem m) addr) 177 | ) 178 | 179 | (define-fun write-byte ((addr Word) (val Byte) (m Machine)) Machine 180 | (mk-machine2 m (a m) (x m) (y m) (sp m) 181 | (ccS m) (ccV m) (ccI m) (ccD m) (ccC m) (ccZ m) (store (mem m) addr val) (exitLoc m) 182 | ) 183 | ) 184 | 185 | (define-fun indirect-x ((addr Byte) (m Machine)) Word 186 | (let ((newAddr (bvadd addr (x m)))) 187 | (bvor 188 | (bvshl 189 | (ext (select (mem m) (ext (bvadd newAddr #x01)))) 190 | #x0008) 191 | (ext (select (mem m) (ext newAddr)))))) 192 | 193 | (define-fun indirect-y ((addr Byte) (m Machine)) Word 194 | (bvadd (ext (y m)) 195 | (bvor 196 | (bvshl 197 | (ext (select (mem m) (ext (bvadd addr #x01)))) 198 | #x0008) 199 | (ext (select (mem m) (ext addr)))))) 200 | 201 | ; todo jmp indirect bug 202 | (define-fun indirect ((addr Word) (m Machine)) Word 203 | (bvor 204 | (bvshl 205 | (ext (select (mem m) (bvadd addr #x0001))) 206 | #x0008) 207 | (ext (select (mem m) addr)))) 208 | 209 | (define-fun absolute-x ((addr Word) (m Machine)) Word 210 | (bvadd (ext (x m)) addr) 211 | ) 212 | 213 | (define-fun absolute-y ((addr Word) (m Machine)) Word 214 | (bvadd (ext (y m)) addr) 215 | ) 216 | 217 | (define-fun zp-x ((addr Byte) (m Machine)) Word 218 | (ext (bvadd addr (x m))) 219 | ) 220 | 221 | (define-fun zp-y ((addr Byte) (m Machine)) Word 222 | (ext (bvadd addr (y m))) 223 | ) 224 | 225 | (define-fun zp ((addr Byte)) Word 226 | (ext addr) 227 | ) 228 | 229 | (define-fun stack ((m Machine)) Word 230 | (bvadd #x0100 (ext (sp m))) 231 | ) 232 | 233 | (declare-const aLive Bool) 234 | (declare-const xLive Bool) 235 | (declare-const yLive Bool) 236 | (declare-const sLive Bool) 237 | (declare-const zLive Bool) 238 | (declare-const cLive Bool) 239 | (declare-const vLive Bool) 240 | 241 | (define-fun sameState ((m1 Machine) (m2 Machine)) Bool 242 | (and 243 | (= (mem m1) (mem m2)) 244 | (= (exitLoc m1) (exitLoc m2)) 245 | (=> aLive (= (a m1) (a m2))) 246 | (=> xLive (= (x m1) (x m2))) 247 | (=> yLive (= (y m1) (y m2))) 248 | (=> sLive (= (ccS m1) (ccS m2))) 249 | (=> vLive (= (ccV m1) (ccV m2))) 250 | (= (ccI m1) (ccI m2)) 251 | (= (ccD m1) (ccD m2)) 252 | (=> cLive (= (ccC m1) (ccC m2))) 253 | (=> zLive (= (ccZ m1) (ccZ m2))) 254 | ) 255 | ) 256 | 257 | (define-fun tax ((m Machine)) Machine 258 | (set-xSZ (a m) m) 259 | ) 260 | 261 | (define-fun txa ((m Machine)) Machine 262 | (set-aSZ (x m) m) 263 | ) 264 | 265 | (define-fun tay ((m Machine)) Machine 266 | (set-ySZ (a m) m) 267 | ) 268 | 269 | (define-fun tya ((m Machine)) Machine 270 | (set-aSZ (y m) m) 271 | ) 272 | 273 | (define-fun inx ((m Machine)) Machine 274 | (set-xSZ (bvadd (x m) #x01) m) 275 | ) 276 | 277 | (define-fun dex ((m Machine)) Machine 278 | (set-xSZ (bvsub (x m) #x01) m) 279 | ) 280 | 281 | (define-fun iny ((m Machine)) Machine 282 | (set-ySZ (bvadd (y m) #x01) m) 283 | ) 284 | 285 | (define-fun dey ((m Machine)) Machine 286 | (set-ySZ (bvsub (y m) #x01) m) 287 | ) 288 | 289 | (define-fun clc ((m Machine)) Machine 290 | (set-ccC false m) 291 | ) 292 | 293 | (define-fun sec ((m Machine)) Machine 294 | (set-ccC true m) 295 | ) 296 | 297 | (define-fun cli ((m Machine)) Machine 298 | (set-ccI false m) 299 | ) 300 | 301 | (define-fun sei ((m Machine)) Machine 302 | (set-ccI true m) 303 | ) 304 | 305 | (define-fun clv ((m Machine)) Machine 306 | (set-ccV false m) 307 | ) 308 | 309 | (define-fun cld ((m Machine)) Machine 310 | (set-ccD false m) 311 | ) 312 | 313 | (define-fun sed ((m Machine)) Machine 314 | (set-ccD true m) 315 | ) 316 | 317 | (define-fun tsx ((m Machine)) Machine 318 | (set-x (sp m) m) 319 | ) 320 | 321 | (define-fun txs ((m Machine)) Machine 322 | (set-sp (x m) m) 323 | ) 324 | 325 | (define-fun nop ((m Machine)) Machine 326 | m 327 | ) 328 | 329 | (define-fun cpxImm ((imm Byte) (m Machine)) Machine 330 | (set-ccC 331 | (bvuge (x m) imm) 332 | (set-ccS 333 | (bvuge (bvsub (x m) imm) #x80) 334 | (set-ccZ (= (x m) imm) m)))) 335 | 336 | (define-fun cpxAbs ((addr Word) (m Machine)) Machine 337 | (cpxImm (read-byte addr m) m) 338 | ) 339 | 340 | (define-fun cmpImm ((imm Byte) (m Machine)) Machine 341 | (set-ccC 342 | (bvuge (a m) imm) 343 | (set-ccS 344 | (bvuge (bvsub (a m) imm) #x80) 345 | (set-ccZ (= (a m) imm) m)))) 346 | 347 | (define-fun cmpAbs ((addr Word) (m Machine)) Machine 348 | (cmpImm (read-byte addr m) m) 349 | ) 350 | 351 | (define-fun cpyImm ((imm Byte) (m Machine)) Machine 352 | (set-ccC 353 | (bvuge (y m) imm) 354 | (set-ccS 355 | (bvuge (bvsub (y m) imm) #x80) 356 | (set-ccZ (= (y m) imm) m)))) 357 | 358 | (define-fun cpyAbs ((addr Word) (m Machine)) Machine 359 | (cpyImm (read-byte addr m) m) 360 | ) 361 | 362 | (define-fun andImm ((imm Byte) (m Machine)) Machine 363 | (set-aSZ (bvand imm (a m)) m) 364 | ) 365 | 366 | (define-fun andAbs ((addr Word) (m Machine)) Machine 367 | (andImm (read-byte addr m) m) 368 | ) 369 | 370 | (define-fun oraImm ((imm Byte) (m Machine)) Machine 371 | (set-aSZ (bvor imm (a m)) m) 372 | ) 373 | 374 | (define-fun oraAbs ((addr Word) (m Machine)) Machine 375 | (oraImm (read-byte addr m) m) 376 | ) 377 | 378 | (define-fun eorImm ((imm Byte) (m Machine)) Machine 379 | (set-aSZ (bvxor imm (a m)) m) 380 | ) 381 | 382 | (define-fun eorAbs ((addr Word) (m Machine)) Machine 383 | (eorImm (read-byte addr m) m) 384 | ) 385 | 386 | (define-fun ldaImm ((imm Byte) (m Machine)) Machine 387 | (set-aSZ imm m) 388 | ) 389 | 390 | (define-fun ldaAbs ((addr Word) (m Machine)) Machine 391 | (ldaImm (read-byte addr m) m) 392 | ) 393 | 394 | (define-fun ldaZp ((addr Byte) (m Machine)) Machine 395 | (ldaAbs (ext addr) m) 396 | ) 397 | 398 | (define-fun ldaZpX ((addr Byte) (m Machine)) Machine 399 | (ldaAbs (zp-x addr m) m) 400 | ) 401 | 402 | (define-fun ldaAbsX ((addr Word) (m Machine)) Machine 403 | (ldaAbs (absolute-x addr m) m) 404 | ) 405 | 406 | (define-fun ldaAbsY ((addr Word) (m Machine)) Machine 407 | (ldaAbs (absolute-y addr m) m) 408 | ) 409 | 410 | (define-fun ldaIndX ((addr Byte) (m Machine)) Machine 411 | (ldaAbs (indirect-x addr m) m) 412 | ) 413 | 414 | (define-fun ldxImm ((imm Byte) (m Machine)) Machine 415 | (set-xSZ imm m) 416 | ) 417 | 418 | (define-fun ldxAbs ((addr Word) (m Machine)) Machine 419 | (ldxImm (read-byte addr m) m) 420 | ) 421 | 422 | (define-fun ldyImm ((imm Byte) (m Machine)) Machine 423 | (set-ySZ imm m) 424 | ) 425 | 426 | (define-fun ldyAbs ((addr Word) (m Machine)) Machine 427 | (ldyImm (read-byte addr m) m) 428 | ) 429 | 430 | (define-fun pla ((m Machine)) Machine 431 | (let ((newM (set-sp (bvadd #x01 (sp m)) m))) 432 | (set-a (read-byte (stack newM) newM) newM) 433 | )) 434 | 435 | (define-fun pha ((m Machine)) Machine 436 | (set-sp (bvadd #x01 (sp m)) (write-byte (stack m) (a m) m)) 437 | ) 438 | 439 | (define-fun get-p ((m Machine)) Byte 440 | (bvor 441 | (ite (ccS m) #x80 #x00) 442 | (ite (ccV m) #x40 #x00) 443 | (ite (ccD m) #x08 #x00) 444 | (ite (ccI m) #x04 #x00) 445 | (ite (ccZ m) #x02 #x00) 446 | (ite (ccC m) #x01 #x00) 447 | ) 448 | ) 449 | 450 | (define-fun set-p ((p Byte) (m Machine)) Machine 451 | (set-ccS 452 | (= #x80 (bvand p #x80)) 453 | (set-ccV 454 | (= #x40 (bvand p #x40)) 455 | (set-ccD 456 | (= #x08 (bvand p #x08)) 457 | (set-ccI 458 | (= #x04 (bvand p #x04)) 459 | (set-ccZ 460 | (= #x02 (bvand p #x02)) 461 | (set-ccC 462 | (= #x01 (bvand p #x01)) 463 | m))))))) 464 | 465 | (define-fun plp ((m Machine)) Machine 466 | (let ((newM (set-sp (bvadd #x01 (sp m)) m))) 467 | (set-p (read-byte (stack newM) newM) newM) 468 | )) 469 | 470 | (define-fun php ((m Machine)) Machine 471 | (set-sp (bvadd #x01 (sp m)) (write-byte (stack m) (get-p m) m)) 472 | ) 473 | 474 | (define-fun staAbs ((addr Word) (m Machine)) Machine 475 | (write-byte addr (a m) m) 476 | ) 477 | 478 | (define-fun stxAbs ((addr Word) (m Machine)) Machine 479 | (write-byte addr (x m) m) 480 | ) 481 | 482 | (define-fun styAbs ((addr Word) (m Machine)) Machine 483 | (write-byte addr (y m) m) 484 | ) 485 | 486 | (define-fun bitAbs ((addr Word) (m Machine)) Machine 487 | (let ((byte (read-byte addr m))) 488 | (set-ccZ 489 | (= #x00 (bvand (a m) byte)) 490 | (set-ccS 491 | (= #x80 (bvand byte #x80)) 492 | (set-ccV 493 | (= #x40 (bvand byte #x40)) 494 | m))))) 495 | 496 | (define-fun rts ((m Machine)) Machine 497 | (set-exit (Left Rts) m) 498 | ) 499 | 500 | (define-fun rti ((m Machine)) Machine 501 | (set-exit (Left Rti) m) 502 | ) 503 | 504 | (define-fun jmp ((addr Word) (m Machine)) Machine 505 | (set-exit (Right addr) m)) 506 | 507 | (define-fun jmpi ((addr Word) (m Machine)) Machine 508 | (set-exit (Right (indirect addr m)) m) 509 | ) 510 | 511 | (define-fun beq ((addr Word) (m Machine)) Machine 512 | (ite (ccZ m) 513 | (set-exit (Right addr) m) 514 | m 515 | ) 516 | ) 517 | (define-fun bne ((addr Word) (m Machine)) Machine 518 | (ite (ccZ m) 519 | m 520 | (set-exit (Right addr) m) 521 | ) 522 | ) 523 | 524 | (define-fun bcs ((addr Word) (m Machine)) Machine 525 | (ite (ccC m) 526 | (set-exit (Right addr) m) 527 | m 528 | ) 529 | ) 530 | (define-fun bcc((addr Word) (m Machine)) Machine 531 | (ite (ccC m) 532 | m 533 | (set-exit (Right addr) m) 534 | ) 535 | ) 536 | 537 | (define-fun bvs ((addr Word) (m Machine)) Machine 538 | (ite (ccV m) 539 | (set-exit (Right addr) m) 540 | m 541 | ) 542 | ) 543 | (define-fun bvc ((addr Word) (m Machine)) Machine 544 | (ite (ccV m) 545 | m 546 | (set-exit (Right addr) m) 547 | ) 548 | ) 549 | 550 | (define-fun bmi ((addr Word) (m Machine)) Machine 551 | (ite (ccS m) 552 | (set-exit (Right addr) m) 553 | m 554 | ) 555 | ) 556 | (define-fun bpl ((addr Word) (m Machine)) Machine 557 | (ite (ccS m) 558 | m 559 | (set-exit (Right addr) m) 560 | ) 561 | ) 562 | 563 | (define-fun incAbs ((addr Word) (m Machine)) Machine 564 | (let ((newVal (bvadd #x01 (read-byte addr m)))) 565 | (write-byte addr newVal (set-SZ newVal m)))) 566 | 567 | (define-fun decAbs ((addr Word) (m Machine)) Machine 568 | (let ((newVal (bvsub (read-byte addr m) #x01))) 569 | (write-byte addr newVal (set-SZ newVal m)))) 570 | 571 | (define-fun aslA ((m Machine)) Machine 572 | (set-ccC 573 | (= #x80 (bvand #x80 (a m))) 574 | (set-aSZ (bvshl (a m) #x01) m) 575 | ) 576 | ) 577 | 578 | (define-fun aslAbs ((addr Word) (m Machine)) Machine 579 | (let ((val (read-byte addr m))) 580 | (set-ccC 581 | (= #x80 (bvand #x80 val)) 582 | (write-byte addr (bvshl val #x01) (set-SZ (bvshl val #x01) m)) 583 | ) 584 | ) 585 | ) 586 | 587 | (define-fun rolA ((m Machine)) Machine 588 | (let ((val (bvor (bvshl (a m) #x01) (ite (ccC m) #x01 #x00)))) 589 | (set-ccC 590 | (= #x80 (bvand #x80 (a m))) 591 | (set-aSZ val m)))) 592 | 593 | (define-fun rolAbs ((addr Word) (m Machine)) Machine 594 | (let ((o_val (read-byte addr m))) 595 | (let ((val (bvor (bvshl o_val #x01) (ite (ccC m) #x01 #x00)))) 596 | (set-ccC 597 | (= #x80 (bvand #x80 o_val)) 598 | (write-byte addr val m))))) 599 | 600 | (define-fun rorA ((m Machine)) Machine 601 | (let ((val (bvor (bvlshr (a m) #x01) (ite (ccC m) #x80 #x00)))) 602 | (set-ccC 603 | (= #x01 (bvand #x01 (a m))) 604 | (set-aSZ val m)))) 605 | 606 | (define-fun rorAbs ((addr Word) (m Machine)) Machine 607 | (let ((o_val (read-byte addr m))) 608 | (let ((val (bvor (bvlshr o_val #x01) (ite (ccC m) #x80 #x00)))) 609 | (set-ccC 610 | (= #x01 (bvand #x01 o_val)) 611 | (write-byte addr val m))))) 612 | 613 | (define-fun lsrA ((m Machine)) Machine 614 | (set-ccC 615 | (= #x01 (bvand #x01 (a m))) 616 | (set-aSZ (bvlshr (a m) #x01) m) 617 | ) 618 | ) 619 | 620 | (define-fun lsrAbs ((addr Word) (m Machine)) Machine 621 | (let ((val (read-byte addr m))) 622 | (set-ccC 623 | (= #x01 (bvand #x01 val)) 624 | (write-byte addr (bvlshr val #x01) (set-SZ (bvlshr val #x01) m)) 625 | ) 626 | ) 627 | ) 628 | 629 | (define-fun adcImm ((imm Byte) (m Machine)) Machine 630 | (let ((sum (bvadd (ext imm) (ext (a m)) (ite (ccC m) #x0001 #x0000)))) 631 | (set-ccV 632 | (= #x80 (bvand (bvxor (lobyte sum) (a m)) (bvxor (lobyte sum) imm) #x80)) 633 | (set-ccC 634 | (= (hibyte sum) #x01) 635 | (set-aSZ (lobyte sum) m)) 636 | ) 637 | ) 638 | ) 639 | 640 | (define-fun adcAbs ((addr Word) (m Machine)) Machine 641 | (adcImm (read-byte addr m) m) 642 | ) 643 | 644 | (define-fun sbcImm ((imm Byte) (m Machine)) Machine 645 | (adcImm (bvnot imm) m) 646 | ) 647 | 648 | (define-fun sbcAbs ((addr Word) (m Machine)) Machine 649 | (sbcImm (read-byte addr m) m) 650 | ) 651 | 652 | (declare-const someState Machine) 653 | (assert (= (Left End) (exitLoc someState))) 654 | 655 | (assert aLive) 656 | (assert xLive) 657 | (assert yLive) 658 | (assert sLive) 659 | (assert zLive) 660 | (assert cLive) 661 | (assert vLive) 662 | 663 | -------------------------------------------------------------------------------- /LICENSE-z3.txt: -------------------------------------------------------------------------------- 1 | Z3 2 | Copyright (c) Microsoft Corporation 3 | All rights reserved. 4 | MIT License 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | enumerator: enumerator.cpp *.h 3 | g++ -Iinclude -g -Wall -O3 -flto -Wno-reorder -std=c++14 -o enumerator enumerator.cpp -L. -lz3 -pthread 4 | 5 | run: enumerator 6 | env LD_LIBRARY_PATH=. ./enumerator 7 | 8 | enumerator2: enumerator2.cpp *.h 9 | g++ -Iinclude -g -Wall -O3 -flto -Wno-reorder -std=c++14 -o enumerator2 enumerator2.cpp -L. -lz3 -lprofiler -pthread 10 | 11 | run2: enumerator2 12 | env LD_LIBRARY_PATH=. ./enumerator2 $(ARGS) 13 | 14 | asm: 15 | g++ -Iinclude -g -Wall -O3 -flto -Wno-reorder -std=c++14 -save-temps -fverbose-asm enumerator2.cpp -L. -lz3 -lprofiler -pthread 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # A peephole superoptimizer for the 6502 3 | 4 | A peephole superoptimizer uses superoptimization techniques 5 | to automatically discover replacement patterns for instruction 6 | sequences. See [this paper](https://theory.stanford.edu/~aiken/publications/papers/asplos06.pdf) for an overview of the idea and an application 7 | to x86 processors. 8 | 9 | The original paper has these (simplified) steps: 10 | 11 | 1. Harvest sequences from existing binaries to find sequences 12 | that are good targets for optimization. 13 | 2. Run the sequences a few times to get a fingerprint of their behavior. 14 | 3. Run all of the instruction sequences up to a certain length and 15 | get finderprints of their behavior. 16 | 4. Use a theorem prover to check sequences with the same fingerprint 17 | to see if they are actually equivalent. 18 | 19 | In 2006, they were able to harvest millions of sequences, and enumerate 20 | all of the sequences up to 3 opcodes. With the benefit of 12 years of 21 | processor improvements and a simpler instruction set in 6502, I hope to 22 | reach at least length 4. Right now I am skipping the harvest step -- 23 | I don't have a good corpus of permissively licensed binaries for the 6502. 24 | Instead I am checking equivalence between the enumerated instructions. 25 | 26 | ## Machine model 27 | 28 | Not all machine instructions are supported. The main limitation is that jumps and branches must jump outside of the sequence (no loops). This also means that the jsr and brk instructions are not supported, because they have an unknown effect on the machine state before returning. 29 | 30 | The 6502 has 16-bit addresses, as well as special instructions to access the zero-page 0x0000 to 0x00FF. In an instruction sequence, there can be 3 possibly different 16-bit addresses labeled Absolute0 to Absolute2, and 4 possibly different zero-page addresses labeled Zp0 to Zp3. For instructions that take immediate arguments, there are two 8-bit immediates C0 and C1 which have arbitrary values, and operands can include 0, 1, C0, C1, C0+1, C1+1, and C0+C1. This can find some constant folding optimizations. 31 | 32 | Currently, all of memory is considered when determining equivalence. This does not model the hardware stack well -- once a value is popped, it should be treated as possibly dead, because an interrupt might override it. We should immediately change it to an arbitrary value again. After running, we should ignore values off the top of the stack for equivalence, to find optimizations that eliminate spills to the stack. A software stack could also be added. Accessing the hardware stack would look like `lda Stack, x`, and Stack would act like the other absolute variables. Values popped from the software stack won't immediately decay to unknown, but won't be counted when checking equivalence. 33 | 34 | ## Techniques 35 | 36 | Performing checks with the theorem prover is very expensive, so there are several techniques to eliminate them: 37 | 38 | 1. Finger-printing: Test each sequence on a small number of inital machine states, and hash the results. This quick test will group together all instruction sequences which might be equivalent. The original paper was optimizing for the same machine, so it could execute the instruction sequences on the processor. It sandboxed memory accesses to wrap within a 256 byte range. We are cross-compiling, so the code is emulated. We give it access to an entire virtual memory space of 64k, but we use a hash function to implement read for the initial state and save only the addresses written to. 39 | 2. Canonicalization: Instruction sequences with the same shape but different names for the unknowns can be considered together -- simply rename the first absolute address to Absolute0, the first zero-page address to Zp0, etc. The original paper enumerated only the canonical sequences, and then found the hash that would result from each possible variant. In this implementation, the enumeration is a small percentage of the runtime, so all sequences are generated, but when finding optimizations, only we only check canonical sequences against the other sequences with the same fingerprint. 40 | 3. Pruning during enumeration: If any subsequence of a sequence is known to be non-optimal, then skip the sequence. In order to make the most effective use of this, the enumerator will have to be changed to enumerate in cost order instead of number of instructions. 41 | 3. Only compare if there are possible gains. If all of the sequences in a group are the same, skip the group. Sort each sequence by cost, then only compare sequences with sequences that are cheaper. The orignal paper used the execution of test machine states to approximate the time cost of each sequence. The 6502 has a simpler execution model where most instructions have fixed cycle cost, and some have a 1 or 2 cycle penalty based on runtime conditions, like taking a branch or crossing a page boundary. This allows a simple cost to be assigned to each instruction, with possible cycle penalties represented as fractions of a cycle. The cost of a sequence is simply the sum of cost of the instructions. In the future, the solver could be used to find cases where the penalties must or cannot happen. Besides time, other models like code size should have some weight. 42 | 4. Operand masks: Each instruction sequences uses the unknown operands Absolute0, Zp0, etc. The candidate cheaper sequence cannot use more operands than were provided in the input, so if there are any, skip that sequence. 43 | 5. Future work: data flow checks. One useful fact is that if a sequence has an output -- either register, processor flag, or memory location -- that it never takes as an input, then it must change that output for some inputs. It cannot preserve it in all cases. That means we only need to check sequences that also have that output. 44 | 45 | Here are some sample replacement templates: 46 | 47 | ``` 48 | lsr Absolute0; bmi Absolute1 -> lsr Absolute0 49 | ``` 50 | 51 | ``` 52 | clc; sec; -> sec 53 | ``` 54 | 55 | etc. 56 | -------------------------------------------------------------------------------- /abstract_machine.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "z3++.h" 5 | 6 | z3::expr mkArray(z3::context& c) { 7 | z3::sort byte = c.bv_sort(8); 8 | z3::sort word = c.bv_sort(16); 9 | z3::sort array = c.array_sort(word, byte); 10 | return c.constant("memory", array); 11 | } 12 | 13 | /** 14 | * This struct represents a 6502 processor which symbolically 15 | * executes its instructions. It uses the z3 API to provide 16 | * symbolic execution. 17 | */ 18 | struct abstract_machine { 19 | 20 | /** 21 | * Creates a new abstract_machine. Pass in the z3 context. 22 | * The initial state of the machine has everything as an 23 | * arbitrary unknown value. _earlyExit is set to assume the 24 | * control flow exits normally. 25 | */ 26 | abstract_machine(z3::context& c) : 27 | c(c), 28 | falsy(c.bool_val(false)), 29 | truthy(c.bool_val(true)), 30 | _a(c.bv_const("a", 8)), 31 | _x(c.bv_const("x", 8)), 32 | _y(c.bv_const("y", 8)), 33 | _sp(c.bv_const("sp", 8)), 34 | _ccS(c.bool_const("ccS")), 35 | _ccV(c.bool_const("ccV")), 36 | _ccI(c.bool_const("ccI")), 37 | _ccD(c.bool_const("ccD")), 38 | _ccC(c.bool_const("ccC")), 39 | _ccZ(c.bool_const("ccZ")), 40 | _memory(mkArray(c)), 41 | _earlyExit(c.num_val(0, c.bv_sort(17))) { 42 | std::string absoluteName("absolute"); 43 | for (int i = 0; i < 16; i++) { 44 | absoluteVars.push_back(c.bv_const((absoluteName + std::to_string(i)).c_str(), 16)); 45 | } 46 | 47 | std::string immediateName("immedate"); 48 | for (int i = 0; i < 16; i++) { 49 | immediateVars.push_back(c.bv_const((immediateName + std::to_string(i)).c_str(), 8)); 50 | } 51 | 52 | std::string zpName("zp"); 53 | for (int i = 0; i < 16; i++) { 54 | zpVars.push_back(c.bv_const((zpName + std::to_string(i)).c_str(), 8)); 55 | } 56 | } 57 | 58 | std::vector absoluteVars; 59 | std::vector immediateVars; 60 | std::vector zpVars; 61 | 62 | z3::expr absolute(uint8_t number) { 63 | return absoluteVars.at(number); 64 | } 65 | 66 | z3::expr immediate(uint8_t number) { 67 | return immediateVars.at(number); 68 | } 69 | 70 | z3::expr zp(uint8_t number) { 71 | return zpVars.at(number); 72 | } 73 | 74 | z3::expr constant(uint8_t number) { 75 | return c.bv_val(number, 8); 76 | } 77 | 78 | void simplify() { 79 | _a = _a.simplify(); 80 | _x = _x.simplify(); 81 | _y = _y.simplify(); 82 | _sp = _sp.simplify(); 83 | _ccC = _ccC.simplify(); 84 | _ccV = _ccV.simplify(); 85 | _ccI = _ccI.simplify(); 86 | _ccS = _ccS.simplify(); 87 | _ccD = _ccD.simplify(); 88 | _ccZ = _ccZ.simplify(); 89 | _memory = _memory.simplify(); 90 | _earlyExit = _earlyExit.simplify(); 91 | } 92 | 93 | /** 94 | * Runs a single instruction. 95 | */ 96 | void instruction(instruction op) { 97 | emulator emu; 98 | emu.instruction(*this, op); 99 | } 100 | 101 | /** 102 | * Writes the val to the addr given. Returns the current memory array. 103 | */ 104 | z3::expr write(z3::expr const & addr, z3::expr const & val) { 105 | return _memory = E(z3::store(_memory, addr, val),_memory); 106 | } 107 | 108 | /** 109 | * Reads the value at addr and returns it as an 8-bit bitvector. 110 | */ 111 | z3::expr read(z3::expr const & addr) { 112 | return z3::select(_memory, addr); 113 | } 114 | 115 | /** 116 | * Takes an 8-bit bitvector and zero-extends it to a 16-bit bv. 117 | */ 118 | z3::expr extend(z3::expr const & val) { 119 | Z3_ast r = Z3_mk_zero_ext(c, 8, val); 120 | return z3::expr(val.ctx(), r); 121 | } 122 | 123 | /** 124 | * If-then-else. If c, then t, else e. 125 | */ 126 | z3::expr ite(z3::expr const & c, z3::expr const & t, z3::expr const & e) const { 127 | z3::check_context(c, t); z3::check_context(c, e); 128 | assert(c.is_bool()); 129 | Z3_ast r = Z3_mk_ite(c.ctx(), c, t, e); 130 | c.check_error(); 131 | return z3::expr(c.ctx(), r); 132 | } 133 | 134 | /** 135 | * If-then-else with 8-bit literals instead of `z3::expr`s. 136 | */ 137 | z3::expr ite(z3::expr const & cond, uint8_t t, uint8_t e) const { 138 | return ite(cond, c.num_val(t, _a.get_sort()), c.num_val(e, _a.get_sort())); 139 | } 140 | 141 | /** 142 | * Shifts the input left by 1. 143 | */ 144 | z3::expr shl(z3::expr const & val) const { 145 | return z3::to_expr(val.ctx(), Z3_mk_bvshl(val.ctx(), val, val.ctx().num_val(1, val.get_sort()))); 146 | } 147 | 148 | /** 149 | * Shifts the input right by 1 (logical shift right). 150 | */ 151 | z3::expr shr(z3::expr const & val) const { 152 | return z3::to_expr(val.ctx(), Z3_mk_bvlshr(val.ctx(), val, val.ctx().num_val(1, val.get_sort()))); 153 | } 154 | 155 | /** 156 | * Extracts the low byte of the 16-bit bv. 157 | */ 158 | z3::expr lobyte(z3::expr const & val) const { 159 | return val.extract(7, 0); 160 | } 161 | 162 | /** 163 | * Extracts the high byte of the 16-bit bv. 164 | */ 165 | z3::expr hibyte(z3::expr const & val) const { 166 | return val.extract(15, 8); 167 | } 168 | 169 | /** 170 | * Unsigned greater-than or equal. 171 | */ 172 | z3::expr uge(z3::expr const & first, z3::expr const & second) { 173 | return z3::uge(first, second); 174 | } 175 | 176 | /** 177 | * Unsigned greater-than or equal, with a constant. 178 | */ 179 | z3::expr uge(z3::expr const & first, uint8_t second) { 180 | return z3::uge(first, second); 181 | } 182 | 183 | /** 184 | * Causes the machine to exit early with an rts. 185 | */ 186 | void rts() { 187 | earlyExit(c.num_val(0x00001, _earlyExit.get_sort())); 188 | } 189 | 190 | /** 191 | * Causes the machine to exit early with an rti. 192 | */ 193 | void rti() { 194 | earlyExit(c.num_val(0x00002, _earlyExit.get_sort())); 195 | } 196 | 197 | /** 198 | * Causes the machine to exit early with a jump to the target. 199 | */ 200 | void jmp(z3::expr const & target) { 201 | Z3_ast r = Z3_mk_zero_ext(c, 1, target); 202 | z3::expr target2(c, r); 203 | earlyExit(target2 | 0x10000); 204 | } 205 | 206 | /** 207 | * Causes the machine to exit early with a branch if cond is true. 208 | */ 209 | void branch(z3::expr cond, z3::expr const & target) { 210 | Z3_ast r = Z3_mk_zero_ext(c, 1, target); 211 | z3::expr target2(c, r); 212 | earlyExit(ite(cond, target2, _earlyExit)); 213 | } 214 | 215 | /** 216 | * Sets the sign and zero flags based on the sign and value of 217 | * val, then returns val. 218 | */ 219 | z3::expr setSZ(z3::expr const & val) { 220 | ccS(z3::uge(val, 0x80)); 221 | ccZ(val == 0); 222 | return val; 223 | } 224 | 225 | z3::context& c; 226 | 227 | // The boolean values, as z3 exprs. 228 | const z3::expr falsy; 229 | const z3::expr truthy; 230 | 231 | /** 232 | * If the machine exits early because of a branch, rts, jmp, etc., 233 | * then we need to freeze the state of the machine. To accomplish this, 234 | * all assignments to the machine state should be guarded with e.g. 235 | * 236 | * ``` 237 | * _a = E(new_val, _a); 238 | * ``` 239 | * 240 | * Returns the new_val if the machine hasn't exited early, 241 | * otherwise the same value as the original. 242 | */ 243 | z3::expr E(z3::expr new_val, z3::expr same) const { 244 | return ite(0x0 == _earlyExit, new_val, same); 245 | } 246 | 247 | // The internal state of the machine. 248 | z3::expr _a; z3::expr a(z3::expr const & val) { return _a = E(val, _a); } 249 | z3::expr _x; z3::expr x(z3::expr const & val) { return _x = E(val, _x); } 250 | z3::expr _y; z3::expr y(z3::expr const & val) { return _y = E(val, _y); } 251 | z3::expr _sp; z3::expr sp(z3::expr const & val) { return _sp = E(val, _sp); } 252 | z3::expr _ccS; z3::expr ccS(z3::expr const & val) { return _ccS = E(val, _ccS); } 253 | z3::expr _ccV; z3::expr ccV(z3::expr const & val) { return _ccV = E(val, _ccV); } 254 | z3::expr _ccI; z3::expr ccI(z3::expr const & val) { return _ccI = E(val, _ccI); } 255 | z3::expr _ccD; z3::expr ccD(z3::expr const & val) { return _ccD = E(val, _ccD); } 256 | z3::expr _ccC; z3::expr ccC(z3::expr const & val) { return _ccC = E(val, _ccC); } 257 | z3::expr _ccZ; z3::expr ccZ(z3::expr const & val) { return _ccZ = E(val, _ccZ); } 258 | 259 | z3::expr _memory; 260 | 261 | /** 262 | * _earlyExit can be 0 for normal flow through the end of the 263 | * instructions, 1 for exit by rts, 2 for exit by rti, or 264 | * (0x10000 | addr) for an exit by jump or branch to an address. 265 | */ 266 | z3::expr _earlyExit; z3::expr earlyExit(z3::expr const & val) { return _earlyExit = E(val, _earlyExit); } 267 | }; 268 | -------------------------------------------------------------------------------- /cycles.cpp: -------------------------------------------------------------------------------- 1 | #include "stdint.h" 2 | #include 3 | #include 4 | #include 5 | 6 | enum class instruction_name { 7 | NONE = 0, 8 | ADC = 1, 9 | AND = 2, 10 | ASL = 3, 11 | BCC = 4, 12 | BCS = 5, 13 | BEQ = 6, 14 | BIT = 7, 15 | BMI = 8, 16 | BNE = 9, 17 | BPL = 10, 18 | BRK = 11, 19 | BVC = 12, 20 | BVS = 13, 21 | CLC = 14, 22 | CLD = 15, 23 | CLI = 16, 24 | CLV = 17, 25 | CMP = 18, 26 | CPX = 19, 27 | CPY = 20, 28 | DEC = 21, 29 | DEX = 22, 30 | DEY = 23, 31 | EOR = 24, 32 | INC = 25, 33 | INX = 26, 34 | INY = 27, 35 | JMP = 28, 36 | JSR = 29, 37 | LDA = 30, 38 | LDX = 31, 39 | LDY = 32, 40 | LSR = 33, 41 | NOP = 34, 42 | ORA = 35, 43 | PHA = 36, 44 | PHP = 37, 45 | PLA = 38, 46 | PLP = 39, 47 | ROL = 40, 48 | ROR = 41, 49 | RTI = 42, 50 | RTS = 43, 51 | SBC = 44, 52 | SEC = 45, 53 | SED = 46, 54 | SEI = 47, 55 | STA = 48, 56 | STX = 49, 57 | STY = 50, 58 | TAX = 51, 59 | TAY = 52, 60 | TSX = 53, 61 | TXA = 54, 62 | TXS = 55, 63 | TYA = 56, 64 | 65 | JSRI = 57, 66 | LSRA = 58, 67 | ASLA = 59, 68 | ROLA = 60, 69 | RORA = 61, 70 | }; 71 | 72 | enum class addr_mode { 73 | NONE = 0, 74 | ABSOLUTE = 1, 75 | ABSOLUTE_X = 2, 76 | ABSOLUTE_Y = 3, 77 | X_INDIRECT = 4, 78 | INDIRECT_Y = 5, 79 | ZERO_PAGE = 6, 80 | ZERO_PAGE_X = 7, 81 | ZERO_PAGE_Y = 8, 82 | IMMEDIATE = 9, 83 | }; 84 | 85 | typedef struct instruction { 86 | uint16_t data; 87 | 88 | instruction(): data(0) {} 89 | 90 | instruction(instruction_name name, addr_mode mode, uint8_t number) { 91 | data = (uint16_t)name; 92 | data <<= 4; 93 | data |= (uint16_t)mode; 94 | data <<= 4; 95 | data |= number; 96 | } 97 | 98 | instruction_name name() { 99 | return (instruction_name)((data & 0xFF00) >> 8); 100 | } 101 | 102 | addr_mode mode() { 103 | return (addr_mode)((data & 0xF0) >> 4); 104 | } 105 | 106 | uint8_t number() { 107 | return data & 0xF; 108 | } 109 | 110 | } instruction; 111 | 112 | typedef struct instruction_info { 113 | instruction ins; 114 | uint8_t cycles; 115 | uint8_t bytes; 116 | 117 | bool operator<(const instruction_info &other) const { 118 | if (cycles < other.cycles) { return true; } 119 | else if (cycles == other.cycles) { return bytes < other.bytes; } 120 | return false; 121 | } 122 | } instruction_info; 123 | 124 | typedef struct instruction_seq { 125 | uint8_t cycles; 126 | uint8_t bytes; 127 | instruction instructions[7]; 128 | 129 | instruction_seq(uint8_t c, uint8_t b) : cycles(c), bytes(b) { 130 | for (int i = 0; i < 7; i++) { 131 | instructions[i] = instruction(instruction_name::NONE, addr_mode::NONE, 0); 132 | } 133 | } 134 | 135 | instruction_seq add(instruction_info info) { 136 | instruction_seq newSeq(this->cycles + info.cycles, this->bytes + info.bytes); 137 | for (int i = 0; i < 7; i++) { 138 | if (this->instructions[i].name() == instruction_name::NONE) { 139 | newSeq.instructions[i] = info.ins; 140 | break; 141 | } else { 142 | newSeq.instructions[i] = this->instructions[i]; 143 | } 144 | } 145 | return newSeq; 146 | } 147 | 148 | bool operator>(const instruction_seq &other) const { 149 | if (cycles > other.cycles) { return true; } 150 | else if (cycles == other.cycles) { return bytes > other.bytes; } 151 | return false; 152 | } 153 | } instruction_seq; 154 | 155 | static instruction_info instructions[] = { 156 | { instruction(instruction_name::ADC, addr_mode::IMMEDIATE, 0), 20, 2 }, 157 | { instruction(instruction_name::ADC, addr_mode::ZERO_PAGE, 0), 30, 2 }, 158 | { instruction(instruction_name::ADC, addr_mode::ZERO_PAGE_X, 0), 40, 2 }, 159 | { instruction(instruction_name::ADC, addr_mode::ABSOLUTE, 0), 40, 3 }, 160 | { instruction(instruction_name::ADC, addr_mode::ABSOLUTE_X, 0), 41, 3 }, 161 | { instruction(instruction_name::ADC, addr_mode::ABSOLUTE_Y, 0), 41, 3 }, 162 | { instruction(instruction_name::ADC, addr_mode::X_INDIRECT, 0), 60, 2 }, 163 | { instruction(instruction_name::ADC, addr_mode::INDIRECT_Y, 0), 51, 2 }, 164 | 165 | { instruction(instruction_name::AND, addr_mode::IMMEDIATE, 0), 20, 2 }, 166 | { instruction(instruction_name::AND, addr_mode::ZERO_PAGE, 0), 30, 2 }, 167 | { instruction(instruction_name::AND, addr_mode::ZERO_PAGE_X, 0), 40, 2 }, 168 | { instruction(instruction_name::AND, addr_mode::ABSOLUTE, 0), 40, 3 }, 169 | { instruction(instruction_name::AND, addr_mode::ABSOLUTE_X, 0), 41, 3 }, 170 | { instruction(instruction_name::AND, addr_mode::ABSOLUTE_Y, 0), 41, 3 }, 171 | { instruction(instruction_name::AND, addr_mode::X_INDIRECT, 0), 60, 2 }, 172 | { instruction(instruction_name::AND, addr_mode::INDIRECT_Y, 0), 51, 2 }, 173 | 174 | { instruction(instruction_name::ASLA, addr_mode::NONE, 0), 20, 1 }, 175 | { instruction(instruction_name::ASL, addr_mode::ZERO_PAGE, 0), 50, 2 }, 176 | { instruction(instruction_name::ASL, addr_mode::ZERO_PAGE_X, 0), 60, 2 }, 177 | { instruction(instruction_name::ASL, addr_mode::ABSOLUTE, 0), 60, 3 }, 178 | { instruction(instruction_name::ASL, addr_mode::ABSOLUTE_X, 0), 70, 3 }, 179 | 180 | { instruction(instruction_name::BIT, addr_mode::ZERO_PAGE, 0), 30, 2 }, 181 | { instruction(instruction_name::BIT, addr_mode::ABSOLUTE, 0), 40, 3 }, 182 | 183 | { instruction(instruction_name::BPL, addr_mode::ABSOLUTE, 0), 25, 2 }, 184 | { instruction(instruction_name::BMI, addr_mode::ABSOLUTE, 0), 25, 2 }, 185 | { instruction(instruction_name::BVC, addr_mode::ABSOLUTE, 0), 25, 2 }, 186 | { instruction(instruction_name::BVS, addr_mode::ABSOLUTE, 0), 25, 2 }, 187 | { instruction(instruction_name::BCC, addr_mode::ABSOLUTE, 0), 25, 2 }, 188 | { instruction(instruction_name::BCS, addr_mode::ABSOLUTE, 0), 25, 2 }, 189 | { instruction(instruction_name::BNE, addr_mode::ABSOLUTE, 0), 25, 2 }, 190 | { instruction(instruction_name::BEQ, addr_mode::ABSOLUTE, 0), 25, 2 }, 191 | 192 | // { instruction(instruction_name::BRK, addr_mode::NONE, 0), 70, 2 }, 193 | 194 | { instruction(instruction_name::CMP, addr_mode::IMMEDIATE, 0), 20, 2 }, 195 | { instruction(instruction_name::CMP, addr_mode::ZERO_PAGE, 0), 30, 2 }, 196 | { instruction(instruction_name::CMP, addr_mode::ZERO_PAGE_X, 0), 40, 2 }, 197 | { instruction(instruction_name::CMP, addr_mode::ABSOLUTE, 0), 40, 3 }, 198 | { instruction(instruction_name::CMP, addr_mode::ABSOLUTE_X, 0), 41, 3 }, 199 | { instruction(instruction_name::CMP, addr_mode::ABSOLUTE_Y, 0), 41, 3 }, 200 | { instruction(instruction_name::CMP, addr_mode::X_INDIRECT, 0), 60, 2 }, 201 | { instruction(instruction_name::CMP, addr_mode::INDIRECT_Y, 0), 51, 2 }, 202 | 203 | { instruction(instruction_name::CPX, addr_mode::IMMEDIATE, 0), 20, 2 }, 204 | { instruction(instruction_name::CPX, addr_mode::ZERO_PAGE, 0), 30, 2 }, 205 | { instruction(instruction_name::CPX, addr_mode::ABSOLUTE, 0), 40, 3 }, 206 | 207 | { instruction(instruction_name::CPY, addr_mode::IMMEDIATE, 0), 20, 2 }, 208 | { instruction(instruction_name::CPY, addr_mode::ZERO_PAGE, 0), 30, 2 }, 209 | { instruction(instruction_name::CPY, addr_mode::ABSOLUTE, 0), 40, 3 }, 210 | 211 | { instruction(instruction_name::DEC, addr_mode::ZERO_PAGE, 0), 50, 2 }, 212 | { instruction(instruction_name::DEC, addr_mode::ZERO_PAGE_X, 0), 60, 2 }, 213 | { instruction(instruction_name::DEC, addr_mode::ABSOLUTE, 0), 60, 3 }, 214 | { instruction(instruction_name::DEC, addr_mode::ABSOLUTE_X, 0), 70, 3 }, 215 | 216 | { instruction(instruction_name::EOR, addr_mode::IMMEDIATE, 0), 20, 2 }, 217 | { instruction(instruction_name::EOR, addr_mode::ZERO_PAGE, 0), 30, 2 }, 218 | { instruction(instruction_name::EOR, addr_mode::ZERO_PAGE_X, 0), 40, 2 }, 219 | { instruction(instruction_name::EOR, addr_mode::ABSOLUTE, 0), 40, 3 }, 220 | { instruction(instruction_name::EOR, addr_mode::ABSOLUTE_X, 0), 41, 3 }, 221 | { instruction(instruction_name::EOR, addr_mode::ABSOLUTE_Y, 0), 41, 3 }, 222 | { instruction(instruction_name::EOR, addr_mode::X_INDIRECT, 0), 60, 2 }, 223 | { instruction(instruction_name::EOR, addr_mode::INDIRECT_Y, 0), 51, 2 }, 224 | 225 | { instruction(instruction_name::CLC, addr_mode::NONE, 0), 20, 1 }, 226 | { instruction(instruction_name::SEC, addr_mode::NONE, 0), 20, 1 }, 227 | { instruction(instruction_name::CLI, addr_mode::NONE, 0), 20, 1 }, 228 | { instruction(instruction_name::SEI, addr_mode::NONE, 0), 20, 1 }, 229 | { instruction(instruction_name::CLV, addr_mode::NONE, 0), 20, 1 }, 230 | { instruction(instruction_name::CLD, addr_mode::NONE, 0), 20, 1 }, 231 | { instruction(instruction_name::SED, addr_mode::NONE, 0), 20, 1 }, 232 | 233 | { instruction(instruction_name::INC, addr_mode::ZERO_PAGE, 0), 50, 2 }, 234 | { instruction(instruction_name::INC, addr_mode::ZERO_PAGE_X, 0), 60, 2 }, 235 | { instruction(instruction_name::INC, addr_mode::ABSOLUTE, 0), 60, 3 }, 236 | { instruction(instruction_name::INC, addr_mode::ABSOLUTE_X, 0), 70, 3 }, 237 | 238 | { instruction(instruction_name::JMP, addr_mode::ABSOLUTE, 0), 30, 3 }, 239 | { instruction(instruction_name::JSR, addr_mode::ABSOLUTE, 0), 60, 3 }, 240 | 241 | { instruction(instruction_name::LDA, addr_mode::IMMEDIATE, 0), 20, 2 }, 242 | { instruction(instruction_name::LDA, addr_mode::ZERO_PAGE, 0), 30, 2 }, 243 | { instruction(instruction_name::LDA, addr_mode::ZERO_PAGE_X, 0), 40, 2 }, 244 | { instruction(instruction_name::LDA, addr_mode::ABSOLUTE, 0), 40, 3 }, 245 | { instruction(instruction_name::LDA, addr_mode::ABSOLUTE_X, 0), 41, 3 }, 246 | { instruction(instruction_name::LDA, addr_mode::ABSOLUTE_Y, 0), 41, 3 }, 247 | { instruction(instruction_name::LDA, addr_mode::X_INDIRECT, 0), 60, 2 }, 248 | { instruction(instruction_name::LDA, addr_mode::INDIRECT_Y, 0), 51, 2 }, 249 | 250 | { instruction(instruction_name::LDX, addr_mode::IMMEDIATE, 0), 20, 2 }, 251 | { instruction(instruction_name::LDX, addr_mode::ZERO_PAGE, 0), 30, 2 }, 252 | { instruction(instruction_name::LDX, addr_mode::ZERO_PAGE_Y, 0), 40, 2 }, 253 | { instruction(instruction_name::LDX, addr_mode::ABSOLUTE, 0), 40, 3 }, 254 | { instruction(instruction_name::LDX, addr_mode::ABSOLUTE_Y, 0), 41, 3 }, 255 | 256 | { instruction(instruction_name::LDY, addr_mode::IMMEDIATE, 0), 20, 2 }, 257 | { instruction(instruction_name::LDY, addr_mode::ZERO_PAGE, 0), 30, 2 }, 258 | { instruction(instruction_name::LDY, addr_mode::ZERO_PAGE_X, 0), 40, 2 }, 259 | { instruction(instruction_name::LDY, addr_mode::ABSOLUTE, 0), 40, 3 }, 260 | { instruction(instruction_name::LDY, addr_mode::ABSOLUTE_X, 0), 41, 3 }, 261 | 262 | { instruction(instruction_name::LSRA, addr_mode::NONE, 0), 20, 1 }, 263 | { instruction(instruction_name::LSR, addr_mode::ZERO_PAGE, 0), 50, 2 }, 264 | { instruction(instruction_name::LSR, addr_mode::ZERO_PAGE_X, 0), 60, 2 }, 265 | { instruction(instruction_name::LSR, addr_mode::ABSOLUTE, 0), 60, 3 }, 266 | { instruction(instruction_name::LSR, addr_mode::ABSOLUTE_X, 0), 70, 3 }, 267 | 268 | // { instruction(instruction_name::NOP, addr_mode::NONE, 0), 20, 1 }, 269 | 270 | { instruction(instruction_name::ORA, addr_mode::IMMEDIATE, 0), 20, 2 }, 271 | { instruction(instruction_name::ORA, addr_mode::ZERO_PAGE, 0), 30, 2 }, 272 | { instruction(instruction_name::ORA, addr_mode::ZERO_PAGE_X, 0), 40, 2 }, 273 | { instruction(instruction_name::ORA, addr_mode::ABSOLUTE, 0), 40, 3 }, 274 | { instruction(instruction_name::ORA, addr_mode::ABSOLUTE_X, 0), 41, 3 }, 275 | { instruction(instruction_name::ORA, addr_mode::ABSOLUTE_Y, 0), 41, 3 }, 276 | { instruction(instruction_name::ORA, addr_mode::X_INDIRECT, 0), 60, 2 }, 277 | { instruction(instruction_name::ORA, addr_mode::INDIRECT_Y, 0), 51, 2 }, 278 | 279 | { instruction(instruction_name::TAX, addr_mode::NONE, 0), 20, 1 }, 280 | { instruction(instruction_name::TXA, addr_mode::NONE, 0), 20, 1 }, 281 | { instruction(instruction_name::DEX, addr_mode::NONE, 0), 20, 1 }, 282 | { instruction(instruction_name::INX, addr_mode::NONE, 0), 20, 1 }, 283 | { instruction(instruction_name::TAY, addr_mode::NONE, 0), 20, 1 }, 284 | { instruction(instruction_name::TYA, addr_mode::NONE, 0), 20, 1 }, 285 | { instruction(instruction_name::DEY, addr_mode::NONE, 0), 20, 1 }, 286 | { instruction(instruction_name::INY, addr_mode::NONE, 0), 20, 1 }, 287 | 288 | { instruction(instruction_name::ROLA, addr_mode::NONE, 0), 20, 1 }, 289 | { instruction(instruction_name::ROL, addr_mode::ZERO_PAGE, 0), 50, 2 }, 290 | { instruction(instruction_name::ROL, addr_mode::ZERO_PAGE_X, 0), 60, 2 }, 291 | { instruction(instruction_name::ROL, addr_mode::ABSOLUTE, 0), 60, 3 }, 292 | { instruction(instruction_name::ROL, addr_mode::ABSOLUTE_X, 0), 70, 3 }, 293 | 294 | { instruction(instruction_name::RORA, addr_mode::NONE, 0), 20, 1 }, 295 | { instruction(instruction_name::ROR, addr_mode::ZERO_PAGE, 0), 50, 2 }, 296 | { instruction(instruction_name::ROR, addr_mode::ZERO_PAGE_X, 0), 60, 2 }, 297 | { instruction(instruction_name::ROR, addr_mode::ABSOLUTE, 0), 60, 3 }, 298 | { instruction(instruction_name::ROR, addr_mode::ABSOLUTE_X, 0), 70, 3 }, 299 | 300 | { instruction(instruction_name::RTI, addr_mode::NONE, 0), 60, 1 }, 301 | { instruction(instruction_name::RTS, addr_mode::NONE, 0), 60, 1 }, 302 | 303 | { instruction(instruction_name::SBC, addr_mode::IMMEDIATE, 0), 20, 2 }, 304 | { instruction(instruction_name::SBC, addr_mode::ZERO_PAGE, 0), 30, 2 }, 305 | { instruction(instruction_name::SBC, addr_mode::ZERO_PAGE_X, 0), 40, 2 }, 306 | { instruction(instruction_name::SBC, addr_mode::ABSOLUTE, 0), 40, 3 }, 307 | { instruction(instruction_name::SBC, addr_mode::ABSOLUTE_X, 0), 41, 3 }, 308 | { instruction(instruction_name::SBC, addr_mode::ABSOLUTE_Y, 0), 41, 3 }, 309 | { instruction(instruction_name::SBC, addr_mode::X_INDIRECT, 0), 60, 2 }, 310 | { instruction(instruction_name::SBC, addr_mode::INDIRECT_Y, 0), 51, 2 }, 311 | 312 | { instruction(instruction_name::STA, addr_mode::ZERO_PAGE, 0), 30, 2 }, 313 | { instruction(instruction_name::STA, addr_mode::ZERO_PAGE_X, 0), 40, 2 }, 314 | { instruction(instruction_name::STA, addr_mode::ABSOLUTE, 0), 40, 3 }, 315 | { instruction(instruction_name::STA, addr_mode::ABSOLUTE_X, 0), 50, 3 }, 316 | { instruction(instruction_name::STA, addr_mode::ABSOLUTE_Y, 0), 50, 3 }, 317 | { instruction(instruction_name::STA, addr_mode::X_INDIRECT, 0), 60, 2 }, 318 | { instruction(instruction_name::STA, addr_mode::INDIRECT_Y, 0), 60, 2 }, 319 | 320 | { instruction(instruction_name::TXS, addr_mode::NONE, 0), 20, 1 }, 321 | { instruction(instruction_name::TSX, addr_mode::NONE, 0), 20, 1 }, 322 | { instruction(instruction_name::PHA, addr_mode::NONE, 0), 30, 1 }, 323 | { instruction(instruction_name::PLA, addr_mode::NONE, 0), 40, 1 }, 324 | { instruction(instruction_name::PHP, addr_mode::NONE, 0), 30, 1 }, 325 | { instruction(instruction_name::PLP, addr_mode::NONE, 0), 40, 1 }, 326 | 327 | { instruction(instruction_name::STX, addr_mode::ZERO_PAGE, 0), 30, 2 }, 328 | { instruction(instruction_name::STX, addr_mode::ZERO_PAGE_Y, 0), 40, 2 }, 329 | { instruction(instruction_name::STX, addr_mode::ABSOLUTE, 0), 40, 3 }, 330 | 331 | { instruction(instruction_name::STY, addr_mode::ZERO_PAGE, 0), 30, 2 }, 332 | { instruction(instruction_name::STY, addr_mode::ZERO_PAGE_X, 0), 40, 2 }, 333 | { instruction(instruction_name::STY, addr_mode::ABSOLUTE, 0), 40, 3 }, 334 | }; 335 | 336 | void process(instruction_seq seq) { 337 | for (auto ins : seq.instructions) { 338 | if (ins.name() == instruction_name::NONE) { break; } 339 | std::cout << (int)ins.name() << " "; 340 | } 341 | std::cout << (int)seq.cycles << std::endl; // << " " << (int)seq.bytes << std::endl; 342 | } 343 | 344 | int main() { 345 | uint64_t count = 0; 346 | std::sort( 347 | std::begin(instructions), 348 | std::end(instructions), 349 | std::less()); 350 | const int max = 100; 351 | std::priority_queue, std::greater> queue; 352 | instruction_info start { 353 | instruction(instruction_name::ADC, addr_mode::IMMEDIATE, 0), 354 | 2, 355 | 2 356 | }; 357 | queue.emplace(instruction_seq(0, 0)); 358 | while (!queue.empty()) { 359 | instruction_seq top = queue.top(); 360 | queue.pop(); 361 | 362 | count++; 363 | for (instruction_info i : instructions) { 364 | instruction_seq next = top.add(i); 365 | if (next.cycles <= max) { 366 | queue.emplace(next); 367 | } else { 368 | break; 369 | } 370 | } 371 | } 372 | 373 | std::cout << count << std::endl; 374 | } 375 | 376 | // 20: 33 377 | // 30: 59 378 | // 40: 1112 379 | // 50: 2864 380 | // 60: 38131 381 | // 70: 127453 382 | // 80: 1331766 383 | // 90: 5398634 384 | // 100: 47382564 385 | // 110: 221727071 386 | -------------------------------------------------------------------------------- /deps.sh: -------------------------------------------------------------------------------- 1 | apt-get install libz3-dev 2 | -------------------------------------------------------------------------------- /emulator.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include "opcode.h" 7 | #include "operations.h" 8 | 9 | typedef std::tuple instruction2; 10 | //typedef std::tuple instruction_seq; 11 | typedef std::tuple instruction4; 12 | 13 | 14 | struct instruction_seq { 15 | const static int max_length; 16 | opcode ops[3]; 17 | 18 | instruction_seq() 19 | : ops{ opcode::zero, opcode::zero, opcode::zero } {} 20 | 21 | instruction_seq append(opcode op) const { 22 | instruction_seq copy = *this; 23 | for (int i = 0; i < max_length; i++) { 24 | if (copy.ops[i] == opcode::zero) { 25 | copy.ops[i] = op; 26 | break; 27 | } 28 | } 29 | return copy; 30 | } 31 | 32 | std::vector alternates() { 33 | std::vector result; 34 | return result; 35 | } 36 | 37 | bool operator==(const instruction_seq &other) const { 38 | for (int i = 0; i < max_length; i++) { 39 | if (ops[i] != other.ops[i]) return false; 40 | } 41 | return true; 42 | } 43 | 44 | bool in(const std::unordered_set &set) const; 45 | 46 | instruction_seq canonicalize() const { 47 | const int abs_start = 7; 48 | const int zp_start = 0xA; 49 | 50 | uint8_t absolute_vars[3] { 0xFF, 0xFF, 0xFF }; 51 | int abs = abs_start; 52 | uint8_t zp_vars[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; 53 | int zp = zp_start; 54 | bool c0_first = false; 55 | bool c1_first = false; 56 | 57 | for (int i = 0; i < max_length; i++) { 58 | if (ops[i] == opcode::zero) { break; } 59 | uint8_t val = ops[i].mode & 0x0F; 60 | switch (ops[i].mode & 0xF0) { 61 | case 0x00: // Immediate 62 | if (!(c0_first || c1_first)) { 63 | if (val == 0 || 64 | val == 4 || 65 | val == 6) { 66 | c0_first = true; 67 | } else if (val == 1 || val == 5) { 68 | c1_first = true; 69 | } 70 | } 71 | break; 72 | case 0x10: // Absolute 73 | case 0x20: // AbsoluteX 74 | case 0x30: // AbsoluteY 75 | case 0x70: // Indirect 76 | // if we see a new absolute var, give it the next 77 | // value. 78 | if (absolute_vars[val - abs_start] == 0xFF) { 79 | absolute_vars[val - abs_start] = abs++; 80 | } 81 | break; 82 | case 0x40: // ZeroPage 83 | case 0x50: // ZeroPageX 84 | case 0x60: // ZeroPageY 85 | case 0x80: // IndirectX 86 | case 0x90: // IndirectY 87 | // if we see a new zp var, give it the next 88 | // value. 89 | if (zp_vars[val - zp_start] == 0xFF) { 90 | zp_vars[val - zp_start] = zp++; 91 | } 92 | break; 93 | } 94 | } 95 | 96 | instruction_seq result; 97 | 98 | // now return a new instruction_seq with the replacements made. 99 | for (int i = 0; i < max_length; i++) { 100 | if (ops[i] == opcode::zero) { break; } 101 | uint8_t mode = ops[i].mode & 0xF0; 102 | uint8_t operand = ops[i].mode & 0x0F; 103 | Operations op = ops[i].op; 104 | switch (operand) { 105 | case 0: 106 | case 4: // C0 107 | if (c1_first) { 108 | result = result.append(opcode { op, (AddrMode)(mode | (operand + 1)) }); 109 | } 110 | else { 111 | result = result.append(ops[i]); 112 | } 113 | break; 114 | case 1: 115 | case 5: // C1 116 | if (c1_first) { 117 | result = result.append(opcode { op, (AddrMode)(mode | (operand - 1)) }); 118 | } 119 | else { 120 | result = result.append(ops[i]); 121 | } 122 | break; 123 | case 0x7: 124 | case 0x8: 125 | case 0x9: // Absolute 126 | result = result.append(opcode { op, (AddrMode)(mode | absolute_vars[operand - abs_start]) }); 127 | break; 128 | case 0xA: 129 | case 0xB: 130 | case 0xC: 131 | case 0xD: // ZP 132 | result = result.append(opcode { op, (AddrMode)(mode | zp_vars[operand - zp_start]) }); 133 | break; 134 | default: 135 | result = result.append(ops[i]); 136 | } 137 | } 138 | return result; 139 | } 140 | }; 141 | 142 | namespace std { 143 | template <> struct hash 144 | { 145 | size_t operator()(const opcode &x) const 146 | { 147 | return std::hash{}(std::hash{}((uint8_t&)x.op) ^ std::hash{}((uint8_t&)x.mode)); 148 | /* your code here, e.g. "return hash()(x.value);" */ 149 | } 150 | }; 151 | 152 | template <> struct hash 153 | { 154 | size_t operator()(const instruction_seq &x) const 155 | { 156 | size_t result = 0; 157 | for (int i = 0; i < instruction_seq::max_length; i++) { 158 | result ^= std::hash{}(x.ops[i]); 159 | result *= 1337; 160 | } 161 | return std::hash{}(result); 162 | } 163 | }; 164 | } 165 | 166 | bool instruction_seq::in(const std::unordered_set &set) const { 167 | instruction_seq needle = this->canonicalize(); 168 | if (set.find(needle) != set.end()) { return true; } 169 | for (int i = 0; i < max_length - 1; i++) { 170 | instruction_seq s; 171 | s = s.append(needle.ops[i]); 172 | s = s.append(needle.ops[i+1]); 173 | s = s.canonicalize(); 174 | if (set.find(s) != set.end()) { return true; } 175 | } 176 | return false; 177 | } 178 | 179 | const int instruction_seq::max_length = 3; 180 | 181 | template 182 | struct emulator { 183 | 184 | void instruction(machine& m, Operations op, AddrMode mode) const { 185 | auto absoluteVar = m.absolute0; 186 | auto zeroPageVar = m.zp0; 187 | auto immediateVar = m.c0; 188 | switch (mode & 0xF) { 189 | case 0x00: // c0 - good. 190 | break; 191 | case 0x01: // c1 192 | immediateVar = m.c1; 193 | break; 194 | case 0x02: // literal0 195 | immediateVar = m.literal0; 196 | break; 197 | case 0x03: // literal1 198 | immediateVar = m.literal1; 199 | break; 200 | case 0x04: // c0+1 201 | immediateVar = m.c0 + m.literal1; 202 | break; 203 | case 0x05: // c1+1 204 | immediateVar = m.c1 + m.literal1; 205 | break; 206 | case 0x06: // c0+c1 207 | immediateVar = m.c0 + m.c1; 208 | break; 209 | case 0x07: // absolute0 - good 210 | break; 211 | case 0x08: // absolute 1 212 | absoluteVar = m.absolute1; 213 | break; 214 | case 0x09: 215 | absoluteVar = m.absolute2; 216 | break; 217 | case 0x0A: // zp0 - good 218 | break; 219 | case 0x0B: // zp1 220 | zeroPageVar = m.zp1; 221 | break; 222 | case 0x0C: // zp2 223 | zeroPageVar = m.zp2; 224 | break; 225 | case 0x0D: // zp3 226 | zeroPageVar = m.zp3; 227 | break; 228 | case 0x0E: case 0x0F: // none - good 229 | break; 230 | } 231 | switch (mode & 0xF0) { 232 | case 0x00: // immediate 233 | break; 234 | case 0x10: // absolute 235 | break; 236 | case 0x20: // absolute, x 237 | absoluteVar = absoluteVar + m.extend(m._x); 238 | break; 239 | case 0x30: // absolute, y 240 | absoluteVar = absoluteVar + m.extend(m._y); 241 | break; 242 | case 0x40: // zero page 243 | absoluteVar = m.extend(zeroPageVar); 244 | break; 245 | case 0x50: // zeroPage, x 246 | absoluteVar = m.extend((zeroPageVar + m._x) & 0xFF); 247 | break; 248 | case 0x60: // zeroPage, y 249 | absoluteVar = m.extend((zeroPageVar + m._y) & 0xFF); 250 | break; 251 | case 0x70: { // indirect 252 | auto jmpiBug = (absoluteVar & 0xFF00) | ((absoluteVar + 1) & 0xFF); 253 | absoluteVar = (m.extend(m.read(jmpiBug)) << 8) | m.extend(m.read(absoluteVar)); 254 | break; 255 | } 256 | case 0x80: // (indirect, x) 257 | absoluteVar = m.extend(m.read(m.extend(((zeroPageVar + m._x) & 0xFF)))) 258 | | (m.extend(m.read(m.extend((zeroPageVar + m._x + 1) & 0xFF))) << 8); 259 | absoluteVar = (m.extend(m.read(absoluteVar+1)) << 8) | m.extend(m.read(absoluteVar)); 260 | break; 261 | case 0x90: // (indirect), y 262 | absoluteVar = (m.extend(m.read(m.extend(zeroPageVar))) 263 | | (m.extend(m.read(m.extend((zeroPageVar + 1) & 0xFF))) << 8)) + m.extend(m._y); 264 | break; 265 | default: // none 266 | break; 267 | } 268 | if ((mode & 0xF0) != 0) { 269 | immediateVar = m.read(absoluteVar); 270 | } 271 | 272 | // Now either immediateVar, or absoluteVar will hold the correct value. 273 | switch (op) { 274 | case AND: 275 | m.a(m.setSZ(m._a & immediateVar)); 276 | break; 277 | case ORA: 278 | m.a(m.setSZ(m._a | immediateVar)); 279 | break; 280 | case EOR: 281 | m.a(m.setSZ(m._a ^ immediateVar)); 282 | break; 283 | case LDA: 284 | m.a(m.setSZ(immediateVar)); 285 | break; 286 | case LDX: 287 | m.x(m.setSZ(immediateVar)); 288 | break; 289 | case LDY: 290 | m.y(m.setSZ(immediateVar)); 291 | break; 292 | case TXA: 293 | m.a(m.setSZ(m._x)); 294 | break; 295 | case TAX: 296 | m.x(m.setSZ(m._a)); 297 | break; 298 | case TYA: 299 | m.a(m.setSZ(m._y)); 300 | break; 301 | case TAY: 302 | m.y(m.setSZ(m._a)); 303 | break; 304 | case INX: 305 | m.x(m.setSZ(m._x + 1)); 306 | break; 307 | case INY: 308 | m.y(m.setSZ(m._y + 1)); 309 | break; 310 | case DEX: 311 | m.x(m.setSZ(m._x - 1)); 312 | break; 313 | case DEY: 314 | m.y(m.setSZ(m._y - 1)); 315 | break; 316 | case CLC: 317 | m.ccC(m.falsy); 318 | break; 319 | case CLI: 320 | m.ccI(m.falsy); 321 | break; 322 | case CLV: 323 | m.ccV(m.falsy); 324 | break; 325 | case CLD: 326 | m.ccD(m.falsy); 327 | break; 328 | case SEC: 329 | m.ccC(m.truthy); 330 | break; 331 | case SEI: 332 | m.ccI(m.truthy); 333 | break; 334 | case SED: 335 | m.ccD(m.truthy); 336 | break; 337 | case TSX: 338 | m.x(m._sp); 339 | break; 340 | case TXS: 341 | m.sp(m._x); 342 | break; 343 | case NOP: 344 | // nap. 345 | break; 346 | case INC: 347 | m.write(absoluteVar, m.setSZ(immediateVar + 1)); 348 | break; 349 | case DEC: 350 | m.write(absoluteVar, m.setSZ(immediateVar - 1)); 351 | break; 352 | case BIT: 353 | m.ccZ((immediateVar & m._a) == 0); 354 | m.ccS((immediateVar & 0x80) == 0x80); 355 | m.ccV((immediateVar & 0x40) == 0x40); 356 | break; 357 | case ASL_A: 358 | m.ccC((m._a & 0x80) == 0x80); 359 | m.a(m.setSZ(m.shl(m._a))); 360 | break; 361 | case ASL: 362 | m.ccC((immediateVar & 0x80) == 0x80); 363 | m.write(absoluteVar, m.setSZ(m.shl(immediateVar))); 364 | break; 365 | case ROL_A: { 366 | auto val = m.shl(m._a) | m.ite(m._ccC, m.literal1, m.literal0); 367 | m.ccC((m._a & 0x80) == 0x80); 368 | m.a(m.setSZ(val)); 369 | break; 370 | } 371 | case ROL: { 372 | auto val = m.shl(immediateVar) | m.ite(m._ccC, m.literal1, m.literal0); 373 | m.ccC((immediateVar & 0x80) == 0x80); 374 | m.write(absoluteVar, m.setSZ(val)); 375 | break; 376 | } 377 | case LSR_A: 378 | m.ccC((m._a & 0x01) == 0x01); 379 | m.a(m.setSZ(m.shr(m._a))); 380 | break; 381 | case LSR: 382 | m.ccC((immediateVar & 0x01) == 0x01); 383 | m.write(absoluteVar, m.setSZ(m.shr(immediateVar))); 384 | break; 385 | case ROR_A: { 386 | auto val = m.shr(m._a) | m.ite(m._ccC, 0x80, 0x00); 387 | m.ccC((m._a & 0x01) == 0x01); 388 | m.a(m.setSZ(val)); 389 | break; 390 | } 391 | case ROR: { 392 | auto val = m.shr(immediateVar) | m.ite(m._ccC, 0x80, 0x00); 393 | m.ccC((immediateVar & 0x01) == 0x01); 394 | m.write(absoluteVar, m.setSZ(val)); 395 | break; 396 | } 397 | case STA: 398 | m.write(absoluteVar, m._a); 399 | break; 400 | case STX: 401 | m.write(absoluteVar, m._x); 402 | break; 403 | case STY: 404 | m.write(absoluteVar, m._y); 405 | break; 406 | case PLA: 407 | m.sp(m._sp + 1); 408 | m.a(m.read(m.extend(m._sp) + 0x0100)); 409 | break; 410 | case PHA: 411 | m.write(m.extend(m._sp) + 0x0100, m._a); 412 | m.sp(m._sp - 1); 413 | break; 414 | case PHP: { 415 | auto p = m.ite(m._ccS, 0x80, 0x00) 416 | | m.ite(m._ccV, 0x40, 0x00) 417 | | m.ite(m._ccD, 0x08, 0x00) 418 | | m.ite(m._ccI, 0x04, 0x00) 419 | | m.ite(m._ccZ, 0x02, 0x00) 420 | | m.ite(m._ccC, 0x01, 0x00); 421 | m.write(m.extend(m._sp) + 0x0100, p); 422 | m.sp(m._sp - 1); 423 | break; 424 | } 425 | case PLP: { 426 | m.sp(m._sp + 1); 427 | auto pull = m.read(m.extend(m._sp) + 0x0100); 428 | m.ccS(0x80 == (pull & 0x80)); 429 | m.ccV(0x40 == (pull & 0x40)); 430 | m.ccD(0x08 == (pull & 0x08)); 431 | m.ccI(0x04 == (pull & 0x04)); 432 | m.ccZ(0x02 == (pull & 0x02)); 433 | m.ccC(0x01 == (pull & 0x01)); 434 | break; 435 | } 436 | case SBC: 437 | immediateVar = immediateVar ^ 0xFF; 438 | /* fallthrough */ 439 | case ADC: { 440 | auto sum = m.extend(immediateVar) + m.extend(m._a) + m.extend(m.ite(m._ccC, 0x01, 0x00)); 441 | m.ccV(0x80 == (0x80 & (m.lobyte(sum) ^ m._a) & (m.lobyte(sum) ^ immediateVar))); 442 | m.ccC(m.hibyte(sum) == 0x01); 443 | m.a(m.setSZ(m.lobyte(sum))); 444 | break; 445 | } 446 | case CMP: 447 | m.ccC(m.uge(m._a, immediateVar)); 448 | m.ccS(m.uge(m._a - immediateVar, 0x80)); 449 | m.ccZ(m._a == immediateVar); 450 | break; 451 | case CPX: 452 | m.ccC(m.uge(m._x, immediateVar)); 453 | m.ccS(m.uge(m._x - immediateVar, 0x80)); 454 | m.ccZ(m._x == immediateVar); 455 | break; 456 | case CPY: 457 | m.ccC(m.uge(m._y, immediateVar)); 458 | m.ccS(m.uge(m._y - immediateVar, 0x80)); 459 | m.ccZ(m._y == immediateVar); 460 | break; 461 | case JMP: 462 | m.jmp(absoluteVar); 463 | break; 464 | case RTI: 465 | m.rti(); 466 | break; 467 | case RTS: 468 | m.rts(); 469 | break; 470 | case BPL: 471 | m.branch(!m._ccS, absoluteVar); 472 | break; 473 | case BMI: 474 | m.branch(m._ccS, absoluteVar); 475 | break; 476 | case BVS: 477 | m.branch(m._ccV, absoluteVar); 478 | break; 479 | case BVC: 480 | m.branch(!m._ccV, absoluteVar); 481 | break; 482 | case BCC: 483 | m.branch(!m._ccC, absoluteVar); 484 | break; 485 | case BCS: 486 | m.branch(m._ccC, absoluteVar); 487 | break; 488 | case BEQ: 489 | m.branch(m._ccZ, absoluteVar); 490 | break; 491 | case BNE: 492 | m.branch(!m._ccZ, absoluteVar); 493 | break; 494 | } 495 | } 496 | }; 497 | 498 | -------------------------------------------------------------------------------- /emulator2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "instructions2.h" 3 | 4 | template 5 | struct emulator { 6 | void instruction(machine &m, const instruction ins) const { 7 | if (ins.name() == instruction_name::NONE) { 8 | return; 9 | } 10 | 11 | auto absoluteVar = m.absolute(0); 12 | auto immediateVar = m.immediate(0); 13 | 14 | switch(ins.mode()) { 15 | case addr_mode::NONE: 16 | break; // nothing to do 17 | case addr_mode::ABSOLUTE: 18 | absoluteVar = m.absolute(ins.number()); 19 | break; 20 | case addr_mode::ABSOLUTE_X: 21 | absoluteVar = m.absolute(ins.number()) + m.extend(m._x); 22 | break; 23 | case addr_mode::ABSOLUTE_Y: 24 | absoluteVar = m.absolute(ins.number()) + m.extend(m._y); 25 | break; 26 | case addr_mode::X_INDIRECT: 27 | absoluteVar = m.extend(m.read(m.extend(((m.zp(ins.number()) + m._x) & 0xFF)))) 28 | | (m.extend(m.read(m.extend((m.zp(ins.number()) + m._x + 1) & 0xFF))) << 8); 29 | absoluteVar = (m.extend(m.read(absoluteVar+1)) << 8) | m.extend(m.read(absoluteVar)); 30 | break; 31 | case addr_mode::INDIRECT_Y: 32 | absoluteVar = (m.extend(m.read(m.extend(m.zp(ins.number())))) 33 | | (m.extend(m.read(m.extend((m.zp(ins.number()) + 1) & 0xFF))) << 8)) + m.extend(m._y); 34 | break; 35 | case addr_mode::ZERO_PAGE: 36 | absoluteVar = m.extend(m.zp(ins.number())); 37 | break; 38 | case addr_mode::ZERO_PAGE_X: 39 | absoluteVar = m.extend(m.zp(ins.number()) + m._x); 40 | break; 41 | case addr_mode::ZERO_PAGE_Y: 42 | absoluteVar = m.extend(m.zp(ins.number()) + m._y); 43 | break; 44 | case addr_mode::IMMEDIATE: 45 | immediateVar = m.immediate(ins.number()); 46 | break; 47 | case addr_mode::CONSTANT: 48 | immediateVar = m.constant(addr_mode_constant_values[ins.number()]); 49 | } 50 | 51 | // If we aren't using the immediate operand, then read the address we found. 52 | if (ins.mode() != addr_mode::IMMEDIATE && ins.mode() != addr_mode::CONSTANT) { 53 | immediateVar = m.read(absoluteVar); 54 | } 55 | 56 | switch (ins.name()) { 57 | case instruction_name::NONE: 58 | break; // nothing to do 59 | case instruction_name::AND: 60 | m.a(m.setSZ(m._a & immediateVar)); 61 | break; 62 | case instruction_name::ORA: 63 | m.a(m.setSZ(m._a | immediateVar)); 64 | break; 65 | case instruction_name::EOR: 66 | m.a(m.setSZ(m._a ^ immediateVar)); 67 | break; 68 | case instruction_name::LDA: 69 | m.a(m.setSZ(immediateVar)); 70 | break; 71 | case instruction_name::LDX: 72 | m.x(m.setSZ(immediateVar)); 73 | break; 74 | case instruction_name::LDY: 75 | m.y(m.setSZ(immediateVar)); 76 | break; 77 | case instruction_name::TXA: 78 | m.a(m.setSZ(m._x)); 79 | break; 80 | case instruction_name::TAX: 81 | m.x(m.setSZ(m._a)); 82 | break; 83 | case instruction_name::TYA: 84 | m.a(m.setSZ(m._y)); 85 | break; 86 | case instruction_name::TAY: 87 | m.y(m.setSZ(m._a)); 88 | break; 89 | case instruction_name::INX: 90 | m.x(m.setSZ(m._x + 1)); 91 | break; 92 | case instruction_name::INY: 93 | m.y(m.setSZ(m._y + 1)); 94 | break; 95 | case instruction_name::DEX: 96 | m.x(m.setSZ(m._x - 1)); 97 | break; 98 | case instruction_name::DEY: 99 | m.y(m.setSZ(m._y - 1)); 100 | break; 101 | case instruction_name::CLC: 102 | m.ccC(m.falsy); 103 | break; 104 | case instruction_name::CLI: 105 | m.ccI(m.falsy); 106 | break; 107 | case instruction_name::CLV: 108 | m.ccV(m.falsy); 109 | break; 110 | case instruction_name::CLD: 111 | m.ccD(m.falsy); 112 | break; 113 | case instruction_name::SEC: 114 | m.ccC(m.truthy); 115 | break; 116 | case instruction_name::SEI: 117 | m.ccI(m.truthy); 118 | break; 119 | case instruction_name::SED: 120 | m.ccD(m.truthy); 121 | break; 122 | case instruction_name::TSX: 123 | m.x(m._sp); 124 | break; 125 | case instruction_name::TXS: 126 | m.sp(m._x); 127 | break; 128 | case instruction_name::NOP: 129 | // nap. 130 | break; 131 | case instruction_name::INC: 132 | m.write(absoluteVar, m.setSZ(immediateVar + 1)); 133 | break; 134 | case instruction_name::DEC: 135 | m.write(absoluteVar, m.setSZ(immediateVar - 1)); 136 | break; 137 | case instruction_name::BIT: 138 | m.ccZ((immediateVar & m._a) == 0); 139 | m.ccS((immediateVar & 0x80) == 0x80); 140 | m.ccV((immediateVar & 0x40) == 0x40); 141 | break; 142 | case instruction_name::ASLA: 143 | m.ccC((m._a & 0x80) == 0x80); 144 | m.a(m.setSZ(m.shl(m._a))); 145 | break; 146 | case instruction_name::ASL: 147 | m.ccC((immediateVar & 0x80) == 0x80); 148 | m.write(absoluteVar, m.setSZ(m.shl(immediateVar))); 149 | break; 150 | case instruction_name::ROLA: { 151 | auto val = m.shl(m._a) | m.ite(m._ccC, m.constant(1), m.constant(0)); 152 | m.ccC((m._a & 0x80) == 0x80); 153 | m.a(m.setSZ(val)); 154 | break; 155 | } 156 | case instruction_name::ROL: { 157 | auto val = m.shl(immediateVar) | m.ite(m._ccC, m.constant(1), m.constant(0)); 158 | m.ccC((immediateVar & 0x80) == 0x80); 159 | m.write(absoluteVar, m.setSZ(val)); 160 | break; 161 | } 162 | case instruction_name::LSRA: 163 | m.ccC((m._a & 0x01) == 0x01); 164 | m.a(m.setSZ(m.shr(m._a))); 165 | break; 166 | case instruction_name::LSR: 167 | m.ccC((immediateVar & 0x01) == 0x01); 168 | m.write(absoluteVar, m.setSZ(m.shr(immediateVar))); 169 | break; 170 | case instruction_name::RORA: { 171 | auto val = m.shr(m._a) | m.ite(m._ccC, 0x80, 0x00); 172 | m.ccC((m._a & 0x01) == 0x01); 173 | m.a(m.setSZ(val)); 174 | break; 175 | } 176 | case instruction_name::ROR: { 177 | auto val = m.shr(immediateVar) | m.ite(m._ccC, 0x80, 0x00); 178 | m.ccC((immediateVar & 0x01) == 0x01); 179 | m.write(absoluteVar, m.setSZ(val)); 180 | break; 181 | } 182 | case instruction_name::STA: 183 | m.write(absoluteVar, m._a); 184 | break; 185 | case instruction_name::STX: 186 | m.write(absoluteVar, m._x); 187 | break; 188 | case instruction_name::STY: 189 | m.write(absoluteVar, m._y); 190 | break; 191 | case instruction_name::PLA: 192 | m.sp(m._sp + 1); 193 | m.a(m.read(m.extend(m._sp) + 0x0100)); 194 | break; 195 | case instruction_name::PHA: 196 | m.write(m.extend(m._sp) + 0x0100, m._a); 197 | m.sp(m._sp - 1); 198 | break; 199 | case instruction_name::PHP: { 200 | auto p = m.ite(m._ccS, 0x80, 0x00) 201 | | m.ite(m._ccV, 0x40, 0x00) 202 | | m.ite(m._ccD, 0x08, 0x00) 203 | | m.ite(m._ccI, 0x04, 0x00) 204 | | m.ite(m._ccZ, 0x02, 0x00) 205 | | m.ite(m._ccC, 0x01, 0x00); 206 | m.write(m.extend(m._sp) + 0x0100, p); 207 | m.sp(m._sp - 1); 208 | break; 209 | } 210 | case instruction_name::PLP: { 211 | m.sp(m._sp + 1); 212 | auto pull = m.read(m.extend(m._sp) + 0x0100); 213 | m.ccS(0x80 == (pull & 0x80)); 214 | m.ccV(0x40 == (pull & 0x40)); 215 | m.ccD(0x08 == (pull & 0x08)); 216 | m.ccI(0x04 == (pull & 0x04)); 217 | m.ccZ(0x02 == (pull & 0x02)); 218 | m.ccC(0x01 == (pull & 0x01)); 219 | break; 220 | } 221 | case instruction_name::SBC: 222 | immediateVar = immediateVar ^ 0xFF; 223 | /* fallthrough */ 224 | case instruction_name::ADC: { 225 | auto sum = m.extend(immediateVar) + m.extend(m._a) + m.extend(m.ite(m._ccC, 0x01, 0x00)); 226 | m.ccV(0x80 == (0x80 & (m.lobyte(sum) ^ m._a) & (m.lobyte(sum) ^ immediateVar))); 227 | m.ccC(m.hibyte(sum) == 0x01); 228 | m.a(m.setSZ(m.lobyte(sum))); 229 | break; 230 | } 231 | case instruction_name::CMP: 232 | m.ccC(m.uge(m._a, immediateVar)); 233 | m.ccS(m.uge(m._a - immediateVar, 0x80)); 234 | m.ccZ(m._a == immediateVar); 235 | break; 236 | case instruction_name::CPX: 237 | m.ccC(m.uge(m._x, immediateVar)); 238 | m.ccS(m.uge(m._x - immediateVar, 0x80)); 239 | m.ccZ(m._x == immediateVar); 240 | break; 241 | case instruction_name::CPY: 242 | m.ccC(m.uge(m._y, immediateVar)); 243 | m.ccS(m.uge(m._y - immediateVar, 0x80)); 244 | m.ccZ(m._y == immediateVar); 245 | break; 246 | case instruction_name::JMP: 247 | m.jmp(absoluteVar); 248 | break; 249 | case instruction_name::JMPI: { 250 | auto jmpiBug = (absoluteVar & 0xFF00) | ((absoluteVar + 1) & 0xFF); 251 | m.jmp((m.extend(m.read(jmpiBug)) << 8) | m.extend(m.read(absoluteVar))); 252 | break; 253 | } 254 | case instruction_name::RTI: 255 | m.rti(); 256 | break; 257 | case instruction_name::RTS: 258 | m.rts(); 259 | break; 260 | case instruction_name::BPL: 261 | m.branch(!m._ccS, absoluteVar); 262 | break; 263 | case instruction_name::BMI: 264 | m.branch(m._ccS, absoluteVar); 265 | break; 266 | case instruction_name::BVS: 267 | m.branch(m._ccV, absoluteVar); 268 | break; 269 | case instruction_name::BVC: 270 | m.branch(!m._ccV, absoluteVar); 271 | break; 272 | case instruction_name::BCC: 273 | m.branch(!m._ccC, absoluteVar); 274 | break; 275 | case instruction_name::BCS: 276 | m.branch(m._ccC, absoluteVar); 277 | break; 278 | case instruction_name::BEQ: 279 | m.branch(m._ccZ, absoluteVar); 280 | break; 281 | case instruction_name::BNE: 282 | m.branch(!m._ccZ, absoluteVar); 283 | break; 284 | case instruction_name::JSR: 285 | case instruction_name::BRK: 286 | // not implemented 287 | break; 288 | } 289 | } 290 | }; 291 | -------------------------------------------------------------------------------- /enumerator.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "stdint.h" 3 | #include "include/z3++.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "operations.h" 11 | #include "opcode.h" 12 | #include "emulator.h" 13 | #include "random_machine.h" 14 | #include "abstract_machine.h" 15 | #include "queue.h" 16 | 17 | uint8_t length(instruction_seq ops) { 18 | return ops.ops[2] == opcode::zero ? (ops.ops[1] == opcode::zero ? 1 : 2) : 3; 19 | } 20 | 21 | void print(opcode op) { 22 | std::cout << OpNames[op.op] << " " << AddrModeNames[op.mode]; 23 | } 24 | 25 | void print(instruction_seq ops) { 26 | for (int i = 0; i < instruction_seq::max_length; i++) { 27 | if (ops.ops[i] == opcode::zero) { return; } 28 | print(ops.ops[i]); 29 | std::cout << "; "; 30 | } 31 | } 32 | 33 | const float cycles(const instruction_seq ops) { 34 | float cost = 0; 35 | for (int i = 0; i < instruction_seq::max_length; i++) { 36 | cost += operation_costs[ops.ops[i]]; 37 | } 38 | return cost; 39 | } 40 | 41 | z3::check_result equivalent(z3::solver &s, const abstract_machine &ma, const abstract_machine &mb) { 42 | s.push(); 43 | s.add(!( 44 | ma._earlyExit == mb._earlyExit && 45 | ma._ccS == mb._ccS && 46 | ma._ccV == mb._ccV && 47 | ma._ccD == mb._ccD && 48 | ma._ccI == mb._ccI && 49 | ma._ccC == mb._ccC && 50 | ma._ccZ == mb._ccZ && 51 | ma._a == mb._a && 52 | ma._x == mb._x && 53 | ma._y == mb._y && 54 | ma._sp == mb._sp && 55 | ma._memory == mb._memory 56 | )); 57 | 58 | auto result = s.check(); 59 | s.pop(); 60 | return result; 61 | } 62 | 63 | /** 64 | * Generates a bit mask showing the operands 65 | * in use in the given set of instructions. 66 | */ 67 | uint16_t operand_mask(instruction_seq ops) { 68 | uint16_t result = 0; 69 | for (int i = 0; i < instruction_seq::max_length; i++) { 70 | if (ops.ops[i] == opcode::zero) break; 71 | uint8_t operand = ops.ops[i].mode & 0xF; 72 | // handle immediate values 73 | switch (operand) { 74 | case ImmediateC0: case ImmediateC0Plus1: 75 | result |= 0x1; 76 | break; 77 | case ImmediateC1: case ImmediateC1Plus1: 78 | result |= 0x2; 79 | break; 80 | case ImmediateC0PlusC1: 81 | result |= 0x3; 82 | break; 83 | case 0x0E: case Immediate0: case Immediate1: 84 | // no operand, don't do anything. 85 | break; 86 | default: 87 | // for everything else, just use the value as a bit number. 88 | result |= 1 << operand; 89 | } 90 | } 91 | return result; 92 | } 93 | 94 | /** 95 | * The candidate instruction sequence can't use an operand 96 | * that the original sequence doesn't. 97 | */ 98 | inline bool is_possible_optimization_by_operand_masks(uint16_t original, uint16_t candidate) { 99 | return (original & candidate) == candidate; 100 | } 101 | 102 | bool is_canonical(instruction_seq ops) { 103 | int abs = 7; 104 | int zp = 0xA; 105 | bool c0 = false; 106 | for (int i = 0; i < instruction_seq::max_length; i++) { 107 | if (ops.ops[i] == opcode::zero) { break; } 108 | uint8_t val = ops.ops[i].mode & 0x0F; 109 | switch (ops.ops[i].mode & 0xF0) { 110 | case 0x00: // Immediate 111 | if (val == 0 || 112 | val == 4 || 113 | val == 6) { 114 | c0 = true; 115 | } else if (!c0 && (val == 1 || val == 5)) { 116 | return false; 117 | } 118 | break; 119 | case 0x10: // Absolute 120 | case 0x20: // AbsoluteX 121 | case 0x30: // AbsoluteY 122 | case 0x70: // Indirect 123 | if (val > abs) { return false; } 124 | else if (val == abs) { abs++; } 125 | break; 126 | case 0x40: // ZeroPage 127 | case 0x50: // ZeroPageX 128 | case 0x60: // ZeroPageY 129 | case 0x80: // IndirectX 130 | case 0x90: // IndirectY 131 | if (val > zp) { return false; } 132 | else if (val == zp) { zp++; } 133 | break; 134 | } 135 | } 136 | return true; 137 | } 138 | 139 | const static int N_INSTRUCTIONS = (sizeof opcodes) / (sizeof opcodes[0]); 140 | 141 | void enumerate_recursive(uint32_t i_min, uint32_t i_max, const random_machine &m1, const random_machine &m2, instruction_seq path, int depth, std::multimap &buckets, const std::unordered_set &non_optimal) { 142 | for (uint32_t i = i_min; i < i_max; i++) { 143 | instruction_seq new_path = path.append(opcodes[i]); 144 | if (new_path.in(non_optimal)) { 145 | //std::cout << "Skipping non-optimal "; 146 | //print(new_path); 147 | //std::cout << std::endl; 148 | continue; 149 | } 150 | random_machine m1_copy = m1; 151 | random_machine m2_copy = m2; 152 | m1_copy.instruction(opcodes[i]); 153 | m2_copy.instruction(opcodes[i]); 154 | uint32_t hash = m1_copy.hash() ^ m2_copy.hash(); 155 | buckets.insert(std::make_pair(hash, new_path)); 156 | //std::cout << "done." << std::endl; 157 | if (depth > 1) { 158 | enumerate_recursive(0, N_INSTRUCTIONS, m1_copy, m2_copy, new_path, depth - 1, buckets, non_optimal); 159 | } 160 | } 161 | } 162 | 163 | void enumerate_worker(uint32_t i_min, uint32_t i_max, int depth, std::multimap &buckets, const std::unordered_set &non_optimal) { 164 | 165 | std::cout << i_min << std::endl; 166 | 167 | random_machine m1(0xFFA4BCAD); 168 | random_machine m2(0x4572849E); 169 | instruction_seq path; 170 | enumerate_recursive(i_min, i_max, m1, m2, path, depth, buckets, non_optimal); 171 | } 172 | 173 | void enumerate_concurrent(int depth, std::multimap &combined_buckets, const std::unordered_set &non_optimal) { 174 | std::cout << "Starting " << N_THREADS << " threads." << std::endl; 175 | 176 | work_queue> queue; 177 | constexpr int N_TASKS = (sizeof opcodes / sizeof opcodes[0]); 178 | 179 | // divide into separate work items for each opcode. 180 | int n_max = (sizeof opcodes) / (sizeof opcodes[0]); 181 | int step = n_max / N_TASKS; 182 | 183 | for (int i = 0; i < N_TASKS-1; i++) { 184 | queue.add([=](auto &buckets) { 185 | enumerate_worker(i * step, (i + 1) * step, depth, buckets, non_optimal); 186 | }); 187 | } 188 | queue.add([=](auto &buckets) { 189 | enumerate_worker((N_TASKS - 1) * step, n_max, depth, buckets, non_optimal); 190 | }); 191 | 192 | queue.run(); 193 | 194 | std::cout << "Finished hashing. Merging results" << std::endl; 195 | for (auto &buckets : queue.stores) { 196 | std::cout << "Processing some buckets (" << buckets.size() << ")" << std::endl; 197 | combined_buckets.insert(buckets.begin(), buckets.end()); 198 | buckets.clear(); 199 | } 200 | 201 | } 202 | 203 | bool compare_by_cycles(const instruction_seq &a, const instruction_seq &b) { 204 | return cycles(a) < cycles(b); 205 | } 206 | bool compare_by_length(const instruction_seq &a, const instruction_seq &b) { 207 | return length(a) < length(b); 208 | } 209 | 210 | struct process_hashes_thread_context { 211 | std::vector> optimizations; 212 | std::vector> timed_out; 213 | z3::context context; 214 | z3::solver solver; 215 | 216 | process_hashes_thread_context() 217 | : context(), solver(context) { 218 | z3::params p(context); 219 | p.set(":timeout", 1000u); 220 | solver.set(p); 221 | } 222 | }; 223 | 224 | int process_hashes_worker(const std::multimap &combined_buckets, process_hashes_thread_context &ctx, uint64_t hash_min, uint64_t hash_max, bool try_split); 225 | 226 | int process_sequences(std::vector &sequences, process_hashes_thread_context &thread_ctx, bool try_split) { 227 | 228 | if (sequences.size() <= 1) { 229 | return 0; // this instruction sequence must be unique. 230 | } 231 | int l = cycles(sequences[0]); 232 | bool different_costs = false; 233 | for (auto sequence : sequences) { 234 | if (cycles(sequence) != l) { 235 | different_costs = true; 236 | break; 237 | } 238 | } 239 | if (!different_costs) { 240 | return 0; // all of these instructions have the same cost -- no optimizations are possible. 241 | } 242 | 243 | z3::solver solver(thread_ctx.context); 244 | z3::params p(thread_ctx.context); 245 | p.set(":timeout", 1000u); 246 | solver.set(p); 247 | 248 | if (try_split) { 249 | std::multimap buckets; 250 | 251 | constexpr int nMachines = 128; 252 | 253 | for (const auto& seq : sequences) { 254 | uint32_t hash = 0; 255 | for (int m = 0; m < nMachines; m++) { 256 | random_machine machine(0x56346d56 + m*1001); 257 | machine.instruction(seq); 258 | hash ^= machine.hash(); 259 | } 260 | random_machine machine(0); 261 | machine.instruction(seq); 262 | hash ^= machine.hash(); 263 | random_machine machine2(0xFFFFFFFF); 264 | machine2.instruction(seq); 265 | hash ^= machine2.hash(); 266 | buckets.insert(std::make_pair(hash, seq)); 267 | } 268 | return process_hashes_worker(buckets, thread_ctx, 0, 0x100000000, false); 269 | } 270 | 271 | std::sort(sequences.begin(), sequences.end(), compare_by_cycles); 272 | 273 | std::map seq_cycles; 274 | float cost = -1; 275 | for (size_t i = 0; i < sequences.size(); i++) { 276 | auto c = cycles(sequences[i]); 277 | if (cost != c) { 278 | seq_cycles[c] = i; 279 | cost = c; 280 | } 281 | } 282 | 283 | int nComparisons = 0; 284 | 285 | std::vector machines{}; 286 | std::vector operand_masks; 287 | for (const auto &seq : sequences) { 288 | abstract_machine m(thread_ctx.context); 289 | m.instruction(seq); 290 | m.simplify(); 291 | machines.push_back(m); 292 | 293 | operand_masks.push_back(operand_mask(seq)); 294 | } 295 | 296 | // Check instructions starting from the end 297 | for (ssize_t i = sequences.size() - 1; i >= 0; i--) { 298 | const instruction_seq seq = sequences.at(i); 299 | const auto c = cycles(seq); 300 | if (!is_canonical(seq)) { continue; } 301 | for (size_t j = 0; j < seq_cycles[c]; j++) { 302 | // if the candidate uses an unknown that wasn't introduced in the original, 303 | // it can't be an optimization. 304 | if (!is_possible_optimization_by_operand_masks(operand_masks[i], operand_masks[j])) { 305 | // std::cout << "Skipping (mask) "; 306 | // print(seq); 307 | // std::cout << "[" << operand_masks[i] << "] "; 308 | // print(sequences[j]); 309 | // std::cout << "[" << operand_masks[j] << "]" << std::endl; 310 | continue; 311 | } 312 | nComparisons++; 313 | auto equivalence = equivalent(solver, machines[i], machines[j]); 314 | if (equivalence == z3::unsat) { 315 | thread_ctx.optimizations.push_back(std::make_pair(seq, sequences[j])); 316 | print(seq); 317 | std::cout << " <-> "; 318 | print(sequences[j]); 319 | std::cout << " " << operand_masks[i]; 320 | std::cout << std::endl; 321 | break; 322 | } else if (equivalence == z3::unknown) { 323 | thread_ctx.timed_out.push_back(std::make_pair(seq, sequences[j])); 324 | print(seq); 325 | std::cout << " "; 326 | print(sequences[j]); 327 | std::cout << " " << operand_masks[i]; 328 | std::cout << " (TIMED OUT)" << std::endl; 329 | 330 | } 331 | } 332 | } 333 | return nComparisons; 334 | } 335 | 336 | int process_hashes_worker(const std::multimap &combined_buckets, process_hashes_thread_context &thread_ctx, uint64_t hash_min, uint64_t hash_max, bool try_split) { 337 | auto it = combined_buckets.lower_bound(hash_min); 338 | auto end = hash_max == 0x100000000 ? combined_buckets.end() : combined_buckets.lower_bound(hash_max); 339 | 340 | if (try_split) { 341 | std::cout << " Processing hashes from " << std::hex << hash_min << " " << hash_max << std::endl; 342 | } 343 | int nComparisons = 0; 344 | 345 | int64_t last = -1; 346 | std::vector sequences; 347 | for (; it != end && it != combined_buckets.end(); ++it) { 348 | uint32_t hash = it->first; 349 | instruction_seq ops = it->second; 350 | if (hash != last) { 351 | // sequences now contains a list of instruction sequences which are possibly equivalent 352 | nComparisons += process_sequences(sequences, thread_ctx, try_split); 353 | sequences.clear(); 354 | } 355 | sequences.push_back(ops); 356 | last = hash; 357 | } 358 | nComparisons += process_sequences(sequences, thread_ctx, try_split); 359 | if (try_split) { std::cout << "Comparisons 0x" << nComparisons << std::endl; } 360 | return nComparisons; 361 | } 362 | 363 | void retry_timed_out() { 364 | 365 | } 366 | 367 | void process_hashes_concurrent(const std::multimap &combined_buckets, std::vector> &optimizations, uint64_t hash_min, uint64_t hash_max) { 368 | std::cout << "Starting processing of hashes" << std::endl; 369 | 370 | constexpr int N_TASKS = 1024; 371 | work_queue queue; 372 | 373 | uint64_t step = (hash_max - hash_min) / N_TASKS; 374 | for (int i = 0; i < N_TASKS-1; i++) { 375 | queue.add([=, &combined_buckets](auto &ctx) { 376 | process_hashes_worker(combined_buckets, ctx, hash_min + i * step, hash_min + (i + 1) * step, true); 377 | }); 378 | } 379 | queue.add([=, &combined_buckets](auto& ctx) { 380 | process_hashes_worker(combined_buckets, ctx, hash_min + (N_TASKS - 1) * step, hash_max, true); 381 | }); 382 | 383 | queue.run(); 384 | 385 | std::cout << "Done processing hashes." << std::endl; 386 | 387 | std::vector> timed_out; 388 | 389 | for (auto &thread_context : queue.stores) { 390 | std::cout << "One thread found " << thread_context.optimizations.size() << std::endl; 391 | optimizations.insert( 392 | optimizations.end(), 393 | thread_context.optimizations.begin(), 394 | thread_context.optimizations.end()); 395 | timed_out.insert( 396 | timed_out.end(), 397 | thread_context.timed_out.begin(), 398 | thread_context.timed_out.end()); 399 | } 400 | 401 | std::cout << "TIMED OUT COMPARISONS: " << timed_out.size() << std::endl; 402 | for (auto &pair : timed_out) { 403 | std::cout << " "; 404 | print(pair.first); 405 | std::cout << " "; 406 | print(pair.second); 407 | std::cout << std::endl; 408 | } 409 | } 410 | 411 | int main() { 412 | try { 413 | std::unordered_set non_optimal; 414 | std::multimap combined_buckets; 415 | std::vector> optimizations; 416 | enumerate_concurrent(2, combined_buckets, non_optimal); 417 | process_hashes_concurrent(combined_buckets, optimizations, 0, 0x100000000); 418 | std::cout << "Done processing, now results" << std::endl; 419 | for (const auto &pair : optimizations) { 420 | const auto &original = pair.first; 421 | non_optimal.insert(original); 422 | } 423 | combined_buckets.clear(); 424 | enumerate_concurrent(3, combined_buckets, non_optimal); 425 | std::cout << "Done with enumeration -- now processing" << std::endl; 426 | uint32_t lastHash = 0xFFFFFFFF; 427 | for (const auto &pair : combined_buckets) { 428 | const auto hash = pair.first; 429 | const auto seq = pair.second; 430 | if (hash != lastHash) { 431 | std::cout << "-" << std::endl; 432 | lastHash = hash; 433 | } 434 | print(seq); 435 | std::cout << std::endl; 436 | } 437 | process_hashes_concurrent(combined_buckets, optimizations, 0, 0x100000000); 438 | 439 | std::cout << "Size: " << non_optimal.size() << std::endl; 440 | 441 | } catch (z3::exception & ex) { 442 | std::cout << "unexpected error: " << ex << "\n"; 443 | } 444 | } 445 | -------------------------------------------------------------------------------- /enumerator2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "stdint.h" 7 | #include "inttypes.h" 8 | #include "instructions2.h" 9 | #include "emulator2.h" 10 | #include "random_machine.h" 11 | #include "z3++.h" 12 | #include "abstract_machine.h" 13 | #include "fnv.h" 14 | #include "radix-sort.h" 15 | #include 16 | 17 | constexpr int max_cost = 140; 18 | 19 | z3::check_result canBeDifferent(z3::solver &s, const abstract_machine &ma, const abstract_machine &mb) { 20 | s.push(); 21 | s.add(!( 22 | ma._earlyExit == mb._earlyExit && 23 | ma._ccS == mb._ccS && 24 | ma._ccV == mb._ccV && 25 | ma._ccD == mb._ccD && 26 | ma._ccI == mb._ccI && 27 | ma._ccC == mb._ccC && 28 | ma._ccZ == mb._ccZ && 29 | ma._a == mb._a && 30 | ma._x == mb._x && 31 | ma._y == mb._y && 32 | ma._sp == mb._sp && 33 | ma._memory == mb._memory 34 | )); 35 | 36 | auto result = s.check(); 37 | s.pop(); 38 | return result; 39 | } 40 | 41 | typedef struct execution_hash { 42 | typedef std::array buffer_t; 43 | 44 | uint64_t alwaysIncluded; 45 | 46 | buffer_t as_buffer() const { 47 | buffer_t result = {0}; 48 | result[0] = alwaysIncluded; 49 | result[1] = alwaysIncluded >> 8; 50 | result[2] = alwaysIncluded >> 16; 51 | result[3] = alwaysIncluded >> 24; 52 | result[4] = alwaysIncluded >> 32; 53 | result[5] = alwaysIncluded >> 40; 54 | result[6] = alwaysIncluded >> 48; 55 | result[7] = alwaysIncluded >> 56; 56 | return result; 57 | } 58 | 59 | static execution_hash from_buffer(buffer_t buffer) { 60 | execution_hash result; 61 | result.alwaysIncluded = buffer[0] 62 | | (((uint64_t)buffer[1]) << 8) 63 | | (((uint64_t)buffer[2]) << 16) 64 | | (((uint64_t)buffer[3]) << 24) 65 | | (((uint64_t)buffer[4]) << 32) 66 | | (((uint64_t)buffer[5]) << 40) 67 | | (((uint64_t)buffer[6]) << 48) 68 | | (((uint64_t)buffer[7]) << 56); 69 | return result; 70 | } 71 | } execution_hash; 72 | 73 | static const random_machine initial_machines[16] { 74 | // machines with all 0s or all 1s 75 | random_machine(0), 76 | random_machine(0xffffffff), 77 | // simple patterns 78 | random_machine(0x12345678), 79 | random_machine(0xCAFEFACE), 80 | // hex digits of PI 81 | random_machine(0x243F6A88), 82 | random_machine(0x85A308D3), 83 | random_machine(0x13198A2E), 84 | random_machine(0x03707344), 85 | random_machine(0xA4093822), 86 | random_machine(0x299F31D0), 87 | random_machine(0x082EFA98), 88 | random_machine(0xEC4E6C89), 89 | random_machine(0x452821E6), 90 | random_machine(0x38D01377), 91 | random_machine(0xBE5466CF), 92 | random_machine(0x34E90C6C), 93 | }; 94 | 95 | execution_hash hash(instruction_seq seq) { 96 | random_machine rms[16]; 97 | memcpy(rms, initial_machines, sizeof(rms)); 98 | emulator emu; 99 | for (auto instruction : seq.instructions) { 100 | for (auto &rm : rms) { 101 | emu.instruction(rm, instruction); 102 | } 103 | } 104 | 105 | const uint32_t seed = 0x18480949; 106 | fnv_hash hash_all(seed); 107 | for (auto &rm : rms) { 108 | hash_all.add(rm.hash()); 109 | } 110 | 111 | return execution_hash { 112 | .alwaysIncluded = hash_all.hash64(), 113 | }; 114 | } 115 | 116 | typedef struct hash_output_file { 117 | static const int hash_size_used = 8; 118 | static const int hash_size = 8; 119 | static const int instructions_size = 14; 120 | static const int total_size = 22; 121 | std::ofstream file; 122 | 123 | hash_output_file(uint8_t cost, bool trunc) : file("out/result-" + std::to_string(cost) + ".dat", std::ofstream::binary | std::ofstream::out | (trunc ? std::ofstream::trunc : std::ofstream::app)) {} 124 | 125 | void write(const execution_hash &hash, const instruction_seq &seq) { 126 | write_hash(hash); 127 | write_instructions(seq); 128 | } 129 | 130 | void write_hash(const execution_hash &hash) { 131 | auto data = hash.as_buffer(); 132 | file.write((char*)data.data(), data.size()); 133 | } 134 | 135 | void write_instructions(const instruction_seq &seq) { 136 | for (auto &instruction : seq.instructions) { 137 | file.put(instruction.data); 138 | file.put(instruction.data >> 8); 139 | } 140 | } 141 | } hash_output_file; 142 | 143 | typedef struct hash_input_file { 144 | std::ifstream file; 145 | execution_hash last; 146 | 147 | hash_input_file(uint8_t cost) : file("out/result-" + std::to_string(cost) + ".dat", std::ifstream::binary | std::ifstream::in) { 148 | last.alwaysIncluded = 0; 149 | } 150 | 151 | // Assumes the input file is sorted by hash. Reads until it 152 | // finds items with the given hash, and returns a vector of 153 | // the results. Consumes input, so each hash argument must 154 | // be greater than the last. 155 | std::vector get_hashes(uint64_t hash) { 156 | std::vector result; 157 | char buffer[hash_output_file::total_size]; 158 | 159 | if (last.alwaysIncluded == hash) { 160 | result.push_back(last); 161 | } 162 | 163 | while (!file.eof() && last.alwaysIncluded <= hash) { 164 | std::cout << "hash: " << last.alwaysIncluded << std::endl; 165 | file.read(buffer, hash_output_file::total_size); 166 | execution_hash::buffer_t hash_buffer; 167 | for (uint32_t i = 0; i < hash_buffer.size(); i++) { 168 | hash_buffer[i] = buffer[i]; 169 | } 170 | last = execution_hash::from_buffer(hash_buffer); 171 | if (last.alwaysIncluded == hash) { 172 | result.push_back(last); 173 | } 174 | } 175 | 176 | return result; 177 | } 178 | } hash_input_file; 179 | 180 | void display_hash_result(uint8_t *buffer) { 181 | // diplay hash 182 | uint64_t hash = 0; 183 | for (int i = 7; i >= 0; i--) { 184 | hash <<= 8; 185 | hash |= buffer[i]; 186 | } 187 | 188 | printf("%016" PRIx64 " ", hash); 189 | for (int i = hash_output_file::hash_size; i < hash_output_file::total_size; i += 2) { 190 | uint16_t data = buffer[i] + (buffer[i + 1] << 8); 191 | instruction ins; 192 | ins.data = data; 193 | if (ins.name() == instruction_name::NONE) { break; } 194 | for (const auto &info : instructions) { 195 | if (ins.name() == info.ins.name() && ins.mode() == info.ins.mode()) { 196 | std::cout << info.desc << " " << addr_mode_operand_name(ins.mode(), ins.number()) << "; "; 197 | } 198 | } 199 | } 200 | std::cout << std::endl; 201 | } 202 | 203 | typedef struct output_file_manager { 204 | uint8_t start; 205 | std::vector outfiles; 206 | 207 | output_file_manager(uint8_t start) : start(start) { 208 | for (int i = start; i <= max_cost; i++) { 209 | outfiles.push_back(hash_output_file(i, false)); 210 | } 211 | } 212 | 213 | hash_output_file &get_file(uint8_t cost) { 214 | return outfiles.at(cost - start); 215 | } 216 | } output_file_manager; 217 | 218 | int main(int argc, char **argv) { 219 | if (argc < 2) { 220 | std::cerr << "Usage: " << std::endl; 221 | return 1; 222 | } 223 | 224 | // Create all of the output files. 225 | std::vector outfiles; 226 | std::string arg1(argv[1]); 227 | if (arg1 == "init") { 228 | for (int i = 0; i <= max_cost; i++) { 229 | outfiles.push_back(hash_output_file(i, true)); 230 | } 231 | 232 | std::cout << "Initializing" << std::endl; 233 | instruction_seq empty_seq; 234 | auto hash_result = hash(empty_seq); 235 | outfiles[0].write(hash_result, empty_seq); 236 | 237 | int total_instructions = 0; 238 | for (auto &instruction : instructions) { 239 | int variants = addr_mode_variants(instruction.ins.mode()); 240 | for (int variant = 0; variant < variants; variant++) { 241 | total_instructions++; 242 | instruction_seq seq; 243 | instruction_info instruction_variant = instruction; 244 | instruction_variant.ins = instruction_variant.ins.number(variant); 245 | seq = seq.add(instruction_variant); 246 | auto hash_result = hash(seq); 247 | outfiles[seq.cycles].write(hash_result, seq); 248 | } 249 | } 250 | 251 | std::cout << "Seeded " << total_instructions << " total instructions" << std::endl; 252 | } else if (arg1 == "view") { 253 | std::string arg2(argv[2]); 254 | std::ifstream view_file(arg2, std::ifstream::binary | std::ifstream::in); 255 | 256 | std::cout << "Opening " << arg2 << std::endl; 257 | while (!view_file.eof()) { 258 | char buffer[hash_output_file::total_size * 256]; 259 | view_file.read(buffer, hash_output_file::total_size * 256); 260 | std::streamsize dataSize = view_file.gcount(); 261 | for (int j = 0; j < dataSize; j += hash_output_file::total_size) { 262 | display_hash_result((uint8_t*)(buffer + j)); 263 | } 264 | } 265 | } else { 266 | int target = std::stoi(argv[1]); 267 | output_file_manager output_files(target + 1); 268 | std::string file_name = std::string("out/result-") + argv[1] + ".dat"; 269 | std::cout << "Processing sequences with length " << target << std::endl; 270 | 271 | // sort the file by hash 272 | std::cout << "Sorting file:" << std::endl; 273 | radix_sort(file_name.c_str(), hash_output_file::total_size, hash_output_file::hash_size_used * 8); 274 | 275 | ProfilerStart("gperf-profile.log"); 276 | 277 | std::ifstream view_file(file_name, std::ifstream::binary | std::ifstream::in); 278 | // For each instruction type 279 | for (const auto &ins_info : instructions) { 280 | std::cout << "INSTRUCTION: " << (int)ins_info.ins.name() << std::endl; 281 | int variants = addr_mode_variants(ins_info.ins.mode()); 282 | // For each variant of the instruction 283 | for (int variant = 0; variant < variants; variant++) { 284 | std::cout << "VARIANT: " << variant << std::endl; 285 | 286 | view_file.clear(); 287 | view_file.seekg(0, std::ifstream::beg); 288 | // For each section of the file 289 | while (!view_file.eof()) { 290 | char buffer[hash_output_file::total_size * 256]; 291 | view_file.read(buffer, hash_output_file::total_size * 256); 292 | std::streamsize data_size = view_file.gcount(); 293 | // For each instruction sequence hash in the file 294 | for (int buffer_start = 0; buffer_start < data_size; buffer_start += hash_output_file::total_size) { 295 | uint8_t *buffer_ptr = (uint8_t*)(buffer + buffer_start); 296 | instruction_seq seq; 297 | // For each instruction in the sequence 298 | for (int i = hash_output_file::hash_size; i < hash_output_file::total_size; i += 2) { 299 | uint16_t data = buffer_ptr[i] | (buffer_ptr[i + 1] << 8); 300 | instruction ins; 301 | ins.data = data; 302 | if (ins.name() == instruction_name::NONE) { break; } 303 | for (const auto info : instructions) { 304 | if (ins.name() == info.ins.name() && ins.mode() == info.ins.mode()) { 305 | seq = seq.add(info); 306 | break; 307 | } 308 | } 309 | } 310 | 311 | instruction_info next_instruction = ins_info; 312 | next_instruction.ins = next_instruction.ins.number(variant); 313 | seq = seq.add(next_instruction); 314 | auto hash_result = hash(seq); 315 | output_files.get_file(seq.cycles).write(hash_result, seq); 316 | } 317 | } 318 | } 319 | } 320 | 321 | ProfilerStop(); 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /files.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "sys/stat.h" 6 | #include "stdint.h" 7 | #include "stdio.h" 8 | #include "stdlib.h" 9 | #include "errno.h" 10 | #include "string.h" 11 | 12 | std::string int_to_hex(int i) { 13 | std::stringstream stream; 14 | stream << std::setfill('0') << std::setw(2) << std::hex << i; 15 | return stream.str(); 16 | } 17 | 18 | static FILE* files[256*256]; 19 | 20 | void makeFiles() { 21 | mkdir("out", S_IRWXU); 22 | for (int i = 0; i < 256; i++) { 23 | std::string hex = "out/" + int_to_hex(i); 24 | mkdir(hex.c_str(), S_IRWXU); 25 | for (int j = 0; j < 256; j++) { 26 | std::string file = hex + "/" + int_to_hex(j); 27 | FILE *desc = fopen(file.c_str(), "wb"); 28 | if (desc == NULL) { 29 | int err = errno; 30 | std::cerr << "Error opening file: " << strerror(err) << " " << file << std::endl; 31 | exit(-1); 32 | } 33 | fclose(desc); 34 | std::cout << file << std::endl; 35 | } 36 | } 37 | } 38 | 39 | int main(int argc, char **argv) { 40 | makeFiles(); 41 | } 42 | -------------------------------------------------------------------------------- /fnv.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "stdint.h" 4 | 5 | typedef struct fnv_hash { 6 | uint64_t state = 14695981039346656037u; 7 | 8 | fnv_hash(uint32_t seed) { 9 | add(seed); 10 | } 11 | 12 | fnv_hash& add(uint8_t value) { 13 | state = state ^ value; 14 | state = state * 1099511628211u; 15 | return *this; 16 | } 17 | 18 | fnv_hash& add(uint16_t value) { 19 | return this->add((uint8_t)value).add((uint8_t)(value >> 8)); 20 | } 21 | 22 | fnv_hash& add(uint32_t value) { 23 | return this->add((uint16_t)value).add((uint16_t)(value >> 16)); 24 | } 25 | 26 | fnv_hash& add(uint64_t value) { 27 | return this->add((uint32_t)value).add((uint32_t)(value >> 32)); 28 | } 29 | 30 | uint64_t hash64() { 31 | return state; 32 | } 33 | 34 | uint32_t hash32() { 35 | auto hash = hash64(); 36 | return hash ^ (hash >> 32); 37 | } 38 | 39 | uint16_t hash16() { 40 | auto hash = hash32(); 41 | return hash ^ (hash >> 16); 42 | } 43 | 44 | uint8_t hash8() { 45 | auto hash = hash16(); 46 | return hash ^ (hash >> 8); 47 | } 48 | } fnv_hash; 49 | -------------------------------------------------------------------------------- /gperf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | make enumerator2 4 | 5 | env LD_LIBRARY_PATH=. ./enumerator2 40 6 | 7 | google-pprof --web enumerator2 gperf-profile.log 8 | google-pprof --callgrind enumerator2 gperf-profile.log > perf.callgrind 9 | 10 | kcachegrind perf.callgrind 11 | -------------------------------------------------------------------------------- /ideas.txt: -------------------------------------------------------------------------------- 1 | Liveness checks: 2 | - a, x, y, flags ; 4 bytes 3 | 4 | - always live: sp, memory, ip ; 4 byte hash 5 | 6 | 8 bytes: instructions (up to 4 instructions) 7 | 8 | Algorithm: 9 | 1. Write in 0.out the maybe-live registers and 10 | hash of the always live parts resulting from 11 | just the initial random machines with no instructions 12 | added. 13 | 2. Load the smallest file yet to be read. 14 | Sort the file by hash and then cost using a radix sort. 15 | (Requires ~48 passes of the file assuming a binary radix, but can be done 16 | completely outside of memory). 17 | 18 | For each sequence in the file: 19 | - Add each possible instruction to the end of the sequence. 20 | - If the sequence can be optimized at all, then ignore it and continue. 21 | - Calculate the new registers and hash. 22 | - Output the results to the appropriate files based on cost. 23 | 24 | Use a merge-sort-like algorithm to find all sequences with the same 25 | hash from this file and all files with lower cost. Compare them to 26 | discover equivalences depending on liveness. If a sequence is unique 27 | in some liveness criteria, then output it to a new file for the cost. 28 | If a sequence has an unconditional optimization, then don't output it there. 29 | Send all optimization patterns to the optimizations file. 30 | 3. Swap the new file of unique sequences for the original output file. 31 | 4. Go to 2. 32 | 33 | Memory models: 34 | - Model stack effects -- memory decays once it's off the stack. An interrupt 35 | can trigger at any time, causing some decay of the stacks. The harware stack 36 | contents can decay at any time because interrupts push 3 bytes to the stack. 37 | The software stack is under our control. We can provide a red zone of a few 38 | bytes 39 | -------------------------------------------------------------------------------- /include/z3.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | Copyright (c) 2007 Microsoft Corporation 3 | 4 | Module Name: 5 | 6 | z3.h 7 | 8 | Abstract: 9 | 10 | Z3 API. 11 | 12 | Author: 13 | 14 | Nikolaj Bjorner (nbjorner) 15 | Leonardo de Moura (leonardo) 2007-06-8 16 | 17 | Notes: 18 | 19 | --*/ 20 | 21 | #ifndef Z3_H_ 22 | #define Z3_H_ 23 | 24 | #include 25 | #include "z3_macros.h" 26 | #include "z3_api.h" 27 | #include "z3_ast_containers.h" 28 | #include "z3_algebraic.h" 29 | #include "z3_polynomial.h" 30 | #include "z3_rcf.h" 31 | #include "z3_fixedpoint.h" 32 | #include "z3_optimization.h" 33 | #include "z3_interp.h" 34 | #include "z3_fpa.h" 35 | #include "z3_spacer.h" 36 | #endif 37 | 38 | -------------------------------------------------------------------------------- /include/z3_algebraic.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | Copyright (c) 2012 Microsoft Corporation 3 | 4 | Module Name: 5 | 6 | z3_algebraic.h 7 | 8 | Abstract: 9 | 10 | Additional APIs for handling Z3 algebraic numbers encoded as 11 | Z3_ASTs 12 | 13 | Author: 14 | 15 | Leonardo de Moura (leonardo) 2012-12-07 16 | 17 | Notes: 18 | 19 | --*/ 20 | 21 | #ifndef Z3_ALGEBRAIC_H_ 22 | #define Z3_ALGEBRAIC_H_ 23 | 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif // __cplusplus 27 | 28 | /** \defgroup capi C API */ 29 | /*@{*/ 30 | 31 | /** @name Algebraic Numbers */ 32 | /*@{*/ 33 | /** 34 | \brief Return Z3_TRUE if \c can be used as value in the Z3 real algebraic 35 | number package. 36 | 37 | def_API('Z3_algebraic_is_value', BOOL, (_in(CONTEXT), _in(AST))) 38 | */ 39 | Z3_bool Z3_API Z3_algebraic_is_value(Z3_context c, Z3_ast a); 40 | 41 | /** 42 | \brief Return the Z3_TRUE if \c a is positive, and Z3_FALSE otherwise. 43 | 44 | \pre Z3_algebraic_is_value(c, a) 45 | 46 | def_API('Z3_algebraic_is_pos', BOOL, (_in(CONTEXT), _in(AST))) 47 | */ 48 | Z3_bool Z3_API Z3_algebraic_is_pos(Z3_context c, Z3_ast a); 49 | 50 | /** 51 | \brief Return the Z3_TRUE if \c a is negative, and Z3_FALSE otherwise. 52 | 53 | \pre Z3_algebraic_is_value(c, a) 54 | 55 | def_API('Z3_algebraic_is_neg', BOOL, (_in(CONTEXT), _in(AST))) 56 | */ 57 | Z3_bool Z3_API Z3_algebraic_is_neg(Z3_context c, Z3_ast a); 58 | 59 | /** 60 | \brief Return the Z3_TRUE if \c a is zero, and Z3_FALSE otherwise. 61 | 62 | \pre Z3_algebraic_is_value(c, a) 63 | 64 | def_API('Z3_algebraic_is_zero', BOOL, (_in(CONTEXT), _in(AST))) 65 | */ 66 | Z3_bool Z3_API Z3_algebraic_is_zero(Z3_context c, Z3_ast a); 67 | 68 | /** 69 | \brief Return 1 if \c a is positive, 0 if \c a is zero, and -1 if \c a is negative. 70 | 71 | \pre Z3_algebraic_is_value(c, a) 72 | 73 | def_API('Z3_algebraic_sign', INT, (_in(CONTEXT), _in(AST))) 74 | */ 75 | int Z3_API Z3_algebraic_sign(Z3_context c, Z3_ast a); 76 | 77 | /** 78 | \brief Return the value a + b. 79 | 80 | \pre Z3_algebraic_is_value(c, a) 81 | \pre Z3_algebraic_is_value(c, b) 82 | \post Z3_algebraic_is_value(c, result) 83 | 84 | def_API('Z3_algebraic_add', AST, (_in(CONTEXT), _in(AST), _in(AST))) 85 | */ 86 | Z3_ast Z3_API Z3_algebraic_add(Z3_context c, Z3_ast a, Z3_ast b); 87 | 88 | /** 89 | \brief Return the value a - b. 90 | 91 | \pre Z3_algebraic_is_value(c, a) 92 | \pre Z3_algebraic_is_value(c, b) 93 | \post Z3_algebraic_is_value(c, result) 94 | 95 | def_API('Z3_algebraic_sub', AST, (_in(CONTEXT), _in(AST), _in(AST))) 96 | */ 97 | Z3_ast Z3_API Z3_algebraic_sub(Z3_context c, Z3_ast a, Z3_ast b); 98 | 99 | /** 100 | \brief Return the value a * b. 101 | 102 | \pre Z3_algebraic_is_value(c, a) 103 | \pre Z3_algebraic_is_value(c, b) 104 | \post Z3_algebraic_is_value(c, result) 105 | 106 | def_API('Z3_algebraic_mul', AST, (_in(CONTEXT), _in(AST), _in(AST))) 107 | */ 108 | Z3_ast Z3_API Z3_algebraic_mul(Z3_context c, Z3_ast a, Z3_ast b); 109 | 110 | /** 111 | \brief Return the value a / b. 112 | 113 | \pre Z3_algebraic_is_value(c, a) 114 | \pre Z3_algebraic_is_value(c, b) 115 | \pre !Z3_algebraic_is_zero(c, b) 116 | \post Z3_algebraic_is_value(c, result) 117 | 118 | def_API('Z3_algebraic_div', AST, (_in(CONTEXT), _in(AST), _in(AST))) 119 | */ 120 | Z3_ast Z3_API Z3_algebraic_div(Z3_context c, Z3_ast a, Z3_ast b); 121 | 122 | /** 123 | \brief Return the a^(1/k) 124 | 125 | \pre Z3_algebraic_is_value(c, a) 126 | \pre k is even => !Z3_algebraic_is_neg(c, a) 127 | \post Z3_algebraic_is_value(c, result) 128 | 129 | def_API('Z3_algebraic_root', AST, (_in(CONTEXT), _in(AST), _in(UINT))) 130 | */ 131 | Z3_ast Z3_API Z3_algebraic_root(Z3_context c, Z3_ast a, unsigned k); 132 | 133 | /** 134 | \brief Return the a^k 135 | 136 | \pre Z3_algebraic_is_value(c, a) 137 | \post Z3_algebraic_is_value(c, result) 138 | 139 | def_API('Z3_algebraic_power', AST, (_in(CONTEXT), _in(AST), _in(UINT))) 140 | */ 141 | Z3_ast Z3_API Z3_algebraic_power(Z3_context c, Z3_ast a, unsigned k); 142 | 143 | /** 144 | \brief Return Z3_TRUE if a < b, and Z3_FALSE otherwise. 145 | 146 | \pre Z3_algebraic_is_value(c, a) 147 | \pre Z3_algebraic_is_value(c, b) 148 | 149 | def_API('Z3_algebraic_lt', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) 150 | */ 151 | Z3_bool Z3_API Z3_algebraic_lt(Z3_context c, Z3_ast a, Z3_ast b); 152 | 153 | /** 154 | \brief Return Z3_TRUE if a > b, and Z3_FALSE otherwise. 155 | 156 | \pre Z3_algebraic_is_value(c, a) 157 | \pre Z3_algebraic_is_value(c, b) 158 | 159 | def_API('Z3_algebraic_gt', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) 160 | */ 161 | Z3_bool Z3_API Z3_algebraic_gt(Z3_context c, Z3_ast a, Z3_ast b); 162 | 163 | /** 164 | \brief Return Z3_TRUE if a <= b, and Z3_FALSE otherwise. 165 | 166 | \pre Z3_algebraic_is_value(c, a) 167 | \pre Z3_algebraic_is_value(c, b) 168 | 169 | def_API('Z3_algebraic_le', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) 170 | */ 171 | Z3_bool Z3_API Z3_algebraic_le(Z3_context c, Z3_ast a, Z3_ast b); 172 | 173 | /** 174 | \brief Return Z3_TRUE if a >= b, and Z3_FALSE otherwise. 175 | 176 | \pre Z3_algebraic_is_value(c, a) 177 | \pre Z3_algebraic_is_value(c, b) 178 | 179 | def_API('Z3_algebraic_ge', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) 180 | */ 181 | Z3_bool Z3_API Z3_algebraic_ge(Z3_context c, Z3_ast a, Z3_ast b); 182 | 183 | /** 184 | \brief Return Z3_TRUE if a == b, and Z3_FALSE otherwise. 185 | 186 | \pre Z3_algebraic_is_value(c, a) 187 | \pre Z3_algebraic_is_value(c, b) 188 | 189 | def_API('Z3_algebraic_eq', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) 190 | */ 191 | Z3_bool Z3_API Z3_algebraic_eq(Z3_context c, Z3_ast a, Z3_ast b); 192 | 193 | /** 194 | \brief Return Z3_TRUE if a != b, and Z3_FALSE otherwise. 195 | 196 | \pre Z3_algebraic_is_value(c, a) 197 | \pre Z3_algebraic_is_value(c, b) 198 | 199 | def_API('Z3_algebraic_neq', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) 200 | */ 201 | Z3_bool Z3_API Z3_algebraic_neq(Z3_context c, Z3_ast a, Z3_ast b); 202 | 203 | /** 204 | \brief Given a multivariate polynomial p(x_0, ..., x_{n-1}, x_n), returns the 205 | roots of the univariate polynomial p(a[0], ..., a[n-1], x_n). 206 | 207 | \pre p is a Z3 expression that contains only arithmetic terms and free variables. 208 | \pre forall i in [0, n) Z3_algebraic_is_value(c, a[i]) 209 | \post forall r in result Z3_algebraic_is_value(c, result) 210 | 211 | def_API('Z3_algebraic_roots', AST_VECTOR, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST))) 212 | */ 213 | Z3_ast_vector Z3_API Z3_algebraic_roots(Z3_context c, Z3_ast p, unsigned n, Z3_ast a[]); 214 | 215 | /** 216 | \brief Given a multivariate polynomial p(x_0, ..., x_{n-1}), return the 217 | sign of p(a[0], ..., a[n-1]). 218 | 219 | \pre p is a Z3 expression that contains only arithmetic terms and free variables. 220 | \pre forall i in [0, n) Z3_algebraic_is_value(c, a[i]) 221 | 222 | def_API('Z3_algebraic_eval', INT, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST))) 223 | */ 224 | int Z3_API Z3_algebraic_eval(Z3_context c, Z3_ast p, unsigned n, Z3_ast a[]); 225 | 226 | /*@}*/ 227 | /*@}*/ 228 | 229 | #ifdef __cplusplus 230 | } 231 | #endif // __cplusplus 232 | 233 | #endif 234 | -------------------------------------------------------------------------------- /include/z3_ast_containers.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | Copyright (c) 2015 Microsoft Corporation 3 | 4 | Module Name: 5 | 6 | z3_ast_containers.h 7 | 8 | Abstract: 9 | 10 | AST Containers 11 | 12 | Author: 13 | 14 | Christoph M. Wintersteiger (cwinter) 2015-12-03 15 | 16 | Notes: 17 | 18 | --*/ 19 | #ifndef Z3_AST_CONTAINERS_H_ 20 | #define Z3_AST_CONTAINERS_H_ 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif // __cplusplus 25 | 26 | /** \defgroup capi C API */ 27 | /*@{*/ 28 | 29 | /** @name AST vectors */ 30 | /*@{*/ 31 | /** 32 | \brief Return an empty AST vector. 33 | 34 | \remark Reference counting must be used to manage AST vectors, even when the Z3_context was 35 | created using #Z3_mk_context instead of #Z3_mk_context_rc. 36 | 37 | def_API('Z3_mk_ast_vector', AST_VECTOR, (_in(CONTEXT),)) 38 | */ 39 | Z3_ast_vector Z3_API Z3_mk_ast_vector(Z3_context c); 40 | 41 | /** 42 | \brief Increment the reference counter of the given AST vector. 43 | 44 | def_API('Z3_ast_vector_inc_ref', VOID, (_in(CONTEXT), _in(AST_VECTOR))) 45 | */ 46 | void Z3_API Z3_ast_vector_inc_ref(Z3_context c, Z3_ast_vector v); 47 | 48 | /** 49 | \brief Decrement the reference counter of the given AST vector. 50 | 51 | def_API('Z3_ast_vector_dec_ref', VOID, (_in(CONTEXT), _in(AST_VECTOR))) 52 | */ 53 | void Z3_API Z3_ast_vector_dec_ref(Z3_context c, Z3_ast_vector v); 54 | 55 | /** 56 | \brief Return the size of the given AST vector. 57 | 58 | def_API('Z3_ast_vector_size', UINT, (_in(CONTEXT), _in(AST_VECTOR))) 59 | */ 60 | unsigned Z3_API Z3_ast_vector_size(Z3_context c, Z3_ast_vector v); 61 | 62 | /** 63 | \brief Return the AST at position \c i in the AST vector \c v. 64 | 65 | \pre i < Z3_ast_vector_size(c, v) 66 | 67 | def_API('Z3_ast_vector_get', AST, (_in(CONTEXT), _in(AST_VECTOR), _in(UINT))) 68 | */ 69 | Z3_ast Z3_API Z3_ast_vector_get(Z3_context c, Z3_ast_vector v, unsigned i); 70 | 71 | /** 72 | \brief Update position \c i of the AST vector \c v with the AST \c a. 73 | 74 | \pre i < Z3_ast_vector_size(c, v) 75 | 76 | def_API('Z3_ast_vector_set', VOID, (_in(CONTEXT), _in(AST_VECTOR), _in(UINT), _in(AST))) 77 | */ 78 | void Z3_API Z3_ast_vector_set(Z3_context c, Z3_ast_vector v, unsigned i, Z3_ast a); 79 | 80 | /** 81 | \brief Resize the AST vector \c v. 82 | 83 | def_API('Z3_ast_vector_resize', VOID, (_in(CONTEXT), _in(AST_VECTOR), _in(UINT))) 84 | */ 85 | void Z3_API Z3_ast_vector_resize(Z3_context c, Z3_ast_vector v, unsigned n); 86 | 87 | /** 88 | \brief Add the AST \c a in the end of the AST vector \c v. The size of \c v is increased by one. 89 | 90 | def_API('Z3_ast_vector_push', VOID, (_in(CONTEXT), _in(AST_VECTOR), _in(AST))) 91 | */ 92 | void Z3_API Z3_ast_vector_push(Z3_context c, Z3_ast_vector v, Z3_ast a); 93 | 94 | /** 95 | \brief Translate the AST vector \c v from context \c s into an AST vector in context \c t. 96 | 97 | def_API('Z3_ast_vector_translate', AST_VECTOR, (_in(CONTEXT), _in(AST_VECTOR), _in(CONTEXT))) 98 | */ 99 | Z3_ast_vector Z3_API Z3_ast_vector_translate(Z3_context s, Z3_ast_vector v, Z3_context t); 100 | 101 | /** 102 | \brief Convert AST vector into a string. 103 | 104 | def_API('Z3_ast_vector_to_string', STRING, (_in(CONTEXT), _in(AST_VECTOR))) 105 | */ 106 | Z3_string Z3_API Z3_ast_vector_to_string(Z3_context c, Z3_ast_vector v); 107 | 108 | /*@}*/ 109 | 110 | /** @name AST maps */ 111 | /*@{*/ 112 | /** 113 | \brief Return an empty mapping from AST to AST 114 | 115 | \remark Reference counting must be used to manage AST maps, even when the Z3_context was 116 | created using #Z3_mk_context instead of #Z3_mk_context_rc. 117 | 118 | def_API('Z3_mk_ast_map', AST_MAP, (_in(CONTEXT),) ) 119 | */ 120 | Z3_ast_map Z3_API Z3_mk_ast_map(Z3_context c); 121 | 122 | /** 123 | \brief Increment the reference counter of the given AST map. 124 | 125 | def_API('Z3_ast_map_inc_ref', VOID, (_in(CONTEXT), _in(AST_MAP))) 126 | */ 127 | void Z3_API Z3_ast_map_inc_ref(Z3_context c, Z3_ast_map m); 128 | 129 | /** 130 | \brief Decrement the reference counter of the given AST map. 131 | 132 | def_API('Z3_ast_map_dec_ref', VOID, (_in(CONTEXT), _in(AST_MAP))) 133 | */ 134 | void Z3_API Z3_ast_map_dec_ref(Z3_context c, Z3_ast_map m); 135 | 136 | /** 137 | \brief Return true if the map \c m contains the AST key \c k. 138 | 139 | def_API('Z3_ast_map_contains', BOOL, (_in(CONTEXT), _in(AST_MAP), _in(AST))) 140 | */ 141 | Z3_bool Z3_API Z3_ast_map_contains(Z3_context c, Z3_ast_map m, Z3_ast k); 142 | 143 | /** 144 | \brief Return the value associated with the key \c k. 145 | 146 | The procedure invokes the error handler if \c k is not in the map. 147 | 148 | def_API('Z3_ast_map_find', AST, (_in(CONTEXT), _in(AST_MAP), _in(AST))) 149 | */ 150 | Z3_ast Z3_API Z3_ast_map_find(Z3_context c, Z3_ast_map m, Z3_ast k); 151 | 152 | /** 153 | \brief Store/Replace a new key, value pair in the given map. 154 | 155 | def_API('Z3_ast_map_insert', VOID, (_in(CONTEXT), _in(AST_MAP), _in(AST), _in(AST))) 156 | */ 157 | void Z3_API Z3_ast_map_insert(Z3_context c, Z3_ast_map m, Z3_ast k, Z3_ast v); 158 | 159 | /** 160 | \brief Erase a key from the map. 161 | 162 | def_API('Z3_ast_map_erase', VOID, (_in(CONTEXT), _in(AST_MAP), _in(AST))) 163 | */ 164 | void Z3_API Z3_ast_map_erase(Z3_context c, Z3_ast_map m, Z3_ast k); 165 | 166 | /** 167 | \brief Remove all keys from the given map. 168 | 169 | def_API('Z3_ast_map_reset', VOID, (_in(CONTEXT), _in(AST_MAP))) 170 | */ 171 | void Z3_API Z3_ast_map_reset(Z3_context c, Z3_ast_map m); 172 | 173 | /** 174 | \brief Return the size of the given map. 175 | 176 | def_API('Z3_ast_map_size', UINT, (_in(CONTEXT), _in(AST_MAP))) 177 | */ 178 | unsigned Z3_API Z3_ast_map_size(Z3_context c, Z3_ast_map m); 179 | 180 | /** 181 | \brief Return the keys stored in the given map. 182 | 183 | def_API('Z3_ast_map_keys', AST_VECTOR, (_in(CONTEXT), _in(AST_MAP))) 184 | */ 185 | Z3_ast_vector Z3_API Z3_ast_map_keys(Z3_context c, Z3_ast_map m); 186 | 187 | /** 188 | \brief Convert the given map into a string. 189 | 190 | def_API('Z3_ast_map_to_string', STRING, (_in(CONTEXT), _in(AST_MAP))) 191 | */ 192 | Z3_string Z3_API Z3_ast_map_to_string(Z3_context c, Z3_ast_map m); 193 | /*@}*/ 194 | /*@}*/ 195 | 196 | #ifdef __cplusplus 197 | } 198 | #endif // __cplusplus 199 | 200 | #endif 201 | -------------------------------------------------------------------------------- /include/z3_fixedpoint.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | Copyright (c) 2015 Microsoft Corporation 3 | 4 | Module Name: 5 | 6 | z3_fixedpoint.h 7 | 8 | Abstract: 9 | 10 | Fixedpoint API 11 | 12 | Author: 13 | 14 | Christoph M. Wintersteiger (cwinter) 2015-12-03 15 | 16 | Notes: 17 | 18 | --*/ 19 | #ifndef Z3_FIXEDPOINT_H_ 20 | #define Z3_FIXEDPOINT_H_ 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif // __cplusplus 25 | 26 | /** \defgroup capi C API */ 27 | /*@{*/ 28 | 29 | /** @name Fixedpoint facilities */ 30 | /*@{*/ 31 | /** 32 | \brief Create a new fixedpoint context. 33 | 34 | \remark User must use #Z3_fixedpoint_inc_ref and #Z3_fixedpoint_dec_ref to manage fixedpoint objects. 35 | Even if the context was created using #Z3_mk_context instead of #Z3_mk_context_rc. 36 | 37 | def_API('Z3_mk_fixedpoint', FIXEDPOINT, (_in(CONTEXT), )) 38 | */ 39 | Z3_fixedpoint Z3_API Z3_mk_fixedpoint(Z3_context c); 40 | 41 | /** 42 | \brief Increment the reference counter of the given fixedpoint context 43 | 44 | def_API('Z3_fixedpoint_inc_ref', VOID, (_in(CONTEXT), _in(FIXEDPOINT))) 45 | */ 46 | void Z3_API Z3_fixedpoint_inc_ref(Z3_context c, Z3_fixedpoint d); 47 | 48 | /** 49 | \brief Decrement the reference counter of the given fixedpoint context. 50 | 51 | def_API('Z3_fixedpoint_dec_ref', VOID, (_in(CONTEXT), _in(FIXEDPOINT))) 52 | */ 53 | void Z3_API Z3_fixedpoint_dec_ref(Z3_context c, Z3_fixedpoint d); 54 | 55 | /** 56 | \brief Add a universal Horn clause as a named rule. 57 | The \c horn_rule should be of the form: 58 | 59 | \code 60 | horn_rule ::= (forall (bound-vars) horn_rule) 61 | | (=> atoms horn_rule) 62 | | atom 63 | \endcode 64 | 65 | def_API('Z3_fixedpoint_add_rule', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(AST), _in(SYMBOL))) 66 | */ 67 | void Z3_API Z3_fixedpoint_add_rule(Z3_context c, Z3_fixedpoint d, Z3_ast rule, Z3_symbol name); 68 | 69 | /** 70 | \brief Add a Database fact. 71 | 72 | \param c - context 73 | \param d - fixed point context 74 | \param r - relation signature for the row. 75 | \param num_args - number of columns for the given row. 76 | \param args - array of the row elements. 77 | 78 | The number of arguments \c num_args should be equal to the number 79 | of sorts in the domain of \c r. Each sort in the domain should be an integral 80 | (bit-vector, Boolean or or finite domain sort). 81 | 82 | The call has the same effect as adding a rule where \c r is applied to the arguments. 83 | 84 | def_API('Z3_fixedpoint_add_fact', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(FUNC_DECL), _in(UINT), _in_array(3, UINT))) 85 | */ 86 | void Z3_API Z3_fixedpoint_add_fact(Z3_context c, Z3_fixedpoint d, 87 | Z3_func_decl r, 88 | unsigned num_args, unsigned args[]); 89 | 90 | /** 91 | \brief Assert a constraint to the fixedpoint context. 92 | 93 | The constraints are used as background axioms when the fixedpoint engine uses the PDR mode. 94 | They are ignored for standard Datalog mode. 95 | 96 | def_API('Z3_fixedpoint_assert', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(AST))) 97 | */ 98 | void Z3_API Z3_fixedpoint_assert(Z3_context c, Z3_fixedpoint d, Z3_ast axiom); 99 | 100 | /** 101 | \brief Pose a query against the asserted rules. 102 | 103 | \code 104 | query ::= (exists (bound-vars) query) 105 | | literals 106 | \endcode 107 | 108 | query returns 109 | - Z3_L_FALSE if the query is unsatisfiable. 110 | - Z3_L_TRUE if the query is satisfiable. Obtain the answer by calling #Z3_fixedpoint_get_answer. 111 | - Z3_L_UNDEF if the query was interrupted, timed out or otherwise failed. 112 | 113 | def_API('Z3_fixedpoint_query', INT, (_in(CONTEXT), _in(FIXEDPOINT), _in(AST))) 114 | */ 115 | Z3_lbool Z3_API Z3_fixedpoint_query(Z3_context c, Z3_fixedpoint d, Z3_ast query); 116 | 117 | /** 118 | \brief Pose multiple queries against the asserted rules. 119 | 120 | The queries are encoded as relations (function declarations). 121 | 122 | query returns 123 | - Z3_L_FALSE if the query is unsatisfiable. 124 | - Z3_L_TRUE if the query is satisfiable. Obtain the answer by calling #Z3_fixedpoint_get_answer. 125 | - Z3_L_UNDEF if the query was interrupted, timed out or otherwise failed. 126 | 127 | def_API('Z3_fixedpoint_query_relations', INT, (_in(CONTEXT), _in(FIXEDPOINT), _in(UINT), _in_array(2, FUNC_DECL))) 128 | */ 129 | Z3_lbool Z3_API Z3_fixedpoint_query_relations( 130 | Z3_context c, Z3_fixedpoint d, 131 | unsigned num_relations, Z3_func_decl const relations[]); 132 | 133 | /** 134 | \brief Retrieve a formula that encodes satisfying answers to the query. 135 | 136 | 137 | When used in Datalog mode, the returned answer is a disjunction of conjuncts. 138 | Each conjunct encodes values of the bound variables of the query that are satisfied. 139 | In PDR mode, the returned answer is a single conjunction. 140 | 141 | When used in Datalog mode the previous call to Z3_fixedpoint_query must have returned Z3_L_TRUE. 142 | When used with the PDR engine, the previous call must have been either Z3_L_TRUE or Z3_L_FALSE. 143 | 144 | def_API('Z3_fixedpoint_get_answer', AST, (_in(CONTEXT), _in(FIXEDPOINT))) 145 | */ 146 | Z3_ast Z3_API Z3_fixedpoint_get_answer(Z3_context c, Z3_fixedpoint d); 147 | 148 | /** 149 | \brief Retrieve a string that describes the last status returned by #Z3_fixedpoint_query. 150 | 151 | Use this method when #Z3_fixedpoint_query returns Z3_L_UNDEF. 152 | 153 | def_API('Z3_fixedpoint_get_reason_unknown', STRING, (_in(CONTEXT), _in(FIXEDPOINT) )) 154 | */ 155 | Z3_string Z3_API Z3_fixedpoint_get_reason_unknown(Z3_context c, Z3_fixedpoint d); 156 | 157 | /** 158 | \brief Update a named rule. 159 | A rule with the same name must have been previously created. 160 | 161 | def_API('Z3_fixedpoint_update_rule', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(AST), _in(SYMBOL))) 162 | */ 163 | void Z3_API Z3_fixedpoint_update_rule(Z3_context c, Z3_fixedpoint d, Z3_ast a, Z3_symbol name); 164 | 165 | /** 166 | \brief Query the PDR engine for the maximal levels properties are known about predicate. 167 | 168 | This call retrieves the maximal number of relevant unfoldings 169 | of \c pred with respect to the current exploration state. 170 | Note: this functionality is PDR specific. 171 | 172 | def_API('Z3_fixedpoint_get_num_levels', UINT, (_in(CONTEXT), _in(FIXEDPOINT), _in(FUNC_DECL))) 173 | */ 174 | unsigned Z3_API Z3_fixedpoint_get_num_levels(Z3_context c, Z3_fixedpoint d, Z3_func_decl pred); 175 | 176 | /** 177 | Retrieve the current cover of \c pred up to \c level unfoldings. 178 | Return just the delta that is known at \c level. To 179 | obtain the full set of properties of \c pred one should query 180 | at \c level+1 , \c level+2 etc, and include \c level=-1. 181 | 182 | Note: this functionality is PDR specific. 183 | 184 | def_API('Z3_fixedpoint_get_cover_delta', AST, (_in(CONTEXT), _in(FIXEDPOINT), _in(INT), _in(FUNC_DECL))) 185 | */ 186 | Z3_ast Z3_API Z3_fixedpoint_get_cover_delta(Z3_context c, Z3_fixedpoint d, int level, Z3_func_decl pred); 187 | 188 | /** 189 | \brief Add property about the predicate \c pred. 190 | Add a property of predicate \c pred at \c level. 191 | It gets pushed forward when possible. 192 | 193 | Note: level = -1 is treated as the fixedpoint. So passing -1 for the \c level 194 | means that the property is true of the fixed-point unfolding with respect to \c pred. 195 | 196 | Note: this functionality is PDR specific. 197 | 198 | def_API('Z3_fixedpoint_add_cover', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(INT), _in(FUNC_DECL), _in(AST))) 199 | */ 200 | void Z3_API Z3_fixedpoint_add_cover(Z3_context c, Z3_fixedpoint d, int level, Z3_func_decl pred, Z3_ast property); 201 | 202 | /** 203 | \brief Retrieve statistics information from the last call to #Z3_fixedpoint_query. 204 | 205 | def_API('Z3_fixedpoint_get_statistics', STATS, (_in(CONTEXT), _in(FIXEDPOINT))) 206 | */ 207 | Z3_stats Z3_API Z3_fixedpoint_get_statistics(Z3_context c, Z3_fixedpoint d); 208 | 209 | /** 210 | \brief Register relation as Fixedpoint defined. 211 | Fixedpoint defined relations have least-fixedpoint semantics. 212 | For example, the relation is empty if it does not occur 213 | in a head or a fact. 214 | 215 | def_API('Z3_fixedpoint_register_relation', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(FUNC_DECL))) 216 | */ 217 | void Z3_API Z3_fixedpoint_register_relation(Z3_context c, Z3_fixedpoint d, Z3_func_decl f); 218 | 219 | /** 220 | \brief Configure the predicate representation. 221 | 222 | It sets the predicate to use a set of domains given by the list of symbols. 223 | The domains given by the list of symbols must belong to a set 224 | of built-in domains. 225 | 226 | def_API('Z3_fixedpoint_set_predicate_representation', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(FUNC_DECL), _in(UINT), _in_array(3, SYMBOL))) 227 | */ 228 | void Z3_API Z3_fixedpoint_set_predicate_representation( 229 | Z3_context c, 230 | Z3_fixedpoint d, 231 | Z3_func_decl f, 232 | unsigned num_relations, 233 | Z3_symbol const relation_kinds[]); 234 | 235 | /** 236 | \brief Retrieve set of rules from fixedpoint context. 237 | 238 | def_API('Z3_fixedpoint_get_rules', AST_VECTOR, (_in(CONTEXT),_in(FIXEDPOINT))) 239 | */ 240 | Z3_ast_vector Z3_API Z3_fixedpoint_get_rules( 241 | Z3_context c, 242 | Z3_fixedpoint f); 243 | 244 | /** 245 | \brief Retrieve set of background assertions from fixedpoint context. 246 | 247 | def_API('Z3_fixedpoint_get_assertions', AST_VECTOR, (_in(CONTEXT),_in(FIXEDPOINT))) 248 | */ 249 | Z3_ast_vector Z3_API Z3_fixedpoint_get_assertions( 250 | Z3_context c, 251 | Z3_fixedpoint f); 252 | 253 | /** 254 | \brief Set parameters on fixedpoint context. 255 | 256 | def_API('Z3_fixedpoint_set_params', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(PARAMS))) 257 | */ 258 | void Z3_API Z3_fixedpoint_set_params(Z3_context c, Z3_fixedpoint f, Z3_params p); 259 | 260 | /** 261 | \brief Return a string describing all fixedpoint available parameters. 262 | 263 | def_API('Z3_fixedpoint_get_help', STRING, (_in(CONTEXT), _in(FIXEDPOINT))) 264 | */ 265 | Z3_string Z3_API Z3_fixedpoint_get_help(Z3_context c, Z3_fixedpoint f); 266 | 267 | /** 268 | \brief Return the parameter description set for the given fixedpoint object. 269 | 270 | def_API('Z3_fixedpoint_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT), _in(FIXEDPOINT))) 271 | */ 272 | Z3_param_descrs Z3_API Z3_fixedpoint_get_param_descrs(Z3_context c, Z3_fixedpoint f); 273 | 274 | /** 275 | \brief Print the current rules and background axioms as a string. 276 | \param c - context. 277 | \param f - fixedpoint context. 278 | \param num_queries - number of additional queries to print. 279 | \param queries - additional queries. 280 | 281 | def_API('Z3_fixedpoint_to_string', STRING, (_in(CONTEXT), _in(FIXEDPOINT), _in(UINT), _in_array(2, AST))) 282 | */ 283 | Z3_string Z3_API Z3_fixedpoint_to_string( 284 | Z3_context c, 285 | Z3_fixedpoint f, 286 | unsigned num_queries, 287 | Z3_ast queries[]); 288 | 289 | /** 290 | \brief Parse an SMT-LIB2 string with fixedpoint rules. 291 | Add the rules to the current fixedpoint context. 292 | Return the set of queries in the string. 293 | 294 | \param c - context. 295 | \param f - fixedpoint context. 296 | \param s - string containing SMT2 specification. 297 | 298 | def_API('Z3_fixedpoint_from_string', AST_VECTOR, (_in(CONTEXT), _in(FIXEDPOINT), _in(STRING))) 299 | */ 300 | Z3_ast_vector Z3_API Z3_fixedpoint_from_string(Z3_context c, 301 | Z3_fixedpoint f, 302 | Z3_string s); 303 | 304 | /** 305 | \brief Parse an SMT-LIB2 file with fixedpoint rules. 306 | Add the rules to the current fixedpoint context. 307 | Return the set of queries in the file. 308 | 309 | \param c - context. 310 | \param f - fixedpoint context. 311 | \param s - string containing SMT2 specification. 312 | 313 | def_API('Z3_fixedpoint_from_file', AST_VECTOR, (_in(CONTEXT), _in(FIXEDPOINT), _in(STRING))) 314 | */ 315 | Z3_ast_vector Z3_API Z3_fixedpoint_from_file(Z3_context c, 316 | Z3_fixedpoint f, 317 | Z3_string s); 318 | 319 | /** 320 | \brief Create a backtracking point. 321 | 322 | The fixedpoint solver contains a set of rules, added facts and assertions. 323 | The set of rules, facts and assertions are restored upon calling #Z3_fixedpoint_pop. 324 | 325 | \sa Z3_fixedpoint_pop 326 | 327 | def_API('Z3_fixedpoint_push', VOID, (_in(CONTEXT), _in(FIXEDPOINT))) 328 | */ 329 | void Z3_API Z3_fixedpoint_push(Z3_context c, Z3_fixedpoint d); 330 | 331 | /** 332 | \brief Backtrack one backtracking point. 333 | 334 | \sa Z3_fixedpoint_push 335 | 336 | \pre The number of calls to pop cannot exceed calls to push. 337 | 338 | def_API('Z3_fixedpoint_pop', VOID, (_in(CONTEXT), _in(FIXEDPOINT))) 339 | */ 340 | void Z3_API Z3_fixedpoint_pop(Z3_context c, Z3_fixedpoint d); 341 | 342 | /** \brief The following utilities allows adding user-defined domains. */ 343 | 344 | typedef void Z3_fixedpoint_reduce_assign_callback_fptr( 345 | void*, Z3_func_decl, 346 | unsigned, Z3_ast const [], 347 | unsigned, Z3_ast const []); 348 | 349 | typedef void Z3_fixedpoint_reduce_app_callback_fptr( 350 | void*, Z3_func_decl, 351 | unsigned, Z3_ast const [], 352 | Z3_ast*); 353 | 354 | 355 | /** \brief Initialize the context with a user-defined state. */ 356 | void Z3_API Z3_fixedpoint_init(Z3_context c, Z3_fixedpoint d, void* state); 357 | 358 | /** 359 | \brief Register a callback to destructive updates. 360 | 361 | Registers are identified with terms encoded as fresh constants, 362 | */ 363 | void Z3_API Z3_fixedpoint_set_reduce_assign_callback( 364 | Z3_context c ,Z3_fixedpoint d, Z3_fixedpoint_reduce_assign_callback_fptr cb); 365 | 366 | /** \brief Register a callback for buildling terms based on the relational operators. */ 367 | void Z3_API Z3_fixedpoint_set_reduce_app_callback( 368 | Z3_context c, Z3_fixedpoint d, Z3_fixedpoint_reduce_app_callback_fptr cb); 369 | 370 | /*@}*/ 371 | /*@}*/ 372 | 373 | #ifdef __cplusplus 374 | } 375 | #endif // __cplusplus 376 | 377 | #endif 378 | -------------------------------------------------------------------------------- /include/z3_interp.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | Copyright (c) 2014 Microsoft Corporation 3 | 4 | Module Name: 5 | 6 | z3_interp.h 7 | 8 | Abstract: 9 | 10 | API for interpolation 11 | 12 | Author: 13 | 14 | Kenneth McMillan (kenmcmil) 15 | 16 | Notes: 17 | 18 | --*/ 19 | #ifndef Z3_INTERPOLATION_H_ 20 | #define Z3_INTERPOLATION_H_ 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif // __cplusplus 25 | 26 | /** \defgroup capi C API */ 27 | /*@{*/ 28 | 29 | /** @name Interpolation facilities */ 30 | /*@{*/ 31 | /** 32 | \brief Create an AST node marking a formula position for interpolation. 33 | 34 | The node \c a must have Boolean sort. 35 | 36 | def_API('Z3_mk_interpolant', AST, (_in(CONTEXT), _in(AST))) 37 | */ 38 | Z3_ast Z3_API Z3_mk_interpolant(Z3_context c, Z3_ast a); 39 | 40 | 41 | /** \brief This function generates a Z3 context suitable for generation of 42 | interpolants. Formulas can be generated as abstract syntax trees in 43 | this context using the Z3 C API. 44 | 45 | Interpolants are also generated as AST's in this context. 46 | 47 | If cfg is non-null, it will be used as the base configuration 48 | for the Z3 context. This makes it possible to set Z3 options 49 | to be used during interpolation. This feature should be used 50 | with some caution however, as it may be that certain Z3 options 51 | are incompatible with interpolation. 52 | 53 | def_API('Z3_mk_interpolation_context', CONTEXT, (_in(CONFIG),)) 54 | 55 | */ 56 | 57 | Z3_context Z3_API Z3_mk_interpolation_context(Z3_config cfg); 58 | 59 | /** Compute an interpolant from a refutation. This takes a proof of 60 | "false" from a set of formulas C, and an interpolation 61 | pattern. The pattern pat is a formula combining the formulas in C 62 | using logical conjunction and the "interp" operator (see 63 | #Z3_mk_interpolant). This interp operator is logically the identity 64 | operator. It marks the sub-formulas of the pattern for which interpolants should 65 | be computed. The interpolant is a map sigma from marked subformulas to 66 | formulas, such that, for each marked subformula phi of pat (where phi sigma 67 | is phi with sigma(psi) substituted for each subformula psi of phi such that 68 | psi in dom(sigma)): 69 | 70 | 1) phi sigma implies sigma(phi), and 71 | 72 | 2) sigma(phi) is in the common uninterpreted vocabulary between 73 | the formulas of C occurring in phi and those not occurring in 74 | phi 75 | 76 | and moreover pat sigma implies false. In the simplest case 77 | an interpolant for the pattern "(and (interp A) B)" maps A 78 | to an interpolant for A /\ B. 79 | 80 | The return value is a vector of formulas representing sigma. The 81 | vector contains sigma(phi) for each marked subformula of pat, in 82 | pre-order traversal. This means that subformulas of phi occur before phi 83 | in the vector. Also, subformulas that occur multiply in pat will 84 | occur multiply in the result vector. 85 | 86 | In particular, calling Z3_get_interpolant on a pattern of the 87 | form (interp ... (interp (and (interp A_1) A_2)) ... A_N) will 88 | result in a sequence interpolant for A_1, A_2,... A_N. 89 | 90 | Neglecting interp markers, the pattern must be a conjunction of 91 | formulas in C, the set of premises of the proof. Otherwise an 92 | error is flagged. 93 | 94 | Any premises of the proof not present in the pattern are 95 | treated as "background theory". Predicate and function symbols 96 | occurring in the background theory are treated as interpreted and 97 | thus always allowed in the interpolant. 98 | 99 | Interpolant may not necessarily be computable from all 100 | proofs. To be sure an interpolant can be computed, the proof 101 | must be generated by an SMT solver for which interpoaltion is 102 | supported, and the premises must be expressed using only 103 | theories and operators for which interpolation is supported. 104 | 105 | Currently, the only SMT solver that is supported is the legacy 106 | SMT solver. Such a solver is available as the default solver in 107 | \c Z3_context objects produced by #Z3_mk_interpolation_context. 108 | Currently, the theories supported are equality with 109 | uninterpreted functions, linear integer arithmetic, and the 110 | theory of arrays (in SMT-LIB terms, this is AUFLIA). 111 | Quantifiers are allowed. Use of any other operators (including 112 | "labels") may result in failure to compute an interpolant from a 113 | proof. 114 | 115 | Parameters: 116 | 117 | \param c logical context. 118 | \param pf a refutation from premises (assertions) C 119 | \param pat an interpolation pattern over C 120 | \param p parameters 121 | 122 | def_API('Z3_get_interpolant', AST_VECTOR, (_in(CONTEXT), _in(AST), _in(AST), _in(PARAMS))) 123 | */ 124 | 125 | Z3_ast_vector Z3_API Z3_get_interpolant(Z3_context c, Z3_ast pf, Z3_ast pat, Z3_params p); 126 | 127 | /* Compute an interpolant for an unsatisfiable conjunction of formulas. 128 | 129 | This takes as an argument an interpolation pattern as in 130 | #Z3_get_interpolant. This is a conjunction, some subformulas of 131 | which are marked with the "interp" operator (see #Z3_mk_interpolant). 132 | 133 | The conjunction is first checked for unsatisfiability. The result 134 | of this check is returned in the out parameter "status". If the result 135 | is unsat, an interpolant is computed from the refutation as in #Z3_get_interpolant 136 | and returned as a vector of formulas. Otherwise the return value is 137 | an empty formula. 138 | 139 | See #Z3_get_interpolant for a discussion of supported theories. 140 | 141 | The advantage of this function over #Z3_get_interpolant is that 142 | it is not necessary to create a suitable SMT solver and generate 143 | a proof. The disadvantage is that it is not possible to use the 144 | solver incrementally. 145 | 146 | Parameters: 147 | 148 | \param c logical context. 149 | \param pat an interpolation pattern 150 | \param p parameters for solver creation 151 | \param status returns the status of the sat check 152 | \param model returns model if satisfiable 153 | 154 | Return value: status of SAT check 155 | 156 | def_API('Z3_compute_interpolant', INT, (_in(CONTEXT), _in(AST), _in(PARAMS), _out(AST_VECTOR), _out(MODEL))) 157 | */ 158 | 159 | Z3_lbool Z3_API Z3_compute_interpolant(Z3_context c, 160 | Z3_ast pat, 161 | Z3_params p, 162 | Z3_ast_vector *interp, 163 | Z3_model *model); 164 | 165 | /** Return a string summarizing cumulative time used for 166 | interpolation. This string is purely for entertainment purposes 167 | and has no semantics. 168 | 169 | \param ctx The context (currently ignored) 170 | 171 | 172 | def_API('Z3_interpolation_profile', STRING, (_in(CONTEXT),)) 173 | */ 174 | 175 | Z3_string Z3_API Z3_interpolation_profile(Z3_context ctx); 176 | 177 | /** 178 | \brief Read an interpolation problem from file. 179 | 180 | \param ctx The Z3 context. This resets the error handler of ctx. 181 | \param filename The file name to read. 182 | \param num Returns length of sequence. 183 | \param cnsts Returns sequence of formulas (do not free) 184 | \param parents Returns the parents vector (or NULL for sequence) 185 | \param error Returns an error message in case of failure (do not free the string) 186 | \param num_theory Number of theory terms 187 | \param theory Theory terms 188 | 189 | Returns true on success. 190 | 191 | File formats: Currently two formats are supported, based on 192 | SMT-LIB2. For sequence interpolants, the sequence of constraints is 193 | represented by the sequence of "assert" commands in the file. 194 | 195 | For tree interpolants, one symbol of type bool is associated to 196 | each vertex of the tree. For each vertex v there is an "assert" 197 | of the form: 198 | 199 | (implies (and c1 ... cn f) v) 200 | 201 | where c1 .. cn are the children of v (which must precede v in the file) 202 | and f is the formula assiciated to node v. The last formula in the 203 | file is the root vertex, and is represented by the predicate "false". 204 | 205 | A solution to a tree interpolation problem can be thought of as a 206 | valuation of the vertices that makes all the implications true 207 | where each value is represented using the common symbols between 208 | the formulas in the subtree and the remainder of the formulas. 209 | 210 | def_API('Z3_read_interpolation_problem', INT, (_in(CONTEXT), _out(UINT), _out_managed_array(1, AST), _out_managed_array(1, UINT), _in(STRING), _out(STRING), _out(UINT), _out_managed_array(6, AST))) 211 | 212 | */ 213 | 214 | int Z3_API Z3_read_interpolation_problem(Z3_context ctx, 215 | unsigned *num, 216 | Z3_ast *cnsts[], 217 | unsigned *parents[], 218 | Z3_string filename, 219 | Z3_string_ptr error, 220 | unsigned *num_theory, 221 | Z3_ast *theory[]); 222 | 223 | 224 | 225 | /** Check the correctness of an interpolant. The Z3 context must 226 | have no constraints asserted when this call is made. That means 227 | that after interpolating, you must first fully pop the Z3 228 | context before calling this. See Z3_interpolate for meaning of parameters. 229 | 230 | \param ctx The Z3 context. Must be generated by Z3_mk_interpolation_context 231 | \param num The number of constraints in the sequence 232 | \param cnsts Array of constraints (AST's in context ctx) 233 | \param parents The parents vector (or NULL for sequence) 234 | \param interps The interpolant to check 235 | \param error Returns an error message if interpolant incorrect (do not free the string) 236 | \param num_theory Number of theory terms 237 | \param theory Theory terms 238 | 239 | Return value is Z3_L_TRUE if interpolant is verified, Z3_L_FALSE if 240 | incorrect, and Z3_L_UNDEF if unknown. 241 | 242 | def_API('Z3_check_interpolant', INT, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _in_array(1, UINT), _in_array(1, AST), _out(STRING), _in(UINT), _in_array(6, AST))) 243 | */ 244 | 245 | int Z3_API Z3_check_interpolant(Z3_context ctx, 246 | unsigned num, 247 | Z3_ast cnsts[], 248 | unsigned parents[], 249 | Z3_ast *interps, 250 | Z3_string_ptr error, 251 | unsigned num_theory, 252 | Z3_ast theory[]); 253 | 254 | /** Write an interpolation problem to file suitable for reading with 255 | Z3_read_interpolation_problem. The output file is a sequence 256 | of SMT-LIB2 format commands, suitable for reading with command-line Z3 257 | or other interpolating solvers. 258 | 259 | \param ctx The Z3 context. Must be generated by z3_mk_interpolation_context 260 | \param num The number of constraints in the sequence 261 | \param cnsts Array of constraints 262 | \param parents The parents vector (or NULL for sequence) 263 | \param filename The file name to write 264 | \param num_theory Number of theory terms 265 | \param theory Theory terms 266 | 267 | def_API('Z3_write_interpolation_problem', VOID, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _in_array(1, UINT), _in(STRING), _in(UINT), _in_array(5, AST))) 268 | */ 269 | 270 | void Z3_API Z3_write_interpolation_problem(Z3_context ctx, 271 | unsigned num, 272 | Z3_ast cnsts[], 273 | unsigned parents[], 274 | Z3_string filename, 275 | unsigned num_theory, 276 | Z3_ast theory[]); 277 | /*@}*/ 278 | /*@}*/ 279 | 280 | #ifdef __cplusplus 281 | } 282 | #endif // __cplusplus 283 | 284 | #endif 285 | -------------------------------------------------------------------------------- /include/z3_macros.h: -------------------------------------------------------------------------------- 1 | 2 | /*++ 3 | Copyright (c) 2015 Microsoft Corporation 4 | 5 | --*/ 6 | 7 | #ifndef Z3_bool_opt 8 | #define Z3_bool_opt Z3_bool 9 | #endif 10 | 11 | #ifndef Z3_API 12 | # ifdef __GNUC__ 13 | # define Z3_API __attribute__ ((visibility ("default"))) 14 | # else 15 | # define Z3_API 16 | # endif 17 | #endif 18 | 19 | #ifndef DEFINE_TYPE 20 | #define DEFINE_TYPE(T) typedef struct _ ## T *T 21 | #endif 22 | 23 | #ifndef DEFINE_VOID 24 | #define DEFINE_VOID(T) typedef void* T 25 | #endif 26 | -------------------------------------------------------------------------------- /include/z3_optimization.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | Copyright (c) 2015 Microsoft Corporation 3 | 4 | Module Name: 5 | 6 | z3_optimization.h 7 | 8 | Abstract: 9 | 10 | Optimization facilities 11 | 12 | Author: 13 | 14 | Christoph M. Wintersteiger (cwinter) 2015-12-03 15 | 16 | Notes: 17 | 18 | --*/ 19 | #ifndef Z3_OPTIMIZATION_H_ 20 | #define Z3_OPTIMIZATION_H_ 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif // __cplusplus 25 | 26 | /** \defgroup capi C API */ 27 | /*@{*/ 28 | 29 | /** @name Optimization facilities */ 30 | /*@{*/ 31 | /** 32 | \brief Create a new optimize context. 33 | 34 | \remark User must use #Z3_optimize_inc_ref and #Z3_optimize_dec_ref to manage optimize objects. 35 | Even if the context was created using #Z3_mk_context instead of #Z3_mk_context_rc. 36 | 37 | def_API('Z3_mk_optimize', OPTIMIZE, (_in(CONTEXT), )) 38 | */ 39 | Z3_optimize Z3_API Z3_mk_optimize(Z3_context c); 40 | 41 | /** 42 | \brief Increment the reference counter of the given optimize context 43 | 44 | def_API('Z3_optimize_inc_ref', VOID, (_in(CONTEXT), _in(OPTIMIZE))) 45 | */ 46 | void Z3_API Z3_optimize_inc_ref(Z3_context c, Z3_optimize d); 47 | 48 | /** 49 | \brief Decrement the reference counter of the given optimize context. 50 | 51 | def_API('Z3_optimize_dec_ref', VOID, (_in(CONTEXT), _in(OPTIMIZE))) 52 | */ 53 | void Z3_API Z3_optimize_dec_ref(Z3_context c, Z3_optimize d); 54 | 55 | /** 56 | \brief Assert hard constraint to the optimization context. 57 | 58 | def_API('Z3_optimize_assert', VOID, (_in(CONTEXT), _in(OPTIMIZE), _in(AST))) 59 | */ 60 | void Z3_API Z3_optimize_assert(Z3_context c, Z3_optimize o, Z3_ast a); 61 | 62 | /** 63 | \brief Assert soft constraint to the optimization context. 64 | \param c - context 65 | \param o - optimization context 66 | \param a - formula 67 | \param weight - a positive weight, penalty for violating soft constraint 68 | \param id - optional identifier to group soft constraints 69 | 70 | def_API('Z3_optimize_assert_soft', UINT, (_in(CONTEXT), _in(OPTIMIZE), _in(AST), _in(STRING), _in(SYMBOL))) 71 | */ 72 | unsigned Z3_API Z3_optimize_assert_soft(Z3_context c, Z3_optimize o, Z3_ast a, Z3_string weight, Z3_symbol id); 73 | 74 | /** 75 | \brief Add a maximization constraint. 76 | \param c - context 77 | \param o - optimization context 78 | \param t - arithmetical term 79 | def_API('Z3_optimize_maximize', UINT, (_in(CONTEXT), _in(OPTIMIZE), _in(AST))) 80 | */ 81 | unsigned Z3_API Z3_optimize_maximize(Z3_context c, Z3_optimize o, Z3_ast t); 82 | 83 | /** 84 | \brief Add a minimization constraint. 85 | \param c - context 86 | \param o - optimization context 87 | \param t - arithmetical term 88 | 89 | def_API('Z3_optimize_minimize', UINT, (_in(CONTEXT), _in(OPTIMIZE), _in(AST))) 90 | */ 91 | unsigned Z3_API Z3_optimize_minimize(Z3_context c, Z3_optimize o, Z3_ast t); 92 | 93 | /** 94 | \brief Create a backtracking point. 95 | 96 | The optimize solver contains a set of rules, added facts and assertions. 97 | The set of rules, facts and assertions are restored upon calling #Z3_optimize_pop. 98 | 99 | \sa Z3_optimize_pop 100 | 101 | def_API('Z3_optimize_push', VOID, (_in(CONTEXT), _in(OPTIMIZE))) 102 | */ 103 | void Z3_API Z3_optimize_push(Z3_context c, Z3_optimize d); 104 | 105 | /** 106 | \brief Backtrack one level. 107 | 108 | \sa Z3_optimize_push 109 | 110 | \pre The number of calls to pop cannot exceed calls to push. 111 | 112 | def_API('Z3_optimize_pop', VOID, (_in(CONTEXT), _in(OPTIMIZE))) 113 | */ 114 | void Z3_API Z3_optimize_pop(Z3_context c, Z3_optimize d); 115 | 116 | /** 117 | \brief Check consistency and produce optimal values. 118 | \param c - context 119 | \param o - optimization context 120 | 121 | def_API('Z3_optimize_check', INT, (_in(CONTEXT), _in(OPTIMIZE))) 122 | */ 123 | Z3_lbool Z3_API Z3_optimize_check(Z3_context c, Z3_optimize o); 124 | 125 | 126 | /** 127 | \brief Retrieve a string that describes the last status returned by #Z3_optimize_check. 128 | 129 | Use this method when #Z3_optimize_check returns Z3_L_UNDEF. 130 | 131 | def_API('Z3_optimize_get_reason_unknown', STRING, (_in(CONTEXT), _in(OPTIMIZE) )) 132 | */ 133 | Z3_string Z3_API Z3_optimize_get_reason_unknown(Z3_context c, Z3_optimize d); 134 | 135 | /** 136 | \brief Retrieve the model for the last #Z3_optimize_check 137 | 138 | The error handler is invoked if a model is not available because 139 | the commands above were not invoked for the given optimization 140 | solver, or if the result was \c Z3_L_FALSE. 141 | 142 | def_API('Z3_optimize_get_model', MODEL, (_in(CONTEXT), _in(OPTIMIZE))) 143 | */ 144 | Z3_model Z3_API Z3_optimize_get_model(Z3_context c, Z3_optimize o); 145 | 146 | /** 147 | \brief Set parameters on optimization context. 148 | 149 | \param c - context 150 | \param o - optimization context 151 | \param p - parameters 152 | 153 | def_API('Z3_optimize_set_params', VOID, (_in(CONTEXT), _in(OPTIMIZE), _in(PARAMS))) 154 | */ 155 | void Z3_API Z3_optimize_set_params(Z3_context c, Z3_optimize o, Z3_params p); 156 | 157 | /** 158 | \brief Return the parameter description set for the given optimize object. 159 | 160 | \param c - context 161 | \param o - optimization context 162 | 163 | def_API('Z3_optimize_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT), _in(OPTIMIZE))) 164 | */ 165 | Z3_param_descrs Z3_API Z3_optimize_get_param_descrs(Z3_context c, Z3_optimize o); 166 | 167 | /** 168 | \brief Retrieve lower bound value or approximation for the i'th optimization objective. 169 | 170 | \param c - context 171 | \param o - optimization context 172 | \param idx - index of optimization objective 173 | 174 | def_API('Z3_optimize_get_lower', AST, (_in(CONTEXT), _in(OPTIMIZE), _in(UINT))) 175 | */ 176 | Z3_ast Z3_API Z3_optimize_get_lower(Z3_context c, Z3_optimize o, unsigned idx); 177 | 178 | /** 179 | \brief Retrieve upper bound value or approximation for the i'th optimization objective. 180 | 181 | \param c - context 182 | \param o - optimization context 183 | \param idx - index of optimization objective 184 | 185 | def_API('Z3_optimize_get_upper', AST, (_in(CONTEXT), _in(OPTIMIZE), _in(UINT))) 186 | */ 187 | Z3_ast Z3_API Z3_optimize_get_upper(Z3_context c, Z3_optimize o, unsigned idx); 188 | 189 | 190 | /** 191 | \brief Retrieve lower bound value or approximation for the i'th optimization objective. 192 | The returned vector is of length 3. It always contains numerals. 193 | The three numerals are coefficients a, b, c and encode the result of \c Z3_optimize_get_lower 194 | a * infinity + b + c * epsilon. 195 | 196 | \param c - context 197 | \param o - optimization context 198 | \param idx - index of optimization objective 199 | 200 | def_API('Z3_optimize_get_lower_as_vector', AST_VECTOR, (_in(CONTEXT), _in(OPTIMIZE), _in(UINT))) 201 | */ 202 | Z3_ast_vector Z3_API Z3_optimize_get_lower_as_vector(Z3_context c, Z3_optimize o, unsigned idx); 203 | 204 | /** 205 | \brief Retrieve upper bound value or approximation for the i'th optimization objective. 206 | 207 | \param c - context 208 | \param o - optimization context 209 | \param idx - index of optimization objective 210 | 211 | def_API('Z3_optimize_get_upper_as_vector', AST_VECTOR, (_in(CONTEXT), _in(OPTIMIZE), _in(UINT))) 212 | */ 213 | Z3_ast_vector Z3_API Z3_optimize_get_upper_as_vector(Z3_context c, Z3_optimize o, unsigned idx); 214 | 215 | 216 | /** 217 | \brief Print the current context as a string. 218 | \param c - context. 219 | \param o - optimization context. 220 | 221 | def_API('Z3_optimize_to_string', STRING, (_in(CONTEXT), _in(OPTIMIZE))) 222 | */ 223 | Z3_string Z3_API Z3_optimize_to_string(Z3_context c, Z3_optimize o); 224 | 225 | /** 226 | \brief Parse an SMT-LIB2 string with assertions, 227 | soft constraints and optimization objectives. 228 | Add the parsed constraints and objectives to the optimization context. 229 | 230 | \param c - context. 231 | \param o - optimize context. 232 | \param s - string containing SMT2 specification. 233 | 234 | def_API('Z3_optimize_from_string', VOID, (_in(CONTEXT), _in(OPTIMIZE), _in(STRING))) 235 | */ 236 | void Z3_API Z3_optimize_from_string(Z3_context c, Z3_optimize o, Z3_string s); 237 | 238 | /** 239 | \brief Parse an SMT-LIB2 file with assertions, 240 | soft constraints and optimization objectives. 241 | Add the parsed constraints and objectives to the optimization context. 242 | 243 | \param c - context. 244 | \param o - optimize context. 245 | \param s - string containing SMT2 specification. 246 | 247 | def_API('Z3_optimize_from_file', VOID, (_in(CONTEXT), _in(OPTIMIZE), _in(STRING))) 248 | */ 249 | void Z3_API Z3_optimize_from_file(Z3_context c, Z3_optimize o, Z3_string s); 250 | 251 | /** 252 | \brief Return a string containing a description of parameters accepted by optimize. 253 | 254 | def_API('Z3_optimize_get_help', STRING, (_in(CONTEXT), _in(OPTIMIZE))) 255 | */ 256 | Z3_string Z3_API Z3_optimize_get_help(Z3_context c, Z3_optimize t); 257 | 258 | /** 259 | \brief Retrieve statistics information from the last call to #Z3_optimize_check 260 | 261 | def_API('Z3_optimize_get_statistics', STATS, (_in(CONTEXT), _in(OPTIMIZE))) 262 | */ 263 | Z3_stats Z3_API Z3_optimize_get_statistics(Z3_context c, Z3_optimize d); 264 | 265 | /** 266 | \brief Return the set of asserted formulas on the optimization context. 267 | 268 | def_API('Z3_optimize_get_assertions', AST_VECTOR, (_in(CONTEXT), _in(OPTIMIZE))) 269 | */ 270 | Z3_ast_vector Z3_API Z3_optimize_get_assertions(Z3_context c, Z3_optimize o); 271 | 272 | /** 273 | \brief Return objectives on the optimization context. 274 | If the objective function is a max-sat objective it is returned 275 | as a Pseudo-Boolean (minimization) sum of the form (+ (if f1 w1 0) (if f2 w2 0) ...) 276 | If the objective function is entered as a maximization objective, then return 277 | the corresponding minimization objective. In this way the resulting objective 278 | function is always returned as a minimization objective. 279 | 280 | def_API('Z3_optimize_get_objectives', AST_VECTOR, (_in(CONTEXT), _in(OPTIMIZE))) 281 | */ 282 | Z3_ast_vector Z3_API Z3_optimize_get_objectives(Z3_context c, Z3_optimize o); 283 | 284 | /*@}*/ 285 | /*@}*/ 286 | 287 | #ifdef __cplusplus 288 | } 289 | #endif // __cplusplus 290 | 291 | #endif 292 | -------------------------------------------------------------------------------- /include/z3_polynomial.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | Copyright (c) 2012 Microsoft Corporation 3 | 4 | Module Name: 5 | 6 | z3_polynomial.h 7 | 8 | Abstract: 9 | 10 | Additional APIs for polynomials. 11 | 12 | Author: 13 | 14 | Leonardo de Moura (leonardo) 2012-12-09 15 | 16 | Notes: 17 | 18 | --*/ 19 | 20 | #ifndef Z3_POLYNOMIAL_H_ 21 | #define Z3_POLYNOMIAL_H_ 22 | 23 | #ifdef __cplusplus 24 | extern "C" { 25 | #endif // __cplusplus 26 | 27 | /** \defgroup capi C API */ 28 | /*@{*/ 29 | 30 | 31 | /** @name Polynomials */ 32 | /*@{*/ 33 | 34 | /** 35 | \brief Return the nonzero subresultants of \c p and \c q with respect to the "variable" \c x. 36 | 37 | \pre \c p, \c q and \c x are Z3 expressions where \c p and \c q are arithmetic terms. 38 | Note that, any subterm that cannot be viewed as a polynomial is assumed to be a variable. 39 | Example: f(a) is a considered to be a variable in the polynomial 40 | 41 | f(a)*f(a) + 2*f(a) + 1 42 | 43 | def_API('Z3_polynomial_subresultants', AST_VECTOR, (_in(CONTEXT), _in(AST), _in(AST), _in(AST))) 44 | */ 45 | Z3_ast_vector Z3_API Z3_polynomial_subresultants(Z3_context c, Z3_ast p, Z3_ast q, Z3_ast x); 46 | 47 | 48 | /*@}*/ 49 | /*@}*/ 50 | 51 | #ifdef __cplusplus 52 | } 53 | #endif // __cplusplus 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /include/z3_rcf.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | Copyright (c) 2013 Microsoft Corporation 3 | 4 | Module Name: 5 | 6 | z3_rcf.h 7 | 8 | Abstract: 9 | 10 | Additional APIs for handling elements of the Z3 real closed field that contains: 11 | - transcendental extensions 12 | - infinitesimal extensions 13 | - algebraic extensions 14 | 15 | Author: 16 | 17 | Leonardo de Moura (leonardo) 2012-01-05 18 | 19 | Notes: 20 | 21 | --*/ 22 | #ifndef Z3_RCF_H_ 23 | #define Z3_RCF_H_ 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif // __cplusplus 28 | 29 | /** \defgroup capi C API */ 30 | /*@{*/ 31 | 32 | /** @name Real Closed Fields */ 33 | /*@{*/ 34 | /** 35 | \brief Delete a RCF numeral created using the RCF API. 36 | 37 | def_API('Z3_rcf_del', VOID, (_in(CONTEXT), _in(RCF_NUM))) 38 | */ 39 | void Z3_API Z3_rcf_del(Z3_context c, Z3_rcf_num a); 40 | 41 | /** 42 | \brief Return a RCF rational using the given string. 43 | 44 | def_API('Z3_rcf_mk_rational', RCF_NUM, (_in(CONTEXT), _in(STRING))) 45 | */ 46 | Z3_rcf_num Z3_API Z3_rcf_mk_rational(Z3_context c, Z3_string val); 47 | 48 | /** 49 | \brief Return a RCF small integer. 50 | 51 | def_API('Z3_rcf_mk_small_int', RCF_NUM, (_in(CONTEXT), _in(INT))) 52 | */ 53 | Z3_rcf_num Z3_API Z3_rcf_mk_small_int(Z3_context c, int val); 54 | 55 | /** 56 | \brief Return Pi 57 | 58 | def_API('Z3_rcf_mk_pi', RCF_NUM, (_in(CONTEXT),)) 59 | */ 60 | Z3_rcf_num Z3_API Z3_rcf_mk_pi(Z3_context c); 61 | 62 | /** 63 | \brief Return e (Euler's constant) 64 | 65 | def_API('Z3_rcf_mk_e', RCF_NUM, (_in(CONTEXT),)) 66 | */ 67 | Z3_rcf_num Z3_API Z3_rcf_mk_e(Z3_context c); 68 | 69 | /** 70 | \brief Return a new infinitesimal that is smaller than all elements in the Z3 field. 71 | 72 | def_API('Z3_rcf_mk_infinitesimal', RCF_NUM, (_in(CONTEXT),)) 73 | */ 74 | Z3_rcf_num Z3_API Z3_rcf_mk_infinitesimal(Z3_context c); 75 | 76 | /** 77 | \brief Store in roots the roots of the polynomial a[n-1]*x^{n-1} + ... + a[0]. 78 | The output vector \c roots must have size \c n. 79 | It returns the number of roots of the polynomial. 80 | 81 | \pre The input polynomial is not the zero polynomial. 82 | 83 | def_API('Z3_rcf_mk_roots', UINT, (_in(CONTEXT), _in(UINT), _in_array(1, RCF_NUM), _out_array(1, RCF_NUM))) 84 | */ 85 | unsigned Z3_API Z3_rcf_mk_roots(Z3_context c, unsigned n, Z3_rcf_num const a[], Z3_rcf_num roots[]); 86 | 87 | /** 88 | \brief Return the value a + b. 89 | 90 | def_API('Z3_rcf_add', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) 91 | */ 92 | Z3_rcf_num Z3_API Z3_rcf_add(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); 93 | 94 | /** 95 | \brief Return the value a - b. 96 | 97 | def_API('Z3_rcf_sub', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) 98 | */ 99 | Z3_rcf_num Z3_API Z3_rcf_sub(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); 100 | 101 | /** 102 | \brief Return the value a * b. 103 | 104 | def_API('Z3_rcf_mul', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) 105 | */ 106 | Z3_rcf_num Z3_API Z3_rcf_mul(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); 107 | 108 | /** 109 | \brief Return the value a / b. 110 | 111 | def_API('Z3_rcf_div', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) 112 | */ 113 | Z3_rcf_num Z3_API Z3_rcf_div(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); 114 | 115 | /** 116 | \brief Return the value -a 117 | 118 | def_API('Z3_rcf_neg', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM))) 119 | */ 120 | Z3_rcf_num Z3_API Z3_rcf_neg(Z3_context c, Z3_rcf_num a); 121 | 122 | /** 123 | \brief Return the value 1/a 124 | 125 | def_API('Z3_rcf_inv', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM))) 126 | */ 127 | Z3_rcf_num Z3_API Z3_rcf_inv(Z3_context c, Z3_rcf_num a); 128 | 129 | /** 130 | \brief Return the value a^k 131 | 132 | def_API('Z3_rcf_power', RCF_NUM, (_in(CONTEXT), _in(RCF_NUM), _in(UINT))) 133 | */ 134 | Z3_rcf_num Z3_API Z3_rcf_power(Z3_context c, Z3_rcf_num a, unsigned k); 135 | 136 | /** 137 | \brief Return Z3_TRUE if a < b 138 | 139 | def_API('Z3_rcf_lt', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) 140 | */ 141 | Z3_bool Z3_API Z3_rcf_lt(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); 142 | 143 | /** 144 | \brief Return Z3_TRUE if a > b 145 | 146 | def_API('Z3_rcf_gt', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) 147 | */ 148 | Z3_bool Z3_API Z3_rcf_gt(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); 149 | 150 | /** 151 | \brief Return Z3_TRUE if a <= b 152 | 153 | def_API('Z3_rcf_le', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) 154 | */ 155 | Z3_bool Z3_API Z3_rcf_le(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); 156 | 157 | /** 158 | \brief Return Z3_TRUE if a >= b 159 | 160 | def_API('Z3_rcf_ge', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) 161 | */ 162 | Z3_bool Z3_API Z3_rcf_ge(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); 163 | 164 | /** 165 | \brief Return Z3_TRUE if a == b 166 | 167 | def_API('Z3_rcf_eq', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) 168 | */ 169 | Z3_bool Z3_API Z3_rcf_eq(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); 170 | 171 | /** 172 | \brief Return Z3_TRUE if a != b 173 | 174 | def_API('Z3_rcf_neq', BOOL, (_in(CONTEXT), _in(RCF_NUM), _in(RCF_NUM))) 175 | */ 176 | Z3_bool Z3_API Z3_rcf_neq(Z3_context c, Z3_rcf_num a, Z3_rcf_num b); 177 | 178 | /** 179 | \brief Convert the RCF numeral into a string. 180 | 181 | def_API('Z3_rcf_num_to_string', STRING, (_in(CONTEXT), _in(RCF_NUM), _in(BOOL), _in(BOOL))) 182 | */ 183 | Z3_string Z3_API Z3_rcf_num_to_string(Z3_context c, Z3_rcf_num a, Z3_bool compact, Z3_bool html); 184 | 185 | /** 186 | \brief Convert the RCF numeral into a string in decimal notation. 187 | 188 | def_API('Z3_rcf_num_to_decimal_string', STRING, (_in(CONTEXT), _in(RCF_NUM), _in(UINT))) 189 | */ 190 | Z3_string Z3_API Z3_rcf_num_to_decimal_string(Z3_context c, Z3_rcf_num a, unsigned prec); 191 | 192 | /** 193 | \brief Extract the "numerator" and "denominator" of the given RCF numeral. 194 | We have that a = n/d, moreover n and d are not represented using rational functions. 195 | 196 | def_API('Z3_rcf_get_numerator_denominator', VOID, (_in(CONTEXT), _in(RCF_NUM), _out(RCF_NUM), _out(RCF_NUM))) 197 | */ 198 | void Z3_API Z3_rcf_get_numerator_denominator(Z3_context c, Z3_rcf_num a, Z3_rcf_num * n, Z3_rcf_num * d); 199 | 200 | /*@}*/ 201 | /*@}*/ 202 | 203 | #ifdef __cplusplus 204 | } 205 | #endif // __cplusplus 206 | 207 | #endif 208 | -------------------------------------------------------------------------------- /include/z3_spacer.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | Copyright (c) 2017 Arie Gurfinkel 3 | 4 | Module Name: 5 | 6 | z3_spacer.h 7 | 8 | Abstract: 9 | 10 | Spacer API 11 | 12 | Author: 13 | 14 | Arie Gurfinkel (arie) 15 | 16 | Notes: 17 | 18 | --*/ 19 | #ifndef Z3_SPACER_H_ 20 | #define Z3_SPACER_H_ 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif // __cplusplus 25 | 26 | /** \defgroup capi C API */ 27 | /*@{*/ 28 | 29 | /** @name Spacer facilities */ 30 | /*@{*/ 31 | /** 32 | \brief Pose a query against the asserted rules at the given level. 33 | 34 | \code 35 | query ::= (exists (bound-vars) query) 36 | | literals 37 | \endcode 38 | 39 | query returns 40 | - Z3_L_FALSE if the query is unsatisfiable. 41 | - Z3_L_TRUE if the query is satisfiable. Obtain the answer by calling #Z3_fixedpoint_get_answer. 42 | - Z3_L_UNDEF if the query was interrupted, timed out or otherwise failed. 43 | 44 | def_API('Z3_fixedpoint_query_from_lvl', INT, (_in(CONTEXT), _in(FIXEDPOINT), _in(AST), _in(UINT))) 45 | */ 46 | Z3_lbool Z3_API Z3_fixedpoint_query_from_lvl (Z3_context c,Z3_fixedpoint d, Z3_ast query, unsigned lvl); 47 | 48 | /** 49 | \brief Retrieve a bottom-up (from query) sequence of ground facts 50 | 51 | The previous call to Z3_fixedpoint_query must have returned Z3_L_TRUE. 52 | 53 | def_API('Z3_fixedpoint_get_ground_sat_answer', AST, (_in(CONTEXT), _in(FIXEDPOINT))) 54 | */ 55 | Z3_ast Z3_API Z3_fixedpoint_get_ground_sat_answer(Z3_context c,Z3_fixedpoint d); 56 | 57 | /** 58 | \brief Obtain the list of rules along the counterexample trace. 59 | 60 | def_API('Z3_fixedpoint_get_rules_along_trace', AST_VECTOR, (_in(CONTEXT), _in(FIXEDPOINT))) 61 | */ 62 | Z3_ast_vector Z3_API Z3_fixedpoint_get_rules_along_trace(Z3_context c,Z3_fixedpoint d); 63 | 64 | /** 65 | \brief Obtain the list of rules along the counterexample trace. 66 | 67 | def_API('Z3_fixedpoint_get_rule_names_along_trace', SYMBOL, (_in(CONTEXT), _in(FIXEDPOINT))) 68 | */ 69 | Z3_symbol Z3_API Z3_fixedpoint_get_rule_names_along_trace(Z3_context c,Z3_fixedpoint d); 70 | 71 | /** 72 | \brief Add an invariant for the predicate \c pred. 73 | Add an assumed invariant of predicate \c pred. 74 | 75 | Note: this functionality is Spacer specific. 76 | 77 | def_API('Z3_fixedpoint_add_invariant', VOID, (_in(CONTEXT), _in(FIXEDPOINT), _in(FUNC_DECL), _in(AST))) 78 | */ 79 | void Z3_API Z3_fixedpoint_add_invariant(Z3_context c, Z3_fixedpoint d, Z3_func_decl pred, Z3_ast property); 80 | 81 | 82 | /** 83 | Retrieve reachable states of a predicate. 84 | Note: this functionality is Spacer specific. 85 | 86 | def_API('Z3_fixedpoint_get_reachable', AST, (_in(CONTEXT), _in(FIXEDPOINT), _in(FUNC_DECL))) 87 | */ 88 | Z3_ast Z3_API Z3_fixedpoint_get_reachable(Z3_context c, Z3_fixedpoint d, Z3_func_decl pred); 89 | 90 | /** 91 | \brief Project variables given a model 92 | 93 | def_API('Z3_qe_model_project', AST, (_in(CONTEXT), _in(MODEL), _in(UINT), _in_array(2, APP), _in(AST))) 94 | */ 95 | Z3_ast Z3_API Z3_qe_model_project 96 | (Z3_context c, 97 | Z3_model m, 98 | unsigned num_bounds, 99 | Z3_app const bound[], 100 | Z3_ast body); 101 | 102 | 103 | /** 104 | \brief Project variables given a model 105 | 106 | def_API('Z3_qe_model_project_skolem', AST, (_in(CONTEXT), _in(MODEL), _in(UINT), _in_array(2, APP), _in(AST), _in(AST_MAP))) 107 | */ 108 | Z3_ast Z3_API Z3_qe_model_project_skolem 109 | (Z3_context c, 110 | Z3_model m, 111 | unsigned num_bounds, 112 | Z3_app const bound[], 113 | Z3_ast body, 114 | Z3_ast_map map); 115 | 116 | /** 117 | \brief Extrapolates a model of a formula 118 | 119 | def_API('Z3_model_extrapolate', AST, (_in(CONTEXT), _in(MODEL), _in(AST))) 120 | */ 121 | Z3_ast Z3_API Z3_model_extrapolate 122 | (Z3_context c, 123 | Z3_model m, 124 | Z3_ast fml); 125 | 126 | /** 127 | \brief Best-effort quantifier elimination 128 | 129 | def_API ('Z3_qe_lite', AST, (_in(CONTEXT), _in(AST_VECTOR), _in(AST))) 130 | */ 131 | Z3_ast Z3_API Z3_qe_lite 132 | (Z3_context c, 133 | Z3_ast_vector vars, 134 | Z3_ast body); 135 | 136 | /*@}*/ 137 | /*@}*/ 138 | 139 | #ifdef __cplusplus 140 | } 141 | #endif // __cplusplus 142 | 143 | #endif 144 | -------------------------------------------------------------------------------- /include/z3_v1.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | Copyright (c) 2011 Microsoft Corporation 3 | 4 | Module Name: 5 | 6 | z3_v1.h 7 | 8 | Abstract: 9 | 10 | Z3 1.x backwards compatibility macros. 11 | These macros are used to simulate the Z3 API using in the 1.x versions. 12 | This file should only be used by users still using the Z3 1.x API. 13 | 14 | Author: 15 | 16 | Leonardo de Moura (leonardo) 2011-09-22 17 | 18 | Notes: 19 | 20 | --*/ 21 | #ifndef Z3_V1_H_ 22 | #define Z3_V1_H_ 23 | 24 | #include "api/z3.h" 25 | 26 | // Backwards compatibility 27 | #define Z3_type_ast Z3_sort 28 | #define Z3_const_decl_ast Z3_func_decl 29 | #define Z3_const Z3_app 30 | #define Z3_pattern_ast Z3_pattern 31 | #define Z3_UNINTERPRETED_TYPE Z3_UNINTERPRETED_SORT 32 | #define Z3_BOOL_TYPE Z3_BOOL_SORT 33 | #define Z3_INT_TYPE Z3_INT_SORT 34 | #define Z3_REAL_TYPE Z3_REAL_SORT 35 | #define Z3_BV_TYPE Z3_BV_SORT 36 | #define Z3_ARRAY_TYPE Z3_ARRAY_SORT 37 | #define Z3_TUPLE_TYPE Z3_DATATYPE_SORT 38 | #define Z3_UNKNOWN_TYPE Z3_UNKNOWN_SORT 39 | #define Z3_CONST_DECL_AST Z3_FUNC_DECL_AST 40 | #define Z3_TYPE_AST Z3_SORT_AST 41 | #define Z3_SORT_ERROR Z3_TYPE_ERROR 42 | #define Z3_mk_uninterpreted_type Z3_mk_uninterpreted_sort 43 | #define Z3_mk_bool_type Z3_mk_bool_sort 44 | #define Z3_mk_int_type Z3_mk_int_sort 45 | #define Z3_mk_real_type Z3_mk_real_sort 46 | #define Z3_mk_bv_type Z3_mk_bv_sort 47 | #define Z3_mk_array_type Z3_mk_array_sort 48 | #define Z3_mk_tuple_type Z3_mk_tuple_sort 49 | #define Z3_get_type Z3_get_sort 50 | #define Z3_get_pattern_ast Z3_get_pattern 51 | #define Z3_get_type_kind Z3_get_sort_kind 52 | #define Z3_get_type_name Z3_get_sort_name 53 | #define Z3_get_bv_type_size Z3_get_bv_sort_size 54 | #define Z3_get_array_type_domain Z3_get_array_sort_domain 55 | #define Z3_get_array_type_range Z3_get_array_sort_range 56 | #define Z3_get_tuple_type_num_fields Z3_get_tuple_sort_num_fields 57 | #define Z3_get_tuple_type_field_decl Z3_get_tuple_sort_field_decl 58 | #define Z3_get_tuple_type_mk_decl Z3_get_tuple_sort_mk_decl 59 | #define Z3_to_const_ast Z3_to_app 60 | #define Z3_get_numeral_value_string Z3_get_numeral_string 61 | #define Z3_get_const_ast_decl Z3_get_app_decl 62 | #define Z3_get_value Z3_eval_func_decl 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /instructions2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "stdint.h" 4 | 5 | enum class instruction_name { 6 | NONE = 0, 7 | ADC = 1, 8 | AND = 2, 9 | ASL = 3, 10 | BCC = 4, 11 | BCS = 5, 12 | BEQ = 6, 13 | BIT = 7, 14 | BMI = 8, 15 | BNE = 9, 16 | BPL = 10, 17 | BRK = 11, 18 | BVC = 12, 19 | BVS = 13, 20 | CLC = 14, 21 | CLD = 15, 22 | CLI = 16, 23 | CLV = 17, 24 | CMP = 18, 25 | CPX = 19, 26 | CPY = 20, 27 | DEC = 21, 28 | DEX = 22, 29 | DEY = 23, 30 | EOR = 24, 31 | INC = 25, 32 | INX = 26, 33 | INY = 27, 34 | JMP = 28, 35 | JSR = 29, 36 | LDA = 30, 37 | LDX = 31, 38 | LDY = 32, 39 | LSR = 33, 40 | NOP = 34, 41 | ORA = 35, 42 | PHA = 36, 43 | PHP = 37, 44 | PLA = 38, 45 | PLP = 39, 46 | ROL = 40, 47 | ROR = 41, 48 | RTI = 42, 49 | RTS = 43, 50 | SBC = 44, 51 | SEC = 45, 52 | SED = 46, 53 | SEI = 47, 54 | STA = 48, 55 | STX = 49, 56 | STY = 50, 57 | TAX = 51, 58 | TAY = 52, 59 | TSX = 53, 60 | TXA = 54, 61 | TXS = 55, 62 | TYA = 56, 63 | 64 | JMPI = 57, 65 | LSRA = 58, 66 | ASLA = 59, 67 | ROLA = 60, 68 | RORA = 61, 69 | }; 70 | 71 | enum class addr_mode { 72 | // for instructions with no operands 73 | NONE = 0, 74 | // a 16 bit address 75 | ABSOLUTE = 1, 76 | // a 16 bit address + x 77 | ABSOLUTE_X = 2, 78 | // a 16 bit address + y 79 | ABSOLUTE_Y = 3, 80 | // the pointer at (an 8 bit address + x) 81 | X_INDIRECT = 4, 82 | // (the pointer at an 8 bit address) + y 83 | INDIRECT_Y = 5, 84 | // an 8 bit address 85 | ZERO_PAGE = 6, 86 | // an 8 bit address + x 87 | ZERO_PAGE_X = 7, 88 | // an 8 bit address + y 89 | ZERO_PAGE_Y = 8, 90 | // an arbitrary immediate constant 91 | IMMEDIATE = 9, 92 | // specific immediate constants (0, 1, FF, etc.) 93 | CONSTANT = 10, 94 | }; 95 | 96 | inline uint8_t addr_mode_variants(addr_mode mode) { 97 | switch (mode) { 98 | case addr_mode::NONE: 99 | return 1; 100 | case addr_mode::ABSOLUTE: 101 | case addr_mode::ABSOLUTE_X: 102 | case addr_mode::ABSOLUTE_Y: 103 | case addr_mode::INDIRECT_Y: 104 | case addr_mode::X_INDIRECT: 105 | case addr_mode::ZERO_PAGE: 106 | case addr_mode::ZERO_PAGE_X: 107 | case addr_mode::ZERO_PAGE_Y: 108 | case addr_mode::IMMEDIATE: 109 | return 4; 110 | case addr_mode::CONSTANT: 111 | return 10; 112 | } 113 | return 0; 114 | } 115 | 116 | const char *addr_mode_operand_strings[] = { 117 | "", "absolute", "absolute", "absolute", "zp", "zp", "zp", "zp", "zp", "immediate" 118 | }; 119 | 120 | const uint8_t addr_mode_constant_values[] = { 0, 1, 2, 4, 8, 16, 32, 64, 128, 255 }; 121 | 122 | std::string addr_mode_operand_name(addr_mode mode, uint8_t number) { 123 | if (mode == addr_mode::CONSTANT) { 124 | return std::to_string(addr_mode_constant_values[number]); 125 | } else if (mode == addr_mode::NONE) { 126 | return std::string{}; 127 | } else { 128 | return addr_mode_operand_strings[(uint8_t)mode] + std::to_string(number); 129 | } 130 | } 131 | 132 | typedef struct instruction { 133 | uint16_t data; 134 | 135 | instruction(): data(0) {} 136 | 137 | instruction(instruction_name name, addr_mode mode, uint8_t number) { 138 | data = (uint16_t)name; 139 | data <<= 4; 140 | data |= (uint16_t)mode; 141 | data <<= 4; 142 | data |= number; 143 | } 144 | 145 | instruction_name name() const { 146 | return (instruction_name)((data & 0xFF00) >> 8); 147 | } 148 | 149 | addr_mode mode() const { 150 | return (addr_mode)((data & 0xF0) >> 4); 151 | } 152 | 153 | uint8_t number() const { 154 | return data & 0xF; 155 | } 156 | 157 | instruction number(uint8_t number) const { 158 | instruction result; 159 | result.data = (data & 0xFFF0) | (number & 0xF); 160 | return result; 161 | } 162 | 163 | } instruction; 164 | 165 | typedef struct instruction_info { 166 | instruction ins; 167 | uint8_t cycles; 168 | uint8_t bytes; 169 | const char *desc; 170 | 171 | bool operator<(const instruction_info &other) const { 172 | if (cycles < other.cycles) { return true; } 173 | else if (cycles == other.cycles) { return bytes < other.bytes; } 174 | return false; 175 | } 176 | } instruction_info; 177 | 178 | typedef struct instruction_seq { 179 | uint8_t cycles; 180 | uint8_t bytes; 181 | instruction instructions[7]; 182 | 183 | instruction_seq(uint8_t c, uint8_t b) : cycles(c), bytes(b) { 184 | for (int i = 0; i < 7; i++) { 185 | instructions[i] = instruction(instruction_name::NONE, addr_mode::NONE, 0); 186 | } 187 | } 188 | 189 | instruction_seq() : instruction_seq(0, 0) {} 190 | 191 | instruction_seq(std::array data) {} 192 | 193 | instruction_seq add(instruction_info info) const { 194 | instruction_seq newSeq(this->cycles + info.cycles, this->bytes + info.bytes); 195 | for (int i = 0; i < 7; i++) { 196 | if (this->instructions[i].name() == instruction_name::NONE) { 197 | newSeq.instructions[i] = info.ins; 198 | break; 199 | } else { 200 | newSeq.instructions[i] = this->instructions[i]; 201 | } 202 | } 203 | return newSeq; 204 | } 205 | 206 | bool operator>(const instruction_seq &other) const { 207 | if (cycles > other.cycles) { return true; } 208 | else if (cycles == other.cycles) { return bytes > other.bytes; } 209 | return false; 210 | } 211 | } instruction_seq; 212 | 213 | static instruction_info instructions[] = { 214 | { instruction(instruction_name::ADC, addr_mode::IMMEDIATE, 0), 20, 2, "adc.#" }, 215 | { instruction(instruction_name::ADC, addr_mode::CONSTANT, 0), 20, 2, "adc.#" }, 216 | { instruction(instruction_name::ADC, addr_mode::ZERO_PAGE, 0), 30, 2, "adc.z" }, 217 | { instruction(instruction_name::ADC, addr_mode::ZERO_PAGE_X, 0), 40, 2, "adc.zx" }, 218 | { instruction(instruction_name::ADC, addr_mode::ABSOLUTE, 0), 40, 3, "adc" }, 219 | { instruction(instruction_name::ADC, addr_mode::ABSOLUTE_X, 0), 41, 3, "adc.x" }, 220 | { instruction(instruction_name::ADC, addr_mode::ABSOLUTE_Y, 0), 41, 3, "adc.y" }, 221 | { instruction(instruction_name::ADC, addr_mode::X_INDIRECT, 0), 60, 2, "adc.xi" }, 222 | { instruction(instruction_name::ADC, addr_mode::INDIRECT_Y, 0), 51, 2, "adc.iy" }, 223 | 224 | { instruction(instruction_name::AND, addr_mode::IMMEDIATE, 0), 20, 2, "and.#" }, 225 | { instruction(instruction_name::AND, addr_mode::CONSTANT, 0), 20, 2, "and.#" }, 226 | { instruction(instruction_name::AND, addr_mode::ZERO_PAGE, 0), 30, 2, "and.z" }, 227 | { instruction(instruction_name::AND, addr_mode::ZERO_PAGE_X, 0), 40, 2, "and.zx" }, 228 | { instruction(instruction_name::AND, addr_mode::ABSOLUTE, 0), 40, 3, "and" }, 229 | { instruction(instruction_name::AND, addr_mode::ABSOLUTE_X, 0), 41, 3, "and.x" }, 230 | { instruction(instruction_name::AND, addr_mode::ABSOLUTE_Y, 0), 41, 3, "and.y" }, 231 | { instruction(instruction_name::AND, addr_mode::X_INDIRECT, 0), 60, 2, "and.xi" }, 232 | { instruction(instruction_name::AND, addr_mode::INDIRECT_Y, 0), 51, 2, "and.iy" }, 233 | 234 | { instruction(instruction_name::ASLA, addr_mode::NONE, 0), 20, 1, "asl.a" }, 235 | { instruction(instruction_name::ASL, addr_mode::ZERO_PAGE, 0), 50, 2, "asl.z" }, 236 | { instruction(instruction_name::ASL, addr_mode::ZERO_PAGE_X, 0), 60, 2, "asl.zx" }, 237 | { instruction(instruction_name::ASL, addr_mode::ABSOLUTE, 0), 60, 3, "asl" }, 238 | { instruction(instruction_name::ASL, addr_mode::ABSOLUTE_X, 0), 70, 3, "asl.x" }, 239 | 240 | { instruction(instruction_name::BIT, addr_mode::ZERO_PAGE, 0), 30, 2, "bit.z" }, 241 | { instruction(instruction_name::BIT, addr_mode::ABSOLUTE, 0), 40, 3, "bit" }, 242 | 243 | { instruction(instruction_name::BPL, addr_mode::ABSOLUTE, 0), 25, 2, "bpl" }, 244 | { instruction(instruction_name::BMI, addr_mode::ABSOLUTE, 0), 25, 2, "bmi" }, 245 | { instruction(instruction_name::BVC, addr_mode::ABSOLUTE, 0), 25, 2, "bvc" }, 246 | { instruction(instruction_name::BVS, addr_mode::ABSOLUTE, 0), 25, 2, "bvs" }, 247 | { instruction(instruction_name::BCC, addr_mode::ABSOLUTE, 0), 25, 2, "bcc" }, 248 | { instruction(instruction_name::BCS, addr_mode::ABSOLUTE, 0), 25, 2, "bcs" }, 249 | { instruction(instruction_name::BNE, addr_mode::ABSOLUTE, 0), 25, 2, "bne" }, 250 | { instruction(instruction_name::BEQ, addr_mode::ABSOLUTE, 0), 25, 2, "beq" }, 251 | 252 | // { instruction(instruction_name::BRK, addr_mode::NONE, 0), 70, 2 }, 253 | 254 | { instruction(instruction_name::CMP, addr_mode::IMMEDIATE, 0), 20, 2, "cmp.#" }, 255 | { instruction(instruction_name::CMP, addr_mode::CONSTANT, 0), 20, 2, "cmp.#" }, 256 | { instruction(instruction_name::CMP, addr_mode::ZERO_PAGE, 0), 30, 2, "cmp.z" }, 257 | { instruction(instruction_name::CMP, addr_mode::ZERO_PAGE_X, 0), 40, 2, "cmp.zx" }, 258 | { instruction(instruction_name::CMP, addr_mode::ABSOLUTE, 0), 40, 3, "cmp" }, 259 | { instruction(instruction_name::CMP, addr_mode::ABSOLUTE_X, 0), 41, 3, "cmp.x" }, 260 | { instruction(instruction_name::CMP, addr_mode::ABSOLUTE_Y, 0), 41, 3, "cmp.y" }, 261 | { instruction(instruction_name::CMP, addr_mode::X_INDIRECT, 0), 60, 2, "cmp.xi" }, 262 | { instruction(instruction_name::CMP, addr_mode::INDIRECT_Y, 0), 51, 2, "cmp.iy" }, 263 | 264 | { instruction(instruction_name::CPX, addr_mode::IMMEDIATE, 0), 20, 2, "cpx.#" }, 265 | { instruction(instruction_name::CPX, addr_mode::CONSTANT, 0), 20, 2, "cpx.#" }, 266 | { instruction(instruction_name::CPX, addr_mode::ZERO_PAGE, 0), 30, 2, "cpx.z" }, 267 | { instruction(instruction_name::CPX, addr_mode::ABSOLUTE, 0), 40, 3, "cpx" }, 268 | 269 | { instruction(instruction_name::CPY, addr_mode::IMMEDIATE, 0), 20, 2, "cpy.#" }, 270 | { instruction(instruction_name::CPY, addr_mode::CONSTANT, 0), 20, 2, "cpy.#" }, 271 | { instruction(instruction_name::CPY, addr_mode::ZERO_PAGE, 0), 30, 2, "cpy.z" }, 272 | { instruction(instruction_name::CPY, addr_mode::ABSOLUTE, 0), 40, 3, "cpy" }, 273 | 274 | { instruction(instruction_name::DEC, addr_mode::ZERO_PAGE, 0), 50, 2, "dec.z" }, 275 | { instruction(instruction_name::DEC, addr_mode::ZERO_PAGE_X, 0), 60, 2, "dec.zx" }, 276 | { instruction(instruction_name::DEC, addr_mode::ABSOLUTE, 0), 60, 3, "dec" }, 277 | { instruction(instruction_name::DEC, addr_mode::ABSOLUTE_X, 0), 70, 3, "dec.x" }, 278 | 279 | { instruction(instruction_name::EOR, addr_mode::IMMEDIATE, 0), 20, 2, "eor.#" }, 280 | { instruction(instruction_name::EOR, addr_mode::CONSTANT, 0), 20, 2, "eor.#" }, 281 | { instruction(instruction_name::EOR, addr_mode::ZERO_PAGE, 0), 30, 2, "eor.z" }, 282 | { instruction(instruction_name::EOR, addr_mode::ZERO_PAGE_X, 0), 40, 2, "eor.zx" }, 283 | { instruction(instruction_name::EOR, addr_mode::ABSOLUTE, 0), 40, 3, "eor" }, 284 | { instruction(instruction_name::EOR, addr_mode::ABSOLUTE_X, 0), 41, 3, "eor.x" }, 285 | { instruction(instruction_name::EOR, addr_mode::ABSOLUTE_Y, 0), 41, 3, "eor.y" }, 286 | { instruction(instruction_name::EOR, addr_mode::X_INDIRECT, 0), 60, 2, "eor.xi" }, 287 | { instruction(instruction_name::EOR, addr_mode::INDIRECT_Y, 0), 51, 2, "eor.iy" }, 288 | 289 | { instruction(instruction_name::CLC, addr_mode::NONE, 0), 20, 1, "clc" }, 290 | { instruction(instruction_name::SEC, addr_mode::NONE, 0), 20, 1, "sec" }, 291 | { instruction(instruction_name::CLI, addr_mode::NONE, 0), 20, 1, "cli" }, 292 | { instruction(instruction_name::SEI, addr_mode::NONE, 0), 20, 1, "sei" }, 293 | { instruction(instruction_name::CLV, addr_mode::NONE, 0), 20, 1, "clv" }, 294 | { instruction(instruction_name::CLD, addr_mode::NONE, 0), 20, 1, "cld" }, 295 | { instruction(instruction_name::SED, addr_mode::NONE, 0), 20, 1, "sed" }, 296 | 297 | { instruction(instruction_name::INC, addr_mode::ZERO_PAGE, 0), 50, 2, "inc.z" }, 298 | { instruction(instruction_name::INC, addr_mode::ZERO_PAGE_X, 0), 60, 2, "inc.zx" }, 299 | { instruction(instruction_name::INC, addr_mode::ABSOLUTE, 0), 60, 3, "inc" }, 300 | { instruction(instruction_name::INC, addr_mode::ABSOLUTE_X, 0), 70, 3, "inc.x" }, 301 | 302 | { instruction(instruction_name::JMP, addr_mode::ABSOLUTE, 0), 30, 3, "jmp" }, 303 | { instruction(instruction_name::JSR, addr_mode::ABSOLUTE, 0), 60, 3, "jsr" }, 304 | 305 | { instruction(instruction_name::LDA, addr_mode::IMMEDIATE, 0), 20, 2, "lda.#" }, 306 | { instruction(instruction_name::LDA, addr_mode::CONSTANT, 0), 20, 2, "lda.#" }, 307 | { instruction(instruction_name::LDA, addr_mode::ZERO_PAGE, 0), 30, 2, "lda.z" }, 308 | { instruction(instruction_name::LDA, addr_mode::ZERO_PAGE_X, 0), 40, 2, "lda.zx" }, 309 | { instruction(instruction_name::LDA, addr_mode::ABSOLUTE, 0), 40, 3, "lda" }, 310 | { instruction(instruction_name::LDA, addr_mode::ABSOLUTE_X, 0), 41, 3, "lda.x" }, 311 | { instruction(instruction_name::LDA, addr_mode::ABSOLUTE_Y, 0), 41, 3, "lda.y" }, 312 | { instruction(instruction_name::LDA, addr_mode::X_INDIRECT, 0), 60, 2, "lda.xi" }, 313 | { instruction(instruction_name::LDA, addr_mode::INDIRECT_Y, 0), 51, 2, "lda.iy" }, 314 | 315 | { instruction(instruction_name::LDX, addr_mode::IMMEDIATE, 0), 20, 2, "ldx.#" }, 316 | { instruction(instruction_name::LDX, addr_mode::CONSTANT, 0), 20, 2, "ldx.#" }, 317 | { instruction(instruction_name::LDX, addr_mode::ZERO_PAGE, 0), 30, 2, "ldx.z" }, 318 | { instruction(instruction_name::LDX, addr_mode::ZERO_PAGE_Y, 0), 40, 2, "ldx.zy" }, 319 | { instruction(instruction_name::LDX, addr_mode::ABSOLUTE, 0), 40, 3, "ldx" }, 320 | { instruction(instruction_name::LDX, addr_mode::ABSOLUTE_Y, 0), 41, 3, "ldx.y" }, 321 | 322 | { instruction(instruction_name::LDY, addr_mode::IMMEDIATE, 0), 20, 2, "ldy.#" }, 323 | { instruction(instruction_name::LDY, addr_mode::CONSTANT, 0), 20, 2, "ldy.#" }, 324 | { instruction(instruction_name::LDY, addr_mode::ZERO_PAGE, 0), 30, 2, "ldy.z" }, 325 | { instruction(instruction_name::LDY, addr_mode::ZERO_PAGE_X, 0), 40, 2, "ldy.zx" }, 326 | { instruction(instruction_name::LDY, addr_mode::ABSOLUTE, 0), 40, 3, "ldy" }, 327 | { instruction(instruction_name::LDY, addr_mode::ABSOLUTE_X, 0), 41, 3, "ldy.x" }, 328 | 329 | { instruction(instruction_name::LSRA, addr_mode::NONE, 0), 20, 1, "lsr.a" }, 330 | { instruction(instruction_name::LSR, addr_mode::ZERO_PAGE, 0), 50, 2, "lsr.z" }, 331 | { instruction(instruction_name::LSR, addr_mode::ZERO_PAGE_X, 0), 60, 2, "lsr.zx" }, 332 | { instruction(instruction_name::LSR, addr_mode::ABSOLUTE, 0), 60, 3, "lsr" }, 333 | { instruction(instruction_name::LSR, addr_mode::ABSOLUTE_X, 0), 70, 3, "lsr.x" }, 334 | 335 | // { instruction(instruction_name::NOP, addr_mode::NONE, 0), 20, 1 }, 336 | 337 | { instruction(instruction_name::ORA, addr_mode::IMMEDIATE, 0), 20, 2, "ora.#" }, 338 | { instruction(instruction_name::ORA, addr_mode::CONSTANT, 0), 20, 2, "ora.#" }, 339 | { instruction(instruction_name::ORA, addr_mode::ZERO_PAGE, 0), 30, 2, "ora.z" }, 340 | { instruction(instruction_name::ORA, addr_mode::ZERO_PAGE_X, 0), 40, 2, "ora.zx" }, 341 | { instruction(instruction_name::ORA, addr_mode::ABSOLUTE, 0), 40, 3, "ora" }, 342 | { instruction(instruction_name::ORA, addr_mode::ABSOLUTE_X, 0), 41, 3, "ora.x" }, 343 | { instruction(instruction_name::ORA, addr_mode::ABSOLUTE_Y, 0), 41, 3, "ora.y" }, 344 | { instruction(instruction_name::ORA, addr_mode::X_INDIRECT, 0), 60, 2, "ora.xi" }, 345 | { instruction(instruction_name::ORA, addr_mode::INDIRECT_Y, 0), 51, 2, "ora.iy" }, 346 | 347 | { instruction(instruction_name::TAX, addr_mode::NONE, 0), 20, 1, "tax" }, 348 | { instruction(instruction_name::TXA, addr_mode::NONE, 0), 20, 1, "txa" }, 349 | { instruction(instruction_name::DEX, addr_mode::NONE, 0), 20, 1, "dex" }, 350 | { instruction(instruction_name::INX, addr_mode::NONE, 0), 20, 1, "inx" }, 351 | { instruction(instruction_name::TAY, addr_mode::NONE, 0), 20, 1, "tay" }, 352 | { instruction(instruction_name::TYA, addr_mode::NONE, 0), 20, 1, "tya" }, 353 | { instruction(instruction_name::DEY, addr_mode::NONE, 0), 20, 1, "dey" }, 354 | { instruction(instruction_name::INY, addr_mode::NONE, 0), 20, 1, "iny" }, 355 | 356 | { instruction(instruction_name::ROLA, addr_mode::NONE, 0), 20, 1, "rol.a" }, 357 | { instruction(instruction_name::ROL, addr_mode::ZERO_PAGE, 0), 50, 2, "rol.z" }, 358 | { instruction(instruction_name::ROL, addr_mode::ZERO_PAGE_X, 0), 60, 2, "rol.zx" }, 359 | { instruction(instruction_name::ROL, addr_mode::ABSOLUTE, 0), 60, 3, "rol" }, 360 | { instruction(instruction_name::ROL, addr_mode::ABSOLUTE_X, 0), 70, 3, "rol.x" }, 361 | 362 | { instruction(instruction_name::RORA, addr_mode::NONE, 0), 20, 1, "ror.a" }, 363 | { instruction(instruction_name::ROR, addr_mode::ZERO_PAGE, 0), 50, 2, "ror.z" }, 364 | { instruction(instruction_name::ROR, addr_mode::ZERO_PAGE_X, 0), 60, 2, "ror.zx" }, 365 | { instruction(instruction_name::ROR, addr_mode::ABSOLUTE, 0), 60, 3, "ror" }, 366 | { instruction(instruction_name::ROR, addr_mode::ABSOLUTE_X, 0), 70, 3, "ror.x" }, 367 | 368 | { instruction(instruction_name::RTI, addr_mode::NONE, 0), 60, 1, "rti" }, 369 | { instruction(instruction_name::RTS, addr_mode::NONE, 0), 60, 1, "rts" }, 370 | 371 | { instruction(instruction_name::SBC, addr_mode::IMMEDIATE, 0), 20, 2, "sbc.#" }, 372 | { instruction(instruction_name::SBC, addr_mode::CONSTANT, 0), 20, 2, "sbc.#" }, 373 | { instruction(instruction_name::SBC, addr_mode::ZERO_PAGE, 0), 30, 2, "sbc.z" }, 374 | { instruction(instruction_name::SBC, addr_mode::ZERO_PAGE_X, 0), 40, 2, "sbc.zx" }, 375 | { instruction(instruction_name::SBC, addr_mode::ABSOLUTE, 0), 40, 3, "sbc" }, 376 | { instruction(instruction_name::SBC, addr_mode::ABSOLUTE_X, 0), 41, 3, "sbc.x" }, 377 | { instruction(instruction_name::SBC, addr_mode::ABSOLUTE_Y, 0), 41, 3, "sbc.y" }, 378 | { instruction(instruction_name::SBC, addr_mode::X_INDIRECT, 0), 60, 2, "sbc.xi" }, 379 | { instruction(instruction_name::SBC, addr_mode::INDIRECT_Y, 0), 51, 2, "sbc.iy" }, 380 | 381 | { instruction(instruction_name::STA, addr_mode::ZERO_PAGE, 0), 30, 2, "sta.z" }, 382 | { instruction(instruction_name::STA, addr_mode::ZERO_PAGE_X, 0), 40, 2, "sta.zx" }, 383 | { instruction(instruction_name::STA, addr_mode::ABSOLUTE, 0), 40, 3, "sta" }, 384 | { instruction(instruction_name::STA, addr_mode::ABSOLUTE_X, 0), 50, 3, "sta.x" }, 385 | { instruction(instruction_name::STA, addr_mode::ABSOLUTE_Y, 0), 50, 3, "sta.y" }, 386 | { instruction(instruction_name::STA, addr_mode::X_INDIRECT, 0), 60, 2, "sta.xi" }, 387 | { instruction(instruction_name::STA, addr_mode::INDIRECT_Y, 0), 60, 2, "sta.iy" }, 388 | 389 | { instruction(instruction_name::TXS, addr_mode::NONE, 0), 20, 1, "txs" }, 390 | { instruction(instruction_name::TSX, addr_mode::NONE, 0), 20, 1, "tsx" }, 391 | { instruction(instruction_name::PHA, addr_mode::NONE, 0), 30, 1, "pha" }, 392 | { instruction(instruction_name::PLA, addr_mode::NONE, 0), 40, 1, "pla" }, 393 | { instruction(instruction_name::PHP, addr_mode::NONE, 0), 30, 1, "php" }, 394 | { instruction(instruction_name::PLP, addr_mode::NONE, 0), 40, 1, "plp" }, 395 | 396 | { instruction(instruction_name::STX, addr_mode::ZERO_PAGE, 0), 30, 2, "stx.z" }, 397 | { instruction(instruction_name::STX, addr_mode::ZERO_PAGE_Y, 0), 40, 2, "stx.zy" }, 398 | { instruction(instruction_name::STX, addr_mode::ABSOLUTE, 0), 40, 3, "stx" }, 399 | 400 | { instruction(instruction_name::STY, addr_mode::ZERO_PAGE, 0), 30, 2, "sty.z" }, 401 | { instruction(instruction_name::STY, addr_mode::ZERO_PAGE_X, 0), 40, 2, "sty.zx" }, 402 | { instruction(instruction_name::STY, addr_mode::ABSOLUTE, 0), 40, 3, "sty" }, 403 | }; 404 | -------------------------------------------------------------------------------- /libz3.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RussellSprouts/6502-enumerator/96eebf4ef08a9b8c329b4ad362a15b3f2a55ccff/libz3.so -------------------------------------------------------------------------------- /operations.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "stdint.h" 5 | 6 | enum Operations: uint8_t { 7 | ADC = 1, 8 | AND = 2, 9 | ASL = 3, 10 | BIT = 4, 11 | CMP = 5, 12 | CPX = 6, 13 | CPY = 7, 14 | DEC = 8, 15 | EOR = 9, 16 | INC = 10, 17 | JMP = 11, 18 | LDA = 12, 19 | LDX = 13, 20 | LDY = 14, 21 | LSR = 15, 22 | ORA = 16, 23 | ROL = 17, 24 | ROR = 18, 25 | SBC = 19, 26 | STA = 20, 27 | STX = 21, 28 | STY = 22, 29 | ASL_A = 23, 30 | BPL = 24, 31 | BMI = 25, 32 | BVC = 26, 33 | BVS = 27, 34 | BCC = 28, 35 | BCS = 29, 36 | BNE = 30, 37 | BEQ = 31, 38 | CLC = 32, 39 | SEC = 33, 40 | CLI = 34, 41 | SEI = 35, 42 | CLV = 36, 43 | CLD = 37, 44 | SED = 38, 45 | LSR_A = 39, 46 | NOP = 40, 47 | TAX = 41, 48 | TXA = 42, 49 | DEX = 43, 50 | INX = 44, 51 | TAY = 45, 52 | TYA = 46, 53 | DEY = 47, 54 | INY = 48, 55 | ROL_A = 49, 56 | ROR_A = 50, 57 | RTI = 51, 58 | RTS = 52, 59 | TXS = 53, 60 | TSX = 54, 61 | PHA = 55, 62 | PLA = 56, 63 | PHP = 57, 64 | PLP = 58 65 | }; 66 | 67 | const char *OpNames[] = { 68 | "", "adc", "and", "asl", "bit", "cmp", "cpx", "cpy", "dec", "eor", "inc", "jmp", "lda", "ldx", "ldy", "lsr", "ora", "rol", "ror", "sbc", "sta", "stx", "sty", 69 | "asl_a", "bpl", "bmi", "bvc", "bvs", "bcc", "bcs", "bne", "beq", "clc", "sec", "cli", "sei", "clv", "cld", "sed", "lsr_a", "nop", "tax", "txa", "dex", "inx", "tay", "tya", "dey", "iny", "rol_a", "ror_a", "rti", "rts", "txs", "tsx", "pha", "pla", "php", "plp" 70 | }; 71 | 72 | enum AddrMode: uint8_t { 73 | ImmediateC0 = 0x00, 74 | ImmediateC1 = 0x01, 75 | Immediate0 = 0x02, 76 | Immediate1 = 0x03, 77 | ImmediateC0Plus1 = 0x04, 78 | ImmediateC1Plus1 = 0x05, 79 | ImmediateC0PlusC1 = 0x06, 80 | Absolute0 = 0x17, 81 | Absolute1 = 0x18, 82 | Absolute2 = 0x19, 83 | AbsoluteX0 = 0x27, 84 | AbsoluteX1 = 0x28, 85 | AbsoluteX2 = 0x29, 86 | AbsoluteY0 = 0x37, 87 | AbsoluteY1 = 0x38, 88 | AbsoluteY2 = 0x39, 89 | ZeroPage0 = 0x4A, 90 | ZeroPage1 = 0x4B, 91 | ZeroPage2 = 0x4C, 92 | ZeroPage3 = 0x4D, 93 | ZeroPageX0 = 0x5A, 94 | ZeroPageX1 = 0x5B, 95 | ZeroPageX2 = 0x5C, 96 | ZeroPageX3 = 0x5D, 97 | ZeroPageY0 = 0x6A, 98 | ZeroPageY1 = 0x6B, 99 | ZeroPageY2 = 0x6C, 100 | ZeroPageY3 = 0x6D, 101 | Indirect0 = 0x77, 102 | Indirect1 = 0x78, 103 | Indirect2 = 0x79, 104 | IndirectX0 = 0x8A, 105 | IndirectX1 = 0x8B, 106 | IndirectX2 = 0x8C, 107 | IndirectX3 = 0x8D, 108 | IndirectY0 = 0x9A, 109 | IndirectY1 = 0x9B, 110 | IndirectY2 = 0x9C, 111 | IndirectY3 = 0x9D, 112 | None = 0xAE 113 | }; 114 | 115 | std::map AddrModeNames { 116 | { ImmediateC0, "#C0" }, 117 | { ImmediateC1, "#C1"}, 118 | { Immediate0, "#0"}, 119 | { Immediate1, "#1" }, 120 | { ImmediateC0Plus1, "#C0+1" }, 121 | { ImmediateC1Plus1, "#C1+1" }, 122 | { ImmediateC0PlusC1, "#C0+C1" }, 123 | { Absolute0, "Absolute0" }, 124 | { Absolute1, "Absolute1" }, 125 | { Absolute2, "Absolute2" }, 126 | { AbsoluteX0, "Absolute0, x" }, 127 | { AbsoluteX1, "Absolute1, x" }, 128 | { AbsoluteX2, "Absolute2, x" }, 129 | { AbsoluteY0, "Absolute0, y" }, 130 | { AbsoluteY1, "Absolute1, y" }, 131 | { AbsoluteY2, "Absolute2, y" }, 132 | { ZeroPage0, "Zp0" }, 133 | { ZeroPage1, "Zp1" }, 134 | { ZeroPage2, "Zp2" }, 135 | { ZeroPage3, "Zp3" }, 136 | { ZeroPageX0, "Zp0, x" }, 137 | { ZeroPageX1, "Zp1, x" }, 138 | { ZeroPageX2, "Zp2, x" }, 139 | { ZeroPageX3, "Zp3, x" }, 140 | { ZeroPageY0, "Zp0, y" }, 141 | { ZeroPageY1, "Zp1, y" }, 142 | { ZeroPageY2, "Zp2, y" }, 143 | { ZeroPageY3, "Zp3, y" }, 144 | { Indirect0, "(Absolute0)" }, 145 | { Indirect1, "(Absolute1)" }, 146 | { Indirect2, "(Absolute2)" }, 147 | { IndirectX0, "(Zp0, x)" }, 148 | { IndirectX1, "(Zp1, x)" }, 149 | { IndirectX2, "(Zp2, x)" }, 150 | { IndirectX3, "(Zp3, x)" }, 151 | { IndirectY0, "(Zp0), y" }, 152 | { IndirectY1, "(Zp1), y" }, 153 | { IndirectY2, "(Zp2), y" }, 154 | { IndirectY3, "(Zp3), y" }, 155 | { None, "" } 156 | }; 157 | 158 | -------------------------------------------------------------------------------- /optimize.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "instructions2.h" 3 | 4 | enum class used_variables : uint64_t { 5 | absolute = 0xF, 6 | absolute0 = 0x1, 7 | absolute1 = 0x2, 8 | absolute2 = 0x4, 9 | absolute3 = 0x8, 10 | 11 | zp = 0xF0, 12 | zp0 = 0x10, 13 | zp1 = 0x20, 14 | zp2 = 0x40, 15 | zp3 = 0x80, 16 | 17 | immediate = 0xF00, 18 | immediate0 = 0x100, 19 | immediate1 = 0x200, 20 | immediate2 = 0x400, 21 | immediate3 = 0x800, 22 | }; 23 | 24 | /* 25 | NEEDS: 26 | - Canonicalization and rewriting of instruction sequences. 27 | - SSA form of instruction sequence, to allow building a dag of instruction orderings. 28 | 29 | Canonicalization: 30 | 31 | for example, the instruction sequence: 32 | 33 | lda #1 34 | ldx #0 35 | sta absolute0, x 36 | ldx #1 37 | stx absolute0 38 | 39 | Can be represented like this: 40 | 41 | a0, s0, n0, mem0 = start() 42 | a1, s1, n1 = lda(#1) 43 | x1, s2, n2 = ldx(#0) 44 | mem1 = sta(a1, absolute0, x1, mem0) 45 | x2, s3, n3 = ldx(#1) 46 | mem2 = stx(x2, absolute0, mem1) 47 | end(mem2, a1, x2, s3, n3) 48 | 49 | 50 | a0 s0 n0 mem0 = start() 51 | | | | | 52 | x x x | 53 | | 54 | a1 s1 n1 | = lda(#1) 55 | \ | | | 56 | \ x x | 57 | +--------------------+ 58 | x1 | s2 n2 | = ldx(#0) 59 | | | | | \ 60 | x \ x x ---------+---------------+ 61 | ----------------- | | 62 | mem1 \ = sta(a1, absolute0, mem0) 63 | \ | 64 | +----------------)---------------------------+ 65 | | | 66 | x2 s3 n3 | = ldx(#1) | 67 | \ | | | | 68 | \ | | | | 69 | ------)------)----)---+-------- | 70 | | | | | \ 71 | mem2 | | | | = sta(x2, absolute0, mem1) 72 | \ | | | | 73 | ------+------+----+---+---------+----+---+---+---+ 74 | | | | | | 75 | end(mem2, a1, x2, s3, n3) 76 | 77 | Which forms this dag: 78 | 79 | o 80 | / \ 81 | +---- ----+ 82 | lda(#1) ldx(#0) 83 | | | 84 | sta(absolute0, x) 85 | | 86 | ldx(#1) 87 | | 88 | stx(absolute0) 89 | 90 | A topological sort of this graph gives 91 | all of the possible orderings of the instructions, 92 | which doesn't leave much room for changes. 93 | 94 | Now, we can apply optimizations: 95 | 1. "ldx #0 ; sta absolute0, x", where x is not live, 96 | is the same as sta absolute0. 97 | 98 | o 99 | / \ 100 | +--- ----+ 101 | lda(#1) ldx(#1) 102 | | | 103 | sta(absolute0) | 104 | | | 105 | stx(absolute0) 106 | 2. "lda #1 ; ldx #1" is "lda #1 ; tax" 107 | 108 | lda #1 109 | tax 110 | sta absolute0 111 | stx absolute0 112 | 113 | 3. "sta absolute0 ; stx absolute0" is "stx absolute0" 114 | 115 | lda #1 116 | tax 117 | stx absolute0 118 | */ 119 | -------------------------------------------------------------------------------- /queue.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static const int N_THREADS = std::thread::hardware_concurrency(); 6 | 7 | template 8 | struct work_queue { 9 | 10 | using work_item = std::function; 11 | std::vector stores; 12 | std::vector vec; 13 | std::mutex mutex; 14 | unsigned int i = 0; 15 | bool running = false; 16 | 17 | void add(work_item f) { 18 | mutex.lock(); 19 | if (running) { 20 | throw "Cannot add after the work queue has started."; 21 | } 22 | vec.push_back(f); 23 | mutex.unlock(); 24 | } 25 | 26 | bool get_task(work_item *out) { 27 | mutex.lock(); 28 | bool result = false; 29 | if (i < vec.size()) { 30 | *out = vec[i++]; 31 | result = true; 32 | } 33 | mutex.unlock(); 34 | return result; 35 | } 36 | 37 | void thread_worker(ThreadStorage &store) { 38 | work_item f; 39 | while (get_task(&f)) { 40 | f(store); 41 | } 42 | } 43 | 44 | void run() { 45 | running = true; 46 | stores = std::vector(N_THREADS); 47 | 48 | std::thread threads[N_THREADS - 1]; 49 | for (int i = 0; i < N_THREADS - 1; i++) { 50 | threads[i] = std::thread(&work_queue::thread_worker, this, std::ref(stores[i])); 51 | } 52 | thread_worker(stores[N_THREADS - 1]); 53 | 54 | for (int i = 0; i < N_THREADS-1; i++) { 55 | threads[i].join(); 56 | } 57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /radix-sort.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "stdint.h" 6 | 7 | // Implement this to get the bit with the given number 8 | // from the buffer. 0 is the least significant bit. 9 | static inline bool get_bit(const char *buf, const int bit) { 10 | const int byte = buf[bit/8]; 11 | return (byte >> (bit % 8)) & 1; 12 | } 13 | 14 | static void radix_sort(const char *file, const uint8_t size, const uint8_t bits) { 15 | const int buffer_size = size * 256; 16 | 17 | // Read the input file and output to out0 or out1. 18 | { 19 | std::ifstream input(file, std::ifstream::in | std::ifstream::binary); 20 | std::cout << "Initial read of input." << std::endl; 21 | std::ofstream out0("out0.tmp", std::ofstream::out | std::ofstream::trunc | std::ofstream::binary); 22 | std::ofstream out1("out1.tmp", std::ofstream::out | std::ofstream::trunc | std::ofstream::binary); 23 | 24 | while (!input.eof()) { 25 | char buffer[buffer_size]; 26 | input.read(buffer, buffer_size); 27 | std::streamsize dataSize = input.gcount(); 28 | for (int j = 0; j < dataSize; j += size) { 29 | if (get_bit(buffer + j, 0)) { 30 | out1.write(buffer + j, size); 31 | } else { 32 | out0.write(buffer + j, size); 33 | } 34 | } 35 | } 36 | } 37 | 38 | // Now read the previous outputs and output to new files. 39 | for (int i = 1; i < bits; i++) { 40 | std::cout << "Bit " << i << std::endl; 41 | std::ofstream out0(i % 2 ? "out0-2.tmp" : "out0.tmp", std::ofstream::out | std::ofstream::trunc | std::ofstream::binary); 42 | std::ofstream out1(i % 2 ? "out1-2.tmp" : "out1.tmp", std::ofstream::out | std::ofstream::trunc | std::ofstream::binary); 43 | 44 | std::ifstream input0(i % 2 ? "out0.tmp" : "out0-2.tmp", std::ifstream::in | std::ifstream::binary); 45 | std::ifstream input1(i % 2 ? "out1.tmp" : "out1-2.tmp", std::ifstream::in | std::ifstream::binary); 46 | while (!input0.eof()) { 47 | char buffer[buffer_size]; 48 | input0.read(buffer, buffer_size); 49 | std::streamsize dataSize = input0.gcount(); 50 | for (int j = 0; j < dataSize; j += size) { 51 | if (get_bit(buffer + j, i)) { 52 | out1.write(buffer + j, size); 53 | } else { 54 | out0.write(buffer + j, size); 55 | } 56 | } 57 | } 58 | 59 | while (!input1.eof()) { 60 | char buffer[buffer_size]; 61 | input1.read(buffer, buffer_size); 62 | std::streamsize dataSize = input1.gcount(); 63 | for (int j = 0; j < dataSize; j += size) { 64 | if (get_bit(buffer + j, i)) { 65 | out1.write(buffer + j, size); 66 | } else { 67 | out0.write(buffer + j, size); 68 | } 69 | } 70 | } 71 | } 72 | 73 | { 74 | std::ifstream input0(bits % 2 ? "out0.tmp" : "out0-2.tmp", std::ifstream::in | std::ifstream::binary); 75 | std::ifstream input1(bits % 2 ? "out1.tmp" : "out1-2.tmp", std::ifstream::in | std::ifstream::binary); 76 | std::ofstream output(file, std::ofstream::out | std::ofstream::trunc | std::ofstream::binary); 77 | 78 | while (!input0.eof()) { 79 | char buffer[buffer_size]; 80 | input0.read(buffer, buffer_size); 81 | std::streamsize dataSize = input0.gcount(); 82 | output.write(buffer, dataSize); 83 | } 84 | 85 | while (!input1.eof()) { 86 | char buffer[buffer_size]; 87 | input1.read(buffer, buffer_size); 88 | std::streamsize dataSize = input1.gcount(); 89 | output.write(buffer, dataSize); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /random_machine.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "instructions2.h" 5 | #include "stdint.h" 6 | #include "string.h" 7 | 8 | constexpr int NUM_ADDRESSES = 16; 9 | 10 | /** 11 | * random_machine represents a 6502 processor with a random 12 | * initial state, determined by the seed. 13 | */ 14 | struct random_machine { 15 | static const uint8_t literal0 = 0; 16 | static const uint8_t literal1 = 1; 17 | static const bool falsy = false; 18 | static const bool truthy = true; 19 | 20 | random_machine() {} 21 | 22 | uint32_t init; 23 | 24 | random_machine(uint32_t _seed) { 25 | seed = _seed; 26 | init = 2166136261; 27 | init = (init ^ (seed & 0xFF)) * 16777619; 28 | init = (init ^ ((seed >> 8) & 0xFF)) * 16777619; 29 | init = (init ^ ((seed >> 16) & 0xFF)) * 16777619; 30 | init = (init ^ ((seed >> 24) & 0xFF)) * 16777619; 31 | 32 | absoluteVars[0] = fnv(125); 33 | absoluteVars[1] = fnv(126); 34 | absoluteVars[2] = fnv(127); 35 | absoluteVars[3] = fnv(128); 36 | 37 | zpVars[0] = fnv(150); 38 | zpVars[1] = fnv(151); 39 | zpVars[2] = fnv(152); 40 | zpVars[3] = fnv(153); 41 | 42 | immediateVars[0] = fnv(175); 43 | immediateVars[1] = fnv(176); 44 | immediateVars[2] = fnv(177); 45 | immediateVars[3] = fnv(178); 46 | 47 | _a = fnv(113); 48 | _x = fnv(114); 49 | _y = fnv(115); 50 | _sp = fnv(116); 51 | _ccS = (fnv(117)) > 0x80000000; 52 | _ccV = (fnv(118)) > 0x80000000; 53 | _ccI = (fnv(119)) > 0x80000000; 54 | _ccD = (fnv(120)) > 0x80000000; 55 | _ccC = (fnv(121)) > 0x80000000; 56 | _ccZ = (fnv(122)) > 0x80000000; 57 | } 58 | 59 | uint16_t absoluteVars[4]; 60 | uint16_t absolute(uint8_t number) const { 61 | return absoluteVars[number]; 62 | // return fnv(125 + number); 63 | } 64 | 65 | uint8_t zpVars[4]; 66 | uint8_t zp(uint8_t number) const { 67 | return zpVars[number]; 68 | } 69 | 70 | uint8_t immediateVars[4]; 71 | uint8_t immediate(uint8_t number) const { 72 | return immediateVars[number]; 73 | } 74 | 75 | uint8_t constant(uint8_t number) const { 76 | return number; 77 | } 78 | 79 | void instruction(instruction op) { 80 | emulator emu; 81 | emu.instruction(*this, op); 82 | } 83 | 84 | uint32_t seed; 85 | uint32_t earlyExit = 0; 86 | 87 | uint16_t writtenAddresses[NUM_ADDRESSES]; 88 | 89 | uint8_t writtenValues[NUM_ADDRESSES]; 90 | uint8_t numAddressesWritten = 0; 91 | 92 | uint8_t _a; 93 | uint8_t _x; 94 | uint8_t _y; 95 | uint8_t _sp; 96 | bool _ccS; 97 | bool _ccV; 98 | bool _ccI; 99 | bool _ccD; 100 | bool _ccC; 101 | bool _ccZ; 102 | 103 | // once the machine has exited, don't make any more changes 104 | // All methods that change the initial state of the machine 105 | // should be prefaced with E, causing them to be no-ops if 106 | // the machine has exited. 107 | #define E if (earlyExit != 0) { return 0; } 108 | 109 | bool rts() { 110 | E; return (earlyExit = 0x0001); 111 | } 112 | bool rti() { 113 | E; return (earlyExit = 0x0002); 114 | } 115 | bool jmp(uint16_t target) { 116 | E; return (earlyExit = ((uint32_t)target) | 0x10000); 117 | } 118 | bool branch(bool cond, uint16_t target) { 119 | E; if (cond) { return (earlyExit = target | 0x10000); } 120 | return 0; 121 | } 122 | 123 | uint8_t a(uint8_t val) { E return _a = val; } 124 | uint8_t x(uint8_t val) { E return _x = val; } 125 | uint8_t y(uint8_t val) { E return _y = val; } 126 | uint8_t sp(uint8_t val) { E return _sp = val; } 127 | bool ccS(bool val) { E return _ccS = val; } 128 | bool ccV(bool val) { E return _ccV = val; } 129 | bool ccI(bool val) { E return _ccI = val; } 130 | bool ccD(bool val) { E return _ccD = val; } 131 | bool ccC(bool val) { E return _ccC = val; } 132 | bool ccZ(bool val) { E return _ccZ = val; } 133 | 134 | // Reading on the concrete machine uses a randomly 135 | // filled memory space using the fnv hash. 136 | // It also remembers previous stores and returns 137 | // consistent results. 138 | uint8_t read(uint16_t addr) const { 139 | for (int i = 0; i < numAddressesWritten; i++) { 140 | if (writtenAddresses[i] == addr) return writtenValues[i]; 141 | } 142 | return fnv(addr); 143 | } 144 | 145 | uint8_t setSZ(uint8_t val) { 146 | ccS(val >= 0x80); 147 | ccZ(val == 0); 148 | return val; 149 | } 150 | 151 | bool uge(uint8_t first, uint8_t second) const { 152 | return first >= second; 153 | } 154 | 155 | uint16_t inline ite(bool cond, uint16_t conseq, uint16_t alter) const { 156 | return cond ? conseq : alter; 157 | } 158 | 159 | uint16_t inline shl(const uint16_t val) const { 160 | return val << 1; 161 | } 162 | 163 | uint16_t inline shr(const uint16_t val) const { 164 | return val >> 1; 165 | } 166 | 167 | uint8_t inline lobyte(uint16_t val) const { 168 | return val; 169 | } 170 | 171 | uint8_t inline hibyte(uint16_t val) const { 172 | return val >> 8; 173 | } 174 | 175 | // Remember written values so that it returns 176 | // consistent results. 177 | uint8_t write(uint16_t addr, uint8_t val) { 178 | E 179 | for (int i = 0; i < numAddressesWritten; i++) { 180 | if (addr == writtenAddresses[i]) { 181 | return writtenValues[i] = val; 182 | } 183 | } 184 | writtenAddresses[numAddressesWritten] = addr; 185 | return writtenValues[numAddressesWritten++] = val; 186 | } 187 | 188 | #undef E 189 | 190 | uint16_t extend(uint8_t val) const { 191 | return val; 192 | } 193 | 194 | // A hash function based on the fnv hash. 195 | // See: 196 | // http://isthe.com/chongo/tech/comp/fnv/#FNV-1 197 | uint32_t fnv(uint16_t value) const { 198 | if (seed == 0) { 199 | return 0; 200 | } else if (seed == 0xFFFFFFFF) { 201 | return 0xFFFFFFFF; 202 | } 203 | uint32_t hash = init; 204 | // first round use the seed. 205 | hash = hash ^ (value & 0xFF); 206 | hash = hash * 16777619; 207 | hash = hash ^ ((value & 0xFF00) >> 8); 208 | hash = hash * 16777619; 209 | return hash; 210 | } 211 | 212 | /** 213 | * Returns a hash representing the internal state 214 | * of the machine. It follows the rule that if two 215 | * instruction sequences are equivalent and run on two 216 | * random_machines with the same seed, then the machine's 217 | * hashes will be equivalent. 218 | * 219 | * The hash is basically the fnv-32 hash over the bytes 220 | * of the internal state of the machine. 221 | */ 222 | uint32_t hash() const { 223 | uint32_t hash = 2166136261; 224 | #define h(var) hash = (hash ^ (var)) * 16777619 225 | h(_a); 226 | h(_x); 227 | h(_y); 228 | h(_sp); 229 | h(_ccS); 230 | h(_ccV); 231 | h(_ccI); 232 | h(_ccD); 233 | h(_ccC); 234 | h(_ccZ); 235 | 236 | // For each changed address hash the address and value. 237 | for (int i = 0; i < numAddressesWritten; i++) { 238 | auto address = writtenAddresses[i]; 239 | auto value = writtenValues[i]; 240 | if (value != fnv(address)) { 241 | h(address); 242 | h(value); 243 | } 244 | } 245 | h(earlyExit); 246 | #undef h 247 | return hash; 248 | } 249 | }; 250 | --------------------------------------------------------------------------------