Daniel Covington 2 дней назад
Родитель
Сommit
cd7607e330
12 измененных файлов: 1140 добавлений и 7 удалений
  1. +12
    -1
      .claude/settings.local.json
  2. +40
    -0
      samples/kernal-os-skeleton/build_all.bat
  3. +15
    -0
      samples/kernal-os-skeleton/build_vim.bat
  4. +139
    -6
      samples/kernal-os-skeleton/kernal_os.asm
  5. Двоичные данные
      samples/kernal-os-skeleton/kernal_os.d64
  6. Двоичные данные
      samples/kernal-os-skeleton/kernal_os.o
  7. Двоичные данные
      samples/kernal-os-skeleton/kernal_os.prg
  8. +16
    -0
      samples/kernal-os-skeleton/run.bat
  9. +907
    -0
      samples/kernal-os-skeleton/vim.asm
  10. +11
    -0
      samples/kernal-os-skeleton/vim.cfg
  11. Двоичные данные
      samples/kernal-os-skeleton/vim.o
  12. Двоичные данные
      samples/kernal-os-skeleton/vim.prg

+ 12
- 1
.claude/settings.local.json Просмотреть файл

@@ -2,7 +2,18 @@
"permissions": { "permissions": {
"allow": [ "allow": [
"Bash(build.bat)", "Bash(build.bat)",
"PowerShell(Copy-Item \"c:\\\\Development\\\\Commodore\\\\os Experiments\\\\c64os\\\\samples\\\\cc65-mouse-driver-demo\\\\mousedemo.c\" \"c:\\\\Development\\\\Commodore\\\\os Experiments\\\\c64os\\\\samples\\\\cc65-mouse-driver-demo\\\\mouse_driver_demo.c\" -Force)"
"PowerShell(Copy-Item \"c:\\\\Development\\\\Commodore\\\\os Experiments\\\\c64os\\\\samples\\\\cc65-mouse-driver-demo\\\\mousedemo.c\" \"c:\\\\Development\\\\Commodore\\\\os Experiments\\\\c64os\\\\samples\\\\cc65-mouse-driver-demo\\\\mouse_driver_demo.c\" -Force)",
"PowerShell($f = \"c:\\\\Development\\\\Commodore\\\\os Experiments\\\\c64os\\\\samples\\\\kernal-os-skeleton\\\\kernal_os.prg\"; \\(Get-Item $f\\).Length)",
"PowerShell($output = & \"C:\\\\Program Files\\\\GTK3VICE-3.10-win64\\\\bin\\\\x64sc.exe\" -help 2>&1; $output | Where-Object { $_ -match \"device|fs|drive|disk\" } | Select-Object -First 40)",
"PowerShell(cmd /c \"cd /d `\"c:\\\\Development\\\\Commodore\\\\os Experiments\\\\c64os\\\\samples\\\\kernal-os-skeleton`\" && build_all.bat\" 2>&1)",
"Bash(cmd.exe /c build_all.bat)",
"Bash(cmd.exe /c \"build_all.bat\")",
"Bash(echo \"exit: $?\")",
"Bash(cmd.exe /c \"ca65 kernal_os.asm -o kernal_os.o && ld65 -C kernal_os.cfg kernal_os.o -o kernal_os.prg && echo kernal_os OK\")",
"Bash(ca65 \"c:/Development/Commodore/os Experiments/c64os/samples/kernal-os-skeleton/kernal_os.asm\" -o \"c:/Development/Commodore/os Experiments/c64os/samples/kernal-os-skeleton/kernal_os.o\")",
"Bash(cmd /c build_all.bat)",
"Bash(cmd /c \"build_all.bat && echo BUILD_OK || echo BUILD_FAILED\")",
"Bash(echo \"EXIT: $?\")"
] ]
} }
} }

+ 40
- 0
samples/kernal-os-skeleton/build_all.bat Просмотреть файл

@@ -0,0 +1,40 @@
@echo off
setlocal

set "C1541=C:\Program Files\GTK3VICE-3.10-win64\bin\c1541.exe"
set "DISK=kernal_os.d64"

rem ---- build kernal_os.prg ----
call build.bat
if errorlevel 1 goto error

rem ---- build vim.prg ----
call build_vim.bat
if errorlevel 1 goto error

