4live/src/4live.a
2016-11-10 18:50:30 -08:00

912 lines
23 KiB
Plaintext

;
; 4LIVE
; Copyright (c) 2016 by 4am && qkumba
; 100% open source, see LICENSE file
;
!cpu 6502
*=$8000
!ct "lcase.ct"
;
!addr CH = $24
!addr BASL = $28
!addr KSWL = $38
!addr KSWH = $39
!addr GETIOB = $3E3
!addr RECONNECTDOS = $3EA
!addr WARMDOS = $3D0
!addr DOSBASE = $3D2
!addr FILEMAN = $3D6
!addr GETPARM = $3DC
!addr KBD = $C000
!addr STROBE = $C010
!addr LCBANK2 = $C083
!addr ROMIN1 = $C089
!addr LCBANK1 = $C08B
!addr APLDETECT = $FBB3 ;#06 if IIe or later
!addr KEYIN = $FD1B
!addr RDCHAR = $FD35
!addr COUT = $FDED
;private arbitrary addresses only during init
;no zpage use once started
!addr MANPARML = $40
!addr MANPARMH = $41
!addr DOSBUFL = $42
!addr DOSBUFH = $43
!addr OPSRCL = $44
!addr OPSRCH = $45
!addr OPDSTL = $46
!addr OPDSTH = $47
HOTKEY = $80 ; CTRL-@
IMPORTKEY = $89 ; CTRL-I
CLEARKEY = $8E ; CTRL-N
SAVEKEY = $93 ; CTRL-S
;constants
SWAPCOPY = $368
SWAPBUFFER = $800 ; (LoadSaveEnd - LoadSaveStart) size
; currently $39A bytes!
; needed by DiversiDOS
A2E = $06
INVSPACE = $20
LTARROW = $88
DNARROW = $8A
UPARROW = $8B
RETURN = $8D
RTARROW = $95
ESC = $9B
DELETE = $FF
WIDTH = 40
HEIGHT = 24
IOBSLOT = 1
IOBDRIVE = 2
NEXTFILE = 36
PARMRANGE = 6
PARMFILE = 8
PARMWORK = 12
NAMELEN = 30
CREATEFILE = 0
OPENEXISTING = 1
SPECIAL = 8
CMDOPEN = 1
CMDCLOSE = 2
CMDREAD = 3
CMDWRITE = 4
READRANGE = 2
;
Install
;switch cursor type depending on Apple revision
;can't delay in case ROM is banked out because DiversiDOS
lda APLDETECT
cmp #A2E
beq +
lda #INVSPACE
sta CharDel + 1 + BankedCopyStart - $D000
lda #("z" + 1)
sta CharMap1 + 1
sta CharMap2 + 1 + BankedCopyStart - $D000
+
;find a free DOS buffer
ldy #0
sty DOSBUFL
lda DOSBASE
;check for DiversiDOS
cmp #$BF
bne +
!if <SWAPBUFFER {
ldy #<SWAPBUFFER
}
sty ReadBuffer + BankedCopyStart - $D000
lda #>SWAPBUFFER
sta ReadBuffer + 1 + BankedCopyStart - $D000
lda #$A9
sta ExchangeSwapBanked + SWAPCOPYStart - SWAPCOPY
lda LCBANK2
lda LCBANK2
lda #$DD
;back to regular DOS
+ sta DOSBUFH
!byte $2C ;mask LDY
- ldy #NEXTFILE
lda (DOSBUFL), y
tax
iny
lda (DOSBUFL), y
stx DOSBUFL
sta DOSBUFH
;set filename pointer in MLI request packet
stx FileName + BankedCopyStart - $D000
sta FileName + 1 + BankedCopyStart - $D000
;check for empty filename (indicates free buffer)
ldy #0
lda (DOSBUFL), y
bne -
;get file manager parameter list
jsr GETPARM
sty MANPARML
sta MANPARMH
sty MANPARM + 1 + BankedCopyStart - $D000
sta MANPARM + 2 + BankedCopyStart - $D000
;copy DOS buffer pointers to file manager parameter list
lda #NAMELEN
sta OPDSTL
lda #PARMWORK
sta OPDSTH
ldx #(6 - 1)
- ldy OPDSTL
lda (DOSBUFL), y
ldy OPDSTH
sta (MANPARML), y
inc OPDSTL
inc OPDSTH
dex
bpl -
;set input name
ldy #(NAMELEN - 1)
lda #$A0
- cpy #(FileName_e - FileName_b)
bcs +
lda FileName_b, y
+ sta (DOSBUFL), y
dey
bpl -
;open source file and read it if available
jsr GETIOB
sty OPSRCL
sta OPSRCH
ldy #IOBSLOT
lda (OPSRCL), y
lsr
lsr
lsr
lsr
sta FileSlot + BankedCopyStart - $D000
iny ;ldy #IOBDRIVE
lda (OPSRCL), y
sta FileDrive + BankedCopyStart - $D000
;copy to swap space
ldy #(SWAPCOPYEnd - SWAPCOPYStart)
- lda SWAPCOPYStart - 1, y
sta SWAPCOPY - 1, y
dey
bne -
;copy to banked RAM
jsr BankInRAM1
ldx #>(BankedCopyEnd + 255 - BankedCopyStart)
CopyMod
- lda BankedCopyStart, y
sta $D000, y
iny
bne -
inc CopyMod + 2
inc CopyMod + 5
dex
bne -
inx ;ldx #OPENEXISTING
jsr OpenFile
bcs +
;;in the future, the read size will come from the saved file
;;for now, we use a fixed size
;read file size from file
;; jsr ReadFile
;; lda #<LoadSaveStart
;; sta ReadBuffer
;; lda #>LoadSaveStart
;; sta ReadBuffer + 1
jsr ReadFile
jsr CloseFile
+
;set to true if no data file found (carry is set)
rol ClearOnFirstKeypress + 1
ldx SaveCH
stx MyCH + 1
ldy SaveCV
sty MyCV + 1
jsr MyBASCALC
;switch to write mode for future accesses
inc ReadWriteCmd ;lda WRITECMD / sta ReadWriteCmd
lda ROMIN1
;display the welcome message, now that we're finally done
ldy #0
beq ++
CharMap1
cmp #0 ;self-modified
bcs +
cmp #"a"
bcc +
and #$DF
+ jsr COUT
++ iny
lda _WelcomeMessage-1,y
bne CharMap1
lda #<GlobalKeyboardHook
sta KSWL
lda #>GlobalKeyboardHook
sta KSWH
jsr RECONNECTDOS
jmp WARMDOS
_WelcomeMessage
!text $8D, "4Live ready. Press Ctrl-", HOTKEY + $40, " to activate.", $8D, 0
FileName_b
!text "_4LIVE DATA"
FileName_e
SWAPCOPYStart
!pseudopc SWAPCOPY {
;support enhanced flashing cursor while waiting for key, if available.
;it works by passing to the routine the character under the cursor.
;the ROM toggles between that character and the Delete character.
;on an Apple II+, the cursor is an inverse space in DOS mode.
;our cursor flashes.
GlobalKeyboardHook
jsr KEYIN
GetKey
;are we on?
cmp #HOTKEY
;no -> return to DOS
bne ReuseRts
jsr BankInRAM1
jsr RunFromBankedRAM
lda ROMIN1
;lather, rinse, repeat
jsr RDCHAR
jmp GetKey
SwapFILEMAN
jsr FILEMAN
BankInRAM1
bit LCBANK1
bit LCBANK1
ReuseRts
rts
;sorry, I couldn't find a simpler way...
ExchangeSwapBanked
rts ;self-modified to LDA in DiversiDOS environment
!byte <(SWAPBUFFER + LoadSaveEnd - LoadSaveStart)
sta ExchangeSwapSource1 + 1
sta ExchangeSwapTarget1 + 1
lda #((>SWAPBUFFER) - >((<(LoadSaveEnd - LoadSaveStart)) + 255))
sta ExchangeSwapSource1 + 2
sta ExchangeSwapTarget1 + 2
lda #<(LoadSaveStart + LoadSaveEnd - LoadSaveStart)
sta ExchangeSwapSource2 + 1
sta ExchangeSwapTarget2 + 1
lda #((>LoadSaveStart) - >((<(LoadSaveEnd - LoadSaveStart)) + 255))
sta ExchangeSwapSource2 + 2
sta ExchangeSwapTarget2 + 2
ldx #>(LoadSaveEnd - LoadSaveStart)
ldy #<(LoadSaveStart - LoadSaveEnd)
ExchangeSwapSource1
lda $34f3, y ;self-modified
pha
ExchangeSwapSource2
lda $34f3, y ;self-modified
ExchangeSwapTarget1
sta $34f3, y ;self-modified
pla
ExchangeSwapTarget2
sta $34f3, y ;self-modified
iny
bne ExchangeSwapSource1
inc ExchangeSwapSource1 + 2
inc ExchangeSwapSource2 + 2
inc ExchangeSwapTarget1 + 2
inc ExchangeSwapTarget2 + 2
dex
bpl ExchangeSwapSource1
rts
}
SWAPCOPYEnd
!if ((SWAPCOPYEnd + SWAPCOPY - SWAPCOPYStart) > $3d0) {
!error "swap code too large, ends at ",SWAPCOPYEnd + SWAPCOPY - SWAPCOPYStart
}
BankedCopyStart
!pseudopc $D000 {
RunFromBankedRAM
;preserve X across calls
;it's important for DOS
txa
pha
;first, scroll the edit buffer onto the screen
;(this also swaps the current screen contents out so we can restore it later)
jsr ScrollEditBufferIn ;returns X=0
;do the thing
stx IsDirty + 1 ;0=false, non-0=true
;do the thing
jsr EditorMode
;scroll the edit buffer out and the original screen back in
jsr ScrollEditBufferOut
;save to disk (if necessary)
IsDirty
lda #"Q" ;self-modified
beq +
;save to disk
jsr SaveFile
+ pla
tax
rts
ScrollEditBufferIn
lda #<LastLine
sta VirtualBuff1 + 1
lda #>LastLine
sta VirtualBuff1 + 2
lda #<(LastLine - WIDTH)
sta VirtualBuff2 + 1
lda #>(LastLine - WIDTH)
sta VirtualBuff2 + 2
ldx #HEIGHT
stx ScrollLine1 + 1
;copy last line on screen to temporary buffer
-- ldy #(WIDTH - 1)
- lda $07D0, y
VirtualBuff1
sta $34f3, y ;self-modified
dey
bpl -
;copy each line on screen down to next line (leaves top line untouched)
jsr ScrollTextScreenDown
;copy last line of edit buffer to top line on screen
ldy #(WIDTH - 1)
VirtualBuff2
- lda $34f3, y ;self-modified
CharMap2
cmp #0 ;self-modified
bcs +
cmp #"a"
bcc +
and #$DF
+ sta $0400, y
dey
bpl -
;animation delay
jsr Delay ;returns carry set
tay
lda #-WIDTH
ldx #$FF
jsr UpdateVirtualOff
dec ScrollLine1 + 1
ScrollLine1
ldx #"Q" ;self-modified
bne --
rts
ScrollEditBufferOut
lda #<FirstLine
sta VirtualBuff3 + 1
lda #>FirstLine
sta VirtualBuff3 + 2
lda #<(FirstLine + WIDTH)
sta VirtualBuff4 + 1
lda #>(FirstLine + WIDTH)
sta VirtualBuff4 + 2
ldx #HEIGHT
stx ScrollLine2 + 1
;copy first line on screen to temporary buffer
-- ldy #(WIDTH - 1)
- lda $0400, y ;self-modified
VirtualBuff3
sta $34f3, y ;self-modified
dey
bpl -
;copy each line on screen up to next line (leaves bottom line untouched)
jsr ScrollTextScreenUp
;copy first line of edit buffer to last line on screen
ldy #(WIDTH - 1)
VirtualBuff4
- lda $34f3, y ;self-modified
sta $07D0, y
dey
bpl -
;animation delay
jsr Delay ;returns carry set
tax
lda #WIDTH
ldy #<(VirtualBuff3 - VirtualBuff1)
jsr UpdateVirtualOff
dec ScrollLine2 + 1
ScrollLine2
ldx #"Q" ;self-modified
bne --
rts
UpdateVirtualOff
clc
pha
;set up copy addresses for next line
adc VirtualBuff1 + 1, y
sta VirtualBuff1 + 1, y
txa
adc VirtualBuff1 + 2, y
sta VirtualBuff1 + 2, y
clc
pla
+ adc VirtualBuff2 + 1, y
sta VirtualBuff2 + 1, y
txa
adc VirtualBuff2 + 2, y
sta VirtualBuff2 + 2, y
rts
ScrollTextScreenDown
ldx #(HEIGHT - 2)
- lda TextCalcLo, x
sta _a+1
lda TextCalcLo+1, x
sta _b+1
lda TextCalcHi, x
sta _a+2
lda TextCalcHi+1, x
sta _b+2
ldy #(WIDTH - 1)
_a lda $FFFF, y ; self-modified, above
_b sta $FFFF, y ; self-modified, above
dey
bpl _a
dex
bpl -
rts
ScrollTextScreenUp
ldx #1 - HEIGHT
- lda (TextCalcLo + HEIGHT - $101) +1, x
sta _c+1
lda TextCalcLo + HEIGHT - $101, x
sta _d+1
lda (TextCalcHi + HEIGHT - $101) +1, x
sta _c+2
lda TextCalcHi + HEIGHT - $101, x
sta _d+2
ldy #(WIDTH - 1)
_c lda $FFFF, y ; self-modified, above
_d sta $FFFF, y ; self-modified, above
dey
bpl _c
inx
bne -
rts
Delay
lda #1
MyWAIT
sec
-- pha
- sbc #1
bne -
pla
sbc #1
bne --
rts
HandleKeyImportScreen
lda #<(LastLine - WIDTH)
sta VirtualBuff5 + 1
lda #>(LastLine - WIDTH)
sta VirtualBuff5 + 2
ldx #(HEIGHT - 2)
-- lda TextCalcLo, x
sta ScreenBuff1 + 1
lda TextCalcHi, x
sta ScreenBuff1 + 2
ldy #(WIDTH - 1)
VirtualBuff5
- lda $34f3, y ;self-modified
ScreenBuff1
sta $34f3, y ;self-modified
dey
bpl -
sec
lda VirtualBuff5 + 1
sbc #WIDTH
sta VirtualBuff5 + 1
bcs +
dec VirtualBuff5 + 2
+ dex
bpl --
;print anything that isn't a special key
;wrap around screen position when we hit edges
EditorMode
jsr MyKEYIN
cmp #HOTKEY
beq _doneEditorMode
cmp #ESC
beq _doneEditorMode
ClearOnFirstKeypress
ldx #0 ;self-modified
beq +
dec ClearOnFirstKeypress + 1
jsr ClearScreen
+
cmp #LTARROW
beq HandleKeyLineLeft
cmp #DNARROW
beq HandleKeyLineDown
cmp #UPARROW
beq HandleKeyLineUp
cmp #RTARROW
beq HandleKeyLineRight
cmp #RETURN
beq HandleKeyReturn
ldx #1
stx IsDirty + 1
cmp #IMPORTKEY
beq HandleKeyImportScreen
cmp #CLEARKEY
beq HandleKeyClearScreen
jsr MyCOUT
jmp EditorMode ;always
_doneEditorMode
rts
HandleKeyLineLeft
dec MyCH + 1
bpl EditorMode
lda #(WIDTH - 1)
SetColumn
sta MyCH + 1
bpl EditorMode ;always
HandleKeyLineRight
inc MyCH + 1
lda MyCH + 1
eor #WIDTH ;zero on match
bne EditorMode
beq SetColumn ;always
HandleKeyLineUp
ldy MyCV + 1
dey
bpl SetRow
ldy #(HEIGHT - 2)
SetRow
sty MyCV + 1
ldx MyCH + 1
jsr MyBASCALC ;returns flag=plus
bpl EditorMode ;always
HandleKeyClearScreen
jsr ClearScreen ;returns flag=minus
dey ;-2
sty MyCV + 1 ;use big store to trigger wraparound
;fall through
HandleKeyReturn
lda #0
sta MyCH + 1
;fall through
HandleKeyLineDown
ldy MyCV + 1
iny
cpy #(HEIGHT - 1) ;zero on match
bcc SetRow ;not BNE to allow big store use
ldy #0
beq SetRow ;always
ClearScreen ; does not clear status line
pha
ldx #(HEIGHT - 2)
lda #$A0
-- ldy TextCalcLo, x
sty _e+1
ldy TextCalcHi, x
sty _e+2
ldy #(WIDTH - 1)
_e sta $FFFF, y ; self-modified, above
dey
bpl _e
dex
bpl --
pla
rts
MyKEYIN
ldx MyCH + 1
ScreenBuff2
lda $34f3, x ;self-modified
sta ToggleChar + 1
CharDel
lda #DELETE
sec
!byte $24 ;mask CLC
ToggleCarry
clc
bcs +
ToggleChar
lda #"Q" ;self-modified
+ ldx MyCH + 1
ScreenBuff3
sta $34f3, x ;self-modified
;timing is taken from extended ROM
;code is optimised for size
ldx #$31
ldy #0
- lda KBD
bmi +
dey
bne -
dex
bne -
bcc CharDel
+ bcs ToggleCarry ;restore original character, also on exit
bit STROBE
rts
MyCOUT
MyCH
ldx #"Q" ;self-modified
ScreenBuff4
sta $34f3, x ;self-modified
inx
cpx #WIDTH
bne ++
ldx #0
MyCV
ldy #"Q" ;self-modified
iny
cpy #(HEIGHT - 1)
bne MyBASCALC
ldy #0
MyBASCALC
sty MyCV + 1
lda TextCalcLo, y
sta ScreenBuff2 + 1
sta ScreenBuff3 + 1
sta ScreenBuff4 + 1
lda TextCalcHi, y
sta ScreenBuff2 + 2
sta ScreenBuff3 + 2
sta ScreenBuff4 + 2
++ stx MyCH + 1
rts
OpenFile
jsr DOSMLI
!byte OpenMLI_e - OpenMLI_b
OpenMLI_b
!byte CMDOPEN
!byte 0 ;unused
!word 0 ;variable records
!byte 0 ;any volume
FileDrive
!text "Q" ;self-modified
FileSlot
!text "Q" ;self-modified
!byte SPECIAL ;file type for when we save
FileName
!text "4s" ;self-modified
OpenMLI_e
OpenRet
rts
SaveFile
lda MyCH + 1
sta SaveCH
lda MyCV + 1
sta SaveCV
CreateWriteFile
ldx #CREATEFILE
jsr OpenFile
bcs OpenRet
;set write length
;; lda #<something
;; sta WriteSize
;; lda #>something
;; sta WriteSize + 1
jsr WriteFile
CloseFile
jsr DOSMLI
!byte CloseMLI_e - CloseMLI_b
CloseMLI_b
!byte CMDCLOSE
CloseMLI_e
rts
WriteFile
jsr ExchangeSwapBanked
ReadFile
jsr DOSMLI
!byte ReadMLI_e - ReadMLI_b
ReadMLI_b
ReadWriteCmd
!byte CMDREAD
!byte READRANGE
!word 0 ;record number
!word 0 ;offset
;;in the future, ReadSize will be initially 2
;;and ReadBuffer will point to ReadSize
;;so that the initial file size will be read into the Size field
;;for subsequent read of remaining bytes
ReadSize
WriteSize
!word (LoadSaveEnd - LoadSaveStart)
;number of bytes
ReadBuffer
WriteBuffer
!word LoadSaveStart ;buffer, self-modified in DiversiDOS
ReadMLI_e
jmp ExchangeSwapBanked
;first byte fetched is number of byte to follow,
;copy parameters to file manager parameter list,
;and then dispatch the request
DOSMLI
stx XREG + 1
pla
tay
pla
tax
iny
sty OPSRC1 + 1
sty OPSRC2 + 1
bne +
inx
+ stx OPSRC1 + 2
stx OPSRC2 + 2
ldy #0
OPSRC1
lda $34f3, y ;self-modified
tay
clc
adc OPSRC1 + 1
tax
lda OPSRC1 + 2
adc #0
pha
txa
pha
OPSRC2
- lda $34f3, y ;self-modified
dey
MANPARM
sta $34f3, y ;self-modified
bne -
XREG
ldx #"Q" ;self-modified
jmp SwapFILEMAN
TextCalcHi
!byte $04, $04, $05, $05, $06, $06, $07, $07
!byte $04, $04, $05, $05, $06, $06, $07, $07
!byte $04, $04, $05, $05, $06, $06, $07, $07
TextCalcLo
!byte $00, $80, $00, $80, $00, $80, $00, $80
!byte $28, $A8, $28, $A8, $28, $A8, $28, $A8
!byte $50, $D0, $50, $D0, $50, $D0, $50, $D0
LoadSaveStart
;;in the future, this field will contain the number of bytes following for write
;;FileSize ;must be first!
;; !text "4s" ;self-modified
SaveCH
!byte 0 ;loaded from file if exists
SaveCV
!byte 0 ;loaded from file if exists
FirstLine ;lines are stored sequentially, not like text page in memory
!fill WIDTH * 8, $A0
!text " 4LIVE by 4am && qkumba "
!fill WIDTH, $A0
!text " Revision 02 / Serial number 161110 "
!fill WIDTH * 2, $A0
!text " https://github.com/a2-4am/4live "
!fill WIDTH * 9, $A0
LoadSaveEnd
;in the current version, the status line is not saved
;however, when we move to multi-screen support,
;both the status line and the spill line must be saved
;for scrolling to work properly
!fill (WIDTH - 5), $20
!text "4LIVE"
LastLine
!fill WIDTH, $A0
}
BankedCopyEnd