diff --git a/Makefile b/Makefile index 4f7e70d..8eae27c 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ include local.config # Local stuff: +SRC = afscanner.s diskii.s page-about.s page-browse.s page-count.s page-field.s page-graphics.s page-test.s page.s util.s PGM = afscanner SYS = afscannr.system TYPE = SYS @@ -10,7 +11,7 @@ TMPL = template.po DISK = afdisk.po -afscanner: afscanner.s +afscanner: $(SRC) $(ASM) $(PGM).s cp $(TMPL) $(DISK) cat $(PGM) | $(AC) -p $(DISK) $(SYS) $(TYPE) $(ADDR) diff --git a/afscanner.s b/afscanner.s index 6edf688..34796ca 100755 --- a/afscanner.s +++ b/afscanner.s @@ -8,7 +8,8 @@ * 1/16/2016: Version 1.0 * * 1/29/2016: Version 2.0 -* - Restructuring to support paging and scrollong. +* - Restructuring to support paging and scrolling. +* - Split source into multiple files as one file finally got way too big. * - Slowly switching from spaces to tabs as an experiment in code formatting. * - Pages available: * + About (default page) @@ -118,6 +119,8 @@ GETCH = $FD0C PRCR = $FD8E PRHEX = $FDDA COUT = $FDED +MONITOR = $FF69 +USRADR = $3F8 * I/O addresses: @@ -125,16 +128,6 @@ KEYBOARD = $C000 KEYCLEAR = $C010 OpenApple = $C061 -* Disk II addresses: - -PHASEOFF = $C080 -PHASEON = $C081 -MOTOROFF = $C088 -MOTORON = $C089 -DRV0EN = $C08A -Q6L = $C08C -Q7L = $C08E - * Relocate application above HGR and HGR2 page org OriginAddress @@ -163,7 +156,14 @@ OriginEnd ; Marker for code relocation org ProgramAddress -MAIN JSR $C300 ; Assuming 80 columns +MAIN lda #$4C ; JMP + sta USRADR + lda #ProgramAddress + sta USRADR+2 + + JSR $C300 ; Assuming 80 columns JSR PRINT DFB _CLS ASC "AFScanner",$8D @@ -189,1045 +189,18 @@ Initialize stz DSTTRK lda #"A" ; default screen is "About" page jsr SetScreen - -SetupPage - jsr TEXT ; ensure we are out of graphics modes as application has some! - jsr PRINT ; position cursor on bottom line - dfb _HOME - dfb 23,$8D - dfb _CLREOL - dfb $00 - ldx #_init ; vector offset - jsr _VCALL - ror scrolling ; set flag based on C - sty maxlines - sta maxlines+1 - stz topline - stz topline+1 - -DrawPage - jsr PRINT ; Clear and then draw the content area - dfb _HOME,$8d,$8d,0 -; If we aren't scrolling, call _display vector ONCE and then handle keyboard. - bit scrolling - bpl :scroll - lda #0 - tay - ldx #_display - jsr _VCALL - jmp KeyboardWait + ; Falls through to SetupPage in page.s -:scroll lda #0 -:loop pha - clc - adc topline - sta printline - lda topline+1 - adc #0 - sta printline+1 - cmp maxlines+1 - blt :drwlin - lda printline - cmp maxlines - bge :erase - -:drwlin lda printline+1 - ldy printline - ldx #_display - jsr _VCALL - bra :incr - -:erase jsr PRINT - dfb _CLREOL,$8D,0 - -:incr pla - inc - cmp #PAGELEN - blt :loop - -KeyboardWait - lda KEYBOARD - bpl KeyboardWait -; Uppercase the if it's A-Z - cmp #"a" - blt :goodky - cmp #"z"+1 - bge :goodky - and #$df ; uppercase mask -:goodky sta KEYCLEAR - bit OpenApple - bpl :normal -; OpenApple handler - jsr SetScreen - bcs :paging - jsr CLRSCRN - jmp SetupPage -; OA-Up -:paging cmp #UpArrow - bne :nPgUp - ldy #15 -:uploop jsr :up1 - dey - bne :uploop - beq :back ; always -; OA-Down -:nPgUp cmp #DownArrow - bne :chkOAQ - ldy #15 -:dnloop jsr :down1 - dey - bne :dnloop - beq :back ; always -; OA-Q -:chkOAQ cmp #"Q" - bne :back - jsr PRODOSMLI - dfb _MLIQUIT - da QUITPARM -:back jmp DrawPage ; fall through and common return - -; Common keypress handler -; Up -:normal cmp #UpArrow - bne :notUp - jsr :up1 - jmp DrawPage -; Down -:notUp cmp #DownArrow - bne :pgKey - jsr :down1 - jmp DrawPage - -; "Local" subroutines - -; if topline+PAGELEN >= maxlines then return -; topline = topline + 1 -:down1 clc - lda topline - adc #PAGELEN - sta printline - lda printline+1 - adc #0 - sta printline+1 - cmp maxlines+1 - bcc :minus1 - lda printline - cmp maxlines - bge :rts -:minus1 inc topline - bne :rts - inc topline+1 - rts -; if topline = 0 then return -; topline = topline - 1 -:up1 lda topline - ora topline+1 - beq :rts ; already = 0 - sec - lda topline - sbc #1 - sta topline - lda topline+1 - sbc #0 - sta topline+1 -:rts rts - -:pgKey ldx #_keypress - ; Fall through and JMP to _keypress which takes control for local page keys - -* Simple vector call from ZP based on X register - -_VCALL jmp: ($00,x) ; Merlin32 needs to know 16 bit JMP - -* Handle screen change - both called by app init and normal keyboard handler - -SetScreen - ldx #0 -:next bit :data,x - bpl :notf - cmp :data,x - beq :setup - pha - txa - clc - adc #7 - tax - pla - bra :next -:notf sec ; Not found - rts -:setup ldy #0 -:copy inx - lda :data,x - sta _init,y - iny - cpy #6 - bne :copy - clc - rts - -* -* Data per page: Apple ASCII, Initialization, Display, Keypress (7 bytes) -* -* Definitions: -* - BYTE = Character to match on, case insensitive -* - ADDR = Initialize routine (load track, calculate lookup tables, etc). -* Return: -* A:Y for number of lines, -* Carry: Set = no scrolling (graphics, form, etc), Clear = scrolling. -* Note: Initialization should also display 24th line for the page. -* (Cursor is there and line is clear.) -* - ADDR = Display routine. Called with A:Y for line number. Should end with $8D and is -* responsible for clearing the line. -* - ADDR = Keypress handler. NOT A SUBROUTINE. This routine has a number of entry -* points it might call. KeyboardWait for another keypress or SetupPage to -* reinitialize and redraw. -* -:data - dfb "A" - da AboutInit, AboutDisplay, AboutKeypress - dfb "F" - da FieldInit, FieldDisplay, FieldKeypress - dfb "B" - da BrowseInit, BrowseDisplay, BrowseKeypress - dfb "G" - da HgrInit, HgrDisplay, HgrKeypress - dfb "C" - da CountInit, CountDisplay, CountKeypress - dfb "T" - da TestInit, TestDisplay, TestKeypress - dfb 0 ; end - -* -* ABOUT page interface -* - -* Build array of line pointers in INBUF. -AboutInit ; Returns with A:Y = max lines, C = scrolling - jsr PRINT - asc _INVERSE,_MT_ON,_U_ARROW,_D_ARROW,_MT_OFF,_NORMAL," Scroll / " - asc _INVERSE,_MT_ON,_O_APPLE,_NORMAL,"-",_INVERSE,_U_ARROW,_D_ARROW,_MT_OFF,_NORMAL," Page" - dfb 0 - - lda #<:lines - sta PTR - lda #>:lines - sta PTR+1 - ldy #0 ; max lines - ldx #0 ; buffer offset + put page + put page-about + put page-field + put page-browse + put page-graphics + put page-count + put page-test -:save lda PTR - sta inbuf,x - inx - lda PTR+1 - sta inbuf,x - inx - iny - -:loop lda (PTR) - bne :incr - dey ; we're one past... - lda #0 ; high byte of max lines; Y = low byte - clc ; we need scrolling - rts - -:incr lda (PTR) - pha - inc PTR - bne :skip - inc PTR+1 -:skip pla - beq :save - bne :incr - -* Guide 00000000011111111112222222222333333333344444444445555555555666666666677777777778 -* 12345678901234567890123456789012345678901234567890123456789012345678901234567890 -* | + + + + + + + +| -:lines asc _INVERSE," Address Field Scanner ",_NORMAL,$00 - asc "Version 2.0beta",$00 - asc " ",$00 - asc "This application is my lame attempt to understand the old Apple Disk II",$00 - asc "interface.",$00 - asc " ",$00 - asc "As of Version 2 of AFSCANNER, multiple pages have been introduced to better",$00 - asc "review the contents of a disk buffer and analyze a disk.",$00 - asc " ",$00 - asc "Use the ",_INVERSE,_MT_ON,_O_APPLE,_MT_OFF,_NORMAL,"-key combinations to toggle pages.",$00 - asc " ",$00 - asc _INVERSE,_MT_ON,_O_APPLE,_MT_OFF,_NORMAL,"A = This about page.",$00 - asc _INVERSE,_MT_ON,_O_APPLE,_MT_OFF,_NORMAL,"B = Browse track buffer.",$00 - asc _INVERSE,_MT_ON,_O_APPLE,_MT_OFF,_NORMAL,"C = Display buffer counts.",$00 - asc _INVERSE,_MT_ON,_O_APPLE,_MT_OFF,_NORMAL,"F = Address field display. " ; (cont) - asc "(Assuming 'good' address fields on disk.)",$00 - asc _INVERSE,_MT_ON,_O_APPLE,_MT_OFF,_NORMAL,"G = Graphical disk display.",$00 - asc " ",$00 - asc "Source available at https://github.com/a2geek/afscanner",$00 - asc " ",$00 - asc "Global Keys",$00 - asc "===========",$00 - asc " ",$00 - asc _INVERSE,_MT_ON,_D_ARROW,_MT_OFF,_NORMAL," Scroll down 1 line",$00 - asc _INVERSE,_MT_ON,_U_ARROW,_MT_OFF,_NORMAL," Scroll up 1 line",$00 - asc _INVERSE,_MT_ON,_O_APPLE,_D_ARROW,_MT_OFF,_NORMAL," Page down 15 lines",$00 - asc _INVERSE,_MT_ON,_O_APPLE,_U_ARROW,_MT_OFF,_NORMAL," Page up 15 lines",$00 - asc _INVERSE,_MT_ON,_O_APPLE,_MT_OFF,_NORMAL,"Q Quit to ProDOS",$00 - asc " ",$00 - asc "Address Field Display",$00 - asc "=====================",$00 - asc " ",$00 - asc "Display the list of Address Fields, the disk bytes, as well as the decoded",$00 - asc "values to indicate what sectors are phyiscally present and their order on disk.",$00 - asc "Note that the buffer is $2000 (8192) bytes long and some sectors will be",$00 - asc "repeated.",$00 - asc " ",$00 - asc "Headers are currently set to " - dfb 1 - da PROLOGUE - dfb " ",1 - da PROLOGUE+1 - dfb " ",1 - da PROLOGUE+2 - dfb $00 - asc " ",$00 - asc "Browse Track Buffer",$00 - asc "===================",$00 - asc " ",$00 - asc "Browse the raw track buffer. Header fields are hilighted. Note that the ",$00 - asc "buffer is $2000 (8192) bytes long and some sectors will be repeated.",$00 - asc " ",$00 - asc "Headers are currently set to " - dfb 1 - da PROLOGUE - dfb " ",1 - da PROLOGUE+1 - dfb " ",1 - da PROLOGUE+2 - dfb $00 - asc " ",$00 - asc "Graphical Disk Display",$00 - asc "======================",$00 - asc " ",$00 - asc "Scans an entire disk and graphically displays where sync ($FF) bytes appear on",$00 - asc "disk. Note that the length of each bar indicates how many sync bytes were in",$00 - asc "that section of the disk. Each bar represents approximately 46 bytes.",$00 - asc " ",$00 - asc "Display Buffer Counts",$00 - asc "=====================",$00 - asc " ",$00 - asc "Count the number of disk bytes in the buffer and display totals. This may be",$00 - asc "helpful when trying to determine address field bytes.",$00 - asc " ",$00 - asc "-END-",$00 - dfb 0 - -AboutDisplay ; Called with A:Y = line - phy ; assuming < 256 lines - lda #_CLREOL - jsr COUT - pla - asl - tay - lda inbuf,y - tax - lda inbuf+1,y - jsr PRINTP - jmp PRCR - -AboutKeypress ; Called with Acc = key - ; Do Nothing, continue loop - jmp KeyboardWait - -* -* FIELD Interface -* - -FieldInit ; Returns with A:Y = max lines - jsr PRINT - asc _INVERSE,_MT_ON,_U_ARROW,_D_ARROW,_MT_OFF,_NORMAL," Scroll / " - asc _INVERSE,_MT_ON,_O_APPLE,_NORMAL,"-",_INVERSE,_U_ARROW,_D_ARROW,_MT_OFF,_NORMAL," Page / " - asc _MT_ON,_INVERSE,_L_ARROW,_R_ARROW,_NORMAL,_MT_OFF," Track / " - asc "re",_INVERSE,"S",_NORMAL,"can / " - asc _INVERSE,"R",_NORMAL,"ecalibrate / " - asc "goto ",_INVERSE,"T",_NORMAL,"rack" - dfb 0 - -* Scan for our prologue and save positions in INBUF - - jsr ReadTrack ; position head and fully populate buffer - ldx #0 ; index for INBUF - jsr SETUPDATA -:scan ldy #0 ; index for comparisons -:loop lda (DATA),y - cmp PROLOGUE,Y - bne :adv - iny - cpy #3 - bcc :loop -; We found prologue bytes, save DATA address - lda DATA - sta inbuf,x - inx - lda DATA+1 - sta inbuf,x - inx -:adv inc DATA - bne :scan - inc DATA+1 - dec TEMP - bne :scan -; Calculate # of lines - txa - lsr - tay - lda #0 ; Assumed < 256 lines - clc ; we need scrolling - rts - -FieldDisplay ; Called with A:Y = line - tya ; Assuming < 256 lines - asl - tay - lda inbuf,y - sta DATA - pha - lda inbuf+1,y - sta DATA+1 - pha -; Display offset - lda #"+" - jsr COUT - pla - and #$3f - jsr PRHEX - pla - jsr PRHEX - jsr PRINT - asc "- ",$00 -; Display 'disk' bytes - ldy #0 - ldx #0 -:nextg lda :groups,x - sta TEMP -:bytes lda (DATA),y - jsr PRHEX - iny - dec TEMP - bne :bytes - lda #" " - jsr COUT - inx - cpx #6 - bne :nextg - jsr COUT ; 2nd space -; Display values - lda #"V" - ldy #OFFSETV - jsr PRDATA - lda #"T" - ldy #OFFSETT - jsr PRDATA - lda #"S" - ldy #OFFSETS - jsr PRDATA - lda #"C" - ldy #OFFSETC - jsr PRDATA - jmp PRCR -; Address Field byte groupings -:groups dfb 3,2,2,2,2,3 - -FieldKeypress ; Called with Acc = key - ldx #-1 - cmp #LARROW - beq :chgtrk - ldx #1 - cmp #RARROW - beq :chgtrk - cmp #"R" - beq :recal - cmp #"S" - beq :setup - cmp #"T" - beq :gotrk -:back jmp KeyboardWait - -:chgtrk txa - clc - adc CURTRK - cmp #35 ; cap at track 35 (0..34) - bcs :back -:setdst sta DSTTRK -:setup jmp SetupPage ; this redraws page and re-initializes on new track -:recal lda #40 - sta CURTRK ; This forces the recalibration - lda #0 - beq :setdst - -:gotrk jsr CLRSCRN - jsr PRINT - asc "Enter track number: $"00 - jsr READBYTE - bcs :setup - bcc :setdst - -* -* BROWSE interface -* - -BrowseInit ; Returns with Acc = max lines - jsr PRINT - asc _INVERSE,_MT_ON,_U_ARROW,_D_ARROW,_MT_OFF,_NORMAL," Scroll / " - asc _INVERSE,_MT_ON,_O_APPLE,_NORMAL,"-",_INVERSE,_U_ARROW,_D_ARROW,_MT_OFF,_NORMAL," Page / " - asc _MT_ON,_INVERSE,_L_ARROW,_R_ARROW,_NORMAL,_MT_OFF," Track / " - asc "re",_INVERSE,"S",_NORMAL,"can / " - asc _INVERSE,"R",_NORMAL,"ecalibrate / " - asc "goto ",_INVERSE,"T",_NORMAL,"rack" - dfb 0 - -* Scan for our prologue mark them by turning off the high bit - - jsr ReadTrack ; position head and fully populate buffer - jsr SETUPDATA -:scan ldy #0 ; index for comparisons -:loop lda (DATA),y - cmp PROLOGUE,Y - bne :adv - iny - cpy #3 - bcc :loop -; We found prologue bytes, strip off high bit -:strip dey - bmi :adv - lda (DATA),y - and #$7f - sta (DATA),y - bne :strip ; should be always -:adv inc DATA - bne :scan - inc DATA+1 - dec TEMP - bne :scan -; The number of lines is based on how many bytes we show on screen - lda #1 ; A:Y = $100 or 256 lines - ldy #0 - clc ; we need scrolling - rts - -BrowseDisplay ; Called with Acc = line -; Calculate buffer address - sta DATA+1 - sty DATA - ldy #5 ; times 32 -:mult32 asl DATA - rol DATA+1 - dey - bne :mult32 - clc - lda #>DATASTART - adc DATA+1 - sta DATA+1 -; Display offset - lda #"+" - jsr COUT - lda DATA+1 - and #$3f - jsr PRHEX - lda DATA - jsr PRHEX - jsr PRINT - asc "- ",$00 -; Display 'disk' bytes, highlighting the field markers (high bit was stripped off in init routine) - ldy #0 -:nxtbyt lda (DATA),y - bmi :prhex - pha - lda #_INVERSE - jsr COUT - pla - ora #$80 -:prhex jsr PRHEX - lda #_NORMAL - jsr COUT - tya - and #$07 - cmp #$07 - bne :testy - lda #" " - jsr COUT -:testy iny - cpy #32 - bne :nxtbyt - jmp PRCR - -BrowseKeypress ; Called with Acc = key - jmp FieldKeypress ; identical to Field ... for now at least - -* -* HGR/Graphics interface -* - -; Init displays HGR and the static text on the page -HgrInit - jsr PRINT - asc "re",_INVERSE,"S",_NORMAL,"can / " - asc _INVERSE,"R",_NORMAL,"ecalibrate" - dfb 0 -; Lay out a our rudimentary page: - jsr HGR - stz TEMPY -:newlin lda TEMPY - jsr HPOSN - ldy #0 -:line lda TEMPY - and #$8 - bne :lownyb -:hinyb tya - lsr - lsr - lsr - lsr - bra :draw -:lownyb tya - and #$0f -:draw asl - asl - asl - sta TEMP - lda TEMPY - and #$07 - ora TEMP - tax - lda HgrHexFont,x - sta (HBAS),y - iny - cpy #35 - bne :line - inc TEMPY - lda TEMPY - cmp #16 - blt :newlin -; return values - sec ; no scrolloing - lda #0 ; 0 = number of lines (A:Y) - tay - rts - -HgrDisplay -; Store current track to not interfere with other screens - lda DSTTRK - pha -; 144 lines (18 text lines * 8 graphic lines ea) -; buffer = 8192 bytes / 144 lines = 56 bytes ea -; OR NIB suggest 6656 / 144 lines = 46 bytes ea - stz DSTTRK -:nxttrk jsr ReadTrack - lda #16 - sta :ycoord - jsr SETUPDATA - stz :count - lda #46 - sta :bytes -:loop lda (DATA) - cmp #$FF - bne :skip - inc :count -:skip dec :bytes - bne :skip2 - lda :ycoord - jsr HPOSN - ldy CURTRK - ldx #0 - lda :count - beq :empty -:bits inx - lsr - bne :bits -:empty lda :plot,x - sta (HBAS),y - inc :ycoord - lda :ycoord - cmp #160 - bge :botm - stz :count - lda #46 - sta :bytes -:skip2 inc DATA - bne :loop - inc DATA+1 - dec TEMP - bne :loop -:botm inc DSTTRK - lda DSTTRK - cmp #35 - blt :nxttrk -; Restore current target track for other screens - pla - sta DSTTRK - rts -:ycoord dfb 0 -:count dfb 0 -:bytes dfb 0 -:plot hex 000103070f1f3f7fff - -HgrKeypress - cmp #"R" - beq :recal - cmp #"S" - beq :setup -:back jmp KeyboardWait - -:recal lda #40 - sta CURTRK ; This forces the recalibration - stz DSTTRK -:setup jmp SetupPage ; this redraws page and re-initializes, presumably, on a new disk - -HgrHexFont ; Stolen from https://github.com/Michaelangel007/apple2_hgr_font_tutorial - hex 1C22322A26221C00 ; 0 - hex 080C080808081C00 ; 1 - hex 1C22201804023E00 ; 2 - hex 3E20101820221C00 ; 3 - hex 101814123E101000 ; 4 - hex 3E021E2020221C00 ; 5 - hex 3804021E22221C00 ; 6 - hex 3E20100804040400 ; 7 - hex 1C22221C22221C00 ; 8 - hex 1C22223C20100E00 ; 9 - hex 081422223E222200 ; A - hex 1E22221E22221E00 ; B - hex 1C22020202221C00 ; C - hex 1E22222222221E00 ; D - hex 3E02021E02023E00 ; E - hex 3E02021E02020200 ; F - -* -* BUFFER COUNT interface -* - -CountInit ; Returns with A:Y = max lines, C = scrolling - jsr PRINT - asc "(high bit must be set, only showing those bytes)" - dfb 0 -; Set inbuf to zero (only counting $80, so $200-$2ff sufficient) - ldy #0 - tya -:erase sta inbuf,y - iny - bne :erase -; Start counting - jsr ReadTrack - jsr SETUPDATA - ldy #0 -:loop lda (DATA),y - asl ; times 2, we don't care about highbit - tax - inc inbuf,x - bne :skip - inc inbuf+1,x -:skip iny - bne :loop - inc DATA+1 - dec TEMP - bne :loop -; Setup framework - lda #0 ; No lines - tay - sec ; No scrolling - rts - -CountDisplay ; Called with A:Y = line - jsr PRINT - dfb $8D - dfb 14 - asc " Low +0 +1 +2 +3 +4 +5 +6 +7 High",$8D - dfb 19 - asc " ==== ==== ==== ==== ==== ==== ==== ====",$8D - dfb 0 - - ldy #$80 -:next ldx #14 - lda #" " -:tab jsr COUT - dex - bne :tab - tya - jsr PRHEX - lda #" " - jsr COUT - jsr COUT -:line lda #" " ; re-loading space due to loop construction - jsr COUT - tya - asl - tax - lda inbuf+1,x - jsr PRHEX - lda inbuf,x - jsr PRHEX - iny - tya - and #$07 - bne :line - lda #" " - jsr COUT - jsr COUT - jsr COUT - jsr COUT - tya - dec - jsr PRHEX - jsr PRCR - cpy #0 - bne :next - rts - -CountKeypress ; Called with Acc = key - ; Do Nothing, continue loop - jmp KeyboardWait - - -* -* TEST interface -* - -TestInit - lda #>535 - ldy #<535 ; Nice odd number of lines > 256 - rts - -TestDisplay - phy - pha - lda #_CLREOL - jsr COUT - pla - jsr PRHEX - pla - jsr PRHEX - jmp PRCR - -TestKeypress - jmp KeyboardWait - - -* -* DISK II routines -* - -ReadTrack - jsr PRINT - dfb _HOME,$8D,4,$88 - asc "T=" - dfb _PRBYTE - da DSTTRK - dfb 0 - - ldx SLOT16 ; Slot*16 - lda MOTORON,x ; turn on drive - lda DRV0EN,x ; Drive #1 - lda Q7L,x ; Q7 = Low and Q6 = Low => READ mode - lda Q6L,x - jsr ARMMOVE ; Go to our track (app init sets to 40, so first time this recalibrates) -; Fully read the track into buffer @ DATA - jsr SETUPDATA - ldy #0 -:loop lda Q6L,x - bpl :loop - sta (DATA),y - iny - bne :loop - inc DATA+1 - dec TEMP - bne :loop - lda MOTOROFF,x - rts - - -* Setup the data buffer -SETUPDATA LDA #>DATASTART - STA DATA+1 - LDA #DATALEN - STA TEMP ; number of pages to load - RTS - -* Print identifier and 4 and 4 encoded number -* Acc = character -* Output = " ?=" where ? is char in Acc -PRDATA PHA - LDA #" " - JSR COUT - PLA - JSR COUT - LDA #"=" - JSR COUT - LDA (DATA),Y - SEC - ROL - INY - AND (DATA),Y - JMP PRHEX - -* Output routine uses simple RLE type encoding -* High bit set = character; -* High bit clear = -* $00 = exit -* $01 = print byte from address -* $02-$7F = repeat next character -* -* There are two entry points: -* - PRINT = inline text being printed out; implicit PTR increment at start -* - PRINTP = print address passed in AX; no implicit PTR increment at start - -PRINT PLA - STA PTR - PLA - STA PTR+1 - JSR :MORE - LDA PTR+1 - PHA - LDA PTR - PHA - RTS - -PRINTP STA PTR+1 - STX PTR - BRA :START - -:MORE JSR INCPTR -:START LDX #1 ; Assume 1 char to be output - LDA (PTR) - BEQ :EXIT - BMI :SINGLE - CMP #1 - BNE :REPEAT - JSR INCPTR - LDA (PTR) - STA PTR2 - JSR INCPTR - LDA (PTR) - STA PTR2+1 - LDA (PTR2) - JSR PRHEX - BRA :MORE -:REPEAT TAX - JSR INCPTR - LDA (PTR) -:SINGLE JSR COUT - DEX - BNE :SINGLE - BEQ :MORE -:EXIT RTS - -INCPTR INC PTR - BNE :EXIT - INC PTR+1 -:EXIT RTS - -* Clear the data portion of the screen -* (Lines 3-22) and reposition to line 3. - -CLRSCRN LDA #_HOME - JSR COUT - JSR PRCR - LDY #20 -:1 JSR PRCR ; next line - LDA #_CLREOL - JSR COUT - DEY - BNE :1 - LDA #_HOME - JSR COUT - JSR PRCR ; move to row 3 - JMP PRCR - -* Read a byte -* If invalid, carry is set (anything NOT 0-9,A-F is invalid) - -READBYTE JSR READHEX - BCS :EXIT - ASL - ASL - ASL - ASL - STA TEMP - JSR READHEX ; carry falls through to exit - ORA TEMP -:EXIT RTS - -READHEX JSR GETCH - JSR COUT - CMP #"9"+1 - BCC :NUMERIC - CMP #"F"+1 - BCC :ALPHA -:BAD SEC - RTS -:NUMERIC SEC - SBC #"0" - BVS :BAD -:GOOD CLC - RTS -:ALPHA SEC - SBC #"A" - BVS :BAD - ADC #10 -:GOOD2 CLC - RTS - -* Move the Disk II arm: - -ARMMOVE LDA CURTRK - CMP DSTTRK - BNE :MOVE -:THERE LDX SLOT16 ; restore standard slot - RTS -:MOVE PHP - LDA #0 - ROL ; direction from CMP - TAY - LDA CURTRK - LSR - TYA - ROL ; odd/even from track - ASL ; times 2 - TAY - LDA ARMTABLE,Y - JSR ARMPHASE - LDA ARMTABLE+1,Y - JSR ARMPHASE - PLP - BCS :SUB1 -:ADD1 INC CURTRK - BRA ARMMOVE -:SUB1 DEC CURTRK - BRA ARMMOVE - -ARMPHASE ORA SLOT16 - TAX - LDA PHASEON,X - LDA #$56 - JSR DELAY - LDA PHASEOFF,X - RTS - -* Standard 16 sector address prologue -PROLOGUE HEX D5AA96 - -* Phase table for moving arm -* Grouped by inward/outward movement, odd/even track -* To move the arm, two phases need to be triggered -ARMTABLE HEX 0204 - HEX 0600 - HEX 0604 - HEX 0200 - -* ProDOS QUIT parameter tables -QUITPARM DFB 4 ; 4 parameters - DFB 0 ; 0 is the only quit type - DA 0 ; reserved - DFB 0 ; reserved - DA 0 ; reserved + put diskii + put util ProgramEnd diff --git a/diskii.s b/diskii.s new file mode 100755 index 0000000..e67268e --- /dev/null +++ b/diskii.s @@ -0,0 +1,118 @@ +****************************************************************************************** +* +* Disk II routines +* ======================================================================================== +* +****************************************************************************************** + +* Disk II addresses: + +PHASEOFF = $C080 +PHASEON = $C081 +MOTOROFF = $C088 +MOTORON = $C089 +DRV0EN = $C08A +DRV1EN = $C08B +Q6L = $C08C +Q7L = $C08E + +* +* DISK II routines +* + +ReadTrack + jsr PRINT + dfb _HOME,$8D,4,$88 + asc "T=" + dfb _PRBYTE + da DSTTRK + dfb 0 + + ldx SLOT16 ; Slot*16 + lda MOTORON,x ; turn on drive + lda DRV0EN,x ; Drive #1 + lda Q7L,x ; Q7 = Low and Q6 = Low => READ mode + lda Q6L,x + jsr ARMMOVE ; Go to our track (app init sets to 40, so first time this recalibrates) +; Fully read the track into buffer @ DATA + jsr SETUPDATA + ldy #0 +:loop lda Q6L,x + bpl :loop + sta (DATA),y + iny + bne :loop + inc DATA+1 + dec TEMP + bne :loop + lda MOTOROFF,x + rts + +* Move the Disk II arm: + +ARMMOVE LDA CURTRK + CMP DSTTRK + BNE :MOVE +:THERE LDX SLOT16 ; restore standard slot + RTS +:MOVE PHP + LDA #0 + ROL ; direction from CMP + TAY + LDA CURTRK + LSR + TYA + ROL ; odd/even from track + ASL ; times 2 + TAY + LDA ARMTABLE,Y + JSR ARMPHASE + LDA ARMTABLE+1,Y + JSR ARMPHASE + PLP + BCS :SUB1 +:ADD1 INC CURTRK + BRA ARMMOVE +:SUB1 DEC CURTRK + BRA ARMMOVE + +ARMPHASE ORA SLOT16 + TAX + LDA PHASEON,X + LDA #$56 + JSR DELAY + LDA PHASEOFF,X + RTS + +* Phase table for moving arm +* Grouped by inward/outward movement, odd/even track +* To move the arm, two phases need to be triggered +ARMTABLE HEX 0204 + HEX 0600 + HEX 0604 + HEX 0200 + +* Standard DOS 13-sector and 16-sector address/data field bytes + +AddressFieldPrologue + hex D5AA96 +AddressFieldEpilogue + hex DEAAEB +DataFieldPrologue + hex D5AAAD +DataFieldEpilogue + hex DEAAEB + +* DOS 13-sector translate table + +Translate13 + hex ABADAEAFB5B6B7BABBBDBEBFD6D7DADB + hex DDDEDFEAEBEDEEEFF5F6F7FAFBFDFEFF + +* DOS 16-sector translate table + +Translate16 + hex 96979A9B9D9E9FA6A7ABACADAEAFB2B3 + hex B4B5B6B7B9BABBBCBDBEBFCBCDCECFD3 + hex D6D7D9DADBDCDDDEDFE5E6E7E9EAEBEC + hex EDEEEFF2F3F4F5F6F7F9FAFBFCFDFEFF diff --git a/images/BufferCountPage.png b/images/BufferCountPage.png index 5cc775a..36b430f 100644 Binary files a/images/BufferCountPage.png and b/images/BufferCountPage.png differ diff --git a/page-about.s b/page-about.s new file mode 100755 index 0000000..c12a08b --- /dev/null +++ b/page-about.s @@ -0,0 +1,150 @@ +****************************************************************************************** +* +* About Page / Help Page +* +****************************************************************************************** + +* +* ABOUT page interface +* + +* Build array of line pointers in INBUF. +AboutInit ; Returns with A:Y = max lines, C = scrolling + jsr PRINT + asc _INVERSE,_MT_ON,_U_ARROW,_D_ARROW,_MT_OFF,_NORMAL," Scroll / " + asc _INVERSE,_MT_ON,_O_APPLE,_NORMAL,"-",_INVERSE,_U_ARROW,_D_ARROW,_MT_OFF,_NORMAL," Page" + dfb 0 + + lda #<:lines + sta PTR + lda #>:lines + sta PTR+1 + ldy #0 ; max lines + ldx #0 ; buffer offset + +:save lda PTR + sta inbuf,x + inx + lda PTR+1 + sta inbuf,x + inx + iny + +:loop lda (PTR) + bne :incr + dey ; we're one past... + lda #0 ; high byte of max lines; Y = low byte + clc ; we need scrolling + rts + +:incr lda (PTR) + pha + inc PTR + bne :skip + inc PTR+1 +:skip pla + beq :save + bne :incr + +* Guide 00000000011111111112222222222333333333344444444445555555555666666666677777777778 +* 12345678901234567890123456789012345678901234567890123456789012345678901234567890 +* | + + + + + + + +| +:lines asc _INVERSE," Address Field Scanner ",_NORMAL,$00 + asc "Version 2.0beta",$00 + asc " ",$00 + asc "This application is my lame attempt to understand the old Apple Disk II",$00 + asc "interface.",$00 + asc " ",$00 + asc "As of Version 2 of AFSCANNER, multiple pages have been introduced to better",$00 + asc "review the contents of a disk buffer and analyze a disk.",$00 + asc " ",$00 + asc "Use the ",_INVERSE,_MT_ON,_O_APPLE,_MT_OFF,_NORMAL,"-key combinations to toggle pages.",$00 + asc " ",$00 + asc _INVERSE,_MT_ON,_O_APPLE,_MT_OFF,_NORMAL,"A = This about page.",$00 + asc _INVERSE,_MT_ON,_O_APPLE,_MT_OFF,_NORMAL,"B = Browse track buffer.",$00 + asc _INVERSE,_MT_ON,_O_APPLE,_MT_OFF,_NORMAL,"C = Display buffer counts.",$00 + asc _INVERSE,_MT_ON,_O_APPLE,_MT_OFF,_NORMAL,"F = Address field display. " ; (cont) + asc "(Assuming 'good' address fields on disk.)",$00 + asc _INVERSE,_MT_ON,_O_APPLE,_MT_OFF,_NORMAL,"G = Graphical disk display.",$00 + asc " ",$00 + asc _INVERSE,_MT_ON,_O_APPLE,_MT_OFF,_NORMAL,"* = Enter Apple monitor.",$00 + asc " ",$00 + asc "Source available at https://github.com/a2geek/afscanner",$00 + asc " ",$00 + asc "Global Keys",$00 + asc "===========",$00 + asc " ",$00 + asc _INVERSE,_MT_ON,_D_ARROW,_MT_OFF,_NORMAL," Scroll down 1 line",$00 + asc _INVERSE,_MT_ON,_U_ARROW,_MT_OFF,_NORMAL," Scroll up 1 line",$00 + asc _INVERSE,_MT_ON,_O_APPLE,_D_ARROW,_MT_OFF,_NORMAL," Page down 15 lines",$00 + asc _INVERSE,_MT_ON,_O_APPLE,_U_ARROW,_MT_OFF,_NORMAL," Page up 15 lines",$00 + asc _INVERSE,_MT_ON,_O_APPLE,_MT_OFF,_NORMAL,"Q Quit to ProDOS",$00 + asc " ",$00 + asc "Address Field Display",$00 + asc "=====================",$00 + asc " ",$00 + asc "Display the list of Address Fields, the disk bytes, as well as the decoded",$00 + asc "values to indicate what sectors are phyiscally present and their order on disk.",$00 + asc "Note that the buffer is $2000 (8192) bytes long and some sectors will be",$00 + asc "repeated.",$00 + asc " ",$00 + asc "Headers are currently set to " + dfb 1 + da AddressFieldPrologue + dfb " ",1 + da AddressFieldPrologue+1 + dfb " ",1 + da AddressFieldPrologue+2 + dfb $00 + asc " ",$00 + asc "Browse Track Buffer",$00 + asc "===================",$00 + asc " ",$00 + asc "Browse the raw track buffer. Header fields are hilighted. Note that the ",$00 + asc "buffer is $2000 (8192) bytes long and some sectors will be repeated.",$00 + asc " ",$00 + asc "Headers are currently set to " + dfb 1 + da AddressFieldPrologue + dfb " ",1 + da AddressFieldPrologue+1 + dfb " ",1 + da AddressFieldPrologue+2 + dfb $00 + asc " ",$00 + asc "Graphical Disk Display",$00 + asc "======================",$00 + asc " ",$00 + asc "Scans an entire disk and graphically displays where sync ($FF) bytes appear on",$00 + asc "disk. Note that the length of each bar indicates how many sync bytes were in",$00 + asc "that section of the disk. Each bar represents approximately 46 bytes.",$00 + asc " ",$00 + asc "Display Buffer Counts",$00 + asc "=====================",$00 + asc " ",$00 + asc "Count the number of disk bytes in the buffer and display totals. This may be",$00 + asc "helpful when trying to determine address field bytes.",$00 + asc " ",$00 + asc "To assist with identification, non-zero values or 13-sector bytes or 16-sector",$00 + asc "bytes may be highlighted.",$00 + asc " ",$00 + asc "-END-",$00 + dfb 0 + +AboutDisplay ; Called with A:Y = line + phy ; assuming < 256 lines + lda #_CLREOL + jsr COUT + pla + asl + tay + lda inbuf,y + tax + lda inbuf+1,y + jsr PRINTP + jmp PRCR + +AboutKeypress ; Called with Acc = key + ; Do Nothing, continue loop + jmp KeyboardWait + diff --git a/page-browse.s b/page-browse.s new file mode 100755 index 0000000..4d68fd2 --- /dev/null +++ b/page-browse.s @@ -0,0 +1,98 @@ +****************************************************************************************** +* +* Browse Address Buffer Page +* +****************************************************************************************** + +* +* BROWSE interface +* + +BrowseInit ; Returns with Acc = max lines + jsr PRINT + asc _INVERSE,_MT_ON,_U_ARROW,_D_ARROW,_MT_OFF,_NORMAL," Scroll / " + asc _INVERSE,_MT_ON,_O_APPLE,_NORMAL,"-",_INVERSE,_U_ARROW,_D_ARROW,_MT_OFF,_NORMAL," Page / " + asc _MT_ON,_INVERSE,_L_ARROW,_R_ARROW,_NORMAL,_MT_OFF," Track / " + asc "re",_INVERSE,"S",_NORMAL,"can / " + asc _INVERSE,"R",_NORMAL,"ecalibrate / " + asc "goto ",_INVERSE,"T",_NORMAL,"rack" + dfb 0 + +* Scan for our prologue mark them by turning off the high bit + + jsr ReadTrack ; position head and fully populate buffer + jsr SETUPDATA +:scan ldy #0 ; index for comparisons +:loop lda (DATA),y + cmp AddressFieldPrologue,Y + bne :adv + iny + cpy #3 + bcc :loop +; We found prologue bytes, strip off high bit +:strip dey + bmi :adv + lda (DATA),y + and #$7f + sta (DATA),y + bne :strip ; should be always +:adv inc DATA + bne :scan + inc DATA+1 + dec TEMP + bne :scan +; The number of lines is based on how many bytes we show on screen + lda #1 ; A:Y = $100 or 256 lines + ldy #0 + clc ; we need scrolling + rts + +BrowseDisplay ; Called with Acc = line +; Calculate buffer address + sta DATA+1 + sty DATA + ldy #5 ; times 32 +:mult32 asl DATA + rol DATA+1 + dey + bne :mult32 + clc + lda #>DATASTART + adc DATA+1 + sta DATA+1 +; Display offset + lda #"+" + jsr COUT + lda DATA+1 + and #$3f + jsr PRHEX + lda DATA + jsr PRHEX + jsr PRINT + asc "- ",$00 +; Display 'disk' bytes, highlighting the field markers (high bit was stripped off in init routine) + ldy #0 +:nxtbyt lda (DATA),y + bmi :prhex + pha + lda #_INVERSE + jsr COUT + pla + ora #$80 +:prhex jsr PRHEX + lda #_NORMAL + jsr COUT + tya + and #$07 + cmp #$07 + bne :testy + lda #" " + jsr COUT +:testy iny + cpy #32 + bne :nxtbyt + jmp PRCR + +BrowseKeypress ; Called with Acc = key + jmp FieldKeypress ; identical to Field ... for now at least + diff --git a/page-count.s b/page-count.s new file mode 100755 index 0000000..199b664 --- /dev/null +++ b/page-count.s @@ -0,0 +1,232 @@ +****************************************************************************************** +* +* Buffer Count Page +* +****************************************************************************************** + +* Count Settings values (most of these are implicit): + +Count_NONE = 0 +Count_NONZERO = 1 +Count_13SECTOR = 2 +Count_16SECTOR = 3 + +CountInit ; Returns with A:Y = max lines, C = scrolling + jsr CountStatusBar + +; Set inbuf to zero (only counting $80, so $200-$2ff sufficient) + ldy #0 + tya +:erase sta inbuf,y + iny + bne :erase +; Start counting + jsr ReadTrack + jsr SETUPDATA + ldy #0 +:loop lda (DATA),y + asl ; times 2, we don't care about highbit + tax + inc inbuf,x + bne :skip + inc inbuf+1,x +:skip iny + bne :loop + inc DATA+1 + dec TEMP + bne :loop + + jsr SetCountFlags + +; Setup framework + lda #0 ; No lines + tay + sec ; No scrolling + rts + +CountDisplay ; Called with A:Y = line + jsr PRINT + dfb $8D + dfb 14 + asc " Low +0 +1 +2 +3 +4 +5 +6 +7 High",$8D + dfb 19 + asc " ==== ==== ==== ==== ==== ==== ==== ====",$8D + dfb 0 + +; Y = byte, X = temp, A = temp + ldy #$80 +:next ldx #14 + lda #" " +:tab jsr COUT + dex + bne :tab + tya + jsr PRHEX + lda #" " + jsr COUT + jsr COUT +:line lda #" " ; re-loading space due to loop construction + jsr COUT + tya + asl + tax + lda inbuf+1,x + bpl :norm + pha + lda #_INVERSE + jsr COUT + pla +:norm and #$7f ; ensure high-bit clear in the count as it is our highlight flag + jsr PRHEX + lda inbuf,x + jsr PRHEX + lda #_NORMAL + jsr COUT + iny + tya + and #$07 + bne :line + lda #" " + jsr COUT + jsr COUT + jsr COUT + jsr COUT + tya + dec + jsr PRHEX + jsr PRCR + cpy #0 + bne :next + jsr PRCR + jsr PRCR + ; Fall through to CountStatusBar for redisplay + +CountStatusBar + jsr PRINT + asc "[-]",$00 + lda #_NORMAL + ldx CountSettings + cpx #Count_NONE + bne :prnone + lda #_INVERSE +:prnone jsr COUT + jsr PRINT + asc "None",_NORMAL," [0]",$00 + + lda #_NORMAL + ldx CountSettings + cpx #Count_NONZERO + bne :prnzro + lda #_INVERSE +:prnzro jsr COUT + jsr PRINT + asc "Non-Zero",_NORMAL," [3]",$00 + + lda #_NORMAL + ldx CountSettings + cpx #Count_13SECTOR + bne :pr13 + lda #_INVERSE +:pr13 jsr COUT + jsr PRINT + asc "13-Sector",_NORMAL," [6]",$00 + + lda #_NORMAL + ldx CountSettings + cpx #Count_16SECTOR + bne :pr16 + lda #_INVERSE +:pr16 jsr COUT + jsr PRINT + asc "16-Sector",_NORMAL,$00 + rts + +CountKeypress ; Called with Acc = key + ldx #0 +:test cmp :keys,x + beq :set + inx + cpx #4 + bne :test + jmp KeyboardWait +:keys asc "-036" + +:set stx CountSettings + jsr SetCountFlags + jmp DrawPage + +SetCountFlags + lda CountSettings + asl + tax + jmp (:jmp,x) +:jmp da CountClear + da CountNonZero + da Count13Sector + da Count16Sector + +CountClear + ldy #$80 +:loop tya + asl + tax + lda inbuf+1,x + and #$7f + sta inbuf+1,x + iny + bne :loop + rts + +CountNonZero + jsr CountClear + ldy #$80 +:loop tya + asl + tax + lda inbuf,x + ora inbuf+1,x + beq :next + lda inbuf+1,x + ora #$80 + sta inbuf+1,x +:next iny + bne :loop + rts + +Count13Sector + jsr CountClear + ldy #0 +:loop lda Translate13,y + jsr CountSetBit + iny + cpy #32 + bne :loop + +; Set the reserved bytes which are identical for 13-sector and 16-sector formats +CountSetReserved + lda #$D5 + jsr CountSetBit + lda #$AA + jsr CountSetBit + rts + +Count16Sector + jsr CountClear + ldy #0 +:loop lda Translate16,y + jsr CountSetBit + iny + cpy #64 + bne :loop + beq CountSetReserved + +CountSetBit + asl + tax + lda inbuf+1,x + ora #$80 + sta inbuf+1,x + rts + +CountSettings + dfb 0 diff --git a/page-field.s b/page-field.s new file mode 100755 index 0000000..6b2ca6d --- /dev/null +++ b/page-field.s @@ -0,0 +1,139 @@ +****************************************************************************************** +* +* Address Field Page +* +****************************************************************************************** + +* +* FIELD Interface +* + +FieldInit ; Returns with A:Y = max lines + jsr PRINT + asc _INVERSE,_MT_ON,_U_ARROW,_D_ARROW,_MT_OFF,_NORMAL," Scroll / " + asc _INVERSE,_MT_ON,_O_APPLE,_NORMAL,"-",_INVERSE,_U_ARROW,_D_ARROW,_MT_OFF,_NORMAL," Page / " + asc _MT_ON,_INVERSE,_L_ARROW,_R_ARROW,_NORMAL,_MT_OFF," Track / " + asc "re",_INVERSE,"S",_NORMAL,"can / " + asc _INVERSE,"R",_NORMAL,"ecalibrate / " + asc "goto ",_INVERSE,"T",_NORMAL,"rack" + dfb 0 + +* Scan for our prologue and save positions in INBUF + + jsr ReadTrack ; position head and fully populate buffer + ldx #0 ; index for INBUF + jsr SETUPDATA +:scan ldy #0 ; index for comparisons +:loop lda (DATA),y + cmp AddressFieldPrologue,Y + bne :adv + iny + cpy #3 + bcc :loop +; We found prologue bytes, save DATA address + lda DATA + sta inbuf,x + inx + lda DATA+1 + sta inbuf,x + inx +:adv inc DATA + bne :scan + inc DATA+1 + dec TEMP + bne :scan +; Calculate # of lines + txa + lsr + tay + lda #0 ; Assumed < 256 lines + clc ; we need scrolling + rts + +FieldDisplay ; Called with A:Y = line + tya ; Assuming < 256 lines + asl + tay + lda inbuf,y + sta DATA + pha + lda inbuf+1,y + sta DATA+1 + pha +; Display offset + lda #"+" + jsr COUT + pla + and #$3f + jsr PRHEX + pla + jsr PRHEX + jsr PRINT + asc "- ",$00 +; Display 'disk' bytes + ldy #0 + ldx #0 +:nextg lda :groups,x + sta TEMP +:bytes lda (DATA),y + jsr PRHEX + iny + dec TEMP + bne :bytes + lda #" " + jsr COUT + inx + cpx #6 + bne :nextg + jsr COUT ; 2nd space +; Display values + lda #"V" + ldy #OFFSETV + jsr PRDATA + lda #"T" + ldy #OFFSETT + jsr PRDATA + lda #"S" + ldy #OFFSETS + jsr PRDATA + lda #"C" + ldy #OFFSETC + jsr PRDATA + jmp PRCR +; Address Field byte groupings +:groups dfb 3,2,2,2,2,3 + +FieldKeypress ; Called with Acc = key + ldx #-1 + cmp #LARROW + beq :chgtrk + ldx #1 + cmp #RARROW + beq :chgtrk + cmp #"R" + beq :recal + cmp #"S" + beq :setup + cmp #"T" + beq :gotrk +:back jmp KeyboardWait + +:chgtrk txa + clc + adc CURTRK + cmp #35 ; cap at track 35 (0..34) + bcs :back +:setdst sta DSTTRK +:setup jmp SetupPage ; this redraws page and re-initializes on new track +:recal lda #40 + sta CURTRK ; This forces the recalibration + lda #0 + beq :setdst + +:gotrk jsr CLRSCRN + jsr PRINT + asc "Enter track number: $"00 + jsr READBYTE + bcs :setup + bcc :setdst + diff --git a/page-graphics.s b/page-graphics.s new file mode 100755 index 0000000..267f5ea --- /dev/null +++ b/page-graphics.s @@ -0,0 +1,143 @@ +****************************************************************************************** +* +* HGR Disk Display +* +****************************************************************************************** + +* +* HGR/Graphics interface +* + +; Init displays HGR and the static text on the page +HgrInit + jsr PRINT + asc "re",_INVERSE,"S",_NORMAL,"can / " + asc _INVERSE,"R",_NORMAL,"ecalibrate" + dfb 0 +; Lay out a our rudimentary page: + jsr HGR + stz TEMPY +:newlin lda TEMPY + jsr HPOSN + ldy #0 +:line lda TEMPY + and #$8 + bne :lownyb +:hinyb tya + lsr + lsr + lsr + lsr + bra :draw +:lownyb tya + and #$0f +:draw asl + asl + asl + sta TEMP + lda TEMPY + and #$07 + ora TEMP + tax + lda HgrHexFont,x + sta (HBAS),y + iny + cpy #35 + bne :line + inc TEMPY + lda TEMPY + cmp #16 + blt :newlin +; return values + sec ; no scrolloing + lda #0 ; 0 = number of lines (A:Y) + tay + rts + +HgrDisplay +; Store current track to not interfere with other screens + lda DSTTRK + pha +; 144 lines (18 text lines * 8 graphic lines ea) +; buffer = 8192 bytes / 144 lines = 56 bytes ea +; OR NIB suggest 6656 / 144 lines = 46 bytes ea + stz DSTTRK +:nxttrk jsr ReadTrack + lda #16 + sta :ycoord + jsr SETUPDATA + stz :count + lda #46 + sta :bytes +:loop lda (DATA) + cmp #$FF + bne :skip + inc :count +:skip dec :bytes + bne :skip2 + lda :ycoord + jsr HPOSN + ldy CURTRK + ldx #0 + lda :count + beq :empty +:bits inx + lsr + bne :bits +:empty lda :plot,x + sta (HBAS),y + inc :ycoord + lda :ycoord + cmp #160 + bge :botm + stz :count + lda #46 + sta :bytes +:skip2 inc DATA + bne :loop + inc DATA+1 + dec TEMP + bne :loop +:botm inc DSTTRK + lda DSTTRK + cmp #35 + blt :nxttrk +; Restore current target track for other screens + pla + sta DSTTRK + rts +:ycoord dfb 0 +:count dfb 0 +:bytes dfb 0 +:plot hex 000103070f1f3f7fff + +HgrKeypress + cmp #"R" + beq :recal + cmp #"S" + beq :setup +:back jmp KeyboardWait + +:recal lda #40 + sta CURTRK ; This forces the recalibration + stz DSTTRK +:setup jmp SetupPage ; this redraws page and re-initializes, presumably, on a new disk + +HgrHexFont ; Stolen from https://github.com/Michaelangel007/apple2_hgr_font_tutorial + hex 1C22322A26221C00 ; 0 + hex 080C080808081C00 ; 1 + hex 1C22201804023E00 ; 2 + hex 3E20101820221C00 ; 3 + hex 101814123E101000 ; 4 + hex 3E021E2020221C00 ; 5 + hex 3804021E22221C00 ; 6 + hex 3E20100804040400 ; 7 + hex 1C22221C22221C00 ; 8 + hex 1C22223C20100E00 ; 9 + hex 081422223E222200 ; A + hex 1E22221E22221E00 ; B + hex 1C22020202221C00 ; C + hex 1E22222222221E00 ; D + hex 3E02021E02023E00 ; E + hex 3E02021E02020200 ; F + diff --git a/page-test.s b/page-test.s new file mode 100755 index 0000000..27ffc4a --- /dev/null +++ b/page-test.s @@ -0,0 +1,31 @@ +****************************************************************************************** +* +* Test Page +* +* Used for independent testing various pieces of funcionality. +* +****************************************************************************************** + +* +* TEST interface +* + +TestInit + lda #>535 + ldy #<535 ; Nice odd number of lines > 256 + rts + +TestDisplay + phy + pha + lda #_CLREOL + jsr COUT + pla + jsr PRHEX + pla + jsr PRHEX + jmp PRCR + +TestKeypress + jmp KeyboardWait + diff --git a/page.s b/page.s new file mode 100755 index 0000000..1ff4c51 --- /dev/null +++ b/page.s @@ -0,0 +1,224 @@ +****************************************************************************************** +* +* Primary page controller. +* ======================================================================================== +* +****************************************************************************************** + + +SetupPage + jsr TEXT ; ensure we are out of graphics modes as application has some! + jsr PRINT ; position cursor on bottom line + dfb _HOME + dfb 23,$8D + dfb _CLREOL + dfb $00 + ldx #_init ; vector offset + jsr _VCALL + ror scrolling ; set flag based on C + sty maxlines + sta maxlines+1 + stz topline + stz topline+1 + +DrawPage + jsr PRINT ; Clear and then draw the content area + dfb _HOME,$8d,$8d,0 + +; If we aren't scrolling, call _display vector ONCE and then handle keyboard. + bit scrolling + bpl :scroll + lda #0 + tay + ldx #_display + jsr _VCALL + jmp KeyboardWait + +:scroll lda #0 +:loop pha + clc + adc topline + sta printline + lda topline+1 + adc #0 + sta printline+1 + cmp maxlines+1 + blt :drwlin + lda printline + cmp maxlines + bge :erase + +:drwlin lda printline+1 + ldy printline + ldx #_display + jsr _VCALL + bra :incr + +:erase jsr PRINT + dfb _CLREOL,$8D,0 + +:incr pla + inc + cmp #PAGELEN + blt :loop + +KeyboardWait + lda KEYBOARD + bpl KeyboardWait +; Uppercase the if it's A-Z + cmp #"a" + blt :goodky + cmp #"z"+1 + bge :goodky + and #$df ; uppercase mask +:goodky sta KEYCLEAR + bit OpenApple + bpl :normal +; OpenApple handler + jsr SetScreen + bcs :paging + jsr CLRSCRN + jmp SetupPage +; OA-Up +:paging cmp #UpArrow + bne :nPgUp + ldy #15 +:uploop jsr :up1 + dey + bne :uploop + beq :back ; always +; OA-Down +:nPgUp cmp #DownArrow + bne :chkOAQ + ldy #15 +:dnloop jsr :down1 + dey + bne :dnloop + beq :back ; always +; OA-Q +:chkOAQ cmp #"Q" + bne :chkOA7 + jsr PRODOSMLI + dfb _MLIQUIT + da QUITPARM +:back jmp DrawPage ; fall through and common return + +; OA-* +:chkOA7 cmp #"*" + bne :back + jsr PRINT + asc _CLS,"Press CTRL-Y to re-enter AFScanner.",$8D,$00 + jmp MONITOR + +; Common keypress handler +; Up +:normal cmp #UpArrow + bne :notUp + jsr :up1 + jmp DrawPage +; Down +:notUp cmp #DownArrow + bne :pgKey + jsr :down1 + jmp DrawPage + +; "Local" subroutines + +; if topline+PAGELEN >= maxlines then return +; topline = topline + 1 +:down1 clc + lda topline + adc #PAGELEN + sta printline + lda printline+1 + adc #0 + sta printline+1 + cmp maxlines+1 + bcc :minus1 + lda printline + cmp maxlines + bge :rts +:minus1 inc topline + bne :rts + inc topline+1 + rts +; if topline = 0 then return +; topline = topline - 1 +:up1 lda topline + ora topline+1 + beq :rts ; already = 0 + sec + lda topline + sbc #1 + sta topline + lda topline+1 + sbc #0 + sta topline+1 +:rts rts + +:pgKey ldx #_keypress + ; Fall through and JMP to _keypress which takes control for local page keys + +* Simple vector call from ZP based on X register + +_VCALL jmp: ($00,x) ; Merlin32 needs to know 16 bit JMP + +* Handle screen change - both called by app init and normal keyboard handler + +SetScreen + ldx #0 +:next bit :data,x + bpl :notf + cmp :data,x + beq :setup + pha + txa + clc + adc #7 + tax + pla + bra :next +:notf sec ; Not found + rts +:setup ldy #0 +:copy inx + lda :data,x + sta _init,y + iny + cpy #6 + bne :copy + clc + rts + +* +* Data per page: Apple ASCII, Initialization, Display, Keypress (7 bytes) +* +* Definitions: +* - BYTE = Character to match on, case insensitive +* - ADDR = Initialize routine (load track, calculate lookup tables, etc). +* Return: +* A:Y for number of lines, +* Carry: Set = no scrolling (graphics, form, etc), Clear = scrolling. +* Note: Initialization should also display 24th line for the page. +* (Cursor is there and line is clear.) +* - ADDR = Display routine. Called with A:Y for line number. Should end with $8D and is +* responsible for clearing the line. +* - ADDR = Keypress handler. NOT A SUBROUTINE. This routine has a number of entry +* points it might call. KeyboardWait for another keypress or SetupPage to +* reinitialize and redraw. +* +:data + dfb "A" + da AboutInit, AboutDisplay, AboutKeypress + dfb "F" + da FieldInit, FieldDisplay, FieldKeypress + dfb "B" + da BrowseInit, BrowseDisplay, BrowseKeypress + dfb "G" + da HgrInit, HgrDisplay, HgrKeypress + dfb "C" + da CountInit, CountDisplay, CountKeypress + dfb "T" + da TestInit, TestDisplay, TestKeypress + dfb 0 ; end + diff --git a/util.s b/util.s new file mode 100755 index 0000000..922bad7 --- /dev/null +++ b/util.s @@ -0,0 +1,146 @@ +****************************************************************************************** +* +* Utility routines +* ======================================================================================== +* +****************************************************************************************** + +* Setup the data buffer +SETUPDATA LDA #>DATASTART + STA DATA+1 + LDA #DATALEN + STA TEMP ; number of pages to load + RTS + +* Print identifier and 4 and 4 encoded number +* Acc = character +* Output = " ?=" where ? is char in Acc +PRDATA PHA + LDA #" " + JSR COUT + PLA + JSR COUT + LDA #"=" + JSR COUT + LDA (DATA),Y + SEC + ROL + INY + AND (DATA),Y + JMP PRHEX + +* Output routine uses simple RLE type encoding +* High bit set = character; +* High bit clear = +* $00 = exit +* $01 = print byte from address +* $02-$7F = repeat next character +* +* There are two entry points: +* - PRINT = inline text being printed out; implicit PTR increment at start +* - PRINTP = print address passed in AX; no implicit PTR increment at start + +PRINT PLA + STA PTR + PLA + STA PTR+1 + JSR :MORE + LDA PTR+1 + PHA + LDA PTR + PHA + RTS + +PRINTP STA PTR+1 + STX PTR + BRA :START + +:MORE JSR INCPTR +:START LDX #1 ; Assume 1 char to be output + LDA (PTR) + BEQ :EXIT + BMI :SINGLE + CMP #1 + BNE :REPEAT + JSR INCPTR + LDA (PTR) + STA PTR2 + JSR INCPTR + LDA (PTR) + STA PTR2+1 + LDA (PTR2) + JSR PRHEX + BRA :MORE +:REPEAT TAX + JSR INCPTR + LDA (PTR) +:SINGLE JSR COUT + DEX + BNE :SINGLE + BEQ :MORE +:EXIT RTS + +INCPTR INC PTR + BNE :EXIT + INC PTR+1 +:EXIT RTS + +* Clear the data portion of the screen +* (Lines 3-22) and reposition to line 3. + +CLRSCRN LDA #_HOME + JSR COUT + JSR PRCR + LDY #20 +:1 JSR PRCR ; next line + LDA #_CLREOL + JSR COUT + DEY + BNE :1 + LDA #_HOME + JSR COUT + JSR PRCR ; move to row 3 + JMP PRCR + +* Read a byte +* If invalid, carry is set (anything NOT 0-9,A-F is invalid) + +READBYTE JSR READHEX + BCS :EXIT + ASL + ASL + ASL + ASL + STA TEMP + JSR READHEX ; carry falls through to exit + ORA TEMP +:EXIT RTS + +READHEX JSR GETCH + JSR COUT + CMP #"9"+1 + BCC :NUMERIC + CMP #"F"+1 + BCC :ALPHA +:BAD SEC + RTS +:NUMERIC SEC + SBC #"0" + BVS :BAD +:GOOD CLC + RTS +:ALPHA SEC + SBC #"A" + BVS :BAD + ADC #10 +:GOOD2 CLC + RTS + +* ProDOS QUIT parameter tables +QUITPARM DFB 4 ; 4 parameters + DFB 0 ; 0 is the only quit type + DA 0 ; reserved + DFB 0 ; reserved + DA 0 ; reserved