rem ---- verify c1541 ----
if not exist "%C1541%" (
echo c1541.exe not found at:
echo %C1541%
exit /b 1
)

rem ---- create fresh D64 and write both PRGs ----
"%C1541%" -format "kernal os,01" d64 "%DISK%"
if errorlevel 1 goto error

"%C1541%" "%DISK%" -write "kernal_os.prg" "kernal_os"
if errorlevel 1 goto error

"%C1541%" "%DISK%" -write "vim.prg" "vim"
if errorlevel 1 goto error

echo.
echo built %DISK% (kernal_os + vim)
goto end

:error
echo build failed.
exit /b 1

:end

+ 15
- 0
samples/kernal-os-skeleton/build_vim.bat Просмотреть файл

@@ -0,0 +1,15 @@
@echo off

ca65 vim.asm -o vim.o
if errorlevel 1 goto error

ld65 -C vim.cfg vim.o -o vim.prg
if errorlevel 1 goto error

echo built vim.prg successfully.
goto end

:error
echo build failed.

:end

+ 139
- 6
samples/kernal-os-skeleton/kernal_os.asm Просмотреть файл

@@ -34,6 +34,7 @@ CHKOUT = $FFC9
CLRCH = $FFCC CLRCH = $FFCC
BASIN = $FFCF BASIN = $FFCF
CLALL = $FFE7 CLALL = $FFE7
LOAD = $FFD5


; ------------------------------------------------------------ ; ------------------------------------------------------------
; system locations ; system locations
@@ -55,6 +56,7 @@ INPUT_MAX = 64
DISK_DEV = 8 DISK_DEV = 8
LFN_DIR = 2 LFN_DIR = 2
LFN_CMD = 15 LFN_CMD = 15
EXEC_AREA = $2000
SYS_ENTRY = 2061 SYS_ENTRY = 2061


; ------------------------------------------------------------ ; ------------------------------------------------------------
@@ -1171,12 +1173,137 @@ check_exit:
jmp request_exit jmp request_exit


command_unknown: command_unknown:
lda #<msg_unknown
ldy #>msg_unknown
jmp do_exec

command_empty:
rts

; ------------------------------------------------------------
; external program loader
; ------------------------------------------------------------
; When the typed word does not match any built-in command,
; search the current disk device for a PRG file with that name.
; If found, load it to EXEC_AREA ($2000) and JSR there.
; The external program should end with RTS to return to the shell.
; All external programs must be assembled for address $2000.
; ------------------------------------------------------------

do_exec:
jsr build_exec_filename
bcc exec_try_load
jmp exec_not_found

exec_try_load:
jsr reset_file_state

lda #LFN_DIR
sta active_lfn
ldx current_device
ldy #1
jsr SETLFS

lda filename_len
ldx #<filename_spec
ldy #>filename_spec
jsr SETNAM

lda #0
ldx #<EXEC_AREA
ldy #>EXEC_AREA
jsr LOAD
bcc exec_loaded

jsr reset_file_state
jmp exec_not_found

exec_loaded:
jsr reset_file_state
jsr set_os_memory

lda #<msg_exec_run
ldy #>msg_exec_run
jsr print_string jsr print_string

ldy #0
exec_print_name:
lda input_buffer,y
beq exec_print_done
cmp #' '
beq exec_print_done
jsr CHROUT
iny
bne exec_print_name

exec_print_done:
lda #CR
jsr CHROUT

lda #<EXEC_AREA
sta PTR1_LO
lda #>EXEC_AREA
sta PTR1_HI
jsr call_indirect
jsr set_os_memory
rts rts


command_empty:
exec_not_found:
lda #<msg_exec_nf
ldy #>msg_exec_nf
jsr print_string

ldy #0
exec_nf_name:
lda input_buffer,y
beq exec_nf_done
cmp #' '
beq exec_nf_done
jsr CHROUT
iny
bne exec_nf_name

exec_nf_done:
lda #CR
jsr CHROUT
rts

; JMP (PTR1) — turns a JSR into an indirect call.
; Caller does: JSR call_indirect
; The called code's RTS returns to call_indirect's caller.
call_indirect:
jmp (PTR1_LO)

