; ; 4LIVE ; a 4am & san inc hack ; !cpu 6502 *=$8000 ; !addr WNDLFT = $20 !addr WNDWDTH = $21 !addr WNDTOP = $22 !addr WNDBTM = $23 !addr CH = $24 !addr CV = $25 !addr GBASL = $26 !addr GBASH = $27 !addr BASL = $28 !addr BASH = $29 !addr BAS2L = $2A !addr BAS2H = $2B !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 ROMIN = $C081 !addr LCBANK2 = $C083 !addr GBASCALC = $F847 !addr BASCALC = $FBC1 !addr SCROLL = $FC70 !addr WAIT = $FCA8 !addr KEYIN = $FD1B !addr COUT = $FDED ;private arbitrary addresses !addr ZMANPARML = $40 !addr ZMANPARMH = $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 ;constants LTARROW = $88 UPARROW = $8B RETURN = $8D RTARROW = $95 ESC = $9B 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 ;find a free DOS buffer ldy #0 sty DOSBUFL lda DOSBASE ;check for DiversiDOS cmp #$BF bne + 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 sta FileName + 1 ;check for empty filename (indicates free buffer) ldy #0 lda (DOSBUFL), y bne - ;get file manager parameter list jsr GETPARM sty ZMANPARML sta ZMANPARMH sty MANPARM + 1 sta MANPARM + 2 ;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 (ZMANPARML), 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 OpenReadFile bcs + lda #0 sta gClearOnFirstKeypress + lda SaveCH sta MyCH lda SaveCV sta MyCV ;switch to write mode for future accesses inc ReadWriteCmd ;lda WRITECMD / sta ReadWriteCmd lda ROMIN ;display the welcome message, now that we're finally done ldy #0 beq + - jsr COUT + iny lda _WelcomeMessage-1,y bne - lda #GlobalKeyboardHook sta KSWH jsr RECONNECTDOS jmp WARMDOS _WelcomeMessage !scrxor $80, $8D, "4LIVE READY. PRESS CTRL-", HOTKEY + $40, " TO ACTIVATE.", $8D, 0 ;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 GetKey ldy CH lda (BASL), y GlobalKeyboardHook ;let DOS handle initial cursor jsr KEYIN ;are we on? cmp #HOTKEY ;yes -> branch beq + ;no -> return to DOS rts + 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 ;do the thing lda #0 ;0=false, non-0=true sta gIsDirty jsr EditorMode ;scroll the edit buffer out and the original screen back in jsr ScrollEditBufferOut ;save to disk (if necessary) lda gIsDirty beq + jsr SaveFile + pla tax ;lather, rinse, repeat jmp GetKey ScrollEditBufferIn ;save real cursor position and use saved edit buffer cursor position instead jsr SwapCoords lda #StatusLine sta _in+2 lda #HEIGHT sta LINE ;copy last line on screen to temporary buffer -- ldy #(WIDTH - 1) - lda $07D0, y sta LineBuffer, y 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) _in lda $FFFF, y ;self-modified above and below sta $0400, y dey bpl _in ;copy temporary buffer to last line in edit buffer lda _in+1 sta _in2+1 lda _in+2 sta _in2+2 ldy #(WIDTH -1) - lda LineBuffer, y _in2 sta $FFFF, y ;self-modified above dey bpl - ;animation delay jsr Delay ;set up copy addresses for next line lda _in+1 sec sbc #WIDTH sta _in+1 bcs + dec _in+2 + dec LINE bne -- rts ScrollEditBufferOut ;restore real cursor position jsr SwapCoords lda #EditBuffer sta _out+2 lda #HEIGHT sta LINE ;copy first line on screen to temporary buffer -- ldy #(WIDTH - 1) - lda $0400, y sta LineBuffer, y dey bpl - ;copy each line on screen up to next line (leaves last line empty) jsr SCROLL ;copy first line of edit buffer to bottom line of screen ldy #(WIDTH - 1) _out lda $FFFF, y ;self-modified above and below sta $07D0, y dey bpl _out ;copy temporary buffer to first line in edit buffer lda _out+1 sta _out2+1 lda _out+2 sta _out2+2 ldy #(WIDTH - 1) - lda LineBuffer, y _out2 sta $FFFF, y ;self-modified above dey bpl - ;animation delay jsr Delay ;set up copy addresses for next line lda _out+1 clc adc #WIDTH sta _out+1 bcc + inc _out+2 + dec LINE bne -- rts ScrollTextScreenDown ldx #$16 - 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 #$27 _a lda $FFFF, y ; self-modified, above _b sta $FFFF, y ; self-modified, above dey bpl _a dex bpl - rts Delay lda #1 jmp WAIT ;swap real X/Y with virtual X/Y ;and recalculate screen position SwapCoords ldx #(6 - 1) - ldy WNDLFT, x lda MyWNDLFT, x sta WNDLFT, x tya sta MyWNDLFT, x dex bpl - jmp BASCALC EditorMode ldy CH lda (BASL), y jsr KEYIN cmp #HOTKEY beq HandleKeyExit cmp #ESC beq HandleKeyExit ldx #1 stx gIsDirty cmp #IMPORTKEY beq HandleKeyImportScreen cmp #CLEARKEY beq HandleKeyClearScreen cmp #LTARROW beq HandleKeyLineLeft cmp #UPARROW beq HandleKeyLineUp cmp #RTARROW beq HandleKeyLineRight ;print anything that isn't a special key ;wrap around screen position when we hit edges pha lda gClearOnFirstKeypress beq + jsr ClearScreen lda #0 sta gClearOnFirstKeypress + pla jsr COUT lda CV eor #(HEIGHT - 1) ;zero on match bne EditorMode SetRow sta CV SetRow1 lda CV jsr BASCALC bcc EditorMode ;always HandleKeyExit rts HandleKeyLineLeft dec CH bpl SetRow1 lda #(WIDTH - 1) SetColumn sta CH bpl SetRow1 ;always HandleKeyLineUp dec CV bpl SetRow1 lda #(HEIGHT - 2) bne SetRow ;always HandleKeyLineRight inc CH lda CH eor #WIDTH ;zero on match bne SetRow1 beq SetColumn ;always HandleKeyImportScreen lda #<(EditBuffer + WIDTH) sta OPSRCL lda #>(EditBuffer + WIDTH) sta OPSRCH ldx #(HEIGHT - 2) ;use graphics function to not disturb text state -- txa jsr GBASCALC ldy #(WIDTH - 1) - lda (OPSRCL), y sta (GBASL), y dey bpl - lda OPSRCL clc adc #WIDTH sta OPSRCL bcc + inc OPSRCH + dex bpl -- jmp EditorMode HandleKeyClearScreen jsr ClearScreen jmp EditorMode ClearScreen ; does not clear status line ldx #(HEIGHT - 2) -- lda TextCalcLo, x sta _c+1 lda TextCalcHi, x sta _c+2 ldy #(WIDTH - 1) lda #$A0 _c sta $FFFF, y ; self-modified, above dey bpl _c dex bpl -- rts OpenReadFile lda LCBANK2 lda LCBANK2 ;support DiversiDOS jsr GETIOB sty OPSRCL sta OPSRCH ldy #IOBSLOT lda (OPSRCL), y lsr lsr lsr lsr sta FileSlot iny ;ldy #IOBDRIVE lda (OPSRCL), y sta FileDrive ldx #OPENEXISTING jsr OpenCommon bcs OpenRet ReadFile WriteFile jsr DOSMLI !byte ReadMLI_e - ReadMLI_b ReadMLI_b ReadWriteCmd !byte CMDREAD !byte READRANGE !word 0 ;record number !word 0 ;offset WriteSize !word -1 ;number of bytes !word LoadSaveStart ;buffer ReadMLI_e CloseFile jsr DOSMLI !byte CloseMLI_e - CloseMLI_b CloseMLI_b !byte CMDCLOSE CloseMLI_e OpenRet rts OpenCommon 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 rts SaveFile lda CH sta SaveCH lda CV sta SaveCV CreateWriteFile ldx #CREATEFILE jsr OpenCommon bcs OpenRet lda #<(LoadSaveEnd - LoadSaveStart) sta WriteSize lda #>(LoadSaveEnd - LoadSaveStart) sta WriteSize + 1 bcc WriteFile ;always ;first byte fetched is number of byte to follow, ;copy parameters to file manager parameter list, ;and then dispatch the request DOSMLI pla tay pla sta OPSRCH iny sty OPSRCL bne + inc OPSRCH + ldy #0 lda (OPSRCL), y tay - lda (OPSRCL), y dey MANPARM sta $34f3, y ;self-modified bne - lda (OPSRCL), y clc adc OPSRCL tay lda OPSRCH adc #0 pha tya pha jmp FILEMAN FileName_b !scrxor $80, $DF, "4LIVE DATA" FileName_e MyWNDLFT !byte 0 MyWNDWDTH !byte WIDTH MyWNDTOP !byte 0 MyWNDBTM !byte HEIGHT MyCH !byte 0 MyCV !byte 0 LINE !byte 0 gIsDirty !byte 0 gClearOnFirstKeypress !byte 1 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 SaveCH !byte 0 ;loaded from file if exists SaveCV !byte 0 ;loaded from file if exists EditBuffer ;lines are stored sequentially, not like text page in memory !fill WIDTH * 8, $A0 !scrxor $80, " 4LIVE by 4am & san inc " !fill WIDTH, $A0 !scrxor $80, " Revision 01 / Serial number 161031 " !fill WIDTH * 2, $A0 !scrxor $80, " https://github.com/a2-4am/4live " !fill WIDTH * 9, $A0 LoadSaveEnd StatusLine ;must be exactly WIDTH bytes ;status line !fill (WIDTH - 5), $20 !scrxor $80, "4LIVE" LineBuffer ;used during scrolling !fill WIDTH, 0