From a34ad824444ba4909eda6cb80741314e08606b36 Mon Sep 17 00:00:00 2001 From: Joppe Blondel Date: Fri, 9 Oct 2026 16:13:50 +0200 Subject: [PATCH] Added start of csave command and bios write block --- apps/basic/basic.asm | 112 ++++++++++++++++++++++++++++++- bios.asm | 29 +++++++- bios.inc | 5 +- tape.asm | 153 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 296 insertions(+), 3 deletions(-) diff --git a/apps/basic/basic.asm b/apps/basic/basic.asm index 1ac03f4..bbaf2e3 100644 --- a/apps/basic/basic.asm +++ b/apps/basic/basic.asm @@ -427,7 +427,7 @@ WORDTB: .WORD PEND .WORD LIST .WORD CLEAR .WORD CLOAD - .WORD FCERR ; csave + .WORD CSAVE .WORD NEW ; RESERVED WORD TOKEN VALUES @@ -4772,4 +4772,114 @@ CLNOHDRMSG: .db "HE", 0ah,0dh,00h CLBLOCKSMSG: .db " blocks", 0ah,0dh,00h CLCLRF: .db 0ah,0dh,00h +CSAVE: ld a,(hl) ; get byte after "CSAVE" + cp ZTIMES ; "*" token? ("CSAVE*") + jp z,FCERR ; yes - array load (ARRLD1) + cp ZPRINT ; "?" ("PRINT token") verify + jp z,FCERR ; yes - flag "verify" + cp '#' ; 'csave#'? + jp z,CSAVEB ; yes - save binary + ; just cload - load basic + jp FCERR + +CSAVEB: inc hl ; skip # + push bc ; save bc + push de ; save de + CALL GETNUM ; Get start address + CALL DEINT ; Convert it to 16 bits + PUSH DE ; Save start address + CALL CHKSYN ; Make sure "," follows + .BYTE ',' + CALL GETNUM ; Get length + CALL DEINT ; Convert it to 16 bits + PUSH DE ; Save length + CALL CHKSYN ; Make sure "," follows + .BYTE ',' + ; hl points to the first character of the string expression + call EVAL + push hl ; save command buffer pos + exx + call GETSTR + call LOADFP ; bc' = addr, e' = length + exx + pop hl ; get back command buffer pos + pop bc ; get length + pop de ; get start address + push hl + push de + call BIOS_CAS_GET_BUFFER + ; hl=buffer, bc=lenght, de=start address + ; Write header block + ld a,1 ; block type header + ld (hl),a + inc hl + call BYTESTOBLOCKS + push bc + ld a,b ; nr of blocks low byte + ld (hl),a + inc hl + ld a,c ; nr of blocks high byte + ld (hl),a + inc hl + push hl + exx +1$: push bc + pop hl ; get str addr to hl + ld a,(hl) + inc hl + push hl + pop bc ; get str addr+1 back into bc + pop hl ; get hl to hl' + ld (hl),a ; save character to buffer + inc hl + push hl + dec e ; count down str len + cp 0 + jr nz,1$ ; repeat for the whole string + exx + pop hl ; get back buffer addr + ld a,0 ; NULL terminate string + ld (hl),a + inc hl + ; Header block complete (rest of block is accepted garbage) + call BIOS_CAS_WRITE_BLOCK + + pop bc ; get back block count + pop de ; get back start address + + pop hl + pop de + pop bc + ret + +; ------------------------------------------------------- +; Convert byte count to required number of 128-byte blocks +; needs bc (length in bytes) +; returns bc (number of blocks) +; uses a +; ------------------------------------------------------- +BYTESTOBLOCKS: + ld a,c + and 07fh ; remember partial-block remainder + + srl b ; BC = BC / 128 + rr c + srl b + rr c + srl b + rr c + srl b + rr c + srl b + rr c + srl b + rr c + srl b + rr c + + or a + ret z ; exact multiple of 128 + inc bc ; include partial final block + ret + .end diff --git a/bios.asm b/bios.asm index 3e22f6f..dd2ccf7 100644 --- a/bios.asm +++ b/bios.asm @@ -106,6 +106,12 @@ boot_copy: jp wait_silence .ORG BIOS_CAS_READ_BLOCK_DRY jp read_block_dry +.ORG BIOS_CAS_GET_BUFFER + jp get_buffer +.ORG BIOS_CAS_WRITE_BLOCK + jp write_block +.ORG BIOS_DELAY_MS + jp delay_ms ; ------------------------------------------------------- ; BIOS ENTRY @@ -528,6 +534,27 @@ bank: out (SYSTEM_REGISTER),a jr 2$ +; ------------------------------------------------------- +; Delay a certain miliseconds +; needs a (ms, zero returns immediately) +; uses b,c +; @1MHz each iteration takes 1000T plus 23T overhead +; ------------------------------------------------------- +delay_ms: + or a ; 4T + ret z ; 5T when not taken + ld b,a ; 4T +1$: + ld c,61 ; 7T +2$: + dec c ; 4T + jr nz,2$ ; 12/7T, loop takes 971T + nop ; 4T + nop ; 4T + dec b ; 4T + jp nz,1$ ; 10T + ret + ; ------------------------------------------------------- ; Strings ; ------------------------------------------------------- @@ -536,4 +563,4 @@ msg_header: .db "BIOS v1.0 - Joppe Blondel",00dh,00ah,000h ; ------------------------------------------------------- ; Other files ; ------------------------------------------------------- -#include "tape.asm" \ No newline at end of file +#include "tape.asm" diff --git a/bios.inc b/bios.inc index 83ea311..27ec44b 100644 --- a/bios.inc +++ b/bios.inc @@ -7,4 +7,7 @@ BIOS_ROM .equ 0c05fh BIOS_BANK .equ 0c062h BIOS_CAS_READ_BLOCK .equ 0c065h BIOS_CAS_WAIT_SILENCE .equ 0c068h -BIOS_CAS_READ_BLOCK_DRY .equ 0c06bh \ No newline at end of file +BIOS_CAS_READ_BLOCK_DRY .equ 0c06bh +BIOS_CAS_GET_BUFFER .equ 0c06eh +BIOS_CAS_WRITE_BLOCK .equ 0c071h +BIOS_DELAY_MS .equ 0c074h \ No newline at end of file diff --git a/tape.asm b/tape.asm index 8ab56a6..3e12d5a 100644 --- a/tape.asm +++ b/tape.asm @@ -557,6 +557,159 @@ wait_silence: ret ; ------------------------------------------------------- +; Return the buffer address in hl +; ------------------------------------------------------- +get_buffer: + ld hl,BLOCK_BUFFER + ret +; ------------------------------------------------------- +; Generate a short pulse halve +; needs d (output state) +; uses a +; before edge 20T +; after edge 402T +; ------------------------------------------------------- +gen_short: + ld a,d ; 4T + xor 1 ; 7T + out (TAPE_PORT),a ; 12T + ld d,a ; 4T + ld a,18 ; 7T +1$: ; loop: 21*a + dec a ; 4T + cp 0 ; 7T + jp nz,1$ ; 10T + ret ; 10T + +; ------------------------------------------------------- +; Generate a long pulse +; needs d (output state) +; uses a +; before edge 20T +; after edge 801T +; ------------------------------------------------------- +gen_long: + ld a,d ; 4T + xor 1 ; 7T + out (TAPE_PORT),a ; 12T + ld d,a ; 4T + ld a,37 ; 7T +1$: ; loop: 21*a + dec a ; 4T + cp 0 ; 7T + jp nz,1$ ; 10T + ret ; 10T + +; ------------------------------------------------------- +; Generate a bit +; needs carry (bit value) +; needs d (output state) +; uses a +; ------------------------------------------------------- +gen_bit: + jr c,1$ ; carry set: generate a one + call gen_short ; zero: short, then long + jp gen_long ; tail call returns to caller +1$: + call gen_long ; one: long, then short + jp gen_short ; tail call returns to caller + +; ------------------------------------------------------- +; Generate a byte +; needs a (byte to generate) +; needs d (output state) +; uses a,b,c +; ------------------------------------------------------- +gen_byte: + ld c,a ; preserve the byte + ld b,8 ; 8 bits +1$: + sla c ;shift bit 7 into carry + call gen_bit + djnz 1$ ; repeat till b=0 + ret + +; ------------------------------------------------------- +; Generate the leader +; needs d (output state) +; uses a,b,c +; ------------------------------------------------------- +gen_leader: + ; short leader + ld b,50 ; generate 50 short pulses +1$: + call gen_short + dec b + ld a,b + cp 0 + jr nz,1$ ; repeat if not done + ; long leader + ld b,50 ; generate 50 long pulses +2$: + call gen_long + dec b + ld a,b + cp 0 + jr nz,2$ ; repeat if not done + ; marker + ld b,8 ; generate 8 short pulses +3$: + call gen_short + dec b + ld a,b + cp 0 + jr nz,3$ ; repeat if not done + ; training byte + ld a,055h + call gen_byte + ret + +; ------------------------------------------------------- +; Write a block +; Writes the block stored in the block buffer +; ------------------------------------------------------- +write_block: + push de + push bc + ; calculate the crc16 over the block + ld hl, BLOCK_BUFFER + ld a, BLOCK_SIZE-2 ; dont use crc value in block + call crc16 + ex de,hl + ld hl, BLOCK_BUFFER+BLOCK_SIZE-2 + ld (hl),de ; save crc to block + ; Write the block + ld a,0 ; Start with a low output + ld d,a + out (TAPE_PORT),a + call gen_leader + ; Generate synchronization bytes + ld a,05ah + call gen_byte + ld a,0c3h + call gen_byte + ; write the block buffer + ld hl, BLOCK_BUFFER + ld e, BLOCK_SIZE +1$: + ld a,(hl) ; get byte from buffer + inc hl + call gen_byte + dec e + ld a,e + cp 0 + jr nz,1$ ; repeat till done + ; Done with buffer, write 0 byte at last + ld a,0 + call gen_byte + ; Generate space + ld a,200 + call delay_ms + + pop bc + pop de + ret +; ------------------------------------------------------- ; Strings ; ------------------------------------------------------- msg_start: .db "PLAY TAPE", 0ah,0dh,00h