afscanner/afscanner.s

1234 lines
25 KiB
ArmAsm
Executable File

******************************************************************************************
*
* Address Field Scanner
* ========================================================================================
* This is a simple Disk II scanner
* to identify address field contents
*
* 1/16/2016: Version 1.0
*
* 1/29/2016: Version 2.0
* - Restructuring to support paging and scrollong.
* - Slowly switching from spaces to tabs as an experiment in code formatting.
* - Pages available:
* + About (default page)
* + Headers (updated Address Field Header information)
* + Browse track buffer (hilights Address Field Header bytes)
* + Graphical disk display
* + Buffer counts
*
******************************************************************************************
TYP SYS
XC ; enable 65C02
* Program Locations
OriginAddress = $2000
ProgramAddress = $6000
ProgramLength = ProgramEnd-ProgramAddress
* Constants:
NUMBYTES = 29 ; Number of bytes on screen
OFFSETV = 3 ; Offset to volume
OFFSETT = OFFSETV+2 ; Offset to track
OFFSETS = OFFSETT+2 ; Offset to sector
OFFSETC = OFFSETS+2 ; Offset to checksum
FAILBYTS = 6656 ; Number of bytes before failure
* 80 column card / print controls:
_PRBYTE = 1 ; print byte @ addr
_CLS = $8C ; clear screen
_INVERSE = $8F
_NORMAL = $8E
_MT_OFF = $98 ; disable MouseText
_HOME = $99 ; home the cursor, not cls
_MT_ON = $9B ; enable MouseText for uppercase inverse characters
_CLREOL = $9D ; clear to EOL
_C_APPLE = "@" ; Closed Apple
_O_APPLE = "A" ; Open Apple
_L_ARROW = "H" ; Left Arrow
_R_ARROW = "U" ; Right Arrow
_U_ARROW = "K" ; Up Arrow
_D_ARROW = "J" ; Down Arrow
_H_LINE = "S" ; Horizontal Line (full width)
* Variable locations:
DUM $0 ; ZP locs
SAMPLES DFB 0 ; number of sector samples
CURTRK DFB 0 ; current track
DSTTRK DFB 0 ; destination track
PTR DA 0 ; primary print pointer
PTR2 DA 0 ; secondary print pointer
SLOT16 DFB 0 ; slot# * 16
COUNTER DA 0 ; fail counter
TEMP DFB 0 ; local variable
TEMPY DFB 0 ; local Y coordinate
DATA DA 0 ; data buffer
_init da 0 ; current page init
_display da 0 ; current page line display handler
_keypress da 0 ; current page keypress handler
scrolling dfb 0 ; flag for scrolling (in high bit)
topline dw 0 ; current line at top of page
maxlines dw 0 ; maximum number of lines available
printline dw 0 ; line being printed
DEND
inbuf = $200 ; reusable buffer
DATASTART = $4000
DATAEND = $6000
DATALEN = DATAEND-DATASTART
PAGELEN = 20 ; number of lines displayed in content window
* High ASCII constants
CTRLH = "H"-$40
LARROW = CTRLH
CTRLU = "U"-$40
RARROW = CTRLU
ESC = $9B
CTRLK = "K"-$40
UpArrow = CTRLK
CTRLJ = "J"-$40
DownArrow = CTRLJ
* ProDOS:
PRODOSMLI = $BF00
_MLIQUIT = $65
* ROM routines and associated addresses:
TEXT = $FB2F
HGR = $F3E2
HPOSN = $F411
HBAS = $26
DELAY = $FCA8
GETCH = $FD0C
PRCR = $FD8E
PRHEX = $FDDA
COUT = $FDED
* I/O addresses:
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
lda #>OriginEnd
sta PTR+1
lda #<OriginEnd
sta PTR
lda #>ProgramAddress
sta PTR2+1
stz PTR2
ldy #0
ldx #>ProgramLength+255 ; account for non-zero low byte
:0 lda (PTR),y
sta (PTR2),y
iny
bne :0
inc PTR+1
inc PTR2+1
dex
bne :0
jmp ProgramAddress
OriginEnd ; Marker for code relocation
* Display screen
org ProgramAddress
MAIN JSR $C300 ; Assuming 80 columns
JSR PRINT
DFB _CLS
ASC "AFScanner",$8D
DFB _MT_ON,_INVERSE,80,_H_LINE,_NORMAL,_MT_OFF ; wraps!
DFB 20,$8D
DFB _MT_ON,_INVERSE,80,_H_LINE,_NORMAL,_MT_OFF ; wraps!
ASC _MT_ON,_INVERSE,_L_ARROW,_NORMAL,", ",_INVERSE,_R_ARROW,_NORMAL,_MT_OFF," Track / "
ASC "re",_INVERSE,"S",_NORMAL,"can / "
ASC _INVERSE,"R",_NORMAL,"ecalibrate / "
ASC "goto ",_INVERSE,"T",_NORMAL,"rack / "
ASC _INVERSE,"ESC",_NORMAL," quit"
DFB _HOME
HEX 8D8D
HEX 00
* Setup local variables
Initialize
ldx #$60 ; assumption = slot 6
stx SLOT16
lda #40
sta CURTRK ; force a recalibration
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
: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
: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 #<DATASTART
STA DATA
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
ProgramEnd