;;; woz-machine-hw-sim.lisp ;;; ;;; based on woz-machine.lisp 6 April 2003 ;;; ;;; Simulate Woz (Steve Wozniak) Machine which controls an Apple II ;;; Disk II floppy controller. ;;; ;;; This codes uses the circuit simulation code in circuit-sim.lisp ;;; ;;; $Revision: 1.5 $ ;;; ;;; $Log: woz-machine-hw-sim.lisp,v $ ;;; Revision 1.5 2007/02/11 15:55:34 jao ;;; 1. Created package "WOZ-MACHINE" ;;; 2. removed some excess woz- and wm- name prefixes ;;; 3. added more explanation to list-p6-sequencer ;;; 4. implemented full make-state-machine (not tested, compared to ;;; make-readwrite-machine.) ;;; ;;; Revision 1.4 2007/02/11 00:29:49 jao ;;; Progress toward more modular design. ;;; ;;; Revision 1.3 2003/11/01 17:49:09 jao ;;; 1. Elaborated modular design ;;; 2. Added data register module ;;; 3. Additional test routines. ;;; ;;; Revision 1.2 2003/11/01 01:28:00 jao ;;; 1. Reversed sense of read-pulse, to represent the true functioning of the hardware. ;;; Read pulses are LOW when a flux transition (representing a 1) is detected by the ;;; floppy disk read head. ;;; ;;; 2. Made progress toward a modular simulation of the Woz machine. My initial simulation ;;; was buggy, not least because I needed to make change #1. ;;; ;;; Revision 1.1 2003/10/21 01:45:10 jao ;;; Initial revision ;;; ;;; ;;; ;;; See Beneath Apple ProDos Appendix D ;;; ;;; (defpackage "WOZ-MACHINE" (:use "CL" "CIRCUIT") (:export "LIST-P6-SEQUENCER" "READ-DISK-BIT-STREAM" "MAKE-READWRITE-MACHINE" "MAKE-STATE-MACHINE")) (in-package "WOZ-MACHINE") ;;; opcodes ;;; ;;; these opcodes are the four P6 ROM data bits which ;;; control the 74LS323 shift register that is the data ;;; register. ;;; ;;; Control bits: CLR active low (pin9) (input from D3, pin 9 output of P6), ;;; clears data register ;;; : SR/DS0 (pin 11) (input from write protect), ;;; data to be shifted into serial output data bit QA'/Q0 ;;; (pin 8) and high-order parallel bit QA/IO0 (pin 7) on ;;; a shift right ;;; [NOTE: Beneath Apple ProDOS is in error: indicates all bits ;;; are immediately set to 1] ;;; : SL/DS7 (pin 18) (input from D2, pin 8 output of P6), ;;; data to be shifted into serial output QH'/Q7 (pin 17) ;;; [no connection] and high-order parallel bit QH/IO7 (pin 16) ;;; : S1 (pin 19) (input from D0, pin 6 output of P6) mode select ;;; : S0 (pin 1) (input from D1, pin 7 output of P6) mode select ;;; ;;; S0 S1 (CLR inactive) ;;; 0 0 : hold data ;;; 1 0 : shift right on clock rise ;;; 0 1 : shift left on clock rise ;;; 1 1 : parallel load on clock rise ;;; ;;; the bits below are listed in the order they are in the Beneath Apple ProDOS ;;; CLR (D3) SL (D2) S0 (D1) S1 (D0) ;;; (defconstant +opcode-clr+ #b0000 "Clear data reg") ;;; all codes with high-bit 0 will clear 0..#b0111 (defconstant +opcode-nop+ #b1000 "No operation") ;;; zero SL, hold data. Also #b1100 (defconstant +opcode-sl0+ #b1001 "Shift left 0") ;;; zero SL, shift left (defconstant +opcode-srwp+ #b1010 "Shift right write-protect state") ;;; zero SL, shift right. also #b1110 (defconstant +opcode-load+ #b1011 "Load data reg") ;;; parallel load on clock rise, also #b1111 (defconstant +opcode-sl1+ #b1101 "Shift left 1") ;;; 1 SL, shift left ;;; #b0000 : standard CLR ;;; #b0001..#b0111 : also CLR ;;; #b1000 : standard NOP ;;; #b1001 : SL0 ;;; #b1010 : standard SRWP ;;; #b1011 : LOAD ;;; #b1100 : alternative NOP ;;; #b1101 : SL1 ;;; #b1110 : alternative SRWP ;;; #b1111 : alternative LOAD ;;; ;;; so there are no hidden opcodes in the machine. See below for ;;; the origin of the WRDATA "operation" ;;; (defun nibble-opcode (nibble) "Returns the keyword symbol representing the opcode" (ecase nibble ((#b0000 #b0001 #b0010 #b0011 #b0100 #b0101 #b0110 #b0111) :CLR) ((#b1000 #b1100) :NOP) (#b1001 :SL0) ((#b1010 #b1110) :SR) ((#b1011 #b1111) :LD) (#b1101 :SL1))) ;;; the P6 ROM address bits are derived as follows ;;; ;;; Q6/A2 (P6 pin 3) and ;;; Q7/A3 (P6 pin 4) are from the 74259 control latch ;;; (9334 pins 11 and 12, respectively) ;;; RD/A4 (pin 5) is the read pulse bit from the Q4&Q3 of the ;;; 74LS174 state latch, processed (I want to say synchronized) ;;; by the 74LS05 inverter, 74LS132 NAND and the feedback of LS174 ;;; Q4 output to D3 input. This is the state machine's concept of ;;; "read pulse," which is related to, but not the same as the analog ;;; read pulse RDDATA which drives the D4 input of the LS174 state latch. ;;; QA'/A1 is the high bit of the data register ;;; ;;; the remaining four address lines are the sequence bits, ;;; held in the LS174 state latch and loaded from D4...D7 outputs ;;; of the P6 (next sequence) ;;; ;;; note that WRDATA is driven from Q0 (pin 2) of the LS174, which is ;;; also the D7 output of the P6. This is completely missing from the ;;; Beneath Apple ProDOS description. One might want to think about ;;; this as another opcode bit. ;;; ;;; Describing the contents of a Sequencer ROM: ;;; ;;; The sequencer rom breaks into portions according to the ;;; major modes: ;;; ;;; Q6 Q7 ;;; L L : enable read sequencing ;;; L H : writing: shift data to WRDATA ;;; H L : check write protect, initialize for writing ;;; H H : load data for writing ;;; ;;; Q6 selects the A2 line on the P6 ;;; Q7 selects the A3 line ;;; high bit of data register selects A1 ;;; read pulse selects A4 ;;; sequence number drives P6 A0('174 Q5), A5(Q2), A6(Q1), A7(Q0) ;;; sequence number stored in P6 D5('174 D5), D4(D2), D6(D1), D7(D0) ;;; ;;; arbitrarily, until I get better info, I'm going to use the sequence ;;; numbering in the Beneath Apple ProDOS, with LSB as P6 D5, MSB as ;;; P6 D7. (eval-when (:compile-toplevel :load-toplevel :execute) (defun assemble-machine-byte-numeric (opcode next-sequence) "Constructs a byte in the P6 sequencer rom, given an opcode and sequence number. MSB of this byte is D7, LSB is D0. D7 D6 D5 D4 D3 D2 D1 D0 SN3 SN2 SN1 SN0 CLR SL S0 S1 (SN = next sequence number)" (let ((num 0)) (setf (ldb (byte 4 0) num) opcode (ldb (byte 4 4) num) next-sequence) num))) (eval-when (:compile-toplevel :load-toplevel :execute) (defun assemble-machine-byte (opcode-sym next-sequence) "Constructs a byte in the P6 sequencer ROM, given an opcode symbol, and a next sequence number. opcode-sym can be one of the following: :clr (clear) :nop (no operation) :sl0 (shift left zero) :sr (shift right write-protect) :ld (load data register) :sl1 (shift left one)" (assemble-machine-byte-numeric (ecase opcode-sym (:clr +opcode-clr+) (:nop +opcode-nop+) (:sl0 +opcode-sl0+) (:sr +opcode-srwp+) (:ld +opcode-load+) (:sl1 +opcode-sl1+)) next-sequence))) (eval-when (:compile-toplevel :load-toplevel :execute) (defun assemble-machine-sequence (opcode-list) "Assembles a column of the machine sequence, expressed in the format ((opcode-sym0 next0) (opcode-sym1 next1) ... (opcode-sym15 next15))" (let ((l (length opcode-list))) (unless (= l 16) (error "A sequence must have 16 entries")) (mapcar #'(lambda (pair) (assemble-machine-byte (second pair) (first pair))) opcode-list)))) (defconstant +check-write-protect+ (make-list 16 :initial-element (assemble-machine-byte :sr 0)) "Contents of columns with Q6 On, Q7 Off: check write protect. This is repeated four times in the ROM, to cover all cases of the data register MSB and the read pulse.") (defconstant +read-hiclear-pulse+ (assemble-machine-sequence '((1 :nop) (2 :sl1) (#xd :nop) (#xd :nop) (#xd :nop) (#xd :nop) (#xd :nop) (#xd :nop) (#xd :nop) (#xd :nop) (#xc :sl1) (#xd :sl0) (#xd :sl0) (#xd :nop) (#xf :sl1) (#xd :sl1))) "Contents of Woz machine Read sequence: data MSB clear, read pulse present.") (defconstant +read-hiclear-nopulse+ (assemble-machine-sequence '((1 :nop) (2 :sl1) (3 :nop) (4 :nop) (5 :nop) (6 :nop) (7 :nop) (8 :nop) (9 :nop) (2 :sl0) (#xb :sl1) (5 :sl0) (#xd :sl0) (#x0 :nop) (#xf :sl1) (4 :sl1))) "Contents of Woz machine Read sequence: data MSB clear, read pulse absent.") (defconstant +read-hiset-pulse+ (assemble-machine-sequence '((1 :nop) (3 :nop) (0 :nop) (4 :nop) (#xd :nop) (#xd :nop) (#xd :nop) (#xd :nop) (#xd :nop) (#xd :nop) (#xd :nop) (#xd :nop) (#xd :nop) (#xe :nop) (#xf :nop) (#xe :clr))) "Contents of Woz machine Read sequence: data MSB set, read pulse present.") (defconstant +read-hiset-nopulse+ (assemble-machine-sequence '((1 :nop) (3 :nop) (2 :nop) (4 :nop) (5 :nop) (6 :nop) (7 :nop) (8 :nop) (9 :nop) (#xa :nop) (#xb :nop) (#xc :nop) (#xa :clr) (#xe :nop) (#xf :nop) (#xe :clr))) "Contents of Woz machine Read sequence: data MSB set, read pulse absent.") (defconstant +write-shift-hiclear+ (assemble-machine-sequence '((1 :nop) (2 :nop) (3 :sl0) (4 :nop) (5 :nop) (6 :nop) (7 :nop) (0 :nop) (9 :nop) (#xa :nop) (#xb :sl0) (#xc :nop) (#xd :nop) (#xe :nop) (#xf :nop) (#x8 :nop))) "Contents of Woz machine Write sequence: data MSB clear, Q6 off (shift).") (defconstant +write-shift-hiset+ (assemble-machine-sequence '((1 :nop) (2 :nop) (3 :sl0) (4 :nop) (5 :nop) (6 :nop) (7 :nop) (8 :nop) ; is 0 :nop if high bit clear (9 :nop) (#xa :nop) (#xb :sl0) (#xc :nop) (#xd :nop) (#xe :nop) (#xf :nop) (0 :nop))) ; is 8 :nop if high bit clear "Contents of Woz machine Write sequence: data MSB set, Q6 off (shift).") (defconstant +write-load-hiclear+ (assemble-machine-sequence '((1 :nop) (2 :nop) (3 :ld) (4 :nop) (5 :nop) (6 :nop) (7 :nop) (0 :nop) (9 :nop) (#xa :nop) (#xb :ld) (#xc :nop) (#xd :nop) (#xe :nop) (#xf :nop) (#x8 :nop))) "Contents of Woz machine Write sequence: data MSB clear, Q6 on (load).") (defconstant +write-load-hiset+ (assemble-machine-sequence '((1 :nop) (2 :nop) (3 :ld) (4 :nop) (5 :nop) (6 :nop) (7 :nop) (8 :nop) (9 :nop) (#xa :nop) (#xb :ld) (#xc :nop) (#xd :nop) (#xe :nop) (#xf :nop) (#x0 :nop))) "Contents of Woz machine Write sequence: data MSB set, Q6 on (load).") (defun p6-rom-contents () "Contents of P6 rom, in array, suitable for creating a ROM image. Each byte is formatted as in assemble-machine-byte-numeric, and the address sequence is A7 A6 A5 A4 A3 A2 A1 A0 SN3 SN2 SN0 read-pulse Q7 Q6 data-MSB SN1 Note: read-pulse is a LOW level when a read pulse occured." (let ((array (make-array 256 :initial-element 0))) (dotimes (a 256 array) (let ((sn3 (ldb (byte 1 7) a)) (sn2 (ldb (byte 1 6) a)) (sn0 (ldb (byte 1 5) a)) (read-pulse (ldb (byte 1 4) a)) (q7 (ldb (byte 1 3) a)) (q6 (ldb (byte 1 2) a)) (data-msb (ldb (byte 1 1) a)) (sn1 (ldb (byte 1 0) a))) (let ((seqnum 0)) (setf (ldb (byte 1 3) seqnum) sn3 (ldb (byte 1 2) seqnum) sn2 (ldb (byte 1 1) seqnum) sn1 (ldb (byte 1 0) seqnum) sn0) (let ((column-list (cond ((and (= q6 1) (= q7 0)) ; check-write-protect +check-write-protect+) ((and (= q6 0) (= q7 0)) ; read (if (= data-msb 0) (if (= read-pulse 1) +read-hiclear-nopulse+ +read-hiclear-pulse+) (if (= read-pulse 1) +read-hiset-nopulse+ +read-hiset-pulse+))) ((and (= q7 1) (= q6 0)) ; write/shift (if (= data-msb 0) +write-shift-hiclear+ +write-shift-hiset+)) ((and (= q7 1) (= q6 1)) ; write/load (if (= data-msb 0) +write-load-hiclear+ +write-load-hiset+))))) (setf (aref array a) (nth seqnum column-list)))))))) ;;; ;;; need to modularize the design for debug ;;; (defun make-seqclk (seqclk q3-clock motor-on) "Generates the internal sequencer clock from the Apple II bus signal Q3-CLOCK. and MOTOR-ON. Of course, there is only one instance of this NAND gate in the actual design, but it lets the modules operate more independently." (nand-gate q3-clock motor-on seqclk 22) ) (defun make-data-pulse-synchronizer (rddata q3-clock motor-on p6a4) "Creates the processor for input RDDATA disk pulses. A flux transition, represented as an approximately 1 microsecond long high pulse on RDDATA, causes a LOW pulse on p6a4, synchronized to a rising edge of Q3-CLOCK. The pulse width requirement, of course, depends on the Q3-CLOCK's actual timing." (let ((seqclk (make-wire)) (q4bar (make-wire)) (stateq3 (make-wire)) (stateq4 (make-wire))) (make-seqclk seqclk q3-clock motor-on) (d-flipflop-jamclear rddata seqclk stateq4 motor-on 35 35 :clock-edge :rising) (d-flipflop-jamclear stateq4 seqclk stateq3 motor-on 35 35 :clock-edge :rising) (inverter stateq4 q4bar 30) (nand-gate stateq3 q4bar p6a4 22))) (defun make-state-latch (q3-clock motor-on seq-bus nextseq-bus) "ls174 state latch, except for read-pulse synchronizer" (let ((seqclk (make-wire))) (make-seqclk seqclk q3-clock motor-on) (ganged-d-flipflops nextseq-bus seqclk seq-bus motor-on 35 35 :data-in-setup-time 0 :data-in-hold-time 0 :clock-edge :rising))) (defun make-p6-sequencer (q6 q7 data-msb p6a4 seq-bus opcode-bus nextseq-bus motor-on) "The P6 sequencer rom." (let ((rom-enable (make-wire)) (p6-addr-bus (make-bus (nth-wire 1 seq-bus) ; a0: seq1 data-msb ; a1: QA q6 ; a2: q6 q7 ; a3: a7 p6a4 ; a4: processed read pulse (nth-wire 0 seq-bus) ; a5: seq0 (nth-wire 2 seq-bus) ; a6: seq2 (nth-wire 3 seq-bus))) ; a7: seq3, wrdata (p6-data-bus (make-bus (nth-wire 0 opcode-bus) ; d0:s1 (nth-wire 1 opcode-bus) ; d1:s0 (nth-wire 2 opcode-bus) ; d2:sl (nth-wire 3 opcode-bus) ; d3:clear (nth-wire 0 nextseq-bus) ; d4: nextseq0 (nth-wire 1 nextseq-bus) ; d5: nextseq1 (nth-wire 2 nextseq-bus) ; d6: nextseq2 (nth-wire 3 nextseq-bus)))) ; d7: nextseq3 (inverter motor-on rom-enable 30) (make-rom p6-addr-bus p6-data-bus (p6-rom-contents) 70 :enable-pin rom-enable))) (defun make-data-latch (opcode-bus q3-clock data-msb wrprot motor-on data-bus /dev ad0) "The shift register containing the data read from or to be written to the disk." (let ((data-enables (make-bus /dev ad0)) (data-lsb (make-wire)) ; no connection (seqclk (make-wire)) (s1 (nth-wire 0 opcode-bus)) (s0 (nth-wire 1 opcode-bus)) (sl (nth-wire 2 opcode-bus)) (clear (nth-wire 3 opcode-bus))) (make-seqclk seqclk q3-clock motor-on) (make-shift-register-shared-IO data-bus seqclk clear data-enables s0 s1 sl wrprot data-msb data-lsb 39 30))) (defun make-control-register (ad0 ad1 ad2 ad3 /dev /res q6 q7 ph0 ph1 ph2 ph3 drvselect motor-control) "The 9336 latch holding the program-controlled state of the Woz Machine: head stepper motor phases, drive 1/2 select, motor on/off, and the Q6, Q7 mode select switches." (let ((9336-abus (make-bus ad1 ad2 ad3)) (9336-dbus (make-bus ph0 ph1 ph2 ph3 motor-control drvselect q6 q7))) (make-addressable-latch 9336-abus ad0 /dev /res 9336-dbus 28 24 20 25) ;; :setup-data 0 :setup-addr 0 ;; :hold-data 0 :hold-addr 20 ;; :min-clear-width 15 :min-enable-width 15) )) (defun make-556-timer (q4-motor-on motor-control motor-on /res apple-power-on) "Intended to simulate the 556 timer on the Disk II controller. One half provides a power-on reset signal for the original non-autostart Apple II. One half provides 'inertia' for the MOTOR-ON signal, helping speed up closely spaced disk operations, and probably avoiding wear on the motor. Q4-MOTOR-ON is HIGH to turn on the motor, LOW to request turn off. MOTOR-ON is high when the motor is actually on. APPLE-POWER-ON represents the power-on event. It should go high to indicate the appearance of +5V at the peripheral slots." ;;; temporary: digital only (inverter q4-motor-on motor-control 10) (inverter motor-control motor-on 10) ) (defun data-opcode (clear s1 s0 sl) "Reads signals on the specified wires, returning the symbolic Woz Machine opcode." (let ((clr (get-signal clear)) (s1bit (get-signal s1)) (s0bit (get-signal s0)) (slbit (get-signal sl))) (cond ((logical-false clr) :clr) ; 0xxx ((and (logical-false s0bit) (logical-false s1bit)) :nop) ; 1x00 ((and (logical-true s0bit) (logical-false s1bit)) :srwp) ; 1x10 ((and (logical-true s0bit) (logical-true s1bit)) :load) ; 1x11 ((and (logical-false slbit) (logical-false s0bit) (logical-true s1bit)) :sl0) ; 1001 ((and (logical-true slbit) (logical-false s0bit) (logical-true s1bit)) :sl1)))) ; 1101 (defun bus-opcode (opcode-bus) "Reads the wires on the opcode-bus, returning the symbolic opcode." (data-opcode ; (clear s1 s0 sl) (nth-wire 3 opcode-bus) (nth-wire 0 opcode-bus) (nth-wire 1 opcode-bus) (nth-wire 2 opcode-bus))) (defun set-bus-opcode (opcode-bus opcode) (set-signal opcode-bus (ecase opcode (:clr +opcode-clr+) (:nop +opcode-nop+) (:sl0 +opcode-sl0+) (:sr +opcode-srwp+) (:ld +opcode-load+) (:sl1 +opcode-sl1+)))) (defun make-readwrite-machine (apple-data-bus q6 q7 motor-on wrdata rddata wrprot q3-clock ad0 /dev) "Everything except the control latch." (let ((ns0 (make-wire)) (ns1 (make-wire)) (ns2 (make-wire)) (ns3 (make-wire)) (seq0 (make-wire)) (seq1 (make-wire)) (seq2 (make-wire)) (data-msb (make-wire)) (clear (make-wire)) (s0 (make-wire)) (s1 (make-wire)) (sl (make-wire)) (p6a4 (make-wire))) (let ((seq-bus (make-bus seq0 seq1 seq2 wrdata)) (nextseq-bus (make-bus ns0 ns1 ns2 ns3)) (opcode-bus (make-bus s1 s0 sl clear))) (make-data-pulse-synchronizer rddata q3-clock motor-on p6a4) (make-data-latch opcode-bus q3-clock data-msb wrprot motor-on apple-data-bus /dev ad0) (make-p6-sequencer q6 q7 data-msb p6a4 seq-bus opcode-bus nextseq-bus motor-on) (make-state-latch q3-clock motor-on seq-bus nextseq-bus)))) ;;; define a "disk bus" ;;; of signals to/from the Disk II drive(s) ;;; rddata wrdata wrprot ph0 ph1 ph2 ph3 drvselect /enable-1 /enable-2 (defun make-state-machine (apple-address-bus apple-data-bus disk-II-bus q3-clock /dev /res apple-power-on) (let ((q6 (make-wire)) (q7 (make-wire)) (motor-control (make-wire)) (q4-motor-on (make-wire)) (motor-on (make-wire)) (rddata (nth-wire 0 disk-II-bus)) (wrdata (nth-wire 1 disk-II-bus)) (wrprot (nth-wire 2 disk-II-bus)) (ph0 (nth-wire 3 disk-II-bus)) (ph1 (nth-wire 4 disk-II-bus)) (ph2 (nth-wire 5 disk-II-bus)) (ph3 (nth-wire 6 disk-II-bus)) (drvselect (nth-wire 7 disk-II-bus)) (/enable-1 (nth-wire 8 disk-II-bus)) (/enable-2 (nth-wire 9 disk-II-bus))) (make-readwrite-machine apple-data-bus q6 q7 motor-on wrdata rddata wrprot q3-clock (nth-wire 0 apple-address-bus) /dev) (make-control-register (nth-wire 0 apple-address-bus) (nth-wire 1 apple-address-bus) (nth-wire 2 apple-address-bus) (nth-wire 3 apple-address-bus) /dev /res q6 q7 ph0 ph1 ph2 ph3 drvselect q4-motor-on) (make-556-timer q4-motor-on motor-control motor-on /res apple-power-on) (nand-gate motor-on drvselect /enable2 22) (nand-gate motor-on /enable2 /enable1 22))) #|| ;;; ;;; test synchronizer ;;; (progn (setf *agenda* (make-agenda 0) *q3-clock* (make-wire) *rddata* (make-wire) *motoron* (make-wire) *p6a4* (make-wire)) (make-data-pulse-synchronizer *rddata* *q3-clock* *motoron* *p6a4*) (make-clock *q3-clock* 0 :rising 280 210) (probe *p6a4* 'p6a4) (at-time 0 (set-signal *motoron* 0) (set-signal *rddata* 0)) (at-time 100 (set-signal *motoron* 1)) ; initialize machine (at-time 1000 (set-signal *rddata* 1) (format t "~%~%READ PULSE 1 [1000]~%")) (at-time 2000 (set-signal *rddata* 0)) (at-time 5000 (set-signal *rddata* 1) (format t "~%~%READ PULSE 2 [5000]~%")) ; 4 usec pulse spacing (at-time 6000 (set-signal *rddata* 0)) ;; 9000 pulse missing (at-time 9000 (format t "~%~%READ PULSE MISSING [9000]~%~%")) (at-time 13000 (set-signal *rddata* 1) (format t "~%~%READ PULSE in position 4 [13000]~%")) (at-time 14000 (set-signal *rddata* 0)) (propagate-until 28000)) ;;; ;;; result: pulse 1: low 2349, back high 2809 ;;; pulse 2: low 6269, back high 6729 ;;; pulse 4: low 14109, back high 14569 ;;; ;;; test read-write mechanism ;;; (let ((*agenda* (make-agenda 0)) (q3-clock (make-wire)) (q6 (make-wire)) (q7 (make-wire)) (ad0 (make-wire)) (/dev (make-wire)) (rddata (make-wire)) (wrdata (make-wire)) (wrprot (make-wire)) (motor-on (make-wire)) (da0 (make-wire)) (da1 (make-wire)) (da2 (make-wire)) (da3 (make-wire)) (da4 (make-wire)) (da5 (make-wire)) (da6 (make-wire)) (da7 (make-wire)) (phi1 (make-wire))) (let* ((apple-dbus (make-bus da0 da1 da2 da3 da4 da5 da6 da7)) (read-dbus-probe (make-gated-edge-integer-probe apple-dbus phi1 "6502 read" :rising "~8B"))) (labels ((clock-cycle (n) ; nth clock-cycle (+ 280 (* 490 n))) (cycle (n) ; in time for nth-cycle to operate (- (clock-cycle n) 50))) (make-clock phi1 0 :rising 490 490) (make-clock q3-clock 0 :rising 280 210) ; asymmetric 2 MHz clock (make-readwrite-machine apple-dbus q6 q7 motor-on wrdata rddata wrprot q3-clock ad0 /dev) (setf (enabled read-dbus-probe) t) (at-time 0 (set-signal motor-on 0) (set-signal rddata 0) (set-signal /dev 0) (set-signal ad0 0) (set-signal q6 0) (set-signal q7 0)) (at-time 100 (set-signal motor-on 1)) (at-time 1000 (set-signal rddata 1) (format t "~%~%READ PULSE 1 [1000]~%")) (at-time 2000 (set-signal rddata 0)) (at-time 5000 (set-signal rddata 1) (format t "~%~%READ PULSE 2 [5000]~%")) ; 4 usec pulse spacing (at-time 6000 (set-signal rddata 0)) ;; 9000 pulse missing (at-time 9000 (format t "~%~%READ PULSE MISSING [9000]~%~%")) (at-time 13000 (set-signal rddata 1) (format t "~%~%READ PULSE in position 4 [13000]~%")) (at-time 14000 (set-signal rddata 0)) (propagate-until 50000)))) ||# (defun read-disk-bit-stream (disk-bits &optional (disk-pulse-spacing 4000)) "Simulates the reading of a stream of disk data bits using the Woz Machine. disk-bits should be an integer, with the MSB occuring first on the disk. DISK-PULSE-SPACING should be the time, in nanoseconds, between read pulses." (let* ((num-bits (integer-length disk-bits)) (simulation-time (+ 30000 (* num-bits disk-pulse-spacing)))) (let ((*agenda* (make-agenda 0)) (q3-clock (make-wire)) (q6 (make-wire)) (q7 (make-wire)) (ad0 (make-wire)) (/dev (make-wire)) (rddata (make-wire)) (wrdata (make-wire)) (wrprot (make-wire)) (motor-on (make-wire)) (da0 (make-wire)) (da1 (make-wire)) (da2 (make-wire)) (da3 (make-wire)) (da4 (make-wire)) (da5 (make-wire)) (da6 (make-wire)) (da7 (make-wire)) (phi1 (make-wire))) (let* ((apple-dbus (make-bus da0 da1 da2 da3 da4 da5 da6 da7)) (read-dbus-probe (make-gated-edge-integer-probe apple-dbus phi1 "6502 read" :rising "~8,'0B"))) (labels ((clock-cycle (n) ; nth clock-cycle (+ 280 (* 490 n))) (cycle (n) ; in time for nth-cycle to operate (- (clock-cycle n) 50))) (make-clock phi1 0 :rising 490 490) (make-clock q3-clock 0 :rising 280 210) ; asymmetric 2 MHz clock (make-readwrite-machine apple-dbus q6 q7 motor-on wrdata rddata wrprot q3-clock ad0 /dev) (setf (enabled read-dbus-probe) t) (at-time 0 (set-signal motor-on 0) (set-signal rddata 0) (set-signal /dev 0) (set-signal ad0 0) (set-signal q6 0) (set-signal q7 0)) (at-time 100 (set-signal motor-on 1)) (dotimes (b num-bits) (let ((bit (ldb (byte 1 (- num-bits b)) disk-bits))) (when (= bit 1) (let ((pulse-time (+ 1000 (* disk-pulse-spacing b)))) (at-time pulse-time (set-signal rddata 1)) (at-time (+ pulse-time 1000) (set-signal rddata 0)))))) (propagate-until simulation-time)))))) (defun list-p6-sequencer () "Tests the P6 sequencer ROM by dynamically accessing its contents, printing them out in approximately the format used by Sather in _Understanding the Apple IIe_." (let ((q6 (make-wire)) (q7 (make-wire)) (p6a4 (make-wire)) (data-msb (make-wire)) (seq0 (make-wire)) (seq1 (make-wire)) (seq2 (make-wire)) (seq3-wrdata (make-wire)) (clear (make-wire)) (s0 (make-wire)) (s1 (make-wire)) (sl (make-wire)) (nextseq0 (make-wire)) (nextseq1 (make-wire)) (nextseq2 (make-wire)) (nextseq3 (make-wire)) (motor-on (make-wire))) (let ((seq-bus (make-bus seq0 seq1 seq2 seq3-wrdata)) (nextseq-bus (make-bus nextseq0 nextseq1 nextseq2 nextseq3)) (opcode-bus (make-bus s1 s0 sl clear)) (*agenda* (make-agenda 0))) (make-p6-sequencer q6 q7 data-msb p6a4 seq-bus opcode-bus nextseq-bus motor-on) (at-time 0 (set-signal motor-on 1) (format t "Data Dump of Woz Machine Floppy Disk Controller Sequence P6 ROM~%") (format t "Byte format: -~%~%") (format t "Opcodes:~% NOP no-operation~%") (format t " CLR: clear data latch to zero~%") (format t " SL0: shift 0 bit left into data latch~%") (format t " SL1: shift 1 bit left into data latch~%") (format t " LD: load data latch from Apple II data bus~%") (format t " SR: shift state of write-protect bit right into data latch~%~%") (format t "READ PULSE=RP represents flux transition for 1 bit~%") (format t "SHIFT, LOAD, QA, READ/WRITE MODE are control bits~%~%") (format t "WRITE MODE~%~%") (format t " *-----------READ PULSE-------------*-----------NO READ PULSE----------*~%") (format t " *-----SHIFT------*------LOAD-------*------SHIFT------*------LOAD------*~%") (format t "SEQ *--QA'--*---QA---*--QA'---*---QA---*--QA'---*---QA---*--QA'---*---QA--*~%~%")) (let ((time 100)) (at-time (- time 1) (set-signal q7 1)) (dotimes (sn 16) (let ((row sn)) (at-time (- time 1) (format t "~X- " row)) (dolist (rp '(0 1)) (dolist (shift-load '(0 1)) (dolist (qa '(0 1)) ;; dolist and dotimes operate ;; by rebinding, so I need to close over these variables (let ((rp rp) (shift-load shift-load) (qa qa)) (at-time time (set-signal data-msb qa) (set-signal p6a4 rp) (set-signal q6 shift-load) (set-signal seq-bus row)) (at-time (+ time 95) (format t "~X~X-~3A " (get-integer-signal nextseq-bus) (get-integer-signal opcode-bus) (nibble-opcode (get-integer-signal opcode-bus)))) (incf time 100))))) (at-time (- time 2) (format t "~%")))) (at-time (- time 1) (format t "~%~%READ MODE~%~%") (format t " *--------------SHIFT---------------*----------------LOAD--------------*~%") (format t " *------QA'-------*-------QA--------*-------QA'-------*-------QA-------*~%") (format t "SEQ *---RP--*-NO RP--*---RP---*-NO RP--*---RP---*-NO RP--*---RP---*-NO RP-*~%~%")) (at-time (- time 1) (set-signal q7 0)) (dotimes (sn 16) (let ((row sn)) (at-time (- time 1) (format t "~X- " row)) (dolist (shift-load '(0 1)) (dolist (qa '(0 1)) (dolist (rp '(0 1)) ;; dolist and dotimes operate ;; by rebinding, so I need to close over these variables (let ((rp rp) (shift-load shift-load) (qa qa)) (at-time time (set-signal data-msb qa) (set-signal p6a4 rp) (set-signal q6 shift-load) (set-signal seq-bus row)) (at-time (+ time 95) (format t "~X~X-~3A " (get-integer-signal nextseq-bus) (get-integer-signal opcode-bus) (nibble-opcode (get-integer-signal opcode-bus)))) (incf time 100)))))) (at-time (- time 2) (format t "~%")))) (propagate)))) #|| ;;; ;;; test data latch ;;; ;;; (let ((*agenda* (make-agenda 0)) (da0 (make-wire)) (da1 (make-wire)) (da2 (make-wire)) (da3 (make-wire)) (da4 (make-wire)) (da5 (make-wire)) (da6 (make-wire)) (da7 (make-wire)) (clear (make-wire)) (s0 (make-wire)) (s1 (make-wire)) (sl (make-wire)) (q3-clock (make-wire)) (data-msb (make-wire)) (wrprot (make-wire)) (motor-on (make-wire)) (ad0 (make-wire)) (/dev (make-wire))) (let ((apple-dbus (make-bus da0 da1 da2 da3 da4 da5 da6 da7)) (opcode-bus (make-bus s1 s0 sl clear))) (labels ((clock-cycle (n) ; nth clock-cycle (+ 280 (* 490 n))) (cycle (n) ; in time for nth-cycle to operate (- (clock-cycle n) 50)) (set-opcode (code) (set-bus-opcode opcode-bus code))) (probe-integer apple-dbus 'apple-dbus "~2X") (make-data-latch opcode-bus q3-clock data-msb wrprot motor-on apple-dbus /dev ad0) (make-clock q3-clock 0 :rising 280 210) ; asymmetric 2 MHz clock (at-time 0 (set-signal motor-on 0) (set-signal wrprot 1) (set-signal /dev 0) (set-signal ad0 0)) (at-time (cycle 0) (set-signal motor-on 1) (set-opcode :nop)) (at-time (cycle 1) (set-opcode :sl1)) ; 1 (at-time (cycle 2) (set-opcode :sl0)) ; 1000 (at-time (cycle 5) (set-opcode :sl1)) ; 10001 (at-time (cycle 6) (set-opcode :sr)) ; 10001000 ; 7: 11000100 ; 8: 11100010 ; 9: 11110001 (at-time (cycle 10) (set-signal wrprot 0)) ; 10: 01111000 ; 11: 00111100 ; 12: 00011110 ; 13: 00001111 ; 14: 00000111 (at-time (cycle 15) (set-signal apple-dbus #xaa) (set-opcode :ld)) ; 15: 10101010 (at-time (cycle 16) (set-opcode :nop)) (at-time (cycle 17) (set-opcode :sl1)) ; 17: 01010101 ; 18: 10101011 (propagate-until (clock-cycle 30))))) ||# #|| ;;; less modular design. This was broken into three parts: ;;; the read data synchronizer + state latch + P6 rom ;;; (defun make-state-machine (q6 q7 rddata q3-clock s0 s1 sl clear data-msb motor-on wrdata) "Creates a Wozniak State Machine, consisting of the ls174 state latch and P6 Rom of a Disk II controller." (let ((seq0 (make-wire)) (seq1 (make-wire)) (seq2 (make-wire)) (q4bar (make-wire)) (p6a4 (make-wire)) (stateq3 (make-wire)) (stateq4 (make-wire)) (seqclk (make-wire)) (nextseq0 (make-wire)) (nextseq1 (make-wire)) (nextseq2 (make-wire)) (nextseq3 (make-wire)) (rom-enable (make-wire)) ) (let ((p6-addr-bus (make-bus seq1 data-msb q6 q7 p6a4 seq0 seq2 wrdata)) (p6-data-bus (make-bus s1 s0 sl clear nextseq0 nextseq1 nextseq2 nextseq3))) (make-rom p6-addr-bus p6-data-bus (p6-rom-contents) 70 :enable-pin rom-enable) (inverter motor-on rom-enable 30) (let ((state-q-bus (make-bus wrdata seq2 seq0 stateq3 stateq4 seq1)) (state-d-bus (make-bus nextseq3 nextseq2 nextseq0 stateq4 rddata nextseq1))) (ganged-d-flipflops state-d-bus seqclk state-q-bus motor-on 35 35 :data-in-setup-time 0 :data-in-hold-time 0 :clock-edge :rising) (inverter stateq4 q4bar 30) (nand-gate stateq3 q4bar p6a4 22) (nand-gate q3-clock motor-on seqclk 22))))) ||# #|| ;;; test less-modular sequencer (progn (setf *agenda* (make-agenda 0) *q3-clock* (make-wire) *q6* (make-wire) *q7* (make-wire) *rddata* (make-wire) *s0* (make-wire) *s1* (make-wire) *sl* (make-wire) *clear* (make-wire) *data-msb* (make-wire) *motoron* (make-wire) *wrdata* (make-wire) *wrprot* (make-wire)) (mapcar #'(lambda (wire) (add-action wire #'(lambda () (format t "Time ~D: ~A~%" (agenda-time *agenda*) (data-opcode *clear* *s1* *s0* *sl*))))) (list *s0* *s1* *sl* *clear*)) (make-state-machine *q6* *q7* *rddata* *q3-clock* *s0* *s1* *sl* *clear* *data-msb* *motoron* *wrdata*) (make-clock *q3-clock* 0 :rising 280 210) (at-time 0 (set-signal *motoron* 0) (set-signal *wrprot* 0) (set-signal *data-msb* 0) (set-signal *rddata* 0) (set-signal *q7* 0) (set-signal *q6* 0)) (at-time 100 (set-signal *motoron* 1)) ; initialize machine (at-time 1000 (set-signal *rddata* 1) (format t "~%~%READ PULSE 1~%")) (at-time 2000 (set-signal *rddata* 0)) (at-time 5000 (set-signal *rddata* 1) (format t "~%~%READ PULSE 2~%")) ; 4 usec pulse spacing (at-time 6000 (set-signal *rddata* 0)) ;; 9000 pulse missing (at-time 9000 (format t "~%~%READ PULSE MISSING~%~%")) (at-time 13000 (set-signal *rddata* 1) (format t "~%~%READ PULSE in position 4~%")) (at-time 14000 (set-signal *rddata* 0)) (propagate-until 28000)) ||# #|| (progn (setf *agenda* (make-agenda 0) *q3-clock* (make-wire) ; 2 MHz clock on Apple II bus *phi0* (make-wire) ; phase 0 of Apple II bus *phi1* (make-wire) ; phase 1 of Apple II bus *dev* (make-wire) ; /DEV on Apple II bus *iosel* (make-wire) ; /IOSEL on Apple II bus *res* (make-wire) ; /RES on Apple II bus *ph0* (make-wire) ; head stepper phase 0 *ph1* (make-wire) ; head stepper phase 1 *ph2* (make-wire) ; head stepper phase 2 *ph3* (make-wire) ; head stepper phase 3 *q4moton* (make-wire) ; motor control bit output from 9334 *ad0* (make-wire) ; Apple II Address bit 0 *ad1* (make-wire) ; Apple II Address bit 1 *ad2* (make-wire) ; Apple II Address bit 2 *ad3* (make-wire) ; Apple II Address bit 3 *ad4* (make-wire) *ad5* (make-wire) *ad6* (make-wire) *ad7* (make-wire) ; Apple II address bit 7 *drvselect* (make-wire) ; choose drive 0,1 *enbl1* (make-wire) *enbl2* (make-wire) *seqclk* (make-wire) ; clock input to LS323 shift register *q6* (make-wire) ; Q6 control bit *q7* (make-wire) ; Q7 control bit *da7* (make-wire) ; Apple II data bus *da6* (make-wire) *da5* (make-wire) *da4* (make-wire) *da3* (make-wire) *da2* (make-wire) *da1* (make-wire) *da0* (make-wire) ; Apple II data bus 0 *rddata* (make-wire) ; signal from drive, to D4 of LS174 *wrdata* (make-wire) ; Q0 of LS174, A7 of P6 rom *seq2* (make-wire) ; Q1 of LS174, A6 of P6 *seq0* (make-wire) ; Q2 of LS174, A5 of P6 *stateq3* (make-wire) ; Q3 of LS174 *stateq4* (make-wire) ; Q4, D3 of LS174 *seq1* (make-wire) ; Q5 of LS174, A0 of P6 *data-msb* (make-wire) ; MSB of LS323, A1 of P6 *stateq4bar* (make-wire) ; LS174 Q4, inverted *p6a4* (make-wire) ; processed read data *nextseq3* (make-wire) ; D0 of LS174, D7 of P6 rom *nextseq2* (make-wire) ; D1 of LS174, D6 of P6 rom *nextseq0* (make-wire) ; D2 of LS174, D4 of P6 rom *nextseq1* (make-wire) ; D5 of LS174, D5 of P6 rom ;; D3, D4 of 174 process the read data pulse ;; D6, D7 of 174 not used *opcode0* (make-wire) ; D0 of P6 rom, S1 control of LS323 *opcode1* (make-wire) ; D1 of P6 rom, S0 control of LS323 *opcode2* (make-wire) ; D2 of P6 rom, SL of LS323 *opcode3* (make-wire) ; D3 of P6 rom, CLR of LS323 *wrprot* (make-wire) ; write-protect signal from drive: SR of LS323 *MOTORON* (make-wire) ; signal controlling ENBL1, ENBL2, gating clock ;; in real disk II, output of 1/2 556 timer *MOTORCONTROL* (make-wire) ; output of ls05 inverter ;; connected to *q4moton*. Drives an analog input, so perhaps ;; it will need a different wire type. ) (defun print-state-latch () (let ((motor-phases (list (get-signal *ph0*) (get-signal *ph1*) (get-signal *ph2*) (get-signal *ph3*))) (drive-select (get-signal *drvselect*)) (enbl1 (get-signal *enbl1*)) (enbl2 (get-signal *enbl2*)) (motoron (get-signal *motoron*)) (motorcontrol (get-signal *motorcontrol*)) (q6 (get-signal *q6*)) (q7 (get-signal *q7*))) (format t "motor phases: ~A " motor-phases) (when (logical-false enbl1) (format t "DRIVE 1 active ")) (when (logical-false enbl2) (format t "DRIVE 2 active ")) (when (logical-true motoron) (format t "MOTORON is true. Motor should be on. ")) (when (logical-false motorcontrol) (format t "MOTORCONTROL is false. Motor should be on. ")) (cond ((and (logical-true q6) (logical-true q7)) (format t " WRITING/LOAD ")) ((and (logical-false q6) (logical-true q7)) (format t " WRITING/SHIFT ")) ((and (logical-true q6) (logical-false q7)) (format t " CHECK WR PROT ")) ((and (logical-false q6) (logical-false q7)) (format t " READ ")) (t (format t " INVALID!!! "))) (format t "~%"))) ;;; ;;; 9336 control latch ;;; (setf *9336-abus* (make-bus *ad1* *ad2* *ad3*) *9336-dbus* (make-bus *ph0* *ph1* *ph2* *ph3* *q4moton* *drvselect* *q6* *q7*)) (setf *apple-dbus* (make-bus *da0* *da1* *da2* *da3* *da4* *da5* *da6* *da7*)) (setf *apple-abus* (make-bus *ad0* *ad1* *ad2* *ad3* *ad4* *ad5* *ad6* *ad7*)) ;; lowest order 8 bits. The Disk II controller is concerned with #xCnXY ;; where n is the slot (for the P5 boot code rom) ;; and #xC0(n+8)Y, the device I/O locations (make-addressable-latch *9336-abus* *ad0* *dev* *res* *9336-dbus* 28 24 20 25) ;; :setup-data 0 :setup-addr 0 ;; :hold-data 0 :hold-addr 20 ;; :min-clear-width 15 :min-enable-width 15) (add-action *9336-dbus* #'print-state-latch) ;;; times in nsec, from LS259 data sheet. setup, hold, & widths are not yet ;;; enforced, but might as well include them. ;;;; ;;;; DESCRAMBLING THE P6 ROM CONNECTIONS ;;;; ;;;; As defined in the assemble-machine-byte instructions ;;;; the P6 rom data is assembled as ;;;; ;;;; D7 D6 D5 D4 D3 D2 D1 D0 ;;;; SN3 SN2 SN1 SN0 CLR SL S0 S1 (SN = next sequence number) ;;;; ;;;; and the P6 address is ;;;; A7 A6 A5 A4 A3 A2 A1 A0 ;;;; SN3 SN2 SN0 read-pulse Q7 Q6 data-MSB SN1 (SN = current seq number) ;;;; ;;;; now, the schematic uses ;;;; P6.D0 = 323.S1 (opcode0) ;;;; P6.D1 = 323.S0 (opcode1) ;;;; P6.D2 = 323.SL (opcode2) ;;;; P6.D3 = 323.CLR (opcode3) ;;;; P6.D4 = 174.D2 --> 174.Q2 = P6.A5 SN0 [*nextseq0*, *seq0*] ;;;; P6.D5 = 174.D5 --> 174.Q5 = P6.A0 SN1 [*nextseq1*, *seq1*] ;;;; P6.D6 = 174.D1 --> 174.Q1 = P6.A6 SN2 [*nextseq2*, *seq2*] ;;;; P6.D7 = 174.D0 --> 174.Q0 = P6.A7 SN3 [*nextseq3*, *wrdata*] ;;;; ;;;; so the definitions above actually make sense, I think. (setf *p6-addr-bus* (make-bus *seq1* *data-msb* *q6* *q7* *p6a4* *seq0* *seq2* *wrdata*)) (setf *p6-column* (make-bus *q6* *q7* *p6a4* *data-msb*)) (probe *p6-column* "[q6 q7 p6a4 data-msb]") (setf *p6-data-bus* (make-bus *opcode0* *opcode1* *opcode2* *opcode3* *nextseq0* *nextseq1* *nextseq2* *nextseq3*)) (make-rom *p6-addr-bus* *p6-data-bus* (p6-rom-contents) 70 :enable-pin *MOTORCONTROL*) ;;; what is prop delay for 6309 ROM? Sather claims 70 nsec in _Understanding ;;; the Apple IIe_, p. 9-21. ;;; ;;; motorcontrol is not exactly the right ;;; pin, but I'm not simulating the analog timer, so it is close enough. ;;; let's have motor on operate instantaneously for now ;;; *MOTORON* drives reset of ls174 ;;; *MOTORCONTROL* is effectively an enable on P6. ;;; (inverter *q4moton* *MOTORCONTROL* 10) (inverter *MOTORCONTROL* *MOTORON* 10) (nand-gate *drvselect* *MOTORON* *enbl2* 22) ; schmitt trigger inputs, to avoid ;; chatter from 556 timer output? (nand-gate *MOTORON* *enbl2* *enbl1* 22) ; also schmitt trigger input (nand-gate *q3-clock* *MOTORON* *seqclk* 22) ; gate sequencer clock ;; process read pulses (inverter *stateq4* *stateq4bar* 30) (nand-gate *stateq3* *stateq4bar* *p6a4* 22) ;;; ;;; DATA REGISTER LS323 ;;; (setf *data-enables* (make-bus *dev* *ad0*)) (setf *data-lsb* (make-wire)) ; no connection (make-shift-register-shared-IO *apple-dbus* *seqclk* *opcode3* *data-enables* *opcode1* *opcode0* *opcode2* *wrprot* *data-msb* *data-lsb* 39 30) ;;; ;;; state flip-flops LS174 ;;; (setf *state-q-bus* (make-bus *wrdata* *seq2* *seq0* *stateq3* *stateq4* *seq1*)) (setf *state-d-bus* (make-bus *nextseq3* *nextseq2* *nextseq0* *stateq4* *rddata* *nextseq1*)) (ganged-d-flipflops *state-d-bus* *seqclk* *state-q-bus* *motoron* 35 35 :data-in-setup-time 0 :data-in-hold-time 0 :clock-edge :rising) (setf *state-probe* (make-gated-edge-probe *state-d-bus* *seqclk* "State latch [nseq320 q4 data nseq1]" :rising)) (setf (enabled *state-probe*) t) (probe-integer *state-q-bus* 'state "#x~X") ;;; TEST SEQUENCE ;;; times in nsec ;;; (format t "Agenda time is ~D.~%" (agenda-time *agenda*)) (set-signal *res* 0) (set-signal *iosel* 1) (set-signal *dev* 1) (make-clock *q3-clock* 0 :rising 280 210) ; asymmetric 2 MHz clock (make-clock *phi0* 0 :falling 490 490) ; phase 0 clock (make-clock *phi1* 0 :rising 490 490) (at-time 10000 (set-signal *res* 1)) ; much shorter than reality? (at-time 10100 (set-signal *dev* 0) ; twiddle disk II bits. (set-signal *apple-abus* #x89)) ; motor on (at-time 11000 (set-signal *apple-abus* #x8a)) ; drive 1 (at-time 12000 (set-signal *apple-abus* #x8e)) ; read mode (at-time 13000 (set-signal *apple-abus* #x8c) (format t "~%~%~%GOING TO READ MODE~%~%~%") (print-state-latch)) ; read data register ;; (probe *q3-clock* 'q3) ;; (probe *seqclk* 'cp) (setf *opcode-bus* (make-bus *opcode0* *opcode1* *opcode2* *opcode3*)) (probe-integer *opcode-bus* 'opcode "#x~X") (setf *seq-num* (make-bus *seq0* *seq1* *seq2* *wrdata*)) (probe-integer *seq-num* "sequence number") (setf *io-control* (make-bus *q4moton* *drvselect* *q6* *q7*)) (probe *io-control* "IO control") (probe-integer *p6-data-bus* 'p6-data "#x~X") (probe-integer *p6-addr-bus* 'p6-addr "#x~X") (setf *data-probe* (make-gated-edge-probe *apple-dbus* *phi1* "6502 read" :rising)) (probe *apple-dbus* 'apple-dbus) ;; 6502 samples data bus for read transactions when phi1 rises. ;;; make stream of data pulses on rddata (at-time 14000 (set-signal *rddata* 1) (format t "~%~%READ PULSE 1~%") (setf (enabled *data-probe*) t)) ; 1 usec pulse (at-time 15000 (set-signal *rddata* 0)) (at-time 18000 (set-signal *rddata* 1) (format t "~%~%READ PULSE 2~%")) ; 4 usec pulse spacing (at-time 19000 (set-signal *rddata* 0)) ;; 22000 pulse missing (at-time 22000 (format t "~%~%READ PULSE MISSING~%~%")) (at-time 26000 (set-signal *rddata* 1) (format t "~%~%READ PULSE in position 4~%")) (at-time 27000 (set-signal *rddata* 0)) (propagate-until 35000) ) ;; should make 1101 ;;; MISSING BITS: ;;; P5 rom. ;;; S0 of shift register has an additional open-collector inverter ;;; which apparently pulls low when MOTORON is false. ;;; MOTORON is controlled by NOT(*motonq4*) driving 1/2 of the ;;; 556 timer. ;;; The processing of *res* signal is a bit strange: I'm not sure ;;; what the first half of the 556 does with the open-collector ;;; inverter on it's Q. How does it avoid pulling /RES low on the ;;; Apple II? Actually, I think it does pull /RES low for 0.1 sec on ;;; power up. ;;; ;;; WRREQ line of the disk controller is NOT(Q7) (another open-collector inv.) ;;; ENBL1, ENBL2 are determined by MOTORON, *drvselect* ;;; ;;; various VCCs are gated. IOSEL gates the VCC on the P5 ROM ;;; VCC of LS174, P6 ROM are controlled by MOTORON. ;;; ||#