; build filename_spec = "{drive}:{cmdname}" from input_buffer word 0
build_exec_filename:
ldx #0
lda current_drive
clc
adc #'0'
sta filename_spec,x
inx
lda #':'
sta filename_spec,x
inx

ldy #0

exec_copy_name:
lda input_buffer,y
beq exec_name_done
cmp #' '
beq exec_name_done
sta filename_spec,x
inx
iny
cpx #35
bcc exec_copy_name
sec
rts

exec_name_done:
stx filename_len
lda #0
sta filename_spec,x
clc
rts rts


; ------------------------------------------------------------ ; ------------------------------------------------------------
@@ -1958,9 +2085,6 @@ msg_banner:
msg_prompt: msg_prompt:
.byte "OS> ", 0 .byte "OS> ", 0


msg_unknown:
.byte "UNKNOWN. HELP", CR, 0

msg_help: msg_help:
.byte CR .byte CR
.byte "HELP MEM INC DUMP ZERO SIGN", CR .byte "HELP MEM INC DUMP ZERO SIGN", CR
@@ -1969,6 +2093,7 @@ msg_help:
.byte "HEX RUN RENAME COPY", CR .byte "HEX RUN RENAME COPY", CR
.byte "DEVICE DEV DRIVE DEL ERASE", CR .byte "DEVICE DEV DRIVE DEL ERASE", CR
.byte "ABOUT EXIT", CR .byte "ABOUT EXIT", CR
.byte "OTHER: LOAD+RUN PRG AT $2000", CR
.byte CR .byte CR
.byte 0 .byte 0


@@ -2003,6 +2128,8 @@ msg_about:
.byte "KERNAL STAYS MAPPED IN.", CR .byte "KERNAL STAYS MAPPED IN.", CR
.byte "SHELL CODE STARTS AT $080D.", CR .byte "SHELL CODE STARTS AT $080D.", CR
.byte "USE $A000-$BFFF AS WORK RAM.", CR .byte "USE $A000-$BFFF AS WORK RAM.", CR
.byte "EXTERNAL CMDS LOAD AT $2000.", CR
.byte "ASSEMBLE CMDS WITH *=$2000.", CR
.byte CR .byte CR
.byte 0 .byte 0


@@ -2178,6 +2305,12 @@ msg_copy:
msg_copy_usage: msg_copy_usage:
.byte "USAGE: COPY srcname dstname", CR, 0 .byte "USAGE: COPY srcname dstname", CR, 0


msg_exec_run:
.byte "EXEC: ", 0

msg_exec_nf:
.byte "? ", 0

.segment "BSS" .segment "BSS"


workspace_counter: workspace_counter:


Двоичные данные
samples/kernal-os-skeleton/kernal_os.d64 Просмотреть файл


Двоичные данные
samples/kernal-os-skeleton/kernal_os.o Просмотреть файл


Двоичные данные
samples/kernal-os-skeleton/kernal_os.prg Просмотреть файл


+ 16
- 0
samples/kernal-os-skeleton/run.bat Просмотреть файл

@@ -0,0 +1,16 @@
@echo off
setlocal

set "VICE_EXE=C:\Program Files\GTK3VICE-3.10-win64\bin\x64sc.exe"
set "DISK=kernal_os.d64"

if not exist "%VICE_EXE%" (
echo VICE x64sc was not found at:
echo %VICE_EXE%
exit /b 1
)

call build_all.bat
if errorlevel 1 exit /b 1

"%VICE_EXE%" -autostart "%CD%\%DISK%"

+ 907
- 0
samples/kernal-os-skeleton/vim.asm Просмотреть файл

@@ -0,0 +1,907 @@
;
; vim.asm - minimal modal text editor for the kernal-os-skeleton shell
;
; Build:
; ca65 vim.asm -o vim.o
; ld65 -C vim.cfg vim.o -o vim.prg
;
; From the OS shell type: VIM
; All external commands load at $2000 and end with RTS.
;
; Modes
; NORMAL (default) h j k l or cursor keys = move
; i = insert before cursor
; a = append after cursor
; o = open line below
; x = delete char under cursor
; : = enter command mode
; INSERT type text, DEL = backspace, RUN/STOP = back to NORMAL
; COMMAND :q quit :w NAME save to disk
; RUN/STOP = cancel
;
; Text model: the 24x40 text area of screen RAM IS the buffer.
; Row 24 (bottom line) is the status bar.
;

