.feature labels_without_colons .feature leading_dot_in_identifiers .feature c_comments .P02 ; normal 6502 .macro ADR val .addr val .endmacro .macro BLT val BCC val .endmacro .macro BGE val BCS val .endmacro ; Force APPLE 'text' to have high bit on; Will display as NORMAL characters .macro ASC text .repeat .strlen(text), I .byte .strat(text, I) | $80 .endrep .endmacro .struct iob_t type .byte ; table type (always $01) slot .byte ; slot (<<4) drive .byte ; drive (1/2) volume .byte ; volume ($00 = all) track .byte sector .byte dct .word ; low/hi pointer to a DCT data .word ; low/hi pointer to sector data unused .word bytes .word ; # bytes to read/write ($00 == 256) command .byte ; 0=seek, 1=read, 2=write, 4=fmt retval .byte ; return code retvol .byte ; return volume retslot .byte ; return slot retdrive .byte ; return drive .endstruct a_cr = $0d ; Carriage return. RWTS_GETIOB = $03E3 ; low returned in Y, high A ($B7E8 is the normal IOB) RWTS = $03D9 ; needs IOB address in Y/A DOS33_IOB = $B7E8 IOB = DOS33_IOB DOS33_SECBUF = $B4BB ; $B4BB-B5BA RWTS_WRTDIR = $B037 ; hidden routine in DOS to call RWTS (cf. AAL Vol2, iss 8) RWTS_WRTCMD = $B041 ; ... which wants to call write by default and we modify it to do something else F8ROM_YXHEX = $F940 F8ROM_AXHEX = $F941 F8ROM_XHEX = $F944 ; print X register in hex -- kills X,A F8ROM_AHEX = $FDDA ; print A register in hex F8ROM_INIT = $FB2F F8ROM_HOME = $FC58 ; Kills A,Y F8ROM_CROUT = $FC62 ; carriage return out F8ROM_MONWAIT = $FCA8 ; wait for (A*A*2.5 + A*13.5 + 7) * 0.980 usec F8ROM_COUT = $FDED ; Load A with character to print F8ROM_RDKEY = $FD0C WRVEC = $03D0 ; warm re-entry point PHSOFF = $C080 PHSON = $C081 DISKOFF = $C088 DISKON = $C089 DRIVEA = $C08A DISK_LATCHR = $C08C DISK_LATCHW = $C08D DISK_MODER = $C08E DISK_MODEW = $C08F ;; Zero-page addresses used ;; Totally free and clear: 6,7,8,9; eb,ec,ed,ee,ef;fa,fb,fc,fd ZP_STORPTR = $06 ;06,07 ZP_STOR2 = $08 ;08,09 ZP_SCRATCH = $eb ;just eb ZP_TRANSP = $ec ;ec,ed DST = $ee ;ee,ef ZP_SECTP = $fa ;fa,fb ZP_PRINT = $fc ;fc, fd .segment "CODE" START JMP Entry Entry PHP ;save interrupt state SEI ;disable interrupts JSR F8ROM_INIT JSR F8ROM_HOME JSR Prtmsg ASC "Insert a blank floppy in s6d1 and press a key..." .byte $8D, $00 JSR F8ROM_RDKEY ; wait for a keypress ;; set up pointers to SECDATA and TRANS62 LDA #>SECDATA STA ZP_SECTP+1 LDA #TRANS62 ; set up ZP_TRANSP to point at TRANS62 STA ZP_TRANSP+1 ; (a 64-byte lookup table) LDA #NYBDATA STA DST+1 LDA #SECDATA JSR F8ROM_AHEX LDA #NYBDATA JSR F8ROM_AHEX LDA #NYBDATA ; set up ZP_STORPTR to the start of NYBDATA STA ZP_STORPTR+1 LDA #NYBDATA STA ZP_STORPTR+1 ; high address LDA # 0x100 INY ; bytes STY ZP_STOR2+1 ;; Clear output buffer 0x157 bytes LDY #$00 LDA #$00 STA CKSUM ; convenient place to clear the checksum for later @ClearLoop STA (ZP_STORPTR),Y CPY #$57 ; also clear at +0x100 if < 0x157 BLT @AlsoClearHigh ;Y < #$57? Branch @ClearNext INY BEQ @ClearDone ; if Y became 0, we rolled over and are done JMP @ClearLoop @AlsoClearHigh STA (ZP_STOR2),Y JMP @ClearNext @ClearDone ;; Work through the 256 bytes of data to construct the 6and2 data, ;; with ZP_SECTP as the input and ZP_STORPTR as the output ;; LDA #$55 STA IDX2 ;; for (idx6 = 0x0101; idx6 >= 0; idx6--) LDA #$01 STA IDX6 STA IDX6+1 ; IDX6 = 0x0101 @WorkLoopA LDY IDX6 ; val6 = input[idx6 & 0xFF] LDA (ZP_SECTP),Y STA VAL6 LDY IDX2 ; val2 = output[idx2]; LDA (ZP_STORPTR),Y STA VAL2 ;; val2 = (val2 << 1) | (val6 & 1); val6 >>= 1; LSR VAL6+1 ROR VAL6 ; val6 >>= 1, and C = old (val6&1) LDA VAL2 ROL ; A = (val2 << 1) | C STA VAL2 ; val2 = all that jazz ;; another round of the same LSR VAL6+1 ROR VAL6 ; val6 >>= 1, and C = old (val6&1) LDA VAL2 ROL ; A = (val2 << 1) | C STA VAL2 ; val2 = all that jazz LDY IDX2 ; output[idx2] = val2 LDA VAL2 STA (ZP_STORPTR),Y ;; if (idx6 < 0x100) { output[0x56+idx6] = val6; } LDA IDX6+1 CMP #01 BEQ @decidx2 LDA #$56 CLC ADC IDX6 BCC @storeLT100 ;; we need to store in ZP_STOR2 (the result overflowed) TAY ; Y = idx6 + 0x56 and is >= 0x100 LDA VAL6 STA (ZP_STOR2),Y JMP @decidx2 @storeLT100 TAY ; Y = idx6 + 0x56 and is < 0x100 LDA VAL6 STA (ZP_STORPTR),Y JMP @decidx2 @decidx2 ;; if (--idx2 < 0) { idx2 = 0x55; } ;; IDX2 never exceeds $55, so we can test using DEC and BMI/BPL DEC IDX2 BPL @dontresetidx2 ;; ... high bit is set, so we underflowed; reset IDX2 back to $55 LDA #$55 STA IDX2 @dontresetidx2 ;; End of WorkLoopA: 16-bit decrement idx6, and loop to @WorkLoopA ;; if it is >= 0 LDA IDX6 ; sets Z if it's zero BNE @simpledeclo ; if not zero, just decrement and continue LDA IDX6+1 ; low was zero, so repeat w/ high BEQ @DoneWorkloopA ; if high is also zero we're done DEC IDX6+1 @simpledeclo DEC IDX6 ;; continue loop JMP @WorkLoopA ;; Both IDX6 and IDX6+1 reached 0, so we are done the loop @DoneWorkloopA ;; Mask out the "extra" 2-bit data: ;; output[0x54] &= 0x0F; output[0x55] &= 0x0F; LDY #$54 LDA (ZP_STORPTR),Y AND #$0F STA (ZP_STORPTR),Y INY AND #$0F STA (ZP_STORPTR),Y ;; Loop over the data one more time to construct the actual output ;; and compute the checksum ;; Checksum is initialized to 0 above ;; for (int idx6=0; idx6<0x156; idx6++) LDA #$00 STA IDX6+1 STA IDX6 @WorkLoopB LDY IDX6 ; val = output[idx] LDA IDX6+1 EOR #$01 BEQ @LoadFromHi LDA (ZP_STORPTR),Y JMP @c @LoadFromHi LDA (ZP_STOR2),Y @c STA ZP_SCRATCH ;; output[idx] = _trans[cksum^val] LDA CKSUM ; Y = cksum ^ val EOR ZP_SCRATCH TAY LDA IDX6+1 EOR #$01 ; to set C for BEQ coming up BEQ @StoreToHi ; branch based on EOR (if IDX6+1 == 1) LDA (ZP_TRANSP),Y ; A = trans[cksum^val] LDY IDX6 ; restore Y STA (ZP_STORPTR),Y JMP @d @StoreToHi LDA (ZP_TRANSP),Y ; A = trans[cksum^val] LDY IDX6 ; restore Y STA (ZP_STOR2),Y @d ;; cksum = val; LDA ZP_SCRATCH STA CKSUM ;; Complete for loop: idx6++ and loop if idx6 < 0x156 INC IDX6 BEQ @incHigh @f LDA IDX6+1 EOR #$01 BNE @WorkLoopB ; high byte isn't set yet, so continue looping LDA IDX6 CMP #$56 BGE @setCksum ; high byte set, and low >= 0x56 - done loop JMP @WorkLoopB @incHigh INC IDX6+1 JMP @f @setCksum ;; output[342] = _trans[cksum] LDY CKSUM ; A = _trans[cksum] LDA (ZP_TRANSP),Y LDY #$56 STA (ZP_STOR2),Y ; output[0x100 + Y] = A ;; Add 0x157 to ZP_STORPTR (number of bytes we added to the buffer) INC ZP_STORPTR+1 ; add 0x100 LDA ZP_STORPTR CLC ADC #$57 BCC @nocarry INC ZP_STORPTR+1 @nocarry STA ZP_STORPTR @done RTS ;; set CNT, CNT+1, DST, DST+1, VALUE before calling memset @a LDY #0 LDA CNT ORA CNT+1 BEQ @fin LDA VALUE STA (DST),y INC DST BNE @b INC DST+1 @b DEC CNT LDA CNT CMP #$FF BNE @c DEC CNT+1 @c SEC BCS @a @fin RTS ;; Seccmp: compare SECDATA and DOS33_SECBUF (256 bytes) ;; return with carry clear if compare is ok; set if differences are ;; found Seccmp LDA #>SECDATA STA ZP_STORPTR+1 LDA #DOS33_SECBUF STA ZP_STOR2+1 LDA #