mirror of
https://github.com/pevans/erc-c.git
synced 2024-12-22 14:30:45 +00:00
264 lines
12 KiB
NASM
264 lines
12 KiB
NASM
; disk2.asm
|
|
;
|
|
; This is the DISASSEMBLED source code for the Disk II controller ROM.
|
|
; It adds up to 256 bytes of program code, which is all any peripheral
|
|
; card was afforded.
|
|
;
|
|
; NOTE THAT THIS SOURCE CODE IS NOT ORIGINAL TO APPLE. I translated by
|
|
; hand from the machine code in the ROM. Any comments, etc. you see
|
|
; here, are from me--NOT APPLE.
|
|
;
|
|
; For details on the assembly instructions--what they mean and do--this
|
|
; is a good resource:
|
|
; http://www.e-tradition.net/bytes/6502/6502_instruction_set.html
|
|
;
|
|
; Any number that has a $ in front of it means it's a hex number, vs.
|
|
; decimal.
|
|
|
|
; Our definitions for this little program. The EQU symbol is not a
|
|
; formal instruction understood by the 6502 CPU; it's a notation that
|
|
; simply means "equals"; e.g. GBASL equals $26.
|
|
GBASL EQU $26
|
|
GBASH EQU $27
|
|
BAS2H EQU $2B
|
|
A1L EQU $3C
|
|
A1H EQU $3D
|
|
A3L EQU $40
|
|
A3H EQU $41
|
|
STACK EQU $0100
|
|
GCRALT EQU $02D6
|
|
XORSAV EQU $0300
|
|
GCRTAB EQU $0356
|
|
ENTRY EQU $0800
|
|
PHASEOFF EQU $C080
|
|
PHASEON EQU $C081
|
|
TURNON EQU $C089
|
|
SLCTD1 EQU $C08A
|
|
READ EQU $C08C
|
|
SETRD EQU $C08E
|
|
WAIT EQU $FCA8
|
|
FINDSLOT EQU $FF58
|
|
|
|
; This is not needed by the disk controller program itself, but is used
|
|
; by the system ROM to determine if there is a valid controller program
|
|
; here. (There is!)
|
|
00:A2 20 LDX #$20
|
|
|
|
; Here we will write the group coded recording table for our decode
|
|
; process, which is in the GCRTAB address.
|
|
02:A0 00 LDY #$00
|
|
04:A2 03 LDX #$03 ; our loop begins at $03
|
|
06:86 3C NEXTGCR STX A1L ; A1L tracks the loop counter
|
|
08:8A TXA
|
|
09:0A ASL A
|
|
0A:24 3C BIT A1L ; if X and X<<1 have no bits in common
|
|
0C:F0 10 BEQ CONTGCR ; then X will not be written into GCRTAB
|
|
|
|
; This sequence of operations will prime A for the VALIDENT check. Any
|
|
; valid X register value (from which A is derived) will be one where
|
|
; these three operations results in $40; after we loop on VALIDENT and
|
|
; shift A to the right a bunch of times, we'll end up with $00 and exit
|
|
; the loop without tripping the BCS (because none of the first 6 bits
|
|
; were ever high).
|
|
0E:05 3C ORA A1L
|
|
10:49 FF EOR #$FF
|
|
12:29 7E AND #$7E
|
|
14:B0 08 VALIDENT BCS CONTGCR
|
|
16:4A LSR A
|
|
17:D0 FB BNE VALIDENT
|
|
|
|
; Only certain X register values will be written via the STA
|
|
; instruction; what we do write is an iteration of Y from $00..$3F.
|
|
19:98 TYA
|
|
1A:9D 56 03 STA GCRTAB,X
|
|
1D:C8 INY
|
|
1E:E8 CONTGCR INX
|
|
1F:10 E5 BPL NEXTGCR
|
|
|
|
; All this wrangling is here to make a record of the slot number.
|
|
; Because JSR will push the calling address into the stack, we can find
|
|
; the MSB of that address with the LDA STACK,X instruction. All of the
|
|
; ASLs will essentially push the 7 one hex digit over, so $C7 becomes
|
|
; $70. And we store that in BAS2H so we can use it to run operations on
|
|
; the peripheral.
|
|
21:20 58 FF JSR FINDSLOT
|
|
24:BA TSX
|
|
25:BD 00 01 LDA STACK,X ; this will load $C7 into A
|
|
28:0A ASL
|
|
29:0A ASL
|
|
2A:0A ASL
|
|
2B:0A ASL ; and now we have $70
|
|
2C:85 2B STA BAS2H
|
|
|
|
; Ok, with that done, we're going to get everything set up to copy the
|
|
; zero track into RAM. NOTE: I'm not entirely sure why we're doing a
|
|
; READ from the drive before we know we have drive 1 selected and turned
|
|
; on.
|
|
2E:AA TAX
|
|
2F:BD 8E C0 LDA SETRD,X
|
|
32:BD 8C C0 LDA READ,X
|
|
35:BD 8A C0 LDA SLCTD1,X
|
|
38:BD 89 C0 LDA TURNON,X
|
|
|
|
; This loop is going to go through the stepper motor phases, flipping
|
|
; them off and on again. To begin with, X is $70, so we're going to work
|
|
; with phase 0 at the start.
|
|
3B:A0 50 LDY #$50
|
|
3D:BD 80 C0 PHASELOOP LDA PHASEOFF,X
|
|
40:98 TYA
|
|
41:29 03 AND #$03 ; drop all but the first 2 bits
|
|
43:0A ASL ; and shift over
|
|
44:05 2B ORA BAS2H ; and add that to $70
|
|
46:AA TAX
|
|
47:BD 81 C0 LDA PHASEON,X
|
|
4A:A9 56 LDA #$56
|
|
|
|
; In at least one implementation (notably WinApple), the opcode below is
|
|
; rewritten as `A9 00 EA`, which is equivalent to:
|
|
; LDA #$00
|
|
; NOP
|
|
; This would essentially remove the WAIT call. The WAIT subroutine will,
|
|
; in the course of its operation, leave $00 in A, which explains the LDA
|
|
; #$00 opcode sequence. The NOP is there to replace the third byte
|
|
; (which was part of the JSR address in its original form).
|
|
4C:20 A8 FC JSR WAIT ; wait for the motor
|
|
4F:88 DEY
|
|
50:10 EB BPL PHASELOOP
|
|
|
|
; We're setting things up so we can start writing our decoded data into
|
|
; the $08 page in memory, which is where we will ultimately jump to once
|
|
; we finish going through track zero.
|
|
52:85 26 STA GBASL ; A is $00 by this point
|
|
54:85 3D STA A1H
|
|
56:85 41 STA A3H
|
|
58:A9 08 LDA #$08
|
|
5A:85 27 STA GBASH ; so GBASH/L will hold $0800
|
|
|
|
; We're going to check to see if we are at a header marker.
|
|
5C:18 CHKHD CLC
|
|
5D:08 CHKHDC PHP ; hang onto the status for later
|
|
|
|
; Read byte from the disk (BPL is used here because anything that
|
|
; doesn't have bit 7 high is bad data in 6-and-2 encoding).
|
|
5E:BD 8C C0 READHD1 LDA READ,X
|
|
61:10 FB BPL READHD1
|
|
63:49 D5 CHKHD1 EOR #$D5
|
|
65:D0 F7 BNE READHD1 ; try again
|
|
|
|
; Look for the second header byte
|
|
67:BD 8C C0 READHD2 LDA READ,X
|
|
6A:10 FB BPL READHD2
|
|
6C:C9 AA CHKHD2 CMP #$AA
|
|
6E:D0 F3 BNE CHKHD1
|
|
70:EA NOP ; I don't know why we NOP here
|
|
|
|
; Third header byte
|
|
71:BD 8C C0 READHD3 LDA READ,X
|
|
74:10 FB BPL READHD3
|
|
76:C9 96 CMP #$96 ; is this the end of a track marker?
|
|
78:F0 09 BEQ METADATA ; seems to be!
|
|
7A:28 PLP
|
|
7B:90 DF BCC CHKHD ; if A < $96, keep seeking for a header byte
|
|
7D:49 AD EOR #$AD ; if NOT, then this might be the end of a sector header
|
|
7F:F0 25 BEQ DECODE ; so let's get decoding!
|
|
81:D0 D9 BNE CHKHD ; Some other byte we didn't expect...
|
|
|
|
; The metadata is 4-and-4 encoded, which are two bytes that are read in
|
|
; sequence and then AND'd together. The second in the sequence is what
|
|
; will stay behind in A3L; we'll read 3 sequences in all.
|
|
83:A0 03 METADATA LDY #$03
|
|
85:85 40 AGAIN44 STA A3L
|
|
87:BD 8C C0 FIRST44 LDA READ,X ; read a byte
|
|
8A:10 FB BPL FIRST44
|
|
8C:2A ROL A
|
|
8D:85 3C STA A1L
|
|
8F:BD 8C C0 SECOND44 LDA READ,X ; read another byte
|
|
92:10 FB BPL SECOND44
|
|
94:25 3C AND A1L ; intersect with the shifted FIRST44
|
|
96:88 DEY
|
|
97:D0 EC BNE AGAIN44
|
|
|
|
; This is going to pull from before we began checking for a header
|
|
99:28 PLP
|
|
9A:C5 3D CMP A1H
|
|
9C:D0 BE BNE CHKHD
|
|
|
|
; A3H can only be $00, and A3L will have been $96 from the last header
|
|
; byte we read; since $96 - $00 will of course not be zero, this will
|
|
; force a branch back to CHKHD. Why we have this code here is unclear to
|
|
; me.
|
|
9E:A5 40 LDA A3L
|
|
A0:C5 41 CMP A3H
|
|
A2:D0 B8 BNE CHKHD
|
|
|
|
; If C is set, we will jump back to read the next header, _but_ we will
|
|
; not execute the CLC instruction.
|
|
A4:B0 B7 BCS CHKHDC
|
|
|
|
; As we decode bytes, we're referencing the GCRTAB entries we built
|
|
; earlier but from a slightly different address point (hence GCRALT).
|
|
; But make no mistake--we're EORing with GCRTAB data. Note that XORSAV
|
|
; is an entry point ($0300) which is conveniently(!) $56 less than
|
|
; GCRTAB ($0356). It is, though, really just a place to stash the
|
|
; intermediate data.
|
|
A6:A0 56 DECODE LDY #$56 ; loop this many times...
|
|
A8:84 3C SAV2BITS STY A1L ; save in A1L, because we use Y to read
|
|
AA:BC 8C C0 DECBYTE2 LDY READ,X
|
|
AD:10 FB BPL DECBYTE
|
|
AF:59 D6 02 EOR GCRALT,Y
|
|
B2:A4 3C LDY A1L
|
|
B4:88 DEY ; decrement the loop counter
|
|
B5:99 00 03 STA XORSAV,Y ; hang onto the EOR data
|
|
B8:D0 EE BNE SAV2BITS
|
|
|
|
; Looping from zero, now, we're going to write all that intermediate
|
|
; data into the $0800 page (which is what (GBASL),Y resolves to),
|
|
; counting up from $0800..$08FF.
|
|
BA:84 3C SAV6BITS STY A1L
|
|
BC:BC 8C C0 DECBYTE6 LDY READ,X
|
|
BF:10 FB BPL DECBYTE6
|
|
C1:59 D6 02 EOR GCRALT,Y
|
|
C4:A4 3C LDY A1L
|
|
C6:91 26 STA (GBASL),Y
|
|
C8:C8 INY
|
|
C9:D0 EF BNE SAV6BITS
|
|
|
|
; We read ONE more byte, then determine if we need to check for another
|
|
; header again.
|
|
CB:BC 8C C0 FINBYTE LDY READ,X
|
|
CE:10 FB BPL FINBYTE
|
|
D0:59 D6 02 EOR XORTMP,Y
|
|
|
|
; We may also get to here because it looks like we didn't write data
|
|
; properly into the ENTRY page.
|
|
D3:D0 87 BADDATA BNE CHKHD ; another sector?
|
|
|
|
; We're using the first 89 ($56) bytes in XORSAV (which, remember, is
|
|
; $56 less than the GCRTAB address point); if we go below $00 (rolling
|
|
; over to $FF), start over.
|
|
;
|
|
; The bytes we've already written into the ENTRY page need to have those
|
|
; 2 bits we compiled into those 89 bytes pushed back into the data.
|
|
D5:A0 00 LDY #$00
|
|
D7:A2 56 BITLOOP LDX #$56
|
|
D9:CA WRITELOOP DEX
|
|
DA:30 FB BMI BITLOOP ; start over if we went past $00
|
|
DC:B1 26 LDA (GBASL),Y ; load $0800 + Y
|
|
DE:5E 00 03 LSR XORSAV,X ; move bit 0 into carry
|
|
E1:2A ROL A ; now load carry into A, plus the orig contents
|
|
E2:5E 00 03 LSR XORSAV,X ; shift the former bit 1 (now bit 0) into carry again
|
|
E5:2A ROL A ; and again load into A; now we have all 8 bits
|
|
E6:91 26 STA (GBASL),Y ; and save it back to $0800 + Y
|
|
E8:C8 INY
|
|
E9:D0 EE BNE WRITELOOP ; we'll loop here 256 times
|
|
|
|
; We're in the home stretch... we're just double-checking if we copied
|
|
; things into the ENTRY page properly.
|
|
EB:E6 27 INC GBASH ; so now GBASL/H is $0900
|
|
ED:E6 3D INC A1H
|
|
EF:A5 3D LDA A1H
|
|
F1:CD 00 08 CMP ENTRY ; if A < ENTRY
|
|
F4:A6 2B LDX BAS2H
|
|
F6:90 DB BCC BADDATA ; then go back and try again
|
|
F8:4C 01 08 JMP ENTRY+1 ; otherwise, let's boot the software!
|