.setcpu "6502"

; ---- KERNAL ----
GETIN = $FFE4
STOPKEY = $FFE1
CHROUT = $FFD2
SETLFS = $FFBA
SETNAM = $FFBD
OPEN = $FFC0
CLOSE = $FFC3
CHKOUT = $FFC9
CLRCH = $FFCC
CLALL = $FFE7

; ---- hardware ----
SCRRAM = $0400

; ---- layout ----
SCR_COLS = 40
SCR_ROWS = 24 ; rows 0-23 = text, row 24 = status bar

; ---- modes ----
MODE_NORMAL = 0
MODE_INSERT = 1
MODE_CMD = 2

; ---- PETSCII ----
CR_CH = $0D
DEL_CH = $14

; ---- command buffer ----
CMD_MAX = 20

; ---- save LFN ----
SAVE_LFN = 5
SAVE_DEV = 8

; ---- zero page (user area, safe under OS banking) ----
ZP_LO = $FB ; general destination pointer lo
ZP_HI = $FC ; general destination pointer hi
STR_LO = $FD ; string source pointer lo
STR_HI = $FE ; string source pointer hi
ZP_TMP = $02 ; scratch byte

; ================================================================
; PRG load address header
; ================================================================
.segment "LOADADDR"
.word $2000

; ================================================================
; CODE
; ================================================================
.segment "CODE"

; ---- entry ----
vim_main:
jsr CLALL ; close any open channels from shell
jsr vim_init
jsr full_redraw

main_loop:
lda quit_flag
beq ml_no_quit
jmp vim_do_exit
ml_no_quit:

jsr get_key ; waits for keypress; $FF = RUN/STOP
cmp #$FF
bne ml_key_ok
; RUN/STOP pressed
lda cur_mode
beq ml_stop_quit ; normal mode: quit
lda #MODE_NORMAL ; other modes: back to normal
sta cur_mode
jsr full_redraw
jmp main_loop
ml_stop_quit:
lda #1
sta quit_flag
jmp main_loop
ml_key_ok:
sta last_key
lda cur_mode
cmp #MODE_INSERT
bne ml_not_insert
jmp handle_insert
ml_not_insert:
cmp #MODE_CMD
bne ml_not_cmd
jmp handle_cmd
ml_not_cmd:
; fall through = normal mode

; ================================================================
; NORMAL MODE
; ================================================================
handle_normal:
lda last_key
cmp #'H'
beq nm_left
cmp #$9D ; cursor-left hardware key
beq nm_left
cmp #'L'
beq nm_right
cmp #$1D ; cursor-right hardware key
beq nm_right
cmp #'K'
beq nm_up
cmp #$91 ; cursor-up hardware key
beq nm_up
cmp #'J'
beq nm_down
cmp #$11 ; cursor-down hardware key
beq nm_down
cmp #'I'
beq nm_ins
cmp #'A'
beq nm_app
cmp #'X'
beq nm_del
cmp #'O'
beq nm_open
cmp #':'
beq nm_cmd
jmp nm_done
nm_left:
jsr cursor_left
jmp nm_done
nm_right:
jsr cursor_right
jmp nm_done
nm_up:
jsr cursor_up
jmp nm_done
nm_down:
jsr cursor_down
jmp nm_done
nm_ins:
lda #MODE_INSERT
sta cur_mode
jmp nm_done
nm_app:
jsr cursor_right
lda #MODE_INSERT
sta cur_mode
jmp nm_done
nm_del:
jsr delete_char
jmp nm_done
nm_open:
jsr open_line_below
lda #MODE_INSERT
sta cur_mode
jmp nm_done
nm_cmd:
lda #MODE_CMD
sta cur_mode
lda #0
sta cmd_len
nm_done:
jsr full_redraw
jmp main_loop

; ================================================================
; INSERT MODE
; ================================================================
handle_insert:
lda last_key
cmp #CR_CH
beq ins_nl
cmp #DEL_CH
beq ins_bs
cmp #$20 ; ignore control chars below $20
bcc ins_done
cmp #$7F
bcs ins_done
jsr insert_char ; A = PETSCII char to insert
jsr cursor_right
jmp ins_done
ins_nl:
jsr do_newline
jmp ins_done
ins_bs:
jsr backspace_char
ins_done:
jsr full_redraw
jmp main_loop

