diff --git a/firmware/Makefile b/firmware/Makefile index 6634291..0f072b2 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -1,9 +1,26 @@ -all: 0000 0100 0200 0300-franklin 0300-usci - cat 0000 0100 0200 0300-franklin > franklin.bin - cat 0000 0100 0200 0300-usci > usci.bin +all: util.bin 16.bin 13.bin legend-franklin.bin legend-usci.bin + cat util.bin 16.bin 13.bin legend-franklin.bin > firmware-franklin.bin + cat util.bin 16.bin 13.bin legend-usci.bin > firmware-usci.bin -0300-franklin: - xa -o 0300-franklin 0300.s +legend-franklin.bin: + ca65 --target apple2 -o legend-franklin.o legend.s + ld65 --config apple2-asm.cfg -o legend-franklin.bin legend-franklin.o -0300-usci: - xa -DUSCI -o 0300-usci 0300.s +legend-usci.bin: + ca65 --target apple2 -DMICROSCI -o legend-usci.o legend.s + ld65 --config apple2-asm.cfg -o legend-usci.bin legend-usci.o + +util.bin: + ca65 --target apple2 -o util.o util.s + ld65 --config apple2-asm.cfg -o util.bin util.o + +13.bin: + ca65 --target apple2 -DSECSIZE_13 -o 13.o firmware.s + ld65 --config apple2-asm.cfg -o 13.bin 13.o + +16.bin: + ca65 --target apple2 -DSECSIZE_16 -o 16.o firmware.s + ld65 --config apple2-asm.cfg -o 16.bin 16.o + +clean: + rm -f *.o *.bin diff --git a/firmware/firmware.s b/firmware/firmware.s new file mode 100644 index 0000000..328124a --- /dev/null +++ b/firmware/firmware.s @@ -0,0 +1,363 @@ +; Disk ][ controller card "BOOT0" code, found in the slot ROM. Reads the +; BOOT1 code from track 0, sector 0, and jumps to it. +; +; Copyright Apple Computer Inc. +; +; Written by [a genius...Woz?] +; +; Extracted from AppleWin at $C600. +; +; Project created by Andy McFadden, using 6502bench SourceGen v1.5 +; Last updated 2020/01/15 +; +; Converted for use with Micro-SCI repro project 2023 Chris RYU + +STACK := $0100 ;{addr/256} +.ifdef SECSIZE_16 +TABLE_ENTRY := $02d6 +.else +TABLE_ENTRY := BOOT1 +.endif + +TWOS_BUFFER := $0300 ;{addr/86} ;holds the 2-bit chunks +CONV_TAB := $0356 ;{addr/128} ;6+2 conversion table +BOOT1 := $0800 ;{addr/256} ;buffer for next stage of loader +IWM_PH0_OFF := $c080 ;stepper motor control +IWM_PH0_ON := $c081 ;stepper motor control +IWM_MOTOR_ON := $c089 ;starts drive spinning +IWM_SEL_DRIVE_1 := $c08a ;selects drive 1 +IWM_Q6_OFF := $c08c ;read +IWM_Q7_OFF := $c08e ;WP sense/read +MON_WAIT := $fca8 ;delay for (26 + 27*Acc + 5*(Acc*Acc))/2 cycles +PRERR := $ff2d ; 13-sector only +MON_IORTS := $ff58 ;JSR here to find out where one is + +data_ptr := $26 ;{addr/2} ;pointer to BOOT1 data buffer +slot_index := $2b ;{addr/1} ;slot number << 4 +bits := $3c ;{addr/1} ;temp storage for bit manipulation +sector := $3d ;{addr/1} ;sector to read +.ifdef SECSIZE_13 +found_track := $2a ;{addr/1} ;track found +signature := $b5 +.else +found_track := $40 ;{addr/1} ;track found +signature := $96 +.endif +track := $41 ;{addr/1} ;track to read + + .org $c600 +ENTRY: ldx #$20 ;20/00/03 is the controller signature +; +; Generate a decoder table for 6+2 encoded data. +; +; This stores the values $00-$3f in a table on page 3. The byte values that +; will be decoded are non-consecutive, so the decoder entries occupy various +; locations from $36c to $3d5. Nearby bytes are left unchanged. +; +; We want 64 values that have the high bit set and don't have two consecutive 0 +; bits. This is required by the disk hardware. There are 70 possible values, +; so we also mandate that there are two adjacent 1 bits, excluding bit 7. (Note +; that $D5 and $AA, used to identify sector headers, do not meet these criteria, +; which means they never appear in the encoded data.) +; +; In the code below, ASL+BIT+BCS test checks for adjacent 1s: if no two are +; adjacent, the BIT will be zero. If the high bit is set, ASL will set the +; carry. +; +; When we ORA the original and shifted values together, if there were three +; adjacent 0s, there will still be at least two adjacent 0s. We EOR to invert +; the bits, and then look for two adjacent 1s. We do this by just shifting +; right until a 1 shifts into the carry, and if the A-reg is nonzero we know +; there were at least two 1 bits. We need to ignore the bits on the ends: +; nonzero high bit was handled earlier, and the low bit can false-positive +; because ASL always shifts a 0 in (making it look like a 0 in the low bit is +; adjacent to another 0), so we just mask those off with the AND. +; +; For example, we want to decode $A6 to $07. Y=$07 when X=$26... +; TXA --> 0010 0110 +; ASL --> 0100 1100 C=0 (high bit is clear) +; BIT --> Z=0 (only possible with adjacent bits) +; ORA --> 0110 1110 (adjacent 0s become visible) +; EOR --> 1001 0001 (turn them into 1s) +; AND --> 0001 0000 (ignore the hi/lo) +; LSR --> 0000 1000, repeat until A=0 C=1 +; + ldy #$00 +.ifdef SECSIZE_16 + ldx #$03 +CreateDecTabLoop: + stx bits + txa + asl A ;shift left, putting high bit in carry + bit bits ;does shifted version overlap? + beq reject ;no, doesn't have two adjacent 1s + ora bits ;merge + eor #$ff ;invert + and #$7e ;clear hi and lo bits +check_dub0: bcs reject ;initial hi bit set *or* adjacent 0 bits set + lsr A ;shift right, low bit into carry + bne check_dub0 ;if more bits in byte, loop + tya ;we have a winner, store Y to memory + sta CONV_TAB,x ;actual lookup will be on bytes with + ;hi bit set + iny ; so they'll read from CONV_TAB-128 +reject: inx ;try next candidate + bpl CreateDecTabLoop +.else +CreateDecTabLoop: + LDA #$03 + STA bits + CLC + DEY + TYA +LC60B: BIT bits + BEQ CreateDecTabLoop + ROL bits + BCC LC60B + CPY #$D5 ; $D5 is reserved to indicate header + BEQ CreateDecTabLoop + DEX + TXA + STA TABLE_ENTRY,Y + BNE CreateDecTabLoop +.endif +; +; Prep the hardware. +; + jsr MON_IORTS ;known RTS + tsx + lda STACK,x ;pull hi byte of our address off stack +.ifdef SECSIZE_13 + pha +.endif + asl A ;(we assume no interrupts have hit) + asl A ;multiply by 16 + asl A + asl A + sta slot_index ;keep this around + tax +.ifdef SECSIZE_13 + lda #$d0 ; ck fixme + pha +.endif + lda IWM_Q7_OFF,x ;set to read mode + lda IWM_Q6_OFF,x + lda IWM_SEL_DRIVE_1,x ;select drive 1 + lda IWM_MOTOR_ON,x ;spin it up +; +; Blind-seek to track 0. +; + ldy #80 ;80 phases (40 tracks) +seek_loop: lda IWM_PH0_OFF,x ;turn phase N off + tya + and #$03 ;mod the phase number to get 0-3 + asl A ;double it to 0/2/4/6 + ora slot_index ;add in the slot index + tax + lda IWM_PH0_ON,x ;turn on phase 0, 1, 2, or 3 + lda #86 + jsr MON_WAIT ;wait 19664 cycles + dey ;next phase + bpl seek_loop +.ifdef SECSIZE_13 + lda #>TWOS_BUFFER + sta data_ptr+1 ;A-reg is 0 when MON_WAIT returns + lda #BOOT1 ;write the output here + sta data_ptr+1 +.endif +; +; Sector read routine. +; +; Read bytes until we find an address header (D5 AA 96) or data header (D5 AA +; AD), depending on which mode we're in. +; +; This will also be called by the BOOT1 code read from the floppy disk. +; +; On entry: +; X: slot * 16 +; $26-27: data pointer +; $3d: desired sector +; $41: desired track +; +ReadSector: clc ;C=0 to look for addr (C=1 for data) +ReadSector_C: php +@rdbyte1: lda IWM_Q6_OFF,x ;wait for byte + bpl @rdbyte1 ;not yet, loop +@check_d5: eor #$d5 ;is it $d5? + bne @rdbyte1 ;no, keep looking +@rdbyte2: lda IWM_Q6_OFF,x ;grab another byte + bpl @rdbyte2 + cmp #$aa ;is it $aa? + bne @check_d5 ;no, check if it's another $d5 + nop ;(?) +@rdbyte3: lda IWM_Q6_OFF,x ;grab a third byte + bpl @rdbyte3 + cmp #signature ;is it $96? + beq FoundAddress ;winner + plp ;did we want data? + bcc ReadSector ;nope, keep looking + eor #$ad ;yes, see if it's data prologue + beq FoundData ;got it, read the data (note A-reg = 0) + bne ReadSector ;keep looking + +; +; Read the sector address data. Four fields, in 4+4 encoding: volume, track, +; sector, checksum. +; +FoundAddress: ldy #$03 ;sector # is the 3rd item in header +.ifdef SECSIZE_13 +hdr_loop: sty found_track ;store $96, then volume, then track +.else +hdr_loop: sta found_track ;store $96, then volume, then track +.endif +@rdbyte1: lda IWM_Q6_OFF,x ;read first part + bpl @rdbyte1 + rol A ;first byte has bits 7/5/3/1 + sta bits +@rdbyte2: lda IWM_Q6_OFF,x ;read second part + bpl @rdbyte2 + and bits ;merge them + dey ;is this the 3rd item? +.ifdef SECSIZE_13 + bne @rdbyte1 ;nope, keep going +.else + bne hdr_loop ;nope, keep going +.endif + plp ;pull this off to keep stack in balance + cmp sector ;is this the sector we want? + bne ReadSector ;no, go back to looking for addresses +.ifdef SECSIZE_16 + lda found_track + cmp track ;correct track? + bne ReadSector ;no, try again +.endif + bcs ReadSector_C ;correct T/S, find data (branch-always) + +; +; Read the 6+2 encoded sector data. +; +; Values range from $96 - $ff. They must have the high bit set, and must not +; have three consecutive zeroes. +; +; The data bytes are written to disk with a rolling XOR to compute a checksum, +; so we read them back the same way. We keep this in the A-reg for the +; duration. The actual value is always in the range [$00,$3f] (6 bits). +; +; On entry: +; A: $00 +; +FoundData: +.ifdef SECSIZE_13 + ldy #$9a +.else + ldy #86 ;read 86 bytes of data into $300-355 +.endif +read_twos_loop: + sty bits ;each byte has 3 sets of 2 bits, encoded +@rdbyte1: ldy IWM_Q6_OFF,x + bpl @rdbyte1 + eor TABLE_ENTRY,y ;$02d6 + $96 = $36c, first table entry + ldy bits + dey +.ifdef SECSIZE_13 + sta TABLE_ENTRY,y +.else + sta TWOS_BUFFER,y ;store these in our page 3 buffer +.endif + bne read_twos_loop +; +read_sixes_loop: + sty bits ;read 256 bytes of data into $800 +@rdbyte2: ldy IWM_Q6_OFF,x ;each byte has the high 6 bits, encoded + bpl @rdbyte2 +.ifdef SECSIZE_13 + eor TABLE_ENTRY,y +.else + eor CONV_TAB-128,y +.endif + ldy bits + sta (data_ptr),y ;store these in the eventual data buffer + iny + bne read_sixes_loop +; +@rdbyte3: ldy IWM_Q6_OFF,x ;read checksum byte + bpl @rdbyte3 +.ifdef SECSIZE_13 + eor TABLE_ENTRY,y ;does it match? +.else + eor CONV_TAB-128,y ;does it match? +.endif +another: bne ReadSector ;no, try to find one that's undamaged +.ifdef SECSIZE_13 +GRP := $33 + RTS + +DENIB: TAY +LC6D2: LDX #0 +LC6D4: LDA BOOT1,Y + LSR + ROL TWOS_BUFFER+4*GRP,X + LSR + ROL TWOS_BUFFER+3*GRP,X + STA bits + LDA (data_ptr),Y + ASL + ASL + ASL + ORA bits + STA (data_ptr),Y + INY + INX + CPX #GRP + BNE LC6D4 + DEC found_track + BNE LC6D2 + CPY TWOS_BUFFER + BNE ERROR + JMP TWOS_BUFFER+1 +ERROR: JMP PRERR + + .byte $FF +.else +; +; Decode the 6+2 encoding. The high 6 bits of each byte are in place, now we +; just need to shift the low 2 bits of each in. +; + ldy #$00 ;update 256 bytes +init_x: ldx #86 ;run through the 2-bit pieces 3x (86*3=258) +decode_loop: dex + bmi init_x ;if we hit $2ff, go back to $355 + lda (data_ptr),y ;foreach byte in the data buffer... + lsr TWOS_BUFFER,x ; grab the low two bits from the stuff + ; at $300-$355 + rol A ; and roll them into the low two bits + ; of the byte + lsr TWOS_BUFFER,x + rol A + sta (data_ptr),y + iny + bne decode_loop +; +; Advance the data pointer and sector number, and check to see if the sector +; number matches the first byte of BOOT1. If it does, we're done. If not, go +; read the next sector. +; + inc data_ptr+1 + inc sector + lda sector ;sector we'd read next + cmp BOOT1 ;is next sector < BOOT1? + ldx slot_index + bcc another ;yes, go get another sector + ;(note branch x2) +; All done, jump to BOOT1 ($0801). + jmp BOOT1+1 + + .byte 0, 0, 0, 0, 0 ;spare bytes +.endif diff --git a/firmware/0300.s b/firmware/legend.s similarity index 91% rename from firmware/0300.s rename to firmware/legend.s index ff17860..58303f1 100644 --- a/firmware/0300.s +++ b/firmware/legend.s @@ -1,11 +1,11 @@ ; Micro-SCI Floppy Drive Controller info disassembly -PRINTVEC = $08 -PRBL2 = $F94A -HOME = $FC58 -COUT = $FDED +PRINTVEC := $08 +PRBL2 := $F94A +HOME := $FC58 +COUT := $FDED -* = $c600 +.org $c600 ldx #$20 ; controller signature ldy #$00 @@ -19,20 +19,20 @@ COUT = $FDED lda #0 sta PRINTVEC ldy # ; @@ -52,7 +52,7 @@ info .byte $8d, $bc, $ad, $ad, $cb, $c5, $d9, $c2 .byte $ad, $be, $8d, $8d, $08, $ad, $ad, $ad .byte $ad, $ad, $ad, $ad, $ad, $ad, $ca, $d5 .byte $cd, $d0, $c5, $d2, $ad, $ad -#else +.else ; ROM checksum 5dfc32ca ; <--KEYBOARD ACE 1000 REAR--> ; @@ -72,7 +72,7 @@ info .byte $8d, $bc, $ad, $ad, $cb, $c5, $d9, $c2 .byte $c1, $d2, $ad, $ad, $be, $8d, $8d, $07 .byte $a0, $02, $ad, $ad, $ad, $ad, $ad, $ad .byte $ca, $d5, $cd, $d0, $c5, $d2 -#endif +.endif .byte $ad, $ad .byte $ad, $ad, $ad, $ad, $8d, $07, $a1, $0b .byte $a1, $0b, $a1, $8d, $07, $d6, $0b, $d6 diff --git a/firmware/0000 b/firmware/reference/0000 similarity index 100% rename from firmware/0000 rename to firmware/reference/0000 diff --git a/firmware/0100 b/firmware/reference/0100 similarity index 100% rename from firmware/0100 rename to firmware/reference/0100 diff --git a/firmware/0200 b/firmware/reference/0200 similarity index 100% rename from firmware/0200 rename to firmware/reference/0200 diff --git a/firmware/attic/0300-5ddcbbf1 b/firmware/reference/0300-5ddcbbf1 similarity index 100% rename from firmware/attic/0300-5ddcbbf1 rename to firmware/reference/0300-5ddcbbf1 diff --git a/firmware/attic/0300-5dfc32ca b/firmware/reference/0300-5dfc32ca similarity index 100% rename from firmware/attic/0300-5dfc32ca rename to firmware/reference/0300-5dfc32ca diff --git a/firmware/0300-franklin b/firmware/reference/0300-franklin similarity index 100% rename from firmware/0300-franklin rename to firmware/reference/0300-franklin diff --git a/firmware/0300-usci b/firmware/reference/0300-usci similarity index 100% rename from firmware/0300-usci rename to firmware/reference/0300-usci diff --git a/firmware/attic/5ddcbbf1.bin b/firmware/reference/5ddcbbf1.bin similarity index 100% rename from firmware/attic/5ddcbbf1.bin rename to firmware/reference/5ddcbbf1.bin diff --git a/firmware/attic/5dfc32ca.bin b/firmware/reference/5dfc32ca.bin similarity index 100% rename from firmware/attic/5dfc32ca.bin rename to firmware/reference/5dfc32ca.bin diff --git a/firmware/franklin-2732.bin b/firmware/reference/franklin-2732.bin similarity index 100% rename from firmware/franklin-2732.bin rename to firmware/reference/franklin-2732.bin diff --git a/firmware/franklin.bin b/firmware/reference/franklin.bin similarity index 100% rename from firmware/franklin.bin rename to firmware/reference/franklin.bin diff --git a/firmware/franklin_fdc_rom_notes.txt b/firmware/reference/franklin_fdc_rom_notes.txt similarity index 100% rename from firmware/franklin_fdc_rom_notes.txt rename to firmware/reference/franklin_fdc_rom_notes.txt diff --git a/firmware/png/franklin.png b/firmware/reference/png/franklin.png similarity index 100% rename from firmware/png/franklin.png rename to firmware/reference/png/franklin.png diff --git a/firmware/png/u-sci.png b/firmware/reference/png/u-sci.png similarity index 100% rename from firmware/png/u-sci.png rename to firmware/reference/png/u-sci.png diff --git a/firmware/sums b/firmware/sums new file mode 100644 index 0000000..ad7f683 --- /dev/null +++ b/firmware/sums @@ -0,0 +1,9 @@ +# md5sums of original images, compare with *.bin generated here + +4f80448507cf43ab40c17ac08d89e278 13.bin +2020aa1413ff77fe29353f3ee72dc295 16.bin +eb8628274eb1a3601723023d3ffe7ef3 firmware-franklin.bin +94fe3d85c9232b53f28a1c6a2a9c9764 firmware-usci.bin +b2e7d74cdf9b43ee8ae3036b1f21cbd5 legend-franklin.bin +105927e7c409116974a45e366beab55b legend-usci.bin +6681f504c6cc3b6887fef51726c96ca1 util.bin diff --git a/firmware/usci.bin b/firmware/usci.bin deleted file mode 100644 index 8c174f8..0000000 Binary files a/firmware/usci.bin and /dev/null differ diff --git a/firmware/0000.s-wip b/firmware/util.s similarity index 60% rename from firmware/0000.s-wip rename to firmware/util.s index 40ac202..7561e5a 100644 --- a/firmware/0000.s-wip +++ b/firmware/util.s @@ -5,77 +5,87 @@ .setcpu "6502" + .org $c600 -MON_WAIT = $FCA8 -GETLNZ = $FD67 -BELL = $FF3A -IORTS = $FF58 -GETNUM = $FFA7 +A2L := $3e +MON_WAIT := $FCA8 +GETLNZ := $FD67 +BELL := $FF3A +IORTS := $FF58 +GETNUM := $FFA7 -* = $c600 - lda #$A3 - sta $33 + lda #$A3 ; '#' + sta $33 ; set input-prompt character to '#' LC604: jsr BELL -LC607: jsr GETLNZ +LC607: jsr GETLNZ ; start new line and take input lda #$00 sta $F2 -LC60E: sta $C010 +LC60E: sta $C010 ; clear keyboard strobe sta $F3 ldy $F2 - jsr GETNUM + jsr GETNUM ; parse hex into A2, A has last char xor $b0 + ; plus $89 sty $F2 - cmp #$C6 - beq LC607 - cmp #$EC - beq LC64E - cmp #$F0 - beq LC676 - cmp #$F3 - beq LC657 + cmp #$C6 ; $C6 from $8D (return) + beq LC607 ; no more input, go prompt for new input + cmp #$EC ; 'S' + beq CMD_S + cmp #$F0 ; 'W' + beq CMD_W + cmp #$F3 ; 'Z' + beq CMD_Z ldy #$7F - cmp #$06 - beq LC67C - cmp #$EB - beq LC641 + cmp #$06 ; 'M' + beq CMD_MX + cmp #$EB ; 'R' + beq CMD_R ldx #$06 stx $F3 - cmp #$F1 - beq LC67C - cmp #$EA - bne LC604 - brk -LC641: lda #$50 + cmp #$F1 ; 'X' + beq CMD_MX + cmp #$EA ; 'Q' + bne LC604 ; BEEP and prompt for new input + brk ; 'Q' exits out + +; 'R' +CMD_R: lda #$50 sta $FC txa adc #$00 sta $F3 lda #$00 beq LC651 -LC64E: lda $3E + +; 'S': seek logical-track index in A2L ('22S' seeks logical track $22) +CMD_S: lda A2L asl a LC651: sta $F0 ldy #$FF - bne LC67C -LC657: lda $3E + bne CMD_MX + +; 'Z' +CMD_Z: lda A2L asl a sta $F1 ldy #$00 - beq LC67C + beq CMD_MX LC660: lda #$50 jsr MON_WAIT sta $C088,x - ldy $3E + ldy A2L LC66A: jsr MON_WAIT dey bpl LC66A bmi LC68E LC672: lda #$00 beq LC60E -LC676: lda $3E + +; 'W' +CMD_W: lda A2L sta $FF ldy #$0F -LC67C: sty $09 +CMD_MX: sty $09 jsr IORTS tsx lda $0100,x