1 ! Copyright (C) 2006 Chris Double.
2 ! See http://factorcode.org/license.txt for BSD license.
31 TUPLE: cpu b c d e f h l a pc sp halted? last-interrupt cycles ram ;
33 GENERIC: reset ( cpu -- )
34 GENERIC: update-video ( value addr cpu -- )
35 GENERIC: read-port ( port cpu -- byte )
36 GENERIC: write-port ( value port cpu -- )
38 M: cpu update-video ( value addr cpu -- )
41 M: cpu read-port ( port cpu -- byte )
42 #! Read a byte from the hardware port. 'port' should
46 M: cpu write-port ( value port cpu -- )
47 #! Write a byte to the hardware port, where 'port' is
51 CONSTANT: carry-flag 0x01
52 CONSTANT: parity-flag 0x04
53 CONSTANT: half-carry-flag 0x10
54 CONSTANT: interrupt-flag 0x20
55 CONSTANT: zero-flag 0x40
56 CONSTANT: sign-flag 0x80
58 : >word< ( word -- byte byte )
59 #! Explode a word into its two 8 bit values.
60 dup 0xFF bitand swap -8 shift 0xFF bitand swap ;
62 : af>> ( cpu -- word )
63 #! Return the 16-bit pseudo register AF.
64 [ a>> 8 shift ] keep f>> bitor ;
66 : af<< ( value cpu -- )
67 #! Set the value of the 16-bit pseudo register AF
68 [ >word< ] dip swap >>f swap >>a drop ;
70 : bc>> ( cpu -- word )
71 #! Return the 16-bit pseudo register BC.
72 [ b>> 8 shift ] keep c>> bitor ;
74 : bc<< ( value cpu -- )
75 #! Set the value of the 16-bit pseudo register BC
76 [ >word< ] dip swap >>c swap >>b drop ;
78 : de>> ( cpu -- word )
79 #! Return the 16-bit pseudo register DE.
80 [ d>> 8 shift ] keep e>> bitor ;
82 : de<< ( value cpu -- )
83 #! Set the value of the 16-bit pseudo register DE
84 [ >word< ] dip swap >>e swap >>d drop ;
86 : hl>> ( cpu -- word )
87 #! Return the 16-bit pseudo register HL.
88 [ h>> 8 shift ] keep l>> bitor ;
90 : hl<< ( value cpu -- )
91 #! Set the value of the 16-bit pseudo register HL
92 [ >word< ] dip swap >>l swap >>h drop ;
94 : flag-set? ( flag cpu -- bool )
97 : flag-clear? ( flag cpu -- bool )
100 : flag-nz? ( cpu -- bool )
102 f>> zero-flag bitand 0 = ;
104 : flag-z? ( cpu -- bool )
106 f>> zero-flag bitand 0 = not ;
108 : flag-nc? ( cpu -- bool )
110 f>> carry-flag bitand 0 = ;
112 : flag-c? ( cpu -- bool )
114 f>> carry-flag bitand 0 = not ;
116 : flag-po? ( cpu -- bool )
118 f>> parity-flag bitand 0 = ;
120 : flag-pe? ( cpu -- bool )
122 f>> parity-flag bitand 0 = not ;
124 : flag-p? ( cpu -- bool )
126 f>> sign-flag bitand 0 = ;
128 : flag-m? ( cpu -- bool )
130 f>> sign-flag bitand 0 = not ;
132 : read-byte ( addr cpu -- byte )
133 #! Read one byte from memory at the specified address.
134 #! The address is 16-bit, but if a value greater than
135 #! 0xFFFF is provided then return a default value.
142 : read-word ( addr cpu -- word )
143 #! Read a 16-bit word from memory at the specified address.
144 #! The address is 16-bit, but if a value greater than
145 #! 0xFFFF is provided then return a default value.
146 [ read-byte ] 2keep [ 1 + ] dip read-byte 8 shift bitor ;
148 : next-byte ( cpu -- byte )
149 #! Return the value of the byte at PC, and increment PC.
155 : next-word ( cpu -- word )
156 #! Return the value of the word at PC, and increment PC.
163 : write-byte ( value addr cpu -- )
164 #! Write a byte to the specified memory address.
165 over dup 0x2000 < swap 0xFFFF > or [
173 : write-word ( value addr cpu -- )
174 #! Write a 16-bit word to the specified memory address.
175 [ >word< ] 2dip [ write-byte ] 2keep [ 1 + ] dip write-byte ;
177 : cpu-a-bitand ( quot cpu -- )
179 [ a>> swap call bitand ] keep a<< ; inline
181 : cpu-a-bitor ( quot cpu -- )
183 [ a>> swap call bitor ] keep a<< ; inline
185 : cpu-a-bitxor ( quot cpu -- )
187 [ a>> swap call bitxor ] keep a<< ; inline
189 : cpu-a-bitxor= ( value cpu -- )
191 [ a>> bitxor ] keep a<< ;
193 : cpu-f-bitand ( quot cpu -- )
195 [ f>> swap call bitand ] keep f<< ; inline
197 : cpu-f-bitor ( quot cpu -- )
199 [ f>> swap call bitor ] keep f<< ; inline
201 : cpu-f-bitxor ( quot cpu -- )
203 [ f>> swap call bitxor ] keep f<< ; inline
205 : cpu-f-bitor= ( value cpu -- )
207 [ f>> bitor ] keep f<< ;
209 : cpu-f-bitand= ( value cpu -- )
211 [ f>> bitand ] keep f<< ;
213 : cpu-f-bitxor= ( value cpu -- )
215 [ f>> bitxor ] keep f<< ;
217 : set-flag ( cpu flag -- )
220 : clear-flag ( cpu flag -- )
221 bitnot 0xFF bitand swap cpu-f-bitand= ;
223 : update-zero-flag ( result cpu -- )
224 #! If the result of an instruction has the value 0, this
225 #! flag is set, otherwise it is reset.
226 swap 0xFF bitand 0 = [ zero-flag set-flag ] [ zero-flag clear-flag ] if ;
228 : update-sign-flag ( result cpu -- )
229 #! If the most significant bit of the result
230 #! has the value 1 then the flag is set, otherwise
232 swap 0x80 bitand 0 = [ sign-flag clear-flag ] [ sign-flag set-flag ] if ;
234 : update-parity-flag ( result cpu -- )
235 #! If the modulo 2 sum of the bits of the result
236 #! is 0, (ie. if the result has even parity) this flag
237 #! is set, otherwise it is reset.
238 swap 0xFF bitand 2 mod 0 = [ parity-flag set-flag ] [ parity-flag clear-flag ] if ;
240 : update-carry-flag ( result cpu -- )
241 #! If the instruction resulted in a carry (from addition)
242 #! or a borrow (from subtraction or a comparison) out of the
243 #! higher order bit, this flag is set, otherwise it is reset.
244 swap dup 0x100 >= swap 0 < or [ carry-flag set-flag ] [ carry-flag clear-flag ] if ;
246 : update-half-carry-flag ( original change-by result cpu -- )
247 #! If the instruction caused a carry out of bit 3 and into bit 4 of the
248 #! resulting value, the half carry flag is set, otherwise it is reset.
249 #! The 'original' is the original value of the register being changed.
250 #! 'change-by' is the amount it is being added or decremented by.
251 #! 'result' is the result of that change.
252 [ bitxor bitxor 0x10 bitand 0 = not ] dip
253 swap [ half-carry-flag set-flag ] [ half-carry-flag clear-flag ] if ;
255 : update-flags ( result cpu -- )
256 2dup update-carry-flag
257 2dup update-parity-flag
258 2dup update-sign-flag
261 : update-flags-no-carry ( result cpu -- )
262 2dup update-parity-flag
263 2dup update-sign-flag
266 : add-byte ( lhs rhs cpu -- result )
269 [ update-flags ] 2keep
270 [ update-half-carry-flag ] 2keep
273 : add-carry ( change-by result cpu -- change-by result )
274 #! Add the effect of the carry flag to the result
275 flag-c? [ 1 + [ 1 + ] dip ] when ;
277 : add-byte-with-carry ( lhs rhs cpu -- result )
278 #! Add rhs to lhs plus carry.
281 [ update-flags ] 2keep
282 [ update-half-carry-flag ] 2keep
285 : sub-carry ( change-by result cpu -- change-by result )
286 #! Subtract the effect of the carry flag from the result
287 flag-c? [ 1 - [ 1 - ] dip ] when ;
289 : sub-byte ( lhs rhs cpu -- result )
290 #! Subtract rhs from lhs
292 [ update-flags ] 2keep
293 [ update-half-carry-flag ] 2keep
296 : sub-byte-with-carry ( lhs rhs cpu -- result )
297 #! Subtract rhs from lhs and take carry into account
300 [ update-flags ] 2keep
301 [ update-half-carry-flag ] 2keep
304 : inc-byte ( byte cpu -- result )
305 #! Increment byte by one. Note that carry flag is not affected
306 #! by this operation.
308 [ update-flags-no-carry ] 2keep
309 [ update-half-carry-flag ] 2keep
312 : dec-byte ( byte cpu -- result )
313 #! Decrement byte by one. Note that carry flag is not affected
314 #! by this operation.
316 [ update-flags-no-carry ] 2keep
317 [ update-half-carry-flag ] 2keep
320 : inc-word ( w cpu -- w )
321 #! Increment word by one. Note that no flags are modified.
322 drop 1 + 0xFFFF bitand ;
324 : dec-word ( w cpu -- w )
325 #! Decrement word by one. Note that no flags are modified.
326 drop 1 - 0xFFFF bitand ;
328 : add-word ( lhs rhs cpu -- result )
329 #! Add rhs to lhs. Note that only the carry flag is modified
330 #! and only if there is a carry out of the double precision add.
331 [ + ] dip over 0xFFFF > [ carry-flag set-flag ] [ drop ] if 0xFFFF bitand ;
333 : bit3or ( lhs rhs -- 0|1 )
334 #! bitor bit 3 of the two numbers on the stack
335 0b00001000 bitand -3 shift [
336 0b00001000 bitand -3 shift
340 : and-byte ( lhs rhs cpu -- result )
341 #! Logically and rhs to lhs. The carry flag is cleared and
342 #! the half carry is set to the ORing of bits 3 of the operands.
343 [ drop bit3or ] 3keep ! bit3or lhs rhs cpu
344 [ bitand ] dip [ update-flags ] 2keep
345 [ carry-flag clear-flag ] keep
346 rot 0 = [ half-carry-flag set-flag ] [ half-carry-flag clear-flag ] if
349 : xor-byte ( lhs rhs cpu -- result )
350 #! Logically xor rhs to lhs. The carry and half-carry flags are cleared.
351 [ bitxor ] dip [ update-flags ] 2keep
352 [ half-carry-flag carry-flag bitor clear-flag ] keep
355 : or-byte ( lhs rhs cpu -- result )
356 #! Logically or rhs to lhs. The carry and half-carry flags are cleared.
357 [ bitor ] dip [ update-flags ] 2keep
358 [ half-carry-flag carry-flag bitor clear-flag ] keep
361 : decrement-sp ( n cpu -- )
362 #! Decrement the stackpointer by n.
364 [ swap - ] dip sp<< ;
367 #! Save the value of the PC on the stack.
368 [ pc>> ] keep ! pc cpu
369 [ sp>> ] keep ! pc sp cpu
373 #! Push the value of the PC on the stack.
377 : pop-pc ( cpu -- pc )
378 #! Pop the value of the PC off the stack.
381 -2 swap decrement-sp ;
383 : push-sp ( value cpu -- )
384 [ 2 swap decrement-sp ] keep
388 : pop-sp ( cpu -- value )
391 -2 swap decrement-sp ;
393 : call-sub ( addr cpu -- )
394 #! Call the address as a subroutine.
396 [ 0xFFFF bitand ] dip pc<< ;
398 : ret-from-sub ( cpu -- )
399 [ pop-pc ] keep pc<< ;
401 : interrupt ( number cpu -- )
402 #! Perform a hardware interrupt
403 ! "***Interrupt: " write over >hex print
404 dup f>> interrupt-flag bitand 0 = not [
411 : inc-cycles ( n cpu -- )
412 #! Increment the number of cpu cycles
413 [ cycles>> + ] keep cycles<< ;
415 : instruction-cycles ( -- vector )
416 #! Return a 256 element vector containing the cycles for
417 #! each opcode in the 8080 instruction set.
418 \ instruction-cycles get-global [
419 256 f <array> \ instruction-cycles set-global
421 \ instruction-cycles get-global ;
423 : not-implemented ( <cpu> -- )
426 : instructions ( -- vector )
427 #! Return a 256 element vector containing the emulation words for
428 #! each opcode in the 8080 instruction set.
429 \ instructions get-global [
430 256 [ not-implemented ] <array> \ instructions set-global
432 \ instructions get-global ;
434 : set-instruction ( quot n -- )
435 instructions set-nth ;
437 M: cpu reset ( cpu -- )
438 #! Reset the CPU to its poweron state
449 0xFFFF 0 <array> >>ram
451 0x10 >>last-interrupt
455 : <cpu> ( -- cpu ) cpu new dup reset ;
457 : (load-rom) ( n ram -- )
459 -rot [ set-nth ] 2keep [ 1 + ] dip (load-rom)
464 #! Reads the ROM from stdin and stores it in ROM from
466 : load-rom ( filename cpu -- )
467 #! Load the contents of the file into ROM.
468 #! (address 0x0000-0x1FFF).
475 : rom-dir ( -- string )
476 rom-root get [ home "roms" append-path dup exists? [ drop f ] unless ] unless* ;
478 : load-rom* ( seq cpu -- )
479 #! 'seq' is an array of arrays. Each array contains
480 #! an address and filename of a ROM file. The ROM
481 #! file will be loaded at the specified address. This
482 #! file path shoul dbe relative to the '/roms' resource path.
485 swap first2 rom-dir prepend-path binary [
492 "Set 'rom-root' to the path containing the root of the 8080 ROM files." throw
495 : read-instruction ( cpu -- word )
496 #! Read the next instruction from the cpu's program
497 #! counter, and increment the program counter.
498 [ pc>> ] keep ! pc cpu
499 [ over 1 + swap pc<< ] keep
502 : get-cycles ( n -- opcode )
503 #! Returns the cycles for the given instruction value.
504 #! If the opcode is not defined throw an error.
505 dup instruction-cycles nth [
508 [ "Undefined 8080 opcode: " % number>string % ] "" make throw
511 : process-interrupts ( cpu -- )
512 #! Process any hardware interrupts
517 [ [ 16667 - ] dip cycles<< ] keep
518 dup last-interrupt>> 0x10 = [
519 0x08 over last-interrupt<< 0x08 swap interrupt
521 0x10 over last-interrupt<< 0x10 swap interrupt
525 : peek-instruction ( cpu -- word )
526 #! Return the next instruction from the cpu's program
527 #! counter, but don't increment the counter.
528 [ pc>> ] keep read-byte instructions nth first ;
531 [ " PC: " write pc>> >hex 4 CHAR: \s pad-head write ] keep
532 [ " B: " write b>> >hex 2 CHAR: \s pad-head write ] keep
533 [ " C: " write c>> >hex 2 CHAR: \s pad-head write ] keep
534 [ " D: " write d>> >hex 2 CHAR: \s pad-head write ] keep
535 [ " E: " write e>> >hex 2 CHAR: \s pad-head write ] keep
536 [ " F: " write f>> >hex 2 CHAR: \s pad-head write ] keep
537 [ " H: " write h>> >hex 2 CHAR: \s pad-head write ] keep
538 [ " L: " write l>> >hex 2 CHAR: \s pad-head write ] keep
539 [ " A: " write a>> >hex 2 CHAR: \s pad-head write ] keep
540 [ " SP: " write sp>> >hex 4 CHAR: \s pad-head write ] keep
541 [ " cycles: " write cycles>> number>string 5 CHAR: \s pad-head write ] keep
542 [ " " write peek-instruction name>> write " " write ] keep
546 [ " PC: " write pc>> >hex 4 CHAR: \s pad-head write ] keep
547 [ " B: " write b>> >hex 2 CHAR: \s pad-head write ] keep
548 [ " C: " write c>> >hex 2 CHAR: \s pad-head write ] keep
549 [ " D: " write d>> >hex 2 CHAR: \s pad-head write ] keep
550 [ " E: " write e>> >hex 2 CHAR: \s pad-head write ] keep
551 [ " F: " write f>> >hex 2 CHAR: \s pad-head write ] keep
552 [ " H: " write h>> >hex 2 CHAR: \s pad-head write ] keep
553 [ " L: " write l>> >hex 2 CHAR: \s pad-head write ] keep
554 [ " A: " write a>> >hex 2 CHAR: \s pad-head write ] keep
555 [ " SP: " write sp>> >hex 4 CHAR: \s pad-head write ] keep
556 [ " cycles: " write cycles>> number>string 5 CHAR: \s pad-head write ] keep
559 : register-lookup ( string -- vector )
560 #! Given a string containing a register name, return a vector
561 #! where the 1st item is the getter and the 2nd is the setter
562 #! for that register.
571 { "AF" { af>> af<< } }
572 { "BC" { bc>> bc<< } }
573 { "DE" { de>> de<< } }
574 { "HL" { hl>> hl<< } }
575 { "SP" { sp>> sp<< } }
579 : flag-lookup ( string -- vector )
580 #! Given a string containing a flag name, return a vector
581 #! where the 1st item is a word that tests that flag.
583 { "NZ" { flag-nz? } }
584 { "NC" { flag-nc? } }
585 { "PO" { flag-po? } }
586 { "PE" { flag-pe? } }
593 SYMBOLS: $1 $2 $3 $4 ;
595 : replace-patterns ( vector tree -- tree )
606 : (emulate-RST) ( n cpu -- )
608 [ sp>> 2 - dup ] keep ! sp sp cpu
609 [ sp<< ] keep ! sp cpu
610 [ pc>> ] keep ! sp pc cpu
611 swapd [ write-word ] keep ! cpu
614 : (emulate-CALL) ( cpu -- )
616 [ next-word 0xFFFF bitand ] keep ! addr cpu
617 [ sp>> 2 - dup ] keep ! addr sp sp cpu
618 [ sp<< ] keep ! addr sp cpu
619 [ pc>> ] keep ! addr sp pc cpu
620 swapd [ write-word ] keep ! addr cpu
623 : (emulate-RLCA) ( cpu -- )
624 #! The content of the accumulator is rotated left
625 #! one position. The low order bit and the carry flag
626 #! are both set to the value shifd out of the high
627 #! order bit position. Only the carry flag is affected.
628 [ a>> -7 shift ] keep
629 over 0 = [ dup carry-flag clear-flag ] [ dup carry-flag set-flag ] if
630 [ a>> 1 shift 0xFF bitand ] keep
633 : (emulate-RRCA) ( cpu -- )
634 #! The content of the accumulator is rotated right
635 #! one position. The high order bit and the carry flag
636 #! are both set to the value shifd out of the low
637 #! order bit position. Only the carry flag is affected.
638 [ a>> 1 bitand 7 shift ] keep
639 over 0 = [ dup carry-flag clear-flag ] [ dup carry-flag set-flag ] if
640 [ a>> 254 bitand -1 shift ] keep
643 : (emulate-RLA) ( cpu -- )
644 #! The content of the accumulator is rotated left
645 #! one position through the carry flag. The low
646 #! order bit is set equal to the carry flag and
647 #! the carry flag is set to the value shifd out
648 #! of the high order bit. Only the carry flag is
650 [ carry-flag swap flag-set? [ 1 ] [ 0 ] if ] keep
651 [ a>> 127 bitand 7 shift ] keep
652 dup a>> 128 bitand 0 = [ dup carry-flag clear-flag ] [ dup carry-flag set-flag ] if
655 : (emulate-RRA) ( cpu -- )
656 #! The content of the accumulator is rotated right
657 #! one position through the carry flag. The high order
658 #! bit is set to the carry flag and the carry flag is
659 #! set to the value shifd out of the low order bit.
660 #! Only the carry flag is affected.
661 [ carry-flag swap flag-set? [ 0b10000000 ] [ 0 ] if ] keep
662 [ a>> 254 bitand -1 shift ] keep
663 dup a>> 1 bitand 0 = [ dup carry-flag clear-flag ] [ dup carry-flag set-flag ] if
666 : (emulate-CPL) ( cpu -- )
667 #! The contents of the accumulator are complemented
668 #! (zero bits become one, one bits becomes zero).
669 #! No flags are affected.
670 0xFF swap cpu-a-bitxor= ;
672 : (emulate-DAA) ( cpu -- )
673 #! The eight bit number in the accumulator is
674 #! adjusted to form two four-bit binary-coded-decimal
677 dup half-carry-flag swap flag-set? swap
678 a>> 0b1111 bitand 9 > or [ 6 ] [ 0 ] if
681 [ update-flags ] 2keep
682 [ swap 0xFF bitand swap a<< ] keep
684 dup carry-flag swap flag-set? swap
685 a>> -4 shift 0b1111 bitand 9 > or [ 96 ] [ 0 ] if
688 [ update-flags ] 2keep
689 swap 0xFF bitand swap a<< ;
691 : patterns ( -- hashtable )
692 #! table of code quotation patterns for each type of instruction.
695 { "RET-NN" [ ret-from-sub ] }
696 { "RST-0" [ 0 swap (emulate-RST) ] }
697 { "RST-8" [ 8 swap (emulate-RST) ] }
698 { "RST-10H" [ 0x10 swap (emulate-RST) ] }
699 { "RST-18H" [ 0x18 swap (emulate-RST) ] }
700 { "RST-20H" [ 0x20 swap (emulate-RST) ] }
701 { "RST-28H" [ 0x28 swap (emulate-RST) ] }
702 { "RST-30H" [ 0x30 swap (emulate-RST) ] }
703 { "RST-38H" [ 0x38 swap (emulate-RST) ] }
704 { "RET-F|FF" [ dup $1 [ 6 over inc-cycles ret-from-sub ] [ drop ] if ] }
705 { "CP-N" [ [ a>> ] keep [ next-byte ] keep sub-byte drop ] }
706 { "CP-R" [ [ a>> ] keep [ $1 ] keep sub-byte drop ] }
707 { "CP-(RR)" [ [ a>> ] keep [ $1 ] keep [ read-byte ] keep sub-byte drop ] }
708 { "OR-N" [ [ a>> ] keep [ next-byte ] keep [ or-byte ] keep a<< ] }
709 { "OR-R" [ [ a>> ] keep [ $1 ] keep [ or-byte ] keep a<< ] }
710 { "OR-(RR)" [ [ a>> ] keep [ $1 ] keep [ read-byte ] keep [ or-byte ] keep a<< ] }
711 { "XOR-N" [ [ a>> ] keep [ next-byte ] keep [ xor-byte ] keep a<< ] }
712 { "XOR-R" [ [ a>> ] keep [ $1 ] keep [ xor-byte ] keep a<< ] }
713 { "XOR-(RR)" [ [ a>> ] keep [ $1 ] keep [ read-byte ] keep [ xor-byte ] keep a<< ] }
714 { "AND-N" [ [ a>> ] keep [ next-byte ] keep [ and-byte ] keep a<< ] }
715 { "AND-R" [ [ a>> ] keep [ $1 ] keep [ and-byte ] keep a<< ] }
716 { "AND-(RR)" [ [ a>> ] keep [ $1 ] keep [ read-byte ] keep [ and-byte ] keep a<< ] }
717 { "ADC-R,N" [ [ $1 ] keep [ next-byte ] keep [ add-byte-with-carry ] keep $2 ] }
718 { "ADC-R,R" [ [ $1 ] keep [ $3 ] keep [ add-byte-with-carry ] keep $2 ] }
719 { "ADC-R,(RR)" [ [ $1 ] keep [ $3 ] keep [ read-byte ] keep [ add-byte-with-carry ] keep $2 ] }
720 { "ADD-R,N" [ [ $1 ] keep [ next-byte ] keep [ add-byte ] keep $2 ] }
721 { "ADD-R,R" [ [ $1 ] keep [ $3 ] keep [ add-byte ] keep $2 ] }
722 { "ADD-RR,RR" [ [ $1 ] keep [ $3 ] keep [ add-word ] keep $2 ] }
723 { "ADD-R,(RR)" [ [ $1 ] keep [ $3 ] keep [ read-byte ] keep [ add-byte ] keep $2 ] }
724 { "SBC-R,N" [ [ $1 ] keep [ next-byte ] keep [ sub-byte-with-carry ] keep $2 ] }
725 { "SBC-R,R" [ [ $1 ] keep [ $3 ] keep [ sub-byte-with-carry ] keep $2 ] }
726 { "SBC-R,(RR)" [ [ $1 ] keep [ $3 ] keep [ read-byte ] keep [ sub-byte-with-carry ] keep $2 ] }
727 { "SUB-R" [ [ a>> ] keep [ $1 ] keep [ sub-byte ] keep a<< ] }
728 { "SUB-(RR)" [ [ a>> ] keep [ $1 ] keep [ read-byte ] keep [ sub-byte ] keep a<< ] }
729 { "SUB-N" [ [ a>> ] keep [ next-byte ] keep [ sub-byte ] keep a<< ] }
730 { "CPL" [ (emulate-CPL) ] }
731 { "DAA" [ (emulate-DAA) ] }
732 { "RLA" [ (emulate-RLA) ] }
733 { "RRA" [ (emulate-RRA) ] }
734 { "CCF" [ carry-flag swap cpu-f-bitxor= ] }
735 { "SCF" [ carry-flag swap cpu-f-bitor= ] }
736 { "RLCA" [ (emulate-RLCA) ] }
737 { "RRCA" [ (emulate-RRCA) ] }
739 { "DI" [ [ 255 interrupt-flag - ] swap cpu-f-bitand ] }
740 { "EI" [ [ interrupt-flag ] swap cpu-f-bitor ] }
741 { "POP-RR" [ [ pop-sp ] keep $2 ] }
742 { "PUSH-RR" [ [ $1 ] keep push-sp ] }
743 { "INC-R" [ [ $1 ] keep [ inc-byte ] keep $2 ] }
744 { "DEC-R" [ [ $1 ] keep [ dec-byte ] keep $2 ] }
745 { "INC-RR" [ [ $1 ] keep [ inc-word ] keep $2 ] }
746 { "DEC-RR" [ [ $1 ] keep [ dec-word ] keep $2 ] }
747 { "DEC-(RR)" [ [ $1 ] keep [ read-byte ] keep [ dec-byte ] keep [ $1 ] keep write-byte ] }
748 { "INC-(RR)" [ [ $1 ] keep [ read-byte ] keep [ inc-byte ] keep [ $1 ] keep write-byte ] }
749 { "JP-NN" [ [ pc>> ] keep [ read-word ] keep pc<< ] }
750 { "JP-F|FF,NN" [ [ $1 ] keep swap [ [ next-word ] keep [ pc<< ] keep [ cycles>> ] keep swap 5 + swap cycles<< ] [ [ pc>> 2 + ] keep pc<< ] if ] }
751 { "JP-(RR)" [ [ $1 ] keep pc<< ] }
752 { "CALL-NN" [ (emulate-CALL) ] }
753 { "CALL-F|FF,NN" [ [ $1 ] keep swap [ 7 over inc-cycles (emulate-CALL) ] [ [ pc>> 2 + ] keep pc<< ] if ] }
754 { "LD-RR,NN" [ [ next-word ] keep $2 ] }
755 { "LD-RR,RR" [ [ $3 ] keep $2 ] }
756 { "LD-R,N" [ [ next-byte ] keep $2 ] }
757 { "LD-(RR),N" [ [ next-byte ] keep [ $1 ] keep write-byte ] }
758 { "LD-(RR),R" [ [ $3 ] keep [ $1 ] keep write-byte ] }
759 { "LD-R,R" [ [ $3 ] keep $2 ] }
760 { "LD-R,(RR)" [ [ $3 ] keep [ read-byte ] keep $2 ] }
761 { "LD-(NN),RR" [ [ $1 ] keep [ next-word ] keep write-word ] }
762 { "LD-(NN),R" [ [ $1 ] keep [ next-word ] keep write-byte ] }
763 { "LD-RR,(NN)" [ [ next-word ] keep [ read-word ] keep $2 ] }
764 { "LD-R,(NN)" [ [ next-word ] keep [ read-byte ] keep $2 ] }
765 { "OUT-(N),R" [ [ $1 ] keep [ next-byte ] keep write-port ] }
766 { "IN-R,(N)" [ [ next-byte ] keep [ read-port ] keep a<< ] }
767 { "EX-(RR),RR" [ [ $1 ] keep [ read-word ] keep [ $3 ] keep [ $1 ] keep [ write-word ] keep $4 ] }
768 { "EX-RR,RR" [ [ $1 ] keep [ $3 ] keep [ $2 ] keep $4 ] }
771 : 8-bit-registers ( -- parser )
772 #! A parser for 8-bit registers. On a successfull parse the
773 #! parse tree contains a vector. The first item in the vector
774 #! is the getter word for that register with stack effect
775 #! ( cpu -- value ). The second item is the setter word with
776 #! stack effect ( value cpu -- ).
778 main=("A" | "B" | "C" | "D" | "E" | "H" | "L") => [[ register-lookup ]]
781 : all-flags ( -- parser )
782 #! A parser for 16-bit flags.
784 main=("NZ" | "NC" | "PO" | "PE" | "Z" | "C" | "P" | "M") => [[ flag-lookup ]]
787 : 16-bit-registers ( -- parser )
788 #! A parser for 16-bit registers. On a successfull parse the
789 #! parse tree contains a vector. The first item in the vector
790 #! is the getter word for that register with stack effect
791 #! ( cpu -- value ). The second item is the setter word with
792 #! stack effect ( value cpu -- ).
794 main=("AF" | "BC" | "DE" | "HL" | "SP") => [[ register-lookup ]]
797 : all-registers ( -- parser )
798 #! Return a parser that can parse the format
799 #! for 8 bit or 16 bit registers.
800 [ 16-bit-registers , 8-bit-registers , ] choice* ;
802 : indirect ( parser -- parser )
803 #! Given a parser, return a parser which parses the original
804 #! wrapped in brackets, representing an indirect reference.
805 #! eg. BC -> (BC). The value of the original parser is left in
807 "(" ")" surrounded-by ;
809 : generate-instruction ( vector string -- quot )
810 #! Generate the quotation for an instruction, given the instruction in
811 #! the 'string' and a vector containing the arguments for that instruction.
812 patterns at replace-patterns ;
814 : simple-instruction ( token -- parser )
815 #! Return a parser for then instruction identified by the token.
816 #! The parser return parses the token only and expects no additional
817 #! arguments to the instruction.
818 token [ '[ { } _ generate-instruction ] ] action ;
820 : complex-instruction ( type token -- parser )
821 #! Return a parser for an instruction identified by the token.
822 #! The instruction is expected to take additional arguments by
823 #! being combined with other parsers. Then 'type' is used for a lookup
824 #! in a pattern hashtable to return the instruction quotation pattern.
825 token swap [ nip '[ _ generate-instruction ] ] curry action ;
827 : no-params ( ast -- ast )
828 first { } swap curry ;
830 : one-param ( ast -- ast )
833 : two-params ( ast -- ast )
834 first3 append swap curry ;
836 : NOP-instruction ( -- parser )
837 "NOP" simple-instruction ;
839 : RET-NN-instruction ( -- parser )
841 "RET-NN" "RET" complex-instruction ,
843 ] seq* [ no-params ] action ;
845 : RST-0-instruction ( -- parser )
847 "RST-0" "RST" complex-instruction ,
849 ] seq* [ no-params ] action ;
851 : RST-8-instruction ( -- parser )
853 "RST-8" "RST" complex-instruction ,
855 ] seq* [ no-params ] action ;
857 : RST-10H-instruction ( -- parser )
859 "RST-10H" "RST" complex-instruction ,
860 "10H" token sp hide ,
861 ] seq* [ no-params ] action ;
863 : RST-18H-instruction ( -- parser )
865 "RST-18H" "RST" complex-instruction ,
866 "18H" token sp hide ,
867 ] seq* [ no-params ] action ;
869 : RST-20H-instruction ( -- parser )
871 "RST-20H" "RST" complex-instruction ,
872 "20H" token sp hide ,
873 ] seq* [ no-params ] action ;
875 : RST-28H-instruction ( -- parser )
877 "RST-28H" "RST" complex-instruction ,
878 "28H" token sp hide ,
879 ] seq* [ no-params ] action ;
881 : RST-30H-instruction ( -- parser )
883 "RST-30H" "RST" complex-instruction ,
884 "30H" token sp hide ,
885 ] seq* [ no-params ] action ;
887 : RST-38H-instruction ( -- parser )
889 "RST-38H" "RST" complex-instruction ,
890 "38H" token sp hide ,
891 ] seq* [ no-params ] action ;
893 : JP-NN-instruction ( -- parser )
895 "JP-NN" "JP" complex-instruction ,
897 ] seq* [ no-params ] action ;
899 : JP-F|FF,NN-instruction ( -- parser )
901 "JP-F|FF,NN" "JP" complex-instruction ,
904 ] seq* [ one-param ] action ;
906 : JP-(RR)-instruction ( -- parser )
908 "JP-(RR)" "JP" complex-instruction ,
909 16-bit-registers indirect sp ,
910 ] seq* [ one-param ] action ;
912 : CALL-NN-instruction ( -- parser )
914 "CALL-NN" "CALL" complex-instruction ,
916 ] seq* [ no-params ] action ;
918 : CALL-F|FF,NN-instruction ( -- parser )
920 "CALL-F|FF,NN" "CALL" complex-instruction ,
923 ] seq* [ one-param ] action ;
925 : RLCA-instruction ( -- parser )
926 "RLCA" simple-instruction ;
928 : RRCA-instruction ( -- parser )
929 "RRCA" simple-instruction ;
931 : HALT-instruction ( -- parser )
932 "HALT" simple-instruction ;
934 : DI-instruction ( -- parser )
935 "DI" simple-instruction ;
937 : EI-instruction ( -- parser )
938 "EI" simple-instruction ;
940 : CPL-instruction ( -- parser )
941 "CPL" simple-instruction ;
943 : CCF-instruction ( -- parser )
944 "CCF" simple-instruction ;
946 : SCF-instruction ( -- parser )
947 "SCF" simple-instruction ;
949 : DAA-instruction ( -- parser )
950 "DAA" simple-instruction ;
952 : RLA-instruction ( -- parser )
953 "RLA" simple-instruction ;
955 : RRA-instruction ( -- parser )
956 "RRA" simple-instruction ;
958 : DEC-R-instruction ( -- parser )
960 "DEC-R" "DEC" complex-instruction ,
962 ] seq* [ one-param ] action ;
964 : DEC-RR-instruction ( -- parser )
966 "DEC-RR" "DEC" complex-instruction ,
967 16-bit-registers sp ,
968 ] seq* [ one-param ] action ;
970 : DEC-(RR)-instruction ( -- parser )
972 "DEC-(RR)" "DEC" complex-instruction ,
973 16-bit-registers indirect sp ,
974 ] seq* [ one-param ] action ;
976 : POP-RR-instruction ( -- parser )
978 "POP-RR" "POP" complex-instruction ,
980 ] seq* [ one-param ] action ;
982 : PUSH-RR-instruction ( -- parser )
984 "PUSH-RR" "PUSH" complex-instruction ,
986 ] seq* [ one-param ] action ;
988 : INC-R-instruction ( -- parser )
990 "INC-R" "INC" complex-instruction ,
992 ] seq* [ one-param ] action ;
994 : INC-RR-instruction ( -- parser )
996 "INC-RR" "INC" complex-instruction ,
997 16-bit-registers sp ,
998 ] seq* [ one-param ] action ;
1000 : INC-(RR)-instruction ( -- parser )
1002 "INC-(RR)" "INC" complex-instruction ,
1003 all-registers indirect sp ,
1004 ] seq* [ one-param ] action ;
1006 : RET-F|FF-instruction ( -- parser )
1008 "RET-F|FF" "RET" complex-instruction ,
1010 ] seq* [ one-param ] action ;
1012 : AND-N-instruction ( -- parser )
1014 "AND-N" "AND" complex-instruction ,
1016 ] seq* [ no-params ] action ;
1018 : AND-R-instruction ( -- parser )
1020 "AND-R" "AND" complex-instruction ,
1021 8-bit-registers sp ,
1022 ] seq* [ one-param ] action ;
1024 : AND-(RR)-instruction ( -- parser )
1026 "AND-(RR)" "AND" complex-instruction ,
1027 16-bit-registers indirect sp ,
1028 ] seq* [ one-param ] action ;
1030 : XOR-N-instruction ( -- parser )
1032 "XOR-N" "XOR" complex-instruction ,
1034 ] seq* [ no-params ] action ;
1036 : XOR-R-instruction ( -- parser )
1038 "XOR-R" "XOR" complex-instruction ,
1039 8-bit-registers sp ,
1040 ] seq* [ one-param ] action ;
1042 : XOR-(RR)-instruction ( -- parser )
1044 "XOR-(RR)" "XOR" complex-instruction ,
1045 16-bit-registers indirect sp ,
1046 ] seq* [ one-param ] action ;
1048 : OR-N-instruction ( -- parser )
1050 "OR-N" "OR" complex-instruction ,
1052 ] seq* [ no-params ] action ;
1054 : OR-R-instruction ( -- parser )
1056 "OR-R" "OR" complex-instruction ,
1057 8-bit-registers sp ,
1058 ] seq* [ one-param ] action ;
1060 : OR-(RR)-instruction ( -- parser )
1062 "OR-(RR)" "OR" complex-instruction ,
1063 16-bit-registers indirect sp ,
1064 ] seq* [ one-param ] action ;
1066 : CP-N-instruction ( -- parser )
1068 "CP-N" "CP" complex-instruction ,
1070 ] seq* [ no-params ] action ;
1072 : CP-R-instruction ( -- parser )
1074 "CP-R" "CP" complex-instruction ,
1075 8-bit-registers sp ,
1076 ] seq* [ one-param ] action ;
1078 : CP-(RR)-instruction ( -- parser )
1080 "CP-(RR)" "CP" complex-instruction ,
1081 16-bit-registers indirect sp ,
1082 ] seq* [ one-param ] action ;
1084 : ADC-R,N-instruction ( -- parser )
1086 "ADC-R,N" "ADC" complex-instruction ,
1087 8-bit-registers sp ,
1089 ] seq* [ one-param ] action ;
1091 : ADC-R,R-instruction ( -- parser )
1093 "ADC-R,R" "ADC" complex-instruction ,
1094 8-bit-registers sp ,
1097 ] seq* [ two-params ] action ;
1099 : ADC-R,(RR)-instruction ( -- parser )
1101 "ADC-R,(RR)" "ADC" complex-instruction ,
1102 8-bit-registers sp ,
1104 16-bit-registers indirect ,
1105 ] seq* [ two-params ] action ;
1107 : SBC-R,N-instruction ( -- parser )
1109 "SBC-R,N" "SBC" complex-instruction ,
1110 8-bit-registers sp ,
1112 ] seq* [ one-param ] action ;
1114 : SBC-R,R-instruction ( -- parser )
1116 "SBC-R,R" "SBC" complex-instruction ,
1117 8-bit-registers sp ,
1120 ] seq* [ two-params ] action ;
1122 : SBC-R,(RR)-instruction ( -- parser )
1124 "SBC-R,(RR)" "SBC" complex-instruction ,
1125 8-bit-registers sp ,
1127 16-bit-registers indirect ,
1128 ] seq* [ two-params ] action ;
1130 : SUB-R-instruction ( -- parser )
1132 "SUB-R" "SUB" complex-instruction ,
1133 8-bit-registers sp ,
1134 ] seq* [ one-param ] action ;
1136 : SUB-(RR)-instruction ( -- parser )
1138 "SUB-(RR)" "SUB" complex-instruction ,
1139 16-bit-registers indirect sp ,
1140 ] seq* [ one-param ] action ;
1142 : SUB-N-instruction ( -- parser )
1144 "SUB-N" "SUB" complex-instruction ,
1146 ] seq* [ no-params ] action ;
1148 : ADD-R,N-instruction ( -- parser )
1150 "ADD-R,N" "ADD" complex-instruction ,
1151 8-bit-registers sp ,
1153 ] seq* [ one-param ] action ;
1155 : ADD-R,R-instruction ( -- parser )
1157 "ADD-R,R" "ADD" complex-instruction ,
1158 8-bit-registers sp ,
1161 ] seq* [ two-params ] action ;
1163 : ADD-RR,RR-instruction ( -- parser )
1165 "ADD-RR,RR" "ADD" complex-instruction ,
1166 16-bit-registers sp ,
1169 ] seq* [ two-params ] action ;
1171 : ADD-R,(RR)-instruction ( -- parser )
1173 "ADD-R,(RR)" "ADD" complex-instruction ,
1174 8-bit-registers sp ,
1176 16-bit-registers indirect ,
1177 ] seq* [ two-params ] action ;
1179 : LD-RR,NN-instruction ( -- parser )
1182 "LD-RR,NN" "LD" complex-instruction ,
1183 16-bit-registers sp ,
1185 ] seq* [ one-param ] action ;
1187 : LD-R,N-instruction ( -- parser )
1190 "LD-R,N" "LD" complex-instruction ,
1191 8-bit-registers sp ,
1193 ] seq* [ one-param ] action ;
1195 : LD-(RR),N-instruction ( -- parser )
1197 "LD-(RR),N" "LD" complex-instruction ,
1198 16-bit-registers indirect sp ,
1200 ] seq* [ one-param ] action ;
1202 : LD-(RR),R-instruction ( -- parser )
1205 "LD-(RR),R" "LD" complex-instruction ,
1206 16-bit-registers indirect sp ,
1209 ] seq* [ two-params ] action ;
1211 : LD-R,R-instruction ( -- parser )
1213 "LD-R,R" "LD" complex-instruction ,
1214 8-bit-registers sp ,
1217 ] seq* [ two-params ] action ;
1219 : LD-RR,RR-instruction ( -- parser )
1221 "LD-RR,RR" "LD" complex-instruction ,
1222 16-bit-registers sp ,
1225 ] seq* [ two-params ] action ;
1227 : LD-R,(RR)-instruction ( -- parser )
1229 "LD-R,(RR)" "LD" complex-instruction ,
1230 8-bit-registers sp ,
1232 16-bit-registers indirect ,
1233 ] seq* [ two-params ] action ;
1235 : LD-(NN),RR-instruction ( -- parser )
1237 "LD-(NN),RR" "LD" complex-instruction ,
1238 "nn" token indirect sp hide ,
1241 ] seq* [ one-param ] action ;
1243 : LD-(NN),R-instruction ( -- parser )
1245 "LD-(NN),R" "LD" complex-instruction ,
1246 "nn" token indirect sp hide ,
1249 ] seq* [ one-param ] action ;
1251 : LD-RR,(NN)-instruction ( -- parser )
1253 "LD-RR,(NN)" "LD" complex-instruction ,
1254 16-bit-registers sp ,
1256 "nn" token indirect hide ,
1257 ] seq* [ one-param ] action ;
1259 : LD-R,(NN)-instruction ( -- parser )
1261 "LD-R,(NN)" "LD" complex-instruction ,
1262 8-bit-registers sp ,
1264 "nn" token indirect hide ,
1265 ] seq* [ one-param ] action ;
1267 : OUT-(N),R-instruction ( -- parser )
1269 "OUT-(N),R" "OUT" complex-instruction ,
1270 "n" token indirect sp hide ,
1273 ] seq* [ one-param ] action ;
1275 : IN-R,(N)-instruction ( -- parser )
1277 "IN-R,(N)" "IN" complex-instruction ,
1278 8-bit-registers sp ,
1280 "n" token indirect hide ,
1281 ] seq* [ one-param ] action ;
1283 : EX-(RR),RR-instruction ( -- parser )
1285 "EX-(RR),RR" "EX" complex-instruction ,
1286 16-bit-registers indirect sp ,
1289 ] seq* [ two-params ] action ;
1291 : EX-RR,RR-instruction ( -- parser )
1293 "EX-RR,RR" "EX" complex-instruction ,
1294 16-bit-registers sp ,
1297 ] seq* [ two-params ] action ;
1299 : 8080-generator-parser ( -- parser )
1304 RST-10H-instruction ,
1305 RST-18H-instruction ,
1306 RST-20H-instruction ,
1307 RST-28H-instruction ,
1308 RST-30H-instruction ,
1309 RST-38H-instruction ,
1310 JP-F|FF,NN-instruction ,
1312 JP-(RR)-instruction ,
1313 CALL-F|FF,NN-instruction ,
1314 CALL-NN-instruction ,
1328 AND-(RR)-instruction ,
1331 XOR-(RR)-instruction ,
1334 OR-(RR)-instruction ,
1337 CP-(RR)-instruction ,
1338 DEC-RR-instruction ,
1340 DEC-(RR)-instruction ,
1341 POP-RR-instruction ,
1342 PUSH-RR-instruction ,
1343 INC-RR-instruction ,
1345 INC-(RR)-instruction ,
1346 LD-RR,NN-instruction ,
1347 LD-RR,RR-instruction ,
1348 LD-R,N-instruction ,
1349 LD-R,R-instruction ,
1350 LD-(RR),N-instruction ,
1351 LD-(RR),R-instruction ,
1352 LD-R,(RR)-instruction ,
1353 LD-(NN),RR-instruction ,
1354 LD-(NN),R-instruction ,
1355 LD-RR,(NN)-instruction ,
1356 LD-R,(NN)-instruction ,
1357 ADC-R,(RR)-instruction ,
1358 ADC-R,N-instruction ,
1359 ADC-R,R-instruction ,
1360 ADD-R,N-instruction ,
1361 ADD-R,(RR)-instruction ,
1362 ADD-R,R-instruction ,
1363 ADD-RR,RR-instruction ,
1364 SBC-R,N-instruction ,
1365 SBC-R,R-instruction ,
1366 SBC-R,(RR)-instruction ,
1368 SUB-(RR)-instruction ,
1370 RET-F|FF-instruction ,
1371 RET-NN-instruction ,
1372 OUT-(N),R-instruction ,
1373 IN-R,(N)-instruction ,
1374 EX-(RR),RR-instruction ,
1375 EX-RR,RR-instruction ,
1376 ] choice* [ call( -- quot ) ] action ;
1378 : instruction-quotations ( string -- emulate-quot )
1379 #! Given an instruction string, return the emulation quotation for
1380 #! it. This will later be expanded to produce the disassembly and
1381 #! assembly quotations.
1382 8080-generator-parser parse ;
1384 SYMBOL: last-instruction
1387 : parse-instructions ( list -- )
1388 #! Process the list of strings, which should make
1389 #! up an 8080 instruction, and output a quotation
1390 #! that would implement that instruction.
1391 dup " " join instruction-quotations
1393 "_" join [ "emulate-" % % ] "" make create-in dup last-instruction set-global
1394 ] dip ( cpu -- ) define-declared ;
1396 SYNTAX: INSTRUCTION: ";" parse-tokens parse-instructions ;
1399 #! Set the number of cycles for the last instruction that was defined.
1400 scan-token string>number last-opcode get-global instruction-cycles set-nth ;
1403 #! Set the opcode number for the last instruction that was defined.
1404 last-instruction get-global 1quotation scan-token hex>
1405 dup last-opcode set-global set-instruction ;