; ================================================================
; COMMAND MODE
; ================================================================
handle_cmd:
lda last_key
cmp #CR_CH
beq cmd_exec
cmp #DEL_CH
beq cmd_bs
cmp #$20
bcc cmd_done
cmp #$7F
bcs cmd_done
ldx cmd_len
cpx #CMD_MAX
bcs cmd_done
sta cmd_buf,x
inc cmd_len
jmp cmd_done
cmd_bs:
lda cmd_len
beq cmd_done
dec cmd_len
jmp cmd_done
cmd_exec:
jsr run_cmd
cmd_done:
jsr full_redraw
jmp main_loop

; ================================================================
; EXIT
; ================================================================
vim_do_exit:
jsr restore_cursor ; de-invert cursor char before leaving
rts ; return to kernal-os-skeleton shell

; ================================================================
; COMMAND EXECUTION
; ================================================================
run_cmd:
lda cmd_len
beq rc_cancel

; --- :Q quit ---
cmp #1
bne rc_not_q
lda cmd_buf
cmp #'Q'
bne rc_not_q
lda #1
sta quit_flag
lda #MODE_NORMAL
sta cur_mode
rts

rc_not_q:
; --- :W [NAME] save ---
lda cmd_buf
cmp #'W'
bne rc_unknown
jsr do_save
lda #MODE_NORMAL
sta cur_mode
rts

rc_unknown:
; show "?" in status then return to normal
lda #<msg_cmd_err
ldy #>msg_cmd_err
jsr show_status_text
rc_cancel:
lda #MODE_NORMAL
sta cur_mode
rts

; ================================================================
; FILE SAVE
; do_save: reads cmd_buf "W NAME" and writes text to disk
; ================================================================
do_save:
; need at least "W N" (3 chars: W, space, one char of name)
lda cmd_len
cmp #3
bcs ds_len_ok
jmp ds_no_name
ds_len_ok:
lda cmd_buf + 1
cmp #' '
beq ds_space_ok
jmp ds_no_name
ds_space_ok:

; copy filename from cmd_buf[2..] to save_fn
ldy #2
ldx #0
ds_copy_name:
lda cmd_buf,y
beq ds_name_done
cmp #' '
beq ds_name_done
sta save_fn,x
inx
iny
cpx #15
bcc ds_copy_name
ds_name_done:
stx save_fn_len
lda #0
sta save_fn,x

; build "0:NAME,S,W" in full_fn
jsr build_save_fname

; SETLFS
lda #SAVE_LFN
ldx #SAVE_DEV
ldy #2
jsr SETLFS

; SETNAM
lda full_fn_len
ldx #<full_fn
ldy #>full_fn
jsr SETNAM

jsr OPEN
bcc ds_open_ok
; open error
lda #<msg_disk_err
ldy #>msg_disk_err
jsr show_status_text
rts

ds_open_ok:
lda #SAVE_LFN
jsr CHKOUT
bcc ds_write_rows
jsr CLRCH
lda #SAVE_LFN
jsr CLOSE
lda #<msg_disk_err
ldy #>msg_disk_err
jsr show_status_text
rts

ds_write_rows:
lda #0
sta save_row

ds_row_loop:
ldx save_row
lda row_lo,x
sta ZP_LO
lda row_hi,x
sta ZP_HI

; find rightmost non-space column
ldy #SCR_COLS - 1
ds_find_end:
lda (ZP_LO),y
cmp #$20
bne ds_row_found
tya
beq ds_row_empty
dey
jmp ds_find_end

ds_row_empty:
; empty row: just write CR
lda #CR_CH
jsr CHROUT
jmp ds_row_done

ds_row_found:
; write cols 0..Y then CR
sty save_col_end
ldy #0
ds_write_col:
lda (ZP_LO),y
jsr screen_to_petscii
jsr CHROUT
cpy save_col_end
beq ds_write_cr
iny
jmp ds_write_col

ds_write_cr:
lda #CR_CH
jsr CHROUT

ds_row_done:
inc save_row
lda save_row
cmp #SCR_ROWS
bne ds_row_loop

