From 8b3684890a4f8a18422a712123dbffbf26b51176 Mon Sep 17 00:00:00 2001 From: Rob Greene Date: Mon, 8 Feb 2016 21:51:28 -0600 Subject: [PATCH] Separating source into multiple files; updated count page to include hilighting capabilities. --- Makefile | 3 +- afscanner.s | 1071 +----------------------------------- diskii.s | 118 ++++ images/BufferCountPage.png | Bin 17779 -> 18661 bytes page-about.s | 150 +++++ page-browse.s | 98 ++++ page-count.s | 232 ++++++++ page-field.s | 139 +++++ page-graphics.s | 143 +++++ page-test.s | 31 ++ page.s | 224 ++++++++ util.s | 146 +++++ 12 files changed, 1305 insertions(+), 1050 deletions(-) create mode 100755 diskii.s create mode 100755 page-about.s create mode 100755 page-browse.s create mode 100755 page-count.s create mode 100755 page-field.s create mode 100755 page-graphics.s create mode 100755 page-test.s create mode 100755 page.s create mode 100755 util.s 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 5cc775a6d4dc85d782bc5c5ea41c7c4f89154eef..36b430f8f767788fbd7ae2e1466b9708460b58d6 100644 GIT binary patch literal 18661 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 KnRVjIZ~q4jE($CF literal 17779 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: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