; close
jsr CLRCH
lda #SAVE_LFN
jsr CLOSE

lda #<msg_saved
ldy #>msg_saved
jsr show_status_text
rts

ds_no_name:
lda #<msg_need_name
ldy #>msg_need_name
jsr show_status_text
rts

; builds "0:save_fn,S,W" into full_fn, length into full_fn_len
build_save_fname:
ldx #0
lda #'0'
sta full_fn,x
inx
lda #':'
sta full_fn,x
inx
ldy #0
bsf_loop:
lda save_fn,y
beq bsf_suffix
sta full_fn,x
inx
iny
bne bsf_loop
bsf_suffix:
lda #','
sta full_fn,x
inx
lda #'S'
sta full_fn,x
inx
lda #','
sta full_fn,x
inx
lda #'W'
sta full_fn,x
inx
stx full_fn_len
lda #0
sta full_fn,x
rts

; ================================================================
; CURSOR MOVEMENT
; ================================================================
cursor_left:
lda cur_col
beq cl_wrap
dec cur_col
rts
cl_wrap:
lda cur_row
beq cl_top
dec cur_row
lda #SCR_COLS - 1
sta cur_col
cl_top: rts

cursor_right:
lda cur_col
cmp #SCR_COLS - 1
bcs cr_wrap
inc cur_col
rts
cr_wrap:
lda cur_row
cmp #SCR_ROWS - 1
bcs cr_bot
inc cur_row
lda #0
sta cur_col
cr_bot: rts

cursor_up:
lda cur_row
beq cu_top
dec cur_row
cu_top: rts

cursor_down:
lda cur_row
cmp #SCR_ROWS - 1
bcs cd_bot
inc cur_row
cd_bot: rts

; ================================================================
; TEXT EDITING
; ================================================================

; Set ZP_LO/ZP_HI to the screen RAM base for cur_row
row_ptr:
ldx cur_row
lda row_lo,x
sta ZP_LO
lda row_hi,x
sta ZP_HI
rts

; Insert PETSCII char (in A) at cursor; shift rest of line right (last char lost)
insert_char:
jsr petscii_to_screen
sta ZP_TMP
jsr row_ptr
ldy #SCR_COLS - 1
ic_shift:
cpy cur_col
beq ic_write
dey
lda (ZP_LO),y
iny
sta (ZP_LO),y
dey
jmp ic_shift
ic_write:
lda ZP_TMP
ldy cur_col
sta (ZP_LO),y
rts

; Delete char at cursor; shift rest of line left, fill end with space
delete_char:
jsr row_ptr
ldy cur_col
dc_shift:
cpy #SCR_COLS - 1
beq dc_space
iny
lda (ZP_LO),y
dey
sta (ZP_LO),y
iny
jmp dc_shift
dc_space:
lda #$20
sta (ZP_LO),y
rts

; Backspace: move cursor left then delete
backspace_char:
lda cur_col
beq bs_done
jsr cursor_left
jsr delete_char
bs_done:rts

; RETURN in insert mode: move to col 0 of next row
do_newline:
lda cur_row
cmp #SCR_ROWS - 1
bcs nl_done
inc cur_row
lda #0
sta cur_col
nl_done:rts

; Open new line below: advance row, clear it, col = 0
open_line_below:
lda cur_row
cmp #SCR_ROWS - 1
bcs ol_done
inc cur_row
lda #0
sta cur_col
jsr clear_current_row
ol_done:rts

clear_current_row:
jsr row_ptr
ldy #0
lda #$20
ccr_lp: sta (ZP_LO),y
iny
cpy #SCR_COLS
bne ccr_lp
rts

; Clear all 24 text rows to spaces
clear_text:
lda #0
sta ZP_TMP
ct_loop:
ldx ZP_TMP
lda row_lo,x
sta ZP_LO
lda row_hi,x
sta ZP_HI
ldy #0
lda #$20
ct_row: sta (ZP_LO),y
iny
cpy #SCR_COLS
bne ct_row
inc ZP_TMP
lda ZP_TMP
cmp #SCR_ROWS
bne ct_loop
rts

; ================================================================
; SCREEN RENDERING
; ================================================================

; Restore the saved char at (prev_row, prev_col)
restore_cursor:
ldx prev_row
lda row_lo,x
clc
adc prev_col
sta ZP_LO
lda row_hi,x
adc #0
sta ZP_HI
lda prev_char
ldy #0
sta (ZP_LO),y
rts

; Show cursor by inverting char at (cur_row, cur_col); saves old char
show_cursor:
ldx cur_row
lda row_lo,x
clc
adc cur_col
sta ZP_LO
lda row_hi,x
adc #0
sta ZP_HI
ldy #0
lda (ZP_LO),y
sta prev_char
ora #$80 ; set bit 7 = reversed character
sta (ZP_LO),y
lda cur_row
sta prev_row
lda cur_col
sta prev_col
rts

; Draw status bar at row 24
draw_status:
; clear status row
lda row_lo + 24
sta ZP_LO
lda row_hi + 24
sta ZP_HI
ldy #0
lda #$20
dstat_clr:
sta (ZP_LO),y
iny
cpy #SCR_COLS
bne dstat_clr

lda cur_mode
cmp #MODE_INSERT
beq dstat_ins
cmp #MODE_CMD
beq dstat_cmd

; NORMAL
lda #<str_normal
ldy #>str_normal
jsr write_str_to_status
rts

dstat_ins:
lda #<str_insert
ldy #>str_insert
jsr write_str_to_status
rts

dstat_cmd:
; write ":" then cmd_buf
lda row_lo + 24
sta ZP_LO
lda row_hi + 24
sta ZP_HI
lda #$3A ; screen code for ':'
ldy #0
sta (ZP_LO),y
iny
ldx #0
dstat_cmd_lp:
cpx cmd_len
beq dstat_done
lda cmd_buf,x
jsr petscii_to_screen
sta (ZP_LO),y
iny
inx
cpy #SCR_COLS
bcc dstat_cmd_lp
dstat_done:
rts

; Write null-terminated PETSCII string (A=lo, Y=hi) to status row
write_str_to_status:
sta STR_LO
sty STR_HI
lda row_lo + 24
sta ZP_LO
lda row_hi + 24
sta ZP_HI
ldy #0
wss_lp:
lda (STR_LO),y
beq wss_done
jsr petscii_to_screen
sta (ZP_LO),y
iny
cpy #SCR_COLS
bcc wss_lp
wss_done:
rts

; Copy a PETSCII string directly to status (no screen-code conversion)
; Used for messages that already have screen codes (msg_saved etc.)
; Actually we just reuse write_str_to_status since it converts
show_status_text:
jsr write_str_to_status
rts

; Restore cursor, redraw cursor, redraw status bar
full_redraw:
jsr restore_cursor
jsr show_cursor
jsr draw_status
rts

; ================================================================
; INIT
; ================================================================
vim_init:
lda #0
sta cur_row
sta cur_col
sta prev_row
sta prev_col
sta cur_mode
sta cmd_len
sta quit_flag
sta save_row
sta save_col_end
lda #$20 ; cursor starts on a space
sta prev_char
jsr clear_text
rts

; ================================================================
; KEY INPUT
; ================================================================
get_key:
gk_loop:
jsr STOPKEY
bne gk_getin
lda #$FF ; RUN/STOP = special exit code
rts
gk_getin:
jsr GETIN
beq gk_loop ; no key yet, keep polling
rts

; ================================================================
; CHARACTER CONVERSION
; ================================================================

; PETSCII → screen code
; $41-$5A (A-Z unshifted) → $01-$1A (subtract $40)
; $61-$7A (a-z lowercase) → $01-$1A (subtract $60)
; $20-$3F (space,nums,punct) → same
; others: pass through
petscii_to_screen:
cmp #$41
bcc pts_pass
cmp #$5B
bcc pts_upper
cmp #$61
bcc pts_pass
cmp #$7B
bcc pts_lower
pts_pass:
rts
pts_upper:
sec
sbc #$40
rts
pts_lower:
sec
sbc #$60
rts

; screen code → PETSCII (inverse of above)
; $01-$1A → $41-$5A (add $40)
; $20-$3F → same
; $00 → '@'
; others: return space $20
screen_to_petscii:
cmp #$01
bcc stp_at
cmp #$1B
bcc stp_letter
cmp #$20
bcc stp_space
cmp #$40
bcc stp_pass
stp_space:
lda #$20
rts
stp_at:
lda #$40 ; '@'
rts
stp_letter:
clc
adc #$40
rts
stp_pass:
rts

; ================================================================
; RODATA
; ================================================================
.segment "RODATA"

str_normal:
.byte "NORMAL (hjkl=move i=ins x=del :=cmd)", 0

str_insert:
.byte "INSERT (type text, DEL=bksp, STOP=normal)", 0

msg_cmd_err:
.byte "? unknown command", 0

msg_saved:
.byte "saved.", 0

msg_disk_err:
.byte "disk error.", 0

msg_need_name:
.byte "usage: :w filename", 0

; Row-base lookup tables (25 entries: rows 0-23 = text, row 24 = status)
row_lo:
.byte <(SCRRAM + 0*40), <(SCRRAM + 1*40), <(SCRRAM + 2*40)
.byte <(SCRRAM + 3*40), <(SCRRAM + 4*40), <(SCRRAM + 5*40)
.byte <(SCRRAM + 6*40), <(SCRRAM + 7*40), <(SCRRAM + 8*40)
.byte <(SCRRAM + 9*40), <(SCRRAM + 10*40), <(SCRRAM + 11*40)
.byte <(SCRRAM + 12*40), <(SCRRAM + 13*40), <(SCRRAM + 14*40)
.byte <(SCRRAM + 15*40), <(SCRRAM + 16*40), <(SCRRAM + 17*40)
.byte <(SCRRAM + 18*40), <(SCRRAM + 19*40), <(SCRRAM + 20*40)
.byte <(SCRRAM + 21*40), <(SCRRAM + 22*40), <(SCRRAM + 23*40)
.byte <(SCRRAM + 24*40)

row_hi:
.byte >(SCRRAM + 0*40), >(SCRRAM + 1*40), >(SCRRAM + 2*40)
.byte >(SCRRAM + 3*40), >(SCRRAM + 4*40), >(SCRRAM + 5*40)
.byte >(SCRRAM + 6*40), >(SCRRAM + 7*40), >(SCRRAM + 8*40)
.byte >(SCRRAM + 9*40), >(SCRRAM + 10*40), >(SCRRAM + 11*40)
.byte >(SCRRAM + 12*40), >(SCRRAM + 13*40), >(SCRRAM + 14*40)
.byte >(SCRRAM + 15*40), >(SCRRAM + 16*40), >(SCRRAM + 17*40)
.byte >(SCRRAM + 18*40), >(SCRRAM + 19*40), >(SCRRAM + 20*40)
.byte >(SCRRAM + 21*40), >(SCRRAM + 22*40), >(SCRRAM + 23*40)
.byte >(SCRRAM + 24*40)

; ================================================================
; BSS (zero-initialised by the OS / KERNAL CINT on cold start;
; vim_init zeros what it cares about explicitly)
; ================================================================
.segment "BSS"

cur_row: .res 1
cur_col: .res 1
prev_row: .res 1
prev_col: .res 1
prev_char: .res 1 ; screen code saved before cursor inversion
cur_mode: .res 1
cmd_len: .res 1
cmd_buf: .res CMD_MAX + 1
last_key: .res 1
quit_flag: .res 1
save_row: .res 1
save_col_end: .res 1
save_fn: .res 16 ; extracted filename (no path)
save_fn_len: .res 1
full_fn: .res 24 ; "0:filename,S,W"
full_fn_len: .res 1

+ 11
- 0
samples/kernal-os-skeleton/vim.cfg Просмотреть файл

@@ -0,0 +1,11 @@
MEMORY {
LOADADDR: start = $1FFE, size = $0002, type = ro, file = %O;
MAIN: start = $2000, size = $7FFF, type = rw, file = %O;
}

SEGMENTS {
LOADADDR: load = LOADADDR, type = ro;
CODE: load = MAIN, type = ro;
RODATA: load = MAIN, type = ro;
BSS: load = MAIN, type = bss, define = yes;
}

Двоичные данные
samples/kernal-os-skeleton/vim.o Просмотреть файл


Двоичные данные
samples/kernal-os-skeleton/vim.prg Просмотреть файл


Загрузка…
Отмена
Сохранить

Powered by TurnKey Linux.