From 71e242fb9b3c079a9169e8bc5856bc81b7a50bff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Sun, 21 Sep 2025 12:24:45 +0200 Subject: [PATCH 01/34] Archival commit of non-finished port. Disk read/write remains to be implemented --- arch/nanoZ80/README.md | 5 + arch/nanoZ80/bios.z80 | 257 +++++++++++++++++++++++++++++++ arch/nanoZ80/boot.z80 | 24 +++ arch/nanoZ80/build.py | 74 +++++++++ arch/nanoZ80/include/nanoZ80.lib | 72 +++++++++ build.py | 3 +- diskdefs | 11 ++ 7 files changed, 445 insertions(+), 1 deletion(-) create mode 100644 arch/nanoZ80/README.md create mode 100644 arch/nanoZ80/bios.z80 create mode 100644 arch/nanoZ80/boot.z80 create mode 100644 arch/nanoZ80/build.py create mode 100644 arch/nanoZ80/include/nanoZ80.lib diff --git a/arch/nanoZ80/README.md b/arch/nanoZ80/README.md new file mode 100644 index 0000000..b3e684c --- /dev/null +++ b/arch/nanoZ80/README.md @@ -0,0 +1,5 @@ +Platform: the nano-Z80 +====================== + +Port for a simple FPGA Z80 SoC + diff --git a/arch/nanoZ80/bios.z80 b/arch/nanoZ80/bios.z80 new file mode 100644 index 0000000..992e8cb --- /dev/null +++ b/arch/nanoZ80/bios.z80 @@ -0,0 +1,257 @@ +; nano-z80 cpmish BIOS © 2025 Henrik Löfgren +; This file is distributable under the terms of the 2-clause BSD license. +; See COPYING.cpmish in the distribution root directory for more information. + + maclib cpm + maclib nanoZ80 + maclib cpmish + + cseg +label BBASE + +; BIOS jump table. + + jp BOOTE + jp WBOOTE + jp CONSTE + jp CONINE + jp CONOUTE + jp LISTE + jp PUNCHE + jp READERE + jp HOMEE + jp SELDSKE + jp SETTRKE + jp SETSECE + jp SETDMAE + jp READE + jp WRITEE + jp LISTSTE + jp SECTRANE + +; Actual BIOS entrypoints. +; +; The BIOS calls typically use a simple calling convention where the parameter +; is in BC and the result is returned in A and HL. The docs don't mention +; anything about other registers so we'll assume they can be corrupted. In +; addition, our syscall stuff assumes that on return A = L, just like in the +; BDOS, so we have a single parameter and a single response --- all very simple. + +; Cold boot on system startup. +BOOTE: + di + ld a, $1 + out (ROM_DISABLE), a ; disable ROM + ld sp, 0x0100 ; ephemeral startup stack + ei + + ld a, $0 ; set default IOBYTE (not implemented) + ld (IOBYTE), a + + xor a + ld (CDISK), a ; clear current disk / user + + ;ld a, (baudrate) ; initialise serial hardware + ;out (PORT_SERIAL_BAUD), a + + call print + db 0x1a ; clear screen + cpmish_banner 'nanoZ80' + db 0 + ; fall through +call_ccp: + ld a, 0xc3 + ld hl, BBASE + 3 ; init BIOS entrypoint + ld (0x0000), a + ld (0x0001), hl + + ld hl, FBASE + 6 ; init BDOS entrypoint + ld (0x0005), a + ld (0x0006), hl + + ld a, (CDISK) ; current selected disk + ld c, a + jp CBASE ; pass control to CCP + +;stop_disk_motor: +; in a, (PORT_BITPORT) +; or 0x40 ; turn off motor +; out (PORT_BITPORT) +; ret + +;reset_disk_system: +; ld l, 0x03 +; jp syscall + +; Warm boot on application exit. +WBOOTE: + ld sp, 0x0100 ; ephemeral user stack + + ld c, 0 ; select drive 0 + call SELDSKE + ld bc, 0 + call SETTRKE ; select track 0 + + ld hl, CBASE ; location to load + ld bc, 1 ; first sector to load + + ; We just load track 0 (first 8 kB) +boot_loop: + push bc + push hl + + call SETSECE ; set sector to load + + pop bc ; DMA address into BC + push bc + call SETDMAE ; set address + call READE ; actually load the sector + + pop hl ; DMA address back into HL + ld bc, 128 + add hl, bc + + pop bc ; current sector back into bc + ld a, c + inc c + cp 63 ; end of track 0 + jr nz, boot_loop + jr call_ccp + +CONSTE: + in a, (UART_RX_AVAIL) + bit 0, a + ret z + + ld a, 0xff + + ret + +CONINE: + in a, (UART_RX_AVAIL) + bit 0, a + jr z, CONINE + + in a, (UART_RX_DATA) + + ret + +CONOUTE: + in a, (UART_TX_READY) + bit 0, a + jr z, CONOUTE + + ld a,c + out (UART_TX_DATA),a + + ret + + ; Not implemented yet +LISTE: + ; fall through +PUNCHE: + ret + +LISTSTE: + ld a, 0xff + ret + +READERE: + ld a, 0x1a ; Ctrl+z means not implemented + ret + +; Selects a drive, returning the address of the DPH in HL (or 0x0000 on +; error). +SELDSKE: + ld (BDISK), a + + ld hl, drive_a_dph + or a ; Test for 0 + ret z + + ld hl, 0 + ret + +HOMEE: + ld bc, 0 + ; fall through + +SETTRKE: + ld (BTRACK), bc + ret + +SETSECE: + ld (BSECTOR), bc + ret + +SETDMAE: + ld (BDMA), bc + ret + +READE: + ; To be implemented + ret + +WRITEE: + ; To be implemented + ret + +SECTRANE: + ld h, b + ld l, c + ret + +call_hl: + jp (hl) + +; Prints the text immediately following the call to print. +print: + pop hl ; return address points to text to print + ld a, (hl) + inc hl + push hl ; save address after current char + + or a + ret z ; if byte was zero, return + ld c, a + call CONOUTE + jr print + + +drive_a_dph: + dw 0 ; Sector translation vector + dw 0, 0, 0 ; BDOS scratchpad + dw dirbuf ; Directory scratchpad + dw DRVADPB ; Drive parameter block + dw 0 ; Disk change check vector + dw drive_a_bitmap ; Allocation bitmap + +; The drive is sized so that each CP/M track occupies 256 physical sectors. +; This makes the supervisor maths easier as we can just drop the bottom byte +; from the LBA sector count. Each track is therefore 128kB. +label DRVADPB + dw 128 ; Number of CP/M sectors per track + db 4, 15 ; BSH/BLM for 2048-byte blocks + db 0 ; EXM for 2048-byte allocation units and >255 blocks + dw DRIVE_A_BLOCKS-1 ; DSM + dw 127 ; DRM, one fewer than the number of directory entries + db 0xc0, 0x00 ; Initial allocation vector for one directory block + dw 0 ; Size of disk change check vector: zero as this is a fixed disk + dw 1 ; Number of reserved tracks + +drive_a_bitmap: + ds (DRIVE_A_BLOCKS+7) / 8 +dirbuf: + ds 128 + +label BDISK + db 0 +label BTRACK + dw 0 +label BSECTOR + dw 0 +label BDMA + dw 0 + +saved_stack: dw 0 ; user stack pointer while in system calls + diff --git a/arch/nanoZ80/boot.z80 b/arch/nanoZ80/boot.z80 new file mode 100644 index 0000000..0754621 --- /dev/null +++ b/arch/nanoZ80/boot.z80 @@ -0,0 +1,24 @@ +; nano-z80 cpmish BIOS © 2025 Henrik Löfgren +; This file is distributable under the terms of the 2-clause BSD license. +; See COPYING.cpmish in the distribution root directory for more information. + +; This is the boot sector setting where to load from the SD card and where in +; RAM to place the image + + ; Magic numbers + db 0x6e + db 0x61 + db 0x6e + db 0x6f + + ; Image SD card sector + db 0x01 + db 0x00 + db 0x00 + db 0x01 + + ; Image length + db 0x01 + + ; RAM page to load to + db 0xe4 diff --git a/arch/nanoZ80/build.py b/arch/nanoZ80/build.py new file mode 100644 index 0000000..3b98018 --- /dev/null +++ b/arch/nanoZ80/build.py @@ -0,0 +1,74 @@ +from build.ab import simplerule +from build.cpm import diskimage +from utils.build import unix2cpm +from third_party.zmac.build import zmac +from third_party.ld80.build import ld80 + + +zmac(name="boot", src="./boot.z80", relocatable=False) + +zmac( + name="bios", + src="./bios.z80", + deps=[ + "arch/nanoZ80/include/nanoZ80.lib", + "include/cpm.lib", + "include/cpmish.lib", + ], +) + +# Builds the memory image. +ld80( + name="bootfile_mem", + objs={ + 0xE400: ["third_party/zcpr1"], + 0xEC00: ["third_party/zsdos"], + 0xFA00: [".+bios"], + }, +) + +# Repackages the memory image as a boot track. This doesn't include the extra +# section of boot image which exists above the directory. +simplerule( + name="bootfile", + ins=[".+boot", ".+bootfile_mem"], + outs=["=bootfile.img"], + commands=[ + "dd if={ins[0]} of={outs[0]} bs=128 count=1 2> /dev/null", + "dd if={ins[1]} of={outs[0]} bs=128 seek=4 skip=454 count=56 2> /dev/null", + ], + label="nanoZ80", +) + +unix2cpm(name="readme", src="README.md") + +diskimage( + name="diskimage", + format="generic-1m", + bootfile=".+bootfile", + map={ + "-readme.txt": ".+readme", + "dump.com": "cpmtools+dump", + "stat.com": "cpmtools+stat", + "asm.com": "cpmtools+asm", + "copy.com": "cpmtools+copy", + "submit.com": "cpmtools+submit", + "bbcbasic.com": "third_party/bbcbasic+bbcbasic_ADM3A", + "camel80.com": "third_party/camelforth", + "qe.com": "cpmtools+qe_KAYPROII", + }, +) + +# Patches the special extra bit of BDOS/BIOS into the area above the +# directory; yuch. +#simplerule( +# name="diskimage", +# ins=[".+partialimg", ".+bootfile_mem"], +# outs=["=diskimage.img"], +# commands=[ +# "cp {ins[0]} {outs[0]}", +# "chmod +w {outs[0]}", +# "dd if={ins[1]} of={outs[0]} bs=128 seek=56 skip=495 count=9 conv=notrunc 2> /dev/null", +# ], +# label="MKKAYPRO2", +#) diff --git a/arch/nanoZ80/include/nanoZ80.lib b/arch/nanoZ80/include/nanoZ80.lib new file mode 100644 index 0000000..2bad534 --- /dev/null +++ b/arch/nanoZ80/include/nanoZ80.lib @@ -0,0 +1,72 @@ +; nano-Z80 defines + +; Global addresses +RAM_END equ $FFFF ; Top of RAM +INPUT_BUF equ $FF00 ; Monitor input buffer +PAGE_CNT equ $FFFE ; SD card page counter +BOOT_PAGE equ $FFFF ; RAM page to boot from + +; UART IO ports +UART_TX_DATA equ $70 ; UART transmit data +UART_TX_READY equ $71 ; UART transmit ready +UART_RX_DATA equ $72 ; UART receive data +UART_RX_AVAIL equ $73 ; UART receive data available + +; IO mux ports and constants +ROM_DISABLE equ $7e ; ROM disable register, 1=ROM disabled +IO_BANK equ $7f ; IO bank selection register + +IO_SELECT_LEDS equ $00 ; IO bank value for LEDs +IO_SELECT_GPIO equ $01 ; IO bank value for GPIO +IO_SELECT_USB equ $02 ; IO bank value for USB HID +IO_SELECT_SD equ $03 ; IO bank balue for SD Card + +; LEDs IO ports +LEDS_REG equ $00 ; LED registers, bits 0-6 control one LED each +WS2812_R equ $01 ; WS2812 RED +WS2812_G equ $02 ; WS2812 GREEN +WS2812_B equ $03 ; WS2812 BLUE + +; GPIO IO ports +GPIO_DATA1 equ $00 ; GPIO data register 1 +GPIO_DATA2 equ $01 ; GPIO data register 2 +GPIO_DIR1 equ $02 ; GPIO direction register 1 +GPIO_DIR2 equ $03 ; GPIO direction register 2 + +; USB HID IO ports +USB_KEY_AVAIL equ $00 ; USB keyboard input available +USB_KEY_CHAR equ $01 ; USB keyboard data register +USB_KEY_MOD equ $02 ; USB keyboard modifier keys +USB_MOUSE_BUT equ $03 ; USB mouse buttons +USB_MOUSE_DX equ $04 ; USB mouse dx +USB_MOUSE_DY equ $05 ; USB mouse dy +USB_GAME_BUT1 equ $06 ; USB gamepad buttons reg 1 +USB_GAME_BUT2 equ $07 ; USB gamepad buttons reg 2 +USB_NEW_REP equ $08 ; USB new report available +USB_DEV_TYPE equ $09 ; USB device type +USB_ERROR equ $0a ; USB error +KB_AVAIL equ $74 ; Keyboard character available, non-banked +KB_CHAR equ $75 ; Keyboard character, non-banked + +; SD Card interface IO ports +SD_ADDR_0 equ $00 ; SD card address (LSB) +SD_ADDR_1 equ $01 ; .. +SD_ADDR_2 equ $02 ; .. +SD_ADDR_3 equ $03 ; SD card address (MSB) +SD_BUSY equ $04 ; SD card busy flag +SD_READ equ $05 ; SD card read strobe +SD_WRITE equ $06 ; SD card write stribe +SD_PAGE equ $07 ; Select data page (0-3) +SD_STATUS equ $08 ; SD card status +SD_TYPE equ $09 ; SD card type +SD_DATA equ $80 ; Start of data page ($80-$FF) + +; Disk definitions +DRIVE_A_SIZE = 1024 ; kB +DRIVE_A_BLOCKS = DRIVE_A_SIZE / 2 ; 2 kB blocks + +SD_OFFSET_4 = 0x01 +SD_OFFSET_3 = 0x00 +SD_OFFSET_2 = 0x00 +SD_OFFSET_1 = 0x01 + diff --git a/build.py b/build.py index 5110c9e..ec67771 100644 --- a/build.py +++ b/build.py @@ -9,6 +9,7 @@ "lw30.img": "arch/brother/lw30+diskimage", "wp1.img": "arch/brother/wp1+diskimage", "kayproii.img": "arch/kayproii+diskimage", - "nc200.img": "arch/nc200+diskimage" + "nc200.img": "arch/nc200+diskimage", + "nanoZ80.img": "arch/nanoZ80+diskimage" }, ) diff --git a/diskdefs b/diskdefs index 913ab9b..9f488f5 100644 --- a/diskdefs +++ b/diskdefs @@ -91,5 +91,16 @@ diskdef brother-powernote os 2.2 end +# Generic 1 Meg image used for the nanoZ80 +diskdef generic-1m + seclen 128 + tracks 128 + sectrk 64 + blocksize 2048 + maxdir 128 + boottrk 1 + os 2.2 +end + # vim: set ts=4 sw=4 expandtab From 4884448a4bba52e7bbca89c80251165a5f37d22d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Tue, 23 Sep 2025 19:14:44 +0200 Subject: [PATCH 02/34] Implemented read and write. It boots but the filesystem is not read correctly --- arch/nanoZ80/bios.z80 | 125 ++++++++++++++++++++++++++++++- arch/nanoZ80/boot.z80 | 2 +- arch/nanoZ80/include/nanoZ80.lib | 6 +- diskdefs | 4 +- 4 files changed, 127 insertions(+), 10 deletions(-) diff --git a/arch/nanoZ80/bios.z80 b/arch/nanoZ80/bios.z80 index 992e8cb..5251146 100644 --- a/arch/nanoZ80/bios.z80 +++ b/arch/nanoZ80/bios.z80 @@ -178,22 +178,90 @@ HOMEE: SETTRKE: ld (BTRACK), bc + ;call print + ;db 'Track set' + ;db 13,10 + ;db 0 ret SETSECE: ld (BSECTOR), bc + ;call print + ;db 'Sector set' + ;db 13,10 + ;db 0 ret SETDMAE: ld (BDMA), bc + ;call print + ;db 'DMA set' + ;db 13,10 + ;db 0 ret READE: - ; To be implemented + call sd_init + call calc_sd_addr + ; Read strobe + ld a,0 + out (SD_READ),a + call sd_wait + ; Copy data to DMA + ld hl, (BDMA) + ld a, 0 + ld d, 0 +read_sector_copy: + out (SD_PAGE), a + ld c, SD_DATA +read_copy_loop: + in a, (c) + ld (hl), a + inc hl + inc c + ld a, c + cp a, 0 + jr nz, read_copy_loop + inc d + ld a, d + cp a, 4 + jr nz, read_sector_copy + + call sd_init + ;call print + ;db 'Read done' + ;db 13,10 + ;db 0 ret WRITEE: - ; To be implemented + call sd_init + call calc_sd_addr + ; Copy data from DMA + ld hl, (BDMA) + ld a, 0 + ld d, 0 +write_sector_copy: + out (SD_PAGE), a + ld c, SD_DATA +write_copy_loop: + ld a, (hl) + out (c), a + inc hl + inc c + ld a, c + cp a, 0 + jr nz, write_copy_loop + inc d + ld a, d + cp a, 4 + jr nz, write_sector_copy + + ; Write strobe + ld a,0 + out (SD_WRITE),a + call sd_wait + ret SECTRANE: @@ -217,6 +285,54 @@ print: call CONOUTE jr print +; Calculate the SD-card sector address +calc_sd_addr: + ; Load the current track + ld hl, (BTRACK) + ld d, 0 + ; Multiply by 16 + ld b,4 + +shift_loop: + add hl,hl + rl d + djnz shift_loop + + ; Add the sector + ld bc, (BSECTOR) + add hl,bc + jp nc, no_carry_sector + inc d +no_carry_sector: + ; Add SD-card address offset and store + ld a, SD_OFFSET_3 + out (SD_ADDR_3),a + + ld bc, SD_OFFSET_0+SD_OFFSET_1*256 + add hl, bc + jp nc, no_carry_add + inc d +no_carry_add: + ld a, d + add a, SD_OFFSET_2 + + out (SD_ADDR_2),a + ld a,h + out (SD_ADDR_1),a + ld a,l + out (SD_ADDR_0),a + + ret + +sd_init: + ld a, IO_SELECT_SD + out (IO_BANK), a + ; Fall through +sd_wait: + in a, (SD_BUSY) + cp a, 0 + jr nz, sd_wait + ret drive_a_dph: dw 0 ; Sector translation vector @@ -230,7 +346,7 @@ drive_a_dph: ; This makes the supervisor maths easier as we can just drop the bottom byte ; from the LBA sector count. Each track is therefore 128kB. label DRVADPB - dw 128 ; Number of CP/M sectors per track + dw 64 ; Number of CP/M sectors per track db 4, 15 ; BSH/BLM for 2048-byte blocks db 0 ; EXM for 2048-byte allocation units and >255 blocks dw DRIVE_A_BLOCKS-1 ; DSM @@ -252,6 +368,7 @@ label BSECTOR dw 0 label BDMA dw 0 - + + saved_stack: dw 0 ; user stack pointer while in system calls diff --git a/arch/nanoZ80/boot.z80 b/arch/nanoZ80/boot.z80 index 0754621..bf0c9fa 100644 --- a/arch/nanoZ80/boot.z80 +++ b/arch/nanoZ80/boot.z80 @@ -18,7 +18,7 @@ db 0x01 ; Image length - db 0x01 + db 0x0d ; RAM page to load to db 0xe4 diff --git a/arch/nanoZ80/include/nanoZ80.lib b/arch/nanoZ80/include/nanoZ80.lib index 2bad534..97e210a 100644 --- a/arch/nanoZ80/include/nanoZ80.lib +++ b/arch/nanoZ80/include/nanoZ80.lib @@ -65,8 +65,8 @@ SD_DATA equ $80 ; Start of data page ($80-$FF) DRIVE_A_SIZE = 1024 ; kB DRIVE_A_BLOCKS = DRIVE_A_SIZE / 2 ; 2 kB blocks -SD_OFFSET_4 = 0x01 -SD_OFFSET_3 = 0x00 +SD_OFFSET_3 = 0x01 SD_OFFSET_2 = 0x00 -SD_OFFSET_1 = 0x01 +SD_OFFSET_1 = 0x00 +SD_OFFSET_0 = 0x00 diff --git a/diskdefs b/diskdefs index 9f488f5..1e4d5f6 100644 --- a/diskdefs +++ b/diskdefs @@ -93,9 +93,9 @@ end # Generic 1 Meg image used for the nanoZ80 diskdef generic-1m - seclen 128 + seclen 512 tracks 128 - sectrk 64 + sectrk 16 blocksize 2048 maxdir 128 boottrk 1 From eda7533200bd3bf225c46d5ffd92a1ce98f7d95e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Tue, 23 Sep 2025 19:37:25 +0200 Subject: [PATCH 03/34] Fixed deblocking of 512 byte sectors, untested --- arch/nanoZ80/bios.z80 | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/arch/nanoZ80/bios.z80 b/arch/nanoZ80/bios.z80 index 5251146..41dcccb 100644 --- a/arch/nanoZ80/bios.z80 +++ b/arch/nanoZ80/bios.z80 @@ -209,9 +209,9 @@ READE: call sd_wait ; Copy data to DMA ld hl, (BDMA) - ld a, 0 - ld d, 0 -read_sector_copy: + ld a, (BSDPAGE) +; ld d, 0 +;read_sector_copy: out (SD_PAGE), a ld c, SD_DATA read_copy_loop: @@ -222,10 +222,6 @@ read_copy_loop: ld a, c cp a, 0 jr nz, read_copy_loop - inc d - ld a, d - cp a, 4 - jr nz, read_sector_copy call sd_init ;call print @@ -237,11 +233,15 @@ read_copy_loop: WRITEE: call sd_init call calc_sd_addr + + ; Read full page first + ld a, 0 + out (SD_READ),a + call sd_wait + ; Copy data from DMA ld hl, (BDMA) - ld a, 0 - ld d, 0 -write_sector_copy: + ld a, (BSDPAGE) out (SD_PAGE), a ld c, SD_DATA write_copy_loop: @@ -252,10 +252,6 @@ write_copy_loop: ld a, c cp a, 0 jr nz, write_copy_loop - inc d - ld a, d - cp a, 4 - jr nz, write_sector_copy ; Write strobe ld a,0 @@ -298,8 +294,15 @@ shift_loop: rl d djnz shift_loop - ; Add the sector + ; Add the sector divided by 4 ld bc, (BSECTOR) + ld a,c + and a,0x03 + ld (BSDPAGE),a + sra b + rr c + sra b + rr c add hl,bc jp nc, no_carry_sector inc d @@ -366,6 +369,8 @@ label BTRACK dw 0 label BSECTOR dw 0 +label BSDPAGE + dw 0 label BDMA dw 0 From ae70b7ff1d5752e37ea08e40662684e39477fa29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Wed, 24 Sep 2025 09:13:47 +0200 Subject: [PATCH 04/34] Fixed warmboot. Now boots and runs great with basic functionality --- arch/nanoZ80/bios.z80 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/nanoZ80/bios.z80 b/arch/nanoZ80/bios.z80 index 41dcccb..b2022e6 100644 --- a/arch/nanoZ80/bios.z80 +++ b/arch/nanoZ80/bios.z80 @@ -93,9 +93,9 @@ WBOOTE: call SETTRKE ; select track 0 ld hl, CBASE ; location to load - ld bc, 1 ; first sector to load + ld bc, 4 ; first sector to load - ; We just load track 0 (first 8 kB) + ; Reload OS from disk boot_loop: push bc push hl @@ -114,7 +114,7 @@ boot_loop: pop bc ; current sector back into bc ld a, c inc c - cp 63 ; end of track 0 + cp 56 ; end of track 0 jr nz, boot_loop jr call_ccp From 5544087f05cbaec8ac44a8775f10b659f5aff0a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Wed, 24 Sep 2025 13:00:32 +0200 Subject: [PATCH 05/34] Changed boot sector format to include separate load and boot adresses --- arch/nanoZ80/boot.z80 | 4 ++++ arch/nanoZ80/build.py | 15 +-------------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/arch/nanoZ80/boot.z80 b/arch/nanoZ80/boot.z80 index bf0c9fa..9452e50 100644 --- a/arch/nanoZ80/boot.z80 +++ b/arch/nanoZ80/boot.z80 @@ -22,3 +22,7 @@ ; RAM page to load to db 0xe4 + + ; RAM page to boot from + db 0xfa + diff --git a/arch/nanoZ80/build.py b/arch/nanoZ80/build.py index 3b98018..48ce975 100644 --- a/arch/nanoZ80/build.py +++ b/arch/nanoZ80/build.py @@ -55,20 +55,7 @@ "submit.com": "cpmtools+submit", "bbcbasic.com": "third_party/bbcbasic+bbcbasic_ADM3A", "camel80.com": "third_party/camelforth", - "qe.com": "cpmtools+qe_KAYPROII", + "qe.com": "cpmtools+qe_SPECTRUM_NEXT", }, ) -# Patches the special extra bit of BDOS/BIOS into the area above the -# directory; yuch. -#simplerule( -# name="diskimage", -# ins=[".+partialimg", ".+bootfile_mem"], -# outs=["=diskimage.img"], -# commands=[ -# "cp {ins[0]} {outs[0]}", -# "chmod +w {outs[0]}", -# "dd if={ins[1]} of={outs[0]} bs=128 seek=56 skip=495 count=9 conv=notrunc 2> /dev/null", -# ], -# label="MKKAYPRO2", -#) From d97edc2fb894e6036325e7dc98dae8eb6a85388f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Thu, 25 Sep 2025 17:06:26 +0200 Subject: [PATCH 06/34] Added basic support for USB keyboard and HDMI output to BIOS --- arch/nanoZ80/bios.z80 | 25 ++++++++++++++++++++----- arch/nanoZ80/include/nanoZ80.lib | 24 ++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/arch/nanoZ80/bios.z80 b/arch/nanoZ80/bios.z80 index b2022e6..17cebdf 100644 --- a/arch/nanoZ80/bios.z80 +++ b/arch/nanoZ80/bios.z80 @@ -119,21 +119,31 @@ boot_loop: jr call_ccp CONSTE: + in a, (KB_AVAIL) + or a + jr nz, CONST_AVAIL + in a, (UART_RX_AVAIL) bit 0, a ret z - + +CONST_AVAIL: ld a, 0xff ret CONINE: in a, (UART_RX_AVAIL) - bit 0, a - jr z, CONINE + or a + jr z, CONINEUSB in a, (UART_RX_DATA) - + ret +CONINEUSB: + in a, (KB_AVAIL) + or a + jr z, CONINE + in a, (KB_CHAR) ret CONOUTE: @@ -143,7 +153,12 @@ CONOUTE: ld a,c out (UART_TX_DATA),a - +;CONOUTVID: +; in a, (VID_BUSY_AVAIL) +; bit 0, a +; jr nz, CONOUTVID +; ld a,c + out (VID_WRITE_AVAIL),a ret ; Not implemented yet diff --git a/arch/nanoZ80/include/nanoZ80.lib b/arch/nanoZ80/include/nanoZ80.lib index 97e210a..e4c996e 100644 --- a/arch/nanoZ80/include/nanoZ80.lib +++ b/arch/nanoZ80/include/nanoZ80.lib @@ -70,3 +70,27 @@ SD_OFFSET_2 = 0x00 SD_OFFSET_1 = 0x00 SD_OFFSET_0 = 0x00 +; TTY/Video interface IO ports +VID_LINE equ $00 ; Line avaialble for editing +VID_CURSOR_X equ $01 ; Cursor X position +VID_CURSOR_Y equ $02 ; Cursor Y position +VID_CURSOR_VIS equ $03 ; Cursor visible +VID_SCROLL_UP equ $04 ; Scroll up strobe +VID_SCROLL_DOWN equ $05 ; Scroll down strobe +VID_WRITE_CHAR equ $06 ; Write character to TTY +VID_BUSY equ $07 ; TTY busy flag +VID_CLEAR_TO_EOL equ $08 ; Clear to EOL strobe +VID_CLEAR_SCREEN equ $09 ; Clear screen strobe +VID_ENABLED equ $0a ; Enably tty functions +VID_AUTOSCROLL equ $0b ; Enable autoscroll +VID_FG_RED equ $10 ; Foreground red +VID_FG_GREEN equ $11 ; Foreground green +VID_FG_BLUE equ $12 ; Foreground blue +VID_BG_RED equ $13 ; Background red +VID_BG_GREEN equ $14 ; Background green +VID_BG_BLUE equ $15 ; Background blue +VID_LINE_DATA equ $80 ; Data on current line ($80-$CF) +VID_WRITE_AVAIL equ $76 ; Always available TTY write +VID_BUSY_AVAIL equ $77 ; Always avaialble TTY busy + + From 9c11737ea4c9e39ca79e6d9a163488aa187bc685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Thu, 25 Sep 2025 17:41:55 +0200 Subject: [PATCH 07/34] Remove higest bit in text output --- arch/nanoZ80/bios.z80 | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/nanoZ80/bios.z80 b/arch/nanoZ80/bios.z80 index 17cebdf..bce0740 100644 --- a/arch/nanoZ80/bios.z80 +++ b/arch/nanoZ80/bios.z80 @@ -152,6 +152,7 @@ CONOUTE: jr z, CONOUTE ld a,c + and 0x7f ; Remove highest bit out (UART_TX_DATA),a ;CONOUTVID: ; in a, (VID_BUSY_AVAIL) From b35e81e5323d2bded255bfd6637c6ba1adf9ca61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Sat, 27 Sep 2025 12:33:51 +0200 Subject: [PATCH 08/34] Added IOBYTE support and implemented LIST, READER and PUNCH with support for two UARTs --- arch/nanoZ80/bios.z80 | 180 ++++++++++++++++++++++++++----- arch/nanoZ80/include/nanoZ80.lib | 13 ++- 2 files changed, 160 insertions(+), 33 deletions(-) diff --git a/arch/nanoZ80/bios.z80 b/arch/nanoZ80/bios.z80 index bce0740..86ff3e6 100644 --- a/arch/nanoZ80/bios.z80 +++ b/arch/nanoZ80/bios.z80 @@ -29,6 +29,20 @@ label BBASE jp LISTSTE jp SECTRANE +; Default IOBYTE +; The IO is mapped in the following way: +; CRT: USB keyboard and HDMI output +; TTY/LPT: UART header on carrier board +; PTP/PTR/UC1: USB UART on Tang Nano 20k board +; +; LIST PUNCH READER CONSOLE +; 0x81 - 10 00 00 01 - LPT: TTY: TTY: CRT: +; 0x95 - 10 01 01 01 - LPT: PTP: PTR: CRT: +; 0x80 - 10 00 00 00 - LPT: TTY: TTY: TTY: +; 0x83 - 10 00 00 11 - LTP: TTY: TTY: UC1: +; 0x97 - 10 01 01 11 - LTP: PTP: PTR: UC1: +ioconfig: db 0x83 + ; Actual BIOS entrypoints. ; ; The BIOS calls typically use a simple calling convention where the parameter @@ -40,12 +54,12 @@ label BBASE ; Cold boot on system startup. BOOTE: di - ld a, $1 + ld a, 0x01 out (ROM_DISABLE), a ; disable ROM ld sp, 0x0100 ; ephemeral startup stack ei - ld a, $0 ; set default IOBYTE (not implemented) + ld a, (ioconfig) ; set default IOBYTE ld (IOBYTE), a xor a @@ -73,16 +87,6 @@ call_ccp: ld c, a jp CBASE ; pass control to CCP -;stop_disk_motor: -; in a, (PORT_BITPORT) -; or 0x40 ; turn off motor -; out (PORT_BITPORT) -; ret - -;reset_disk_system: -; ld l, 0x03 -; jp syscall - ; Warm boot on application exit. WBOOTE: ld sp, 0x0100 ; ephemeral user stack @@ -119,12 +123,29 @@ boot_loop: jr call_ccp CONSTE: - in a, (KB_AVAIL) + ld a, (IOBYTE) + and 0x03 ; Mask console bits + cp 0 + jr nz, CONST_2 + ; UART B + ld a, IO_SELECT_UART + out (IO_BANK), a + in a, (UART_B_RX_AVAIL) or a jr nz, CONST_AVAIL - + ret +CONST_2: + cp 0x03 + jr nz, CONST_3 + ; UART A in a, (UART_RX_AVAIL) - bit 0, a + or a + jr nz, CONST_AVAIL + ret +CONST_3: + ; CRT + in a, (KB_AVAIL) + or a ret z CONST_AVAIL: @@ -133,49 +154,150 @@ CONST_AVAIL: ret CONINE: + ld a, (IOBYTE) + and 0x03 ; Mask console bits + cp 0 + jr nz, CONIN_2 +UART_B_CONIN: + ld a, IO_SELECT_UART + out (IO_BANK), a + in a, (UART_B_RX_AVAIL) + or a + jr z, UART_B_CONIN + in a, (UART_B_RX_DATA) + ret +CONIN_2: + cp 0x03 + jr nz, CONINEUSB +UART_A_CONIN: in a, (UART_RX_AVAIL) or a - jr z, CONINEUSB + jr z, UART_A_CONIN in a, (UART_RX_DATA) ret +; Default to CRT: CONINEUSB: in a, (KB_AVAIL) or a - jr z, CONINE + jr z, CONINEUSB in a, (KB_CHAR) ret CONOUTE: + ld a, (IOBYTE) + and 0x03 ; Mask console bits + cp 0 + jr nz, CONOUT_2 +UART_B_CONOUT: + ld a, IO_SELECT_UART + out (IO_BANK), a + in a, (UART_B_TX_READY) + bit 0, a + jr z, UART_B_CONOUT + ld a, c + and 0x7f ; Remove highest bit + out (UART_B_TX_DATA), a + ret +CONOUT_2: + cp 0x03 + jr nz, CONOUT_3 +UART_A_CONOUT: in a, (UART_TX_READY) bit 0, a - jr z, CONOUTE - + jr z, UART_A_CONOUT ld a,c and 0x7f ; Remove highest bit out (UART_TX_DATA),a -;CONOUTVID: -; in a, (VID_BUSY_AVAIL) -; bit 0, a -; jr nz, CONOUTVID -; ld a,c - out (VID_WRITE_AVAIL),a + ret +CONOUT_3: + ; Default to CRT + ld a,c + and 0x7f ; Remove highest bit + out (VID_WRITE_X),a ret - ; Not implemented yet LISTE: - ; fall through + ld a,(IOBYTE) + and 0xc0 ; Mask list bits + ; Send to UART B unless CRT: is selected + cp 0x40 + jr nz, LIST_UART_B + ld a, c + out (VID_WRITE_X),a + ret +LIST_UART_B: + ld a,IO_SELECT_UART + out (IO_BANK), a + in a, (UART_B_TX_READY) + or a + jr z, LIST_UART_B + ld a, c + out (UART_B_TX_DATA), a + ret + PUNCHE: + ld a, (IOBYTE) + and 0x30 ; Mask punch bits + or a + jr nz, PUNCH_UART_A + ld a, IO_SELECT_UART + out (IO_BANK), a +PUNCH_UART_B: + in a, (UART_B_TX_READY) + or a + jr z, PUNCH_UART_B + + ld a, c + out (UART_B_TX_DATA), a + ret +PUNCH_UART_A: + in a, (UART_TX_READY) + or a + jr z, PUNCH_UART_A + + ld a, c + out (UART_TX_DATA), a ret LISTSTE: + ld a, (IOBYTE) + and 0xc0 ; Mask list bits + cp 0x40 + jr nz, LISTST_UART_B + ; CRT is always ready + ld a, 0xff + ret +LISTST_UART_B: + ld a, IO_SELECT_UART + out (IO_BANK), a + in a, (UART_B_TX_READY) + or a + ret z ld a, 0xff ret READERE: - ld a, 0x1a ; Ctrl+z means not implemented + ld a, (IOBYTE) + and 0x0c ; Mask reader bits + or a + jr nz, READER_UART_A + ld a, IO_SELECT_UART + out (IO_BANK), a +READER_UART_B: + in a, (UART_B_RX_AVAIL) + or a + jr z, READER_UART_B + + in a, (UART_B_RX_DATA) + ret +READER_UART_A: + in a, (UART_RX_AVAIL) + or a + jr z, READER_UART_A + + in a, (UART_RX_DATA) ret - ; Selects a drive, returning the address of the DPH in HL (or 0x0000 on ; error). SELDSKE: diff --git a/arch/nanoZ80/include/nanoZ80.lib b/arch/nanoZ80/include/nanoZ80.lib index e4c996e..e89e5d4 100644 --- a/arch/nanoZ80/include/nanoZ80.lib +++ b/arch/nanoZ80/include/nanoZ80.lib @@ -11,6 +11,11 @@ UART_TX_DATA equ $70 ; UART transmit data UART_TX_READY equ $71 ; UART transmit ready UART_RX_DATA equ $72 ; UART receive data UART_RX_AVAIL equ $73 ; UART receive data available +UART_B_TX_DATA equ $04 ; UART B transmit data, paged +UART_B_TX_READY equ $05 ; UART B transmit ready, paged +UART_B_RX_DATA equ $06 ; UART B receive data +UART_B_RX_AVAIL equ $07 ; UART B receive data available +UART_B_BAUD equ $08 ; UART B baud rate setting register ; IO mux ports and constants ROM_DISABLE equ $7e ; ROM disable register, 1=ROM disabled @@ -19,8 +24,8 @@ IO_BANK equ $7f ; IO bank selection register IO_SELECT_LEDS equ $00 ; IO bank value for LEDs IO_SELECT_GPIO equ $01 ; IO bank value for GPIO IO_SELECT_USB equ $02 ; IO bank value for USB HID -IO_SELECT_SD equ $03 ; IO bank balue for SD Card - +IO_SELECT_SD equ $03 ; IO bank value for SD Card +IO_SELECT_UART equ $04 ; IO bank value for UART B ; LEDs IO ports LEDS_REG equ $00 ; LED registers, bits 0-6 control one LED each WS2812_R equ $01 ; WS2812 RED @@ -90,7 +95,7 @@ VID_BG_RED equ $13 ; Background red VID_BG_GREEN equ $14 ; Background green VID_BG_BLUE equ $15 ; Background blue VID_LINE_DATA equ $80 ; Data on current line ($80-$CF) -VID_WRITE_AVAIL equ $76 ; Always available TTY write -VID_BUSY_AVAIL equ $77 ; Always avaialble TTY busy +VID_WRITE_X equ $76 ; Always available TTY write +VID_BUSY_X equ $77 ; Always avaialble TTY busy From 22877019b2f15348be28cb2e4370608c02324483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Sat, 27 Sep 2025 13:41:20 +0200 Subject: [PATCH 09/34] Correct IO_PAGE constant for UART_B --- arch/nanoZ80/include/nanoZ80.lib | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/nanoZ80/include/nanoZ80.lib b/arch/nanoZ80/include/nanoZ80.lib index e89e5d4..3b7adb6 100644 --- a/arch/nanoZ80/include/nanoZ80.lib +++ b/arch/nanoZ80/include/nanoZ80.lib @@ -25,7 +25,8 @@ IO_SELECT_LEDS equ $00 ; IO bank value for LEDs IO_SELECT_GPIO equ $01 ; IO bank value for GPIO IO_SELECT_USB equ $02 ; IO bank value for USB HID IO_SELECT_SD equ $03 ; IO bank value for SD Card -IO_SELECT_UART equ $04 ; IO bank value for UART B +IO_SELECT_VID equ $04 ; IO bank for video controller +IO_SELECT_UART equ $05 ; IO bank value for UART B ; LEDs IO ports LEDS_REG equ $00 ; LED registers, bits 0-6 control one LED each WS2812_R equ $01 ; WS2812 RED From 5d9c57c0ec2a6aaea472ee1f1407b0930c37a270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Sat, 27 Sep 2025 16:30:20 +0200 Subject: [PATCH 10/34] Added a B drive --- arch/nanoZ80/bios.z80 | 46 ++++++++++++++++++++++++++++++-- arch/nanoZ80/build.py | 2 ++ arch/nanoZ80/include/nanoZ80.lib | 3 +++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/arch/nanoZ80/bios.z80 b/arch/nanoZ80/bios.z80 index 86ff3e6..52c3188 100644 --- a/arch/nanoZ80/bios.z80 +++ b/arch/nanoZ80/bios.z80 @@ -301,12 +301,21 @@ READER_UART_A: ; Selects a drive, returning the address of the DPH in HL (or 0x0000 on ; error). SELDSKE: - ld (BDISK), a + ld a, (BDISK) + ld b, a + ld a, c + ld (BDISK), a ld hl, drive_a_dph or a ; Test for 0 ret z + ld hl, drive_b_dph + dec a ; Test for 1 + ret z + + ld a, b ; Don't change disk + ld (BDISK), a ld hl, 0 ret @@ -421,6 +430,9 @@ print: ; Calculate the SD-card sector address calc_sd_addr: + ; Store current disk in E register for later + ld a, (BDISK) + ld e, a ; Load the current track ld hl, (BTRACK) ld d, 0 @@ -455,8 +467,16 @@ no_carry_sector: inc d no_carry_add: ld a, d + ld b, e ; Skip 32 MB per disk +find_disk: + inc a + djnz find_disk add a, SD_OFFSET_2 - + ;push af + ;ld a, (BDISK) + ;ld c, a + ;pop af + ;add a, c ; Just skip 32 MB to get to the next disk number out (SD_ADDR_2),a ld a,h out (SD_ADDR_1),a @@ -483,6 +503,15 @@ drive_a_dph: dw 0 ; Disk change check vector dw drive_a_bitmap ; Allocation bitmap +drive_b_dph: + dw 0 ; Sector translation vector + dw 0, 0, 0 ; BDOS scratchpad + dw dirbuf ; Directory scratchpad + dw DRVBDPB ; Drive parameter block + dw 0 ; Disk change check vector + dw drive_b_bitmap ; Allocation bitmap + + ; The drive is sized so that each CP/M track occupies 256 physical sectors. ; This makes the supervisor maths easier as we can just drop the bottom byte ; from the LBA sector count. Each track is therefore 128kB. @@ -496,8 +525,21 @@ label DRVADPB dw 0 ; Size of disk change check vector: zero as this is a fixed disk dw 1 ; Number of reserved tracks +label DRVBDPB + dw 256*4 + db 7, 127 + db 7 + dw DRIVE_B_BLOCKS-1 + dw 511 + db 0x80, 0x00 + dw 0 + dw 0 + drive_a_bitmap: ds (DRIVE_A_BLOCKS+7) / 8 +drive_b_bitmap: + ds (DRIVE_B_BLOCKS+7) / 8 + dirbuf: ds 128 diff --git a/arch/nanoZ80/build.py b/arch/nanoZ80/build.py index 48ce975..ede42ff 100644 --- a/arch/nanoZ80/build.py +++ b/arch/nanoZ80/build.py @@ -56,6 +56,8 @@ "bbcbasic.com": "third_party/bbcbasic+bbcbasic_ADM3A", "camel80.com": "third_party/camelforth", "qe.com": "cpmtools+qe_SPECTRUM_NEXT", + "rawdisk.com": "cpmtools+rawdisk", + "mkfs.com": "cpmtools+mkfs", }, ) diff --git a/arch/nanoZ80/include/nanoZ80.lib b/arch/nanoZ80/include/nanoZ80.lib index 3b7adb6..d9d47c1 100644 --- a/arch/nanoZ80/include/nanoZ80.lib +++ b/arch/nanoZ80/include/nanoZ80.lib @@ -71,6 +71,9 @@ SD_DATA equ $80 ; Start of data page ($80-$FF) DRIVE_A_SIZE = 1024 ; kB DRIVE_A_BLOCKS = DRIVE_A_SIZE / 2 ; 2 kB blocks +DRIVE_B_SIZE = 32*1024 ; kB +DRIVE_B_BLOCKS = DRIVE_B_SIZE / 16 ; 16 kB blocks + SD_OFFSET_3 = 0x01 SD_OFFSET_2 = 0x00 SD_OFFSET_1 = 0x00 From ccb7c68282e3b3cd1c94d01462c1d551728381bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Sat, 27 Sep 2025 17:12:17 +0200 Subject: [PATCH 11/34] Cleanup of multidisk code --- arch/nanoZ80/bios.z80 | 27 +++++++-------------------- arch/nanoZ80/build.py | 2 +- arch/nanoZ80/include/nanoZ80.lib | 4 ++-- diskdefs | 11 +++++++++++ 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/arch/nanoZ80/bios.z80 b/arch/nanoZ80/bios.z80 index 52c3188..0a7bfa3 100644 --- a/arch/nanoZ80/bios.z80 +++ b/arch/nanoZ80/bios.z80 @@ -313,7 +313,7 @@ SELDSKE: ld hl, drive_b_dph dec a ; Test for 1 ret z - + ld a, b ; Don't change disk ld (BDISK), a ld hl, 0 @@ -467,16 +467,8 @@ no_carry_sector: inc d no_carry_add: ld a, d - ld b, e ; Skip 32 MB per disk -find_disk: - inc a - djnz find_disk + add a, e ; Add disk number add a, SD_OFFSET_2 - ;push af - ;ld a, (BDISK) - ;ld c, a - ;pop af - ;add a, c ; Just skip 32 MB to get to the next disk number out (SD_ADDR_2),a ld a,h out (SD_ADDR_1),a @@ -511,22 +503,18 @@ drive_b_dph: dw 0 ; Disk change check vector dw drive_b_bitmap ; Allocation bitmap - -; The drive is sized so that each CP/M track occupies 256 physical sectors. -; This makes the supervisor maths easier as we can just drop the bottom byte -; from the LBA sector count. Each track is therefore 128kB. label DRVADPB dw 64 ; Number of CP/M sectors per track - db 4, 15 ; BSH/BLM for 2048-byte blocks - db 0 ; EXM for 2048-byte allocation units and >255 blocks + db 7, 127 ; BSH/BLM for 16384-byte blocks + db 7 ; EXM for 16384-byte allocation units and >255 blocks dw DRIVE_A_BLOCKS-1 ; DSM - dw 127 ; DRM, one fewer than the number of directory entries - db 0xc0, 0x00 ; Initial allocation vector for one directory block + dw 511 ; DRM, one fewer than the number of directory entries + db 0x80, 0x00 ; Initial allocation vector for one directory block dw 0 ; Size of disk change check vector: zero as this is a fixed disk dw 1 ; Number of reserved tracks label DRVBDPB - dw 256*4 + dw 64 db 7, 127 db 7 dw DRIVE_B_BLOCKS-1 @@ -555,5 +543,4 @@ label BDMA dw 0 -saved_stack: dw 0 ; user stack pointer while in system calls diff --git a/arch/nanoZ80/build.py b/arch/nanoZ80/build.py index ede42ff..ced17d0 100644 --- a/arch/nanoZ80/build.py +++ b/arch/nanoZ80/build.py @@ -44,7 +44,7 @@ diskimage( name="diskimage", - format="generic-1m", + format="nanoz80", bootfile=".+bootfile", map={ "-readme.txt": ".+readme", diff --git a/arch/nanoZ80/include/nanoZ80.lib b/arch/nanoZ80/include/nanoZ80.lib index d9d47c1..d678674 100644 --- a/arch/nanoZ80/include/nanoZ80.lib +++ b/arch/nanoZ80/include/nanoZ80.lib @@ -68,8 +68,8 @@ SD_TYPE equ $09 ; SD card type SD_DATA equ $80 ; Start of data page ($80-$FF) ; Disk definitions -DRIVE_A_SIZE = 1024 ; kB -DRIVE_A_BLOCKS = DRIVE_A_SIZE / 2 ; 2 kB blocks +DRIVE_A_SIZE = 32*1024 ; kB +DRIVE_A_BLOCKS = DRIVE_A_SIZE / 16 ;/ 2 ; 2 kB blocks DRIVE_B_SIZE = 32*1024 ; kB DRIVE_B_BLOCKS = DRIVE_B_SIZE / 16 ; 16 kB blocks diff --git a/diskdefs b/diskdefs index 1e4d5f6..dc6e84e 100644 --- a/diskdefs +++ b/diskdefs @@ -102,5 +102,16 @@ diskdef generic-1m os 2.2 end +diskdef nanoz80 + seclen 512 + tracks 4096 + sectrk 16 + blocksize 16384 + maxdir 512 + boottrk 1 + os 2.2 +end + + # vim: set ts=4 sw=4 expandtab From 367828f582c03b5dfe93c8ec3b3d05051b85f6c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Sat, 27 Sep 2025 20:27:38 +0200 Subject: [PATCH 12/34] Reconfigured for 4x4MB drives --- arch/nanoZ80/bios.z80 | 32 ++++++++++++++++++++++++++++++++ arch/nanoZ80/include/nanoZ80.lib | 4 ++-- diskdefs | 3 ++- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/arch/nanoZ80/bios.z80 b/arch/nanoZ80/bios.z80 index 0a7bfa3..2306df2 100644 --- a/arch/nanoZ80/bios.z80 +++ b/arch/nanoZ80/bios.z80 @@ -314,6 +314,14 @@ SELDSKE: dec a ; Test for 1 ret z + ld hl, drive_c_dph + dec a ; Test for 2 + ret z + + ld hl, drive_d_dph + dec a ; Test for 3 + ret z + ld a, b ; Don't change disk ld (BDISK), a ld hl, 0 @@ -503,6 +511,23 @@ drive_b_dph: dw 0 ; Disk change check vector dw drive_b_bitmap ; Allocation bitmap +drive_c_dph: + dw 0 ; Sector translation vector + dw 0, 0, 0 ; BDOS scratchpad + dw dirbuf ; Directory scratchpad + dw DRVBDPB ; Drive parameter block + dw 0 ; Disk change check vector + dw drive_c_bitmap ; Allocation bitmap + +drive_d_dph: + dw 0 ; Sector translation vector + dw 0, 0, 0 ; BDOS scratchpad + dw dirbuf ; Directory scratchpad + dw DRVBDPB ; Drive parameter block + dw 0 ; Disk change check vector + dw drive_d_bitmap ; Allocation bitmap + +; Boot disk label DRVADPB dw 64 ; Number of CP/M sectors per track db 7, 127 ; BSH/BLM for 16384-byte blocks @@ -513,6 +538,7 @@ label DRVADPB dw 0 ; Size of disk change check vector: zero as this is a fixed disk dw 1 ; Number of reserved tracks +; Other disks label DRVBDPB dw 64 db 7, 127 @@ -523,10 +549,16 @@ label DRVBDPB dw 0 dw 0 + drive_a_bitmap: ds (DRIVE_A_BLOCKS+7) / 8 drive_b_bitmap: ds (DRIVE_B_BLOCKS+7) / 8 +drive_c_bitmap: + ds (DRIVE_B_BLOCKS+7) / 8 +drive_d_bitmap: + ds (DRIVE_B_BLOCKS+7) / 8 + dirbuf: ds 128 diff --git a/arch/nanoZ80/include/nanoZ80.lib b/arch/nanoZ80/include/nanoZ80.lib index d678674..a29d8b1 100644 --- a/arch/nanoZ80/include/nanoZ80.lib +++ b/arch/nanoZ80/include/nanoZ80.lib @@ -68,10 +68,10 @@ SD_TYPE equ $09 ; SD card type SD_DATA equ $80 ; Start of data page ($80-$FF) ; Disk definitions -DRIVE_A_SIZE = 32*1024 ; kB +DRIVE_A_SIZE = 4*1024 ; kB DRIVE_A_BLOCKS = DRIVE_A_SIZE / 16 ;/ 2 ; 2 kB blocks -DRIVE_B_SIZE = 32*1024 ; kB +DRIVE_B_SIZE = 4*1024 ; kB DRIVE_B_BLOCKS = DRIVE_B_SIZE / 16 ; 16 kB blocks SD_OFFSET_3 = 0x01 diff --git a/diskdefs b/diskdefs index dc6e84e..404976e 100644 --- a/diskdefs +++ b/diskdefs @@ -102,9 +102,10 @@ diskdef generic-1m os 2.2 end +# 4 Meg format used for the nanoZ80 diskdef nanoz80 seclen 512 - tracks 4096 + tracks 512 sectrk 16 blocksize 16384 maxdir 512 From 307668c93ca4fafe44d03520076402e9ecaa155f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Mon, 29 Sep 2025 11:13:23 +0200 Subject: [PATCH 13/34] Added TTY support --- arch/nanoZ80/bios.z80 | 17 ++++++++++++----- arch/nanoZ80/boot.z80 | 6 +++--- arch/nanoZ80/build.py | 29 ++++++++++++++++++++++------- arch/nanoZ80/include/nanoZ80.lib | 7 +++++-- cpmtools/libcuss/build.py | 3 ++- cpmtools/libcuss/libcuss.h | 5 +++++ 6 files changed, 49 insertions(+), 18 deletions(-) diff --git a/arch/nanoZ80/bios.z80 b/arch/nanoZ80/bios.z80 index 2306df2..0481e01 100644 --- a/arch/nanoZ80/bios.z80 +++ b/arch/nanoZ80/bios.z80 @@ -5,9 +5,11 @@ maclib cpm maclib nanoZ80 maclib cpmish - + maclib addresses + extern TTYPUTC + extern TTYINIT cseg -label BBASE +;label BBASE ; BIOS jump table. @@ -41,7 +43,7 @@ label BBASE ; 0x80 - 10 00 00 00 - LPT: TTY: TTY: TTY: ; 0x83 - 10 00 00 11 - LTP: TTY: TTY: UC1: ; 0x97 - 10 01 01 11 - LTP: PTP: PTR: UC1: -ioconfig: db 0x83 +ioconfig: db 0x81 ; Actual BIOS entrypoints. ; @@ -67,7 +69,11 @@ BOOTE: ;ld a, (baudrate) ; initialise serial hardware ;out (PORT_SERIAL_BAUD), a - + ;ld a, IO_SELECT_VID + ;out (IO_BANK), a + ;ld a, 0 + ;out (VID_AUTOSCROLL), a + call TTYINIT call print db 0x1a ; clear screen cpmish_banner 'nanoZ80' @@ -214,7 +220,8 @@ CONOUT_3: ; Default to CRT ld a,c and 0x7f ; Remove highest bit - out (VID_WRITE_X),a + ;out (VID_WRITE_X),a + jp TTYPUTC ret LISTE: diff --git a/arch/nanoZ80/boot.z80 b/arch/nanoZ80/boot.z80 index 9452e50..9c710c2 100644 --- a/arch/nanoZ80/boot.z80 +++ b/arch/nanoZ80/boot.z80 @@ -18,11 +18,11 @@ db 0x01 ; Image length - db 0x0d + db 0x0e ; RAM page to load to - db 0xe4 + db 0xe2 ; RAM page to boot from - db 0xfa + db 0xf8 diff --git a/arch/nanoZ80/build.py b/arch/nanoZ80/build.py index ced17d0..ed0f7b6 100644 --- a/arch/nanoZ80/build.py +++ b/arch/nanoZ80/build.py @@ -1,10 +1,11 @@ from build.ab import simplerule -from build.cpm import diskimage +from build.cpm import cpm_addresses, diskimage from utils.build import unix2cpm from third_party.zmac.build import zmac from third_party.ld80.build import ld80 - +(cbase, fbase, bbase) = cpm_addresses(name="addresses", bios_size=0x0800) +#bbase = bbase + 0x200 zmac(name="boot", src="./boot.z80", relocatable=False) zmac( @@ -14,16 +15,30 @@ "arch/nanoZ80/include/nanoZ80.lib", "include/cpm.lib", "include/cpmish.lib", + ".+addresses", + ], +) + +zmac( + name="tty", + src="./tty.z80", + deps=[ + "arch/nanoZ80/include/nanoZ80.lib", + "include/cpm.lib", + "include/cpmish.lib", + "arch/common/utils/tty.lib", + "arch/common/utils/print.lib", ], ) + # Builds the memory image. ld80( name="bootfile_mem", objs={ - 0xE400: ["third_party/zcpr1"], - 0xEC00: ["third_party/zsdos"], - 0xFA00: [".+bios"], + cbase: ["third_party/zcpr1"], + fbase: ["third_party/zsdos"], + 0xf800: [".+bios",".+tty",], }, ) @@ -35,7 +50,7 @@ outs=["=bootfile.img"], commands=[ "dd if={ins[0]} of={outs[0]} bs=128 count=1 2> /dev/null", - "dd if={ins[1]} of={outs[0]} bs=128 seek=4 skip=454 count=56 2> /dev/null", + "dd if={ins[1]} of={outs[0]} bs=128 seek=4 skip=450 count=64 2> /dev/null", ], label="nanoZ80", ) @@ -55,7 +70,7 @@ "submit.com": "cpmtools+submit", "bbcbasic.com": "third_party/bbcbasic+bbcbasic_ADM3A", "camel80.com": "third_party/camelforth", - "qe.com": "cpmtools+qe_SPECTRUM_NEXT", + "qe.com": "cpmtools+qe_NANOZ80", "rawdisk.com": "cpmtools+rawdisk", "mkfs.com": "cpmtools+mkfs", }, diff --git a/arch/nanoZ80/include/nanoZ80.lib b/arch/nanoZ80/include/nanoZ80.lib index a29d8b1..c72eb6b 100644 --- a/arch/nanoZ80/include/nanoZ80.lib +++ b/arch/nanoZ80/include/nanoZ80.lib @@ -90,8 +90,11 @@ VID_WRITE_CHAR equ $06 ; Write character to TTY VID_BUSY equ $07 ; TTY busy flag VID_CLEAR_TO_EOL equ $08 ; Clear to EOL strobe VID_CLEAR_SCREEN equ $09 ; Clear screen strobe -VID_ENABLED equ $0a ; Enably tty functions -VID_AUTOSCROLL equ $0b ; Enable autoscroll +VID_CLEAR_TO_EOS equ $0a ; Clear to EOS strobe +VID_DELETE_LINE equ $0b ; Delete line strobe +VID_INSERT_LINE equ $0c ; Insert line strobe +VID_ENABLED equ $0d ; Enably tty functions +VID_AUTOSCROLL equ $0e ; Enable autoscroll VID_FG_RED equ $10 ; Foreground red VID_FG_GREEN equ $11 ; Foreground green VID_FG_BLUE equ $12 ; Foreground blue diff --git a/cpmtools/libcuss/build.py b/cpmtools/libcuss/build.py index ce5eb3e..a0c34dd 100644 --- a/cpmtools/libcuss/build.py +++ b/cpmtools/libcuss/build.py @@ -10,6 +10,7 @@ "BROTHER_POWERNOTE", "SPECTRUM_PLUS_THREE", "SPECTRUM_NEXT", + "NANOZ80", ] for terminal in libcuss_terminals: @@ -28,4 +29,4 @@ def libcuss_ackprogram(name, deps=[], cflags=[], **kwargs): deps=deps + ["cpmtools/libcuss+libcuss_" + terminal], cflags=["-DLIBCUSS_"+terminal] + cflags, **kwargs - ) + ) diff --git a/cpmtools/libcuss/libcuss.h b/cpmtools/libcuss/libcuss.h index 9c160f7..982740a 100644 --- a/cpmtools/libcuss/libcuss.h +++ b/cpmtools/libcuss/libcuss.h @@ -53,6 +53,11 @@ extern void con_revoff(void); #define SCREENWIDTH 80 #define SCREENHEIGHT 25 #define LIBCUSS_ADM3 +#elif defined LIBCUSS_NANOZ80 + #define SCREENWIDTH 80 + #define SCREENHEIGHT 30 + #define LIBCUSS_ADM3 + #else #error "No libcuss configuration specified." #endif From 2afe510281b4375ea68736302149f2a19cbb6e31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Mon, 29 Sep 2025 11:13:53 +0200 Subject: [PATCH 14/34] Also include actual tty code... --- arch/nanoZ80/tty.z80 | 100 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 arch/nanoZ80/tty.z80 diff --git a/arch/nanoZ80/tty.z80 b/arch/nanoZ80/tty.z80 new file mode 100644 index 0000000..7803846 --- /dev/null +++ b/arch/nanoZ80/tty.z80 @@ -0,0 +1,100 @@ +; nano-z80 cpmish BIOS © 2025 Henrik Löfgren +; This file is distributable under the terms of the 2-clause BSD license. +; See COPYING.cpmish in the distribution root directory for more information. + + maclib cpm + maclib cpmish + maclib nanoZ80 + + extrn SYSIN + extrn SYSOUT + extrn ADDAHL + + public TTYINIT + public TTYPUTC + public TTYPUT8 + public TTYPUT16 + public TTYPUTSI + + cseg + +SCREEN_WIDTH = 80 +SCREEN_HEIGHT = 30 + +CURSOR_UPDATES = 1 +CLEAR_SCREEN_ON_INIT = 1 +EMULATE_CLEAR_TO_EOL = 0 +EMULATE_CLEAR_TO_EOS = 0 + maclib tty + maclib print + +TTYINIT equ tty_init +TTYPUTC equ tty_putc +TTYPUT8 equ tty_puthex8 +TTYPUT16 equ tty_puthex16 +TTYPUTSI equ tty_putsi + +tty_rawwrite: + push af + call video_init + call tty_update_cursor + ; Check if output should be inverted + ld a, (tty_attributes) + bit 0, a + jr z, vid_write + pop af + or a, $80 + push af +vid_write: + pop af + out (VID_WRITE_X), a + ret + +; Moves the cursor to the current location. +tty_update_cursor: + call video_init + ld a, (tty_cursorx) + out (VID_CURSOR_X), a + ld a, (tty_cursory) + out (VID_CURSOR_Y), a + ret + +tty_delete_line: + call tty_update_cursor + call video_init + out (VID_DELETE_LINE), a + call tty_update_cursor + ret + ;jp tty_update_cursor + +tty_insert_line: + call video_init + out (VID_INSERT_LINE), a + ret + ;jp tty_update_cursor + +tty_clear_to_eol: + call video_init + out (VID_CLEAR_TO_EOL), a + ret + ;jp tty_update_cursor + +tty_clear_to_eos: + call video_init + out (VID_CLEAR_TO_EOS), a + ret ;jp tty_update_cursor + + +video_init: + ld a, IO_SELECT_VID + out (IO_BANK), a + ld a,0 +video_init_delay: + inc a + cp a, 50 + jr nz, video_init_delay + ret + + +; vim: ts=4 sw=4 et ft=asm + From 6adafd7b01bab34678a36c0997b3bbe661df5986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Mon, 29 Sep 2025 19:44:28 +0200 Subject: [PATCH 15/34] Harmonized naming --- arch/{nanoZ80 => nano-z80}/README.md | 0 arch/{nanoZ80 => nano-z80}/bios.z80 | 2 +- arch/{nanoZ80 => nano-z80}/boot.z80 | 0 arch/{nanoZ80 => nano-z80}/build.py | 4 ++-- .../include/nanoZ80.lib => nano-z80/include/nano-z80.lib} | 0 arch/{nanoZ80 => nano-z80}/tty.z80 | 2 +- build.py | 2 +- 7 files changed, 5 insertions(+), 5 deletions(-) rename arch/{nanoZ80 => nano-z80}/README.md (100%) rename arch/{nanoZ80 => nano-z80}/bios.z80 (99%) rename arch/{nanoZ80 => nano-z80}/boot.z80 (100%) rename arch/{nanoZ80 => nano-z80}/build.py (95%) rename arch/{nanoZ80/include/nanoZ80.lib => nano-z80/include/nano-z80.lib} (100%) rename arch/{nanoZ80 => nano-z80}/tty.z80 (98%) diff --git a/arch/nanoZ80/README.md b/arch/nano-z80/README.md similarity index 100% rename from arch/nanoZ80/README.md rename to arch/nano-z80/README.md diff --git a/arch/nanoZ80/bios.z80 b/arch/nano-z80/bios.z80 similarity index 99% rename from arch/nanoZ80/bios.z80 rename to arch/nano-z80/bios.z80 index 0481e01..092fa6e 100644 --- a/arch/nanoZ80/bios.z80 +++ b/arch/nano-z80/bios.z80 @@ -3,7 +3,7 @@ ; See COPYING.cpmish in the distribution root directory for more information. maclib cpm - maclib nanoZ80 + maclib nano-z80 maclib cpmish maclib addresses extern TTYPUTC diff --git a/arch/nanoZ80/boot.z80 b/arch/nano-z80/boot.z80 similarity index 100% rename from arch/nanoZ80/boot.z80 rename to arch/nano-z80/boot.z80 diff --git a/arch/nanoZ80/build.py b/arch/nano-z80/build.py similarity index 95% rename from arch/nanoZ80/build.py rename to arch/nano-z80/build.py index ed0f7b6..87ebc53 100644 --- a/arch/nanoZ80/build.py +++ b/arch/nano-z80/build.py @@ -12,7 +12,7 @@ name="bios", src="./bios.z80", deps=[ - "arch/nanoZ80/include/nanoZ80.lib", + "arch/nano-z80/include/nano-z80.lib", "include/cpm.lib", "include/cpmish.lib", ".+addresses", @@ -23,7 +23,7 @@ name="tty", src="./tty.z80", deps=[ - "arch/nanoZ80/include/nanoZ80.lib", + "arch/nano-z80/include/nano-z80.lib", "include/cpm.lib", "include/cpmish.lib", "arch/common/utils/tty.lib", diff --git a/arch/nanoZ80/include/nanoZ80.lib b/arch/nano-z80/include/nano-z80.lib similarity index 100% rename from arch/nanoZ80/include/nanoZ80.lib rename to arch/nano-z80/include/nano-z80.lib diff --git a/arch/nanoZ80/tty.z80 b/arch/nano-z80/tty.z80 similarity index 98% rename from arch/nanoZ80/tty.z80 rename to arch/nano-z80/tty.z80 index 7803846..d1b4344 100644 --- a/arch/nanoZ80/tty.z80 +++ b/arch/nano-z80/tty.z80 @@ -4,7 +4,7 @@ maclib cpm maclib cpmish - maclib nanoZ80 + maclib nano-z80 extrn SYSIN extrn SYSOUT diff --git a/build.py b/build.py index ec67771..ca2f13d 100644 --- a/build.py +++ b/build.py @@ -10,6 +10,6 @@ "wp1.img": "arch/brother/wp1+diskimage", "kayproii.img": "arch/kayproii+diskimage", "nc200.img": "arch/nc200+diskimage", - "nanoZ80.img": "arch/nanoZ80+diskimage" + "nano-z80.img": "arch/nano-z80+diskimage" }, ) From c01fadc81a62f40fe3e6e8a1831e24676b0fd1d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Mon, 29 Sep 2025 20:48:13 +0200 Subject: [PATCH 16/34] Added baudrate setting utility --- arch/nano-z80/build.py | 1 + arch/nano-z80/tools/baudrate.z80 | 116 +++++++++++++++++++++++++++++++ arch/nano-z80/tools/build.py | 11 +++ 3 files changed, 128 insertions(+) create mode 100644 arch/nano-z80/tools/baudrate.z80 create mode 100644 arch/nano-z80/tools/build.py diff --git a/arch/nano-z80/build.py b/arch/nano-z80/build.py index 87ebc53..25438e4 100644 --- a/arch/nano-z80/build.py +++ b/arch/nano-z80/build.py @@ -73,6 +73,7 @@ "qe.com": "cpmtools+qe_NANOZ80", "rawdisk.com": "cpmtools+rawdisk", "mkfs.com": "cpmtools+mkfs", + "baudrate.com": "arch/nano-z80/tools+baudrate", }, ) diff --git a/arch/nano-z80/tools/baudrate.z80 b/arch/nano-z80/tools/baudrate.z80 new file mode 100644 index 0000000..8a2b3f0 --- /dev/null +++ b/arch/nano-z80/tools/baudrate.z80 @@ -0,0 +1,116 @@ + maclib addresses + +IOBANK = 0x7f +IO_SELECT_UART = 0x05 +UART_B_BAUD = 0x08 + + + cseg + org 0x100 + + call print_string_inline + db "nano-z80 baud rate tool" + db 13,10 + db 13,10 + db "Current baudrate setting: " + db 0 + ; Print the current setting + ld a, IO_SELECT_UART + out (IOBANK), a + in a,(UART_B_BAUD) + + cp 0 + jr nz, check9600 + call print_string_inline + db "4800" + db 0 + jr check_done +check9600: + cp 1 + jr nz, check19200 + call print_string_inline + db "9600" + db 0 + jr check_done +check19200: + cp 2 + jr nz, check38400 + call print_string_inline + db "19200" + db 0 + jr check_done +check38400: + cp 3 + jr nz, check57600 + call print_string_inline + db "38400" + db 0 + jr check_done +check57600: + cp 4 + jr nz, check115200 + call print_string_inline + db "57600" + db 0 + jr check_done +check115200: + ; No check needed + call print_string_inline + db "115200" + db 0 +check_done: + call print_string_inline + db 13,10,13,10 + db "Please select new baudrate:" + db 13,10 + db "1: 4800" + db 13,10 + db "2: 9600" + db 13,10 + db "3: 19200" + db 13,10 + db "4: 38400" + db 13,10 + db "5: 57600" + db 13,10 + db "6: 115200" + db 13,10,0 + + ; Get input + call BBASE+0x09 ; CONIN + cp '1' + jr c, illegal_input + cp '7' + jr c, accepted_input + jr illegal_input +accepted_input: + dec a + ld b, a + ld a, IO_SELECT_UART + out (IOBANK), a + ld a, b + out (UART_B_BAUD), a + call print_string_inline + db 13,10 + db "Baudrate updated" + db 0 + ret + +illegal_input: + call print_string_inline + db 13,10 + db "Illegal selection" + db 0 + ret +print_string_inline: + pop hl + ld a,(hl) + inc hl + push hl + cp 0 + ret z + ld c, a + call BBASE+0x0c ; CONOUT + jr print_string_inline + + diff --git a/arch/nano-z80/tools/build.py b/arch/nano-z80/tools/build.py new file mode 100644 index 0000000..18d19bd --- /dev/null +++ b/arch/nano-z80/tools/build.py @@ -0,0 +1,11 @@ +from third_party.zmac.build import zmac + +zmac( + name="baudrate", + relocatable=False, + src="./baudrate.z80", + deps=[ + "arch/nano-z80+addresses", + ], +) + From 46896ecc2196db7d4a87e81710d5925bb6095aae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Thu, 2 Oct 2025 17:57:49 +0200 Subject: [PATCH 17/34] Added simple serial terminal program with X-modem support --- arch/nano-z80/build.py | 1 + arch/nano-z80/tools/build.py | 5 + arch/nano-z80/tools/nanoterm.c | 447 +++++++++++++++++++++++++++++++++ arch/nano-z80/tools/uart.s | 84 +++++++ 4 files changed, 537 insertions(+) create mode 100644 arch/nano-z80/tools/nanoterm.c create mode 100644 arch/nano-z80/tools/uart.s diff --git a/arch/nano-z80/build.py b/arch/nano-z80/build.py index 25438e4..8646ad7 100644 --- a/arch/nano-z80/build.py +++ b/arch/nano-z80/build.py @@ -74,6 +74,7 @@ "rawdisk.com": "cpmtools+rawdisk", "mkfs.com": "cpmtools+mkfs", "baudrate.com": "arch/nano-z80/tools+baudrate", + "nanoterm.com": "arch/nano-z80/tools+nanoterm", }, ) diff --git a/arch/nano-z80/tools/build.py b/arch/nano-z80/tools/build.py index 18d19bd..b1025c6 100644 --- a/arch/nano-z80/tools/build.py +++ b/arch/nano-z80/tools/build.py @@ -1,4 +1,5 @@ from third_party.zmac.build import zmac +from build.ack import ackprogram zmac( name="baudrate", @@ -9,3 +10,7 @@ ], ) +ackprogram( + name="nanoterm", + srcs=["./nanoterm.c", "./uart.s"], +) diff --git a/arch/nano-z80/tools/nanoterm.c b/arch/nano-z80/tools/nanoterm.c new file mode 100644 index 0000000..855725b --- /dev/null +++ b/arch/nano-z80/tools/nanoterm.c @@ -0,0 +1,447 @@ +#include +#include +#include + +#define ESC 0x1b +#define BELL 0x07 +#define BACKSPACE 0x08 +#define TAB 0x09 +#define CR 0x0d +#define LF 0x0a +#define VT 0x0b +#define FF 0x0c +#define LOCAL_CMD 0x11 // ctrl+q +#define SOH 0x01 +#define EOT 0x04 +#define ACK 0x06 +#define DLE 0x10 +#define XON 0x11 +#define XOFF 0x13 +#define NAK 0x15 +#define SYN 0x16 +#define CAN 0x18 +#define SUB 0x1a + +// UART functions written in assembly +extern uint8_t uart_rx_avail(void); +extern uint8_t uart_getc(void); +extern void uart_putc_raw(uint8_t c); +extern uint8_t uart_getbaud(void); +extern void uart_setbaud_raw(uint8_t b); + +uint8_t local_echo; +uint8_t baudrate; + +static FCB xmodem_file; +static uint8_t xmodem_buffer[128]; + +void print(const char* s) +{ + for(;;) { + uint8_t b = *s++; + if(!b) return; + cpm_conout(b); + } +} + + +void crlf(void) +{ + print("\r\n"); +} + +void printx(const char* s) { + print(s); + crlf(); +} + +void printhex4(uint8_t nibble) +{ + nibble &= 0x0f; + if(nibble < 10) + nibble += '0'; + else + nibble += 'a'-10; + cpm_conout(nibble); +} + +void printhex8(uint8_t b) +{ + printhex4(b>>4); + printhex4(b); +} +uint8_t dummy(uint8_t b) { + uint8_t x; + x=b; + return x; +} +// Wrapper which is only used since I for the life of me can't figure out +// the correct calling convention +void uart_putc(uint8_t b) { + dummy(b); + uart_putc_raw(b); +} + +void uart_setbaud(uint8_t b) { + if(b>5) b=5; + + dummy(b); + uart_setbaud_raw(b); +} + + +void print_settings(void) { + printx("Current settings"); + print("Local echo: "); + if(!local_echo) { + printx("OFF"); + } else { + printx("ON"); + } + + print("Baudrate: "); + switch(baudrate) { + case 0: + print("4800"); + break; + case 1: + print("9600"); + break; + case 2: + print("19200") ; + break; + case 3: + print("38400"); + break; + case 4: + print("57600"); + break; + case 5: + print("115200"); + break; + default: + print("Undefined"); + break; + } + crlf(); + crlf(); +} + +void set_baudrate(void) { + uint8_t b; + crlf(); + printx("Select new baudrate"); + printx("1: 4800"); + printx("2: 9600"); + printx("3: 19200"); + printx("4: 38400"); + printx("5: 57600"); + printx("6: 115200"); + b = cpm_conin(); + + b=b-'1'; + if(b>5) { + printx("Invalid selection"); + return; + } + uart_setbaud(b); + baudrate = uart_getbaud(); + crlf(); + print_settings(); +} + +static uint8_t getblockchar(uint8_t *data) { + uint8_t data_available; + uint16_t i; + for(i=0; i<30000; i++) { + data_available = uart_rx_avail(); + + if(data_available) { + *data = uart_getc(); + break; + } + } + return data_available; +} + +static void xmodem_receive(void) { + char filename_input[14]; + uint8_t block_cnt; + uint8_t block_exp = 1; + uint8_t pos = 0; + uint8_t checksum; + uint8_t inp; + uint8_t outp; + uint8_t data_available; + + print("X modem receive"); + crlf(); + print("Enter filename: "); + + filename_input[0]=13; + filename_input[1]=0; + cpm_readline((uint8_t *)filename_input); + crlf(); + + // Reset FCB + memset(&xmodem_file, 0, sizeof(xmodem_file)); + + // Parse filename + cpm_parse_filename(&xmodem_file,&filename_input[2]); + + // Create file + cpm_make_file(&xmodem_file); + + print("Waiting for sender"); + crlf(); + print("Press any key to cancel"); + crlf(); + outp = NAK; + // Transmission + while(1) { + uart_putc(outp); + if(getblockchar(&inp)) { + if(inp == EOT) { + crlf(); + print("Transmission done"); + crlf(); + cpm_close_file(&xmodem_file); + return; + } + if(inp == CAN) { + crlf(); + print("Transmission cancelled"); + crlf(); + cpm_close_file(&xmodem_file); + return; + } + if(inp == SOH) { + // Got header, get package + outp = NAK; + checksum = 0; + getblockchar(&inp); + block_cnt = inp; + getblockchar(&inp); + if((block_cnt == (inp ^ 0xFF)) && (block_cnt == block_exp)) { + // Get block, otherwise retry + for(pos=0; pos<128; pos++) { + getblockchar(&inp); + xmodem_buffer[pos]=inp; + checksum += inp; + } + // Verify checksum + getblockchar(&inp); + if(checksum == inp) { + outp = ACK; + cpm_set_dma(&xmodem_buffer); + cpm_write_sequential(&xmodem_file); + //printi(block_cnt); + } + block_exp++; + } + } + } + cpm_conout('.'); + if(cpm_const()) { + cpm_close_file(&xmodem_file); + return; + } + } + +} + +static void xmodem_send_block(uint8_t block_cnt) { + uint8_t i; + uint8_t checksum; + uint8_t data; + + // Print block number + //printi(block_cnt); + print("."); + + // Send header + uart_putc(SOH); + uart_putc(block_cnt); + uart_putc(block_cnt ^ 0xFF); + + checksum = 0; + // Send data + for(i=0; i<128; i++) { + data = xmodem_buffer[i]; + checksum += data; + uart_putc(data); + } + + // Send checksum + uart_putc(checksum); +} + +static void xmodem_send(void) { + char filename_input[14]; + uint8_t block_cnt = 1; + uint8_t pos = 0; + uint8_t delay; + uint8_t inp; + uint8_t outp; + uint8_t nak_cnt = 0; + + print("X modem send"); + crlf(); + print("Enter filename: "); + + filename_input[0]=13; + filename_input[1]=0; + cpm_readline((uint8_t *)filename_input); + crlf(); + + // Reset FCB + memset(&xmodem_file, 0, sizeof(xmodem_file)); + + // Parse filename + cpm_parse_filename(&xmodem_file,&filename_input[2]); + + // Open file + if(cpm_open_file( &xmodem_file)) { + print("Error opening file\r\n"); + return; + } + + // Load first block + cpm_set_dma(&xmodem_buffer); + cpm_read_sequential(&xmodem_file); + + print("Waiting for receiver..."); + crlf(); + print("Press any key to cancel"); + crlf(); + + while(1) { + if(getblockchar(&inp)) { + if(inp == NAK) { + // Resend block + xmodem_send_block(block_cnt); + nak_cnt++; + if(nak_cnt == 11) { + print("Too many NAKs, aborting"); + crlf(); + uart_putc(CAN); + cpm_close_file(&xmodem_file); + return; + } + } + if(inp == ACK) { + cpm_conout('.'); + // Load next block + cpm_set_dma(&xmodem_buffer); + if(cpm_read_sequential(&xmodem_file)) { + crlf(); + print("Transmission done"); + crlf(); + uart_putc(EOT); + cpm_close_file(&xmodem_file); + return; + } + block_cnt++; + // Send next block + xmodem_send_block(block_cnt); + } + } + if(cpm_const()) { + // Cancel due to keypress + cpm_close_file(&xmodem_file); + uart_putc(CAN); + return; + } + } +} + + +int main(void) { + uint8_t run=1; + uint8_t console_data; + uint8_t uart_data; + + local_echo = 0; + baudrate = uart_getbaud(); + + printx("Nanoterm for the nano-z80"); + printx("Press ctrl-q + h for help"); + crlf(); + print_settings(); + + while(run) { + if(cpm_bios_const()) { + console_data = cpm_bios_conin(); // No echo... + if(console_data == LOCAL_CMD) { + console_data = cpm_conin(); + switch(console_data) { + case 'q': + case 'Q': + // Quit + crlf(); + printx("Goodbye!"); + cpm_warmboot(); + break; + case 'e': + case 'E': + // Toggle local echo + crlf(); + print("Local echo "); + if(!local_echo) { + local_echo = 1; + printx("ON"); + } else { + local_echo = 0; + printx("OFF"); + } + break; + case 'b': + case 'B': + set_baudrate(); + break; + case 'p': + case 'P': + print_settings(); + break; + case 's': + case 'S': + xmodem_send(); + break; + case 'r': + case 'R': + xmodem_receive(); + break; + case LOCAL_CMD: + uart_putc(LOCAL_CMD); + break; + case 'h': + case 'H': + // Print help + crlf(); + printx("Available commands:"); + printx("Ctrl-q + q: Quit"); + printx("Ctrl-q + e: Toggle local echo"); + printx("Ctrl-q + b: Set baudrate"); + printx("Ctrl-q + p: Print current settings"); + printx("Ctrl-q + s: Send file with X-modem"); + printx("Ctrl-q + r: Receive file with X-modem"); + printx("Ctrl-q + ctrl+q: Send ctrl-q "); + break; + default: + break; + } + } else { + if(local_echo == 1) { + cpm_conout(console_data); + } + + uart_putc(console_data); + } + } + if(uart_rx_avail()) { + uart_data = uart_getc(); + cpm_conout(uart_data); + } + + } +} diff --git a/arch/nano-z80/tools/uart.s b/arch/nano-z80/tools/uart.s new file mode 100644 index 0000000..d577868 --- /dev/null +++ b/arch/nano-z80/tools/uart.s @@ -0,0 +1,84 @@ +.sect .text +.sect .rom +.sect .data +.sect .bss + + +IOBANK = 0x7f +IO_SELECT_UART = 0x05 +UART_B_TX_DATA = 0x04 +UART_B_TX_READY = 0x05 +UART_B_RX_DATA = 0x06 +UART_B_RX_AVAIL = 0x07 +UART_B_BAUD = 0x08 + +! Check if there is RX data available, return 1 if there is, 0 if not +.sect .text +.define _uart_rx_avail +_uart_rx_avail: + mvi a, IO_SELECT_UART + out IOBANK + in UART_B_RX_AVAIL + mvi d, 0 + mov e, a + ret + +! Get a byte from the serial port +.sect .text +.define _uart_getc +_uart_getc: + mvi a, IO_SELECT_UART + out IOBANK + in UART_B_RX_DATA + mvi d,0 + mov e,a + ret + +! Send a byte to the serial port, blocking +.sect .text +.define _uart_putc_raw +_uart_putc_raw: + pop d ! pop return address + pop h ! pop character to send + + xthl ! L = character to send + push h + push d + mvi a, IO_SELECT_UART + out IOBANK +uart_send_loop: + in UART_B_TX_READY + cpi 0 + jz uart_send_loop + mov a,l + out UART_B_TX_DATA + ret + +! Get the current baudrate +.sect .text +.define _uart_getbaud +_uart_getbaud: + mvi a, IO_SELECT_UART + out IOBANK + in UART_B_BAUD + mvi d,0 + mov e,a + ret + + ! Set the baudrate +.sect .text +.define _uart_setbaud_raw +_uart_setbaud_raw: + pop d ! pop return address + pop h ! pop setting + + xthl ! L = setting + push h + push d + mvi a, IO_SELECT_UART + out IOBANK + mov a,l + out UART_B_BAUD + ret + + From 9e696dfc8a20619f4162bcf5a5167044825c1732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Thu, 2 Oct 2025 19:53:10 +0200 Subject: [PATCH 18/34] Changed SD-card boot sector to 0x00000000 --- arch/nano-z80/boot.z80 | 2 +- arch/nano-z80/include/nano-z80.lib | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/nano-z80/boot.z80 b/arch/nano-z80/boot.z80 index 9c710c2..dd62518 100644 --- a/arch/nano-z80/boot.z80 +++ b/arch/nano-z80/boot.z80 @@ -12,7 +12,7 @@ db 0x6f ; Image SD card sector - db 0x01 + db 0x00 db 0x00 db 0x00 db 0x01 diff --git a/arch/nano-z80/include/nano-z80.lib b/arch/nano-z80/include/nano-z80.lib index c72eb6b..0962950 100644 --- a/arch/nano-z80/include/nano-z80.lib +++ b/arch/nano-z80/include/nano-z80.lib @@ -74,7 +74,7 @@ DRIVE_A_BLOCKS = DRIVE_A_SIZE / 16 ;/ 2 ; 2 kB blocks DRIVE_B_SIZE = 4*1024 ; kB DRIVE_B_BLOCKS = DRIVE_B_SIZE / 16 ; 16 kB blocks -SD_OFFSET_3 = 0x01 +SD_OFFSET_3 = 0x00 SD_OFFSET_2 = 0x00 SD_OFFSET_1 = 0x00 SD_OFFSET_0 = 0x00 From 0ac350f2b0634efe6fadb2bd38f8e791c8d2fa77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= <106430829+venomix666@users.noreply.github.com> Date: Thu, 2 Oct 2025 20:16:08 +0200 Subject: [PATCH 19/34] Update README.md --- arch/nano-z80/README.md | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/arch/nano-z80/README.md b/arch/nano-z80/README.md index b3e684c..f1bd2fd 100644 --- a/arch/nano-z80/README.md +++ b/arch/nano-z80/README.md @@ -1,5 +1,33 @@ -Platform: the nano-Z80 +Platform: the nano-z80 ====================== -Port for a simple FPGA Z80 SoC +The [nano-z80](https://github.com/venomix666/nano-z80) is a Z80 based SoC for the [Tang Nano 20k FPGA Board](https://wiki.sipeed.com/hardware/en/tang/tang-nano-20k/nano-20k.html). It is specifically designed to run CP/M but on modern hardware - with HDMI output, USB keyboard, SD-card and the processor running at ~25 MHz. + +What you get with this port: + +- 16x2 Mb drives on the SD-card +- Most of an ADM-3a / Kaypro II terminal emulator supporting 80x30 text +- A TPA of about 60kB +- Two serial ports, one on the built in USB UART and on a TTL UART header +- A crude but functional terminal program with X-modem file transfer support +- A fully implemented IO-byte which allows using monitor/keyboard or any of serial ports for the console. + +How to use it +------------- +Write `nano-z80.img` to a micro-SD card using `dd` and press `B` in the nano-z80 monitor to boot from the SD-card. + +By default, only drive `A:` is formatted. To use the additional drives, run `mkfs B:` up to `mkfs P:` to format the other drives once you have booted to CP/M. + +UART B (the one on the TTL UART header) supports changing baudrates, this can be done either using the `baudrate.com` tool or from inside `nanoterm`. + + +Who? +---- + +This port and the nano-z80 project was made by [Henrik Löfgren](https://github.com/venomix666/). + + + + + From 52b9955e4924af74949f235a69d34d62222d8b0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= <106430829+venomix666@users.noreply.github.com> Date: Thu, 2 Oct 2025 20:18:49 +0200 Subject: [PATCH 20/34] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index cb21695..7788369 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ Currently it supports these platforms: - [the Brother WP-2450DS typewriter (and probably others)](arch/brother/wp2450/README.md) - [the Brother PN-8510MDS SuperPowerNote laptop (and probably others)](arch/brother/pn8510/README.md) - [the Brother PN-8800FXB SuperPowerNote laptop (and probably others)](arch/brother/pn8800/README.md) + - [the nano-z80 SoC for the Tang Nano 20k FPGA board](arch/nano-z80/README.md) (Some of these are pretty stale due to difficulty of testing and may not work. Later entries are newer! If you have any problems, please report bugs.) From ed622326bb60e02cb9c230a1a1540da89bc32e1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Thu, 2 Oct 2025 20:24:36 +0200 Subject: [PATCH 21/34] Cleanup and commenting --- arch/nano-z80/bios.z80 | 43 +++++++++++++++++------------------------- arch/nano-z80/tty.z80 | 2 +- 2 files changed, 18 insertions(+), 27 deletions(-) diff --git a/arch/nano-z80/bios.z80 b/arch/nano-z80/bios.z80 index 092fa6e..fc2216e 100644 --- a/arch/nano-z80/bios.z80 +++ b/arch/nano-z80/bios.z80 @@ -67,12 +67,6 @@ BOOTE: xor a ld (CDISK), a ; clear current disk / user - ;ld a, (baudrate) ; initialise serial hardware - ;out (PORT_SERIAL_BAUD), a - ;ld a, IO_SELECT_VID - ;out (IO_BANK), a - ;ld a, 0 - ;out (VID_AUTOSCROLL), a call TTYINIT call print db 0x1a ; clear screen @@ -128,6 +122,7 @@ boot_loop: jr nz, boot_loop jr call_ccp +; CONST CONSTE: ld a, (IOBYTE) and 0x03 ; Mask console bits @@ -159,12 +154,14 @@ CONST_AVAIL: ret +; CONIN CONINE: ld a, (IOBYTE) and 0x03 ; Mask console bits cp 0 jr nz, CONIN_2 UART_B_CONIN: + ; UART B ld a, IO_SELECT_UART out (IO_BANK), a in a, (UART_B_RX_AVAIL) @@ -176,13 +173,14 @@ CONIN_2: cp 0x03 jr nz, CONINEUSB UART_A_CONIN: + ; UART A in a, (UART_RX_AVAIL) or a jr z, UART_A_CONIN in a, (UART_RX_DATA) ret -; Default to CRT: + ; Default to CRT: CONINEUSB: in a, (KB_AVAIL) or a @@ -190,12 +188,14 @@ CONINEUSB: in a, (KB_CHAR) ret +; CONOUT CONOUTE: ld a, (IOBYTE) and 0x03 ; Mask console bits cp 0 jr nz, CONOUT_2 UART_B_CONOUT: + ; UART B ld a, IO_SELECT_UART out (IO_BANK), a in a, (UART_B_TX_READY) @@ -208,7 +208,8 @@ UART_B_CONOUT: CONOUT_2: cp 0x03 jr nz, CONOUT_3 -UART_A_CONOUT: +UART_A_CONOUT: + ; UART A in a, (UART_TX_READY) bit 0, a jr z, UART_A_CONOUT @@ -224,6 +225,7 @@ CONOUT_3: jp TTYPUTC ret +; LIST LISTE: ld a,(IOBYTE) and 0xc0 ; Mask list bits @@ -243,6 +245,7 @@ LIST_UART_B: out (UART_B_TX_DATA), a ret +; PUNCH PUNCHE: ld a, (IOBYTE) and 0x30 ; Mask punch bits @@ -251,6 +254,7 @@ PUNCHE: ld a, IO_SELECT_UART out (IO_BANK), a PUNCH_UART_B: + ; UART B in a, (UART_B_TX_READY) or a jr z, PUNCH_UART_B @@ -259,6 +263,7 @@ PUNCH_UART_B: out (UART_B_TX_DATA), a ret PUNCH_UART_A: + ; UART A in a, (UART_TX_READY) or a jr z, PUNCH_UART_A @@ -267,6 +272,7 @@ PUNCH_UART_A: out (UART_TX_DATA), a ret +; LIST LISTSTE: ld a, (IOBYTE) and 0xc0 ; Mask list bits @@ -284,6 +290,7 @@ LISTST_UART_B: ld a, 0xff ret +; READER READERE: ld a, (IOBYTE) and 0x0c ; Mask reader bits @@ -292,6 +299,7 @@ READERE: ld a, IO_SELECT_UART out (IO_BANK), a READER_UART_B: + ; UART B in a, (UART_B_RX_AVAIL) or a jr z, READER_UART_B @@ -299,6 +307,7 @@ READER_UART_B: in a, (UART_B_RX_DATA) ret READER_UART_A: + ; UART A in a, (UART_RX_AVAIL) or a jr z, READER_UART_A @@ -340,26 +349,14 @@ HOMEE: SETTRKE: ld (BTRACK), bc - ;call print - ;db 'Track set' - ;db 13,10 - ;db 0 ret SETSECE: ld (BSECTOR), bc - ;call print - ;db 'Sector set' - ;db 13,10 - ;db 0 ret SETDMAE: ld (BDMA), bc - ;call print - ;db 'DMA set' - ;db 13,10 - ;db 0 ret READE: @@ -372,8 +369,6 @@ READE: ; Copy data to DMA ld hl, (BDMA) ld a, (BSDPAGE) -; ld d, 0 -;read_sector_copy: out (SD_PAGE), a ld c, SD_DATA read_copy_loop: @@ -386,10 +381,6 @@ read_copy_loop: jr nz, read_copy_loop call sd_init - ;call print - ;db 'Read done' - ;db 13,10 - ;db 0 ret WRITEE: diff --git a/arch/nano-z80/tty.z80 b/arch/nano-z80/tty.z80 index d1b4344..f94d334 100644 --- a/arch/nano-z80/tty.z80 +++ b/arch/nano-z80/tty.z80 @@ -84,7 +84,7 @@ tty_clear_to_eos: out (VID_CLEAR_TO_EOS), a ret ;jp tty_update_cursor - +; This is soooo ugly, but it works for now video_init: ld a, IO_SELECT_VID out (IO_BANK), a From 9e11aec67a72280b6049911c41f66eb0cde77d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= <106430829+venomix666@users.noreply.github.com> Date: Thu, 2 Oct 2025 20:32:10 +0200 Subject: [PATCH 22/34] Update README.md --- arch/nano-z80/README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/arch/nano-z80/README.md b/arch/nano-z80/README.md index f1bd2fd..b889311 100644 --- a/arch/nano-z80/README.md +++ b/arch/nano-z80/README.md @@ -20,6 +20,23 @@ By default, only drive `A:` is formatted. To use the additional drives, run `mkf UART B (the one on the TTL UART header) supports changing baudrates, this can be done either using the `baudrate.com` tool or from inside `nanoterm`. +IO byte +-------------- +The IO is mapped in the following way: +- CRT: USB keyboard and HDMI output +- TTY/LPT: UART header on carrier board +- PTP/PTR/UC1: USB UART on Tang Nano 20k board + +The default value is 0x81. It can be modified according to the table below before booting by first loading to image from the SD-card using the `L` command in the monitor and then writing to address 0xF833 by running for instance `WF833,83` to redirect the console to the USB UART, and then booting by running `JF800`. +``` + LIST PUNCH READER CONSOLE +0x81 - 10 00 00 01 - LPT: TTY: TTY: CRT: +0x95 - 10 01 01 01 - LPT: PTP: PTR: CRT: +0x80 - 10 00 00 00 - LPT: TTY: TTY: TTY: +0x83 - 10 00 00 11 - LTP: TTY: TTY: UC1: +0x97 - 10 01 01 11 - LTP: PTP: PTR: UC1: +``` + Who? ---- From 554ab1d2a502e4b9e7574db6415012c4375223ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= <106430829+venomix666@users.noreply.github.com> Date: Thu, 2 Oct 2025 20:41:47 +0200 Subject: [PATCH 23/34] Update README.md --- arch/nano-z80/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/nano-z80/README.md b/arch/nano-z80/README.md index b889311..ab1a1be 100644 --- a/arch/nano-z80/README.md +++ b/arch/nano-z80/README.md @@ -20,14 +20,16 @@ By default, only drive `A:` is formatted. To use the additional drives, run `mkf UART B (the one on the TTL UART header) supports changing baudrates, this can be done either using the `baudrate.com` tool or from inside `nanoterm`. -IO byte +Modifying the IO byte at boot -------------- The IO is mapped in the following way: - CRT: USB keyboard and HDMI output - TTY/LPT: UART header on carrier board - PTP/PTR/UC1: USB UART on Tang Nano 20k board -The default value is 0x81. It can be modified according to the table below before booting by first loading to image from the SD-card using the `L` command in the monitor and then writing to address 0xF833 by running for instance `WF833,83` to redirect the console to the USB UART, and then booting by running `JF800`. +The default value is 0x81, so that the HDMI output and USB keyboard is used for the console. It can be modified according to the table below before booting by first loading to image from the SD-card using the `L` command in the monitor and then writing to address 0xF833 by running for instance `WF833,83` to redirect the console to the USB UART, and then booting by running `JF800`. + +As the monitor is diplayed both on the USB UART and the HDMI-output/USB-keyboard by default, this can be useful when running with no external display connected. ``` LIST PUNCH READER CONSOLE 0x81 - 10 00 00 01 - LPT: TTY: TTY: CRT: From 73a50f476798384f980c639ba4f6a90ad811fdca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Fri, 3 Oct 2025 18:28:39 +0200 Subject: [PATCH 24/34] Added tools for setting color --- arch/nano-z80/build.py | 2 + arch/nano-z80/tools/build.py | 19 ++++++ arch/nano-z80/tools/colorbg.z80 | 105 ++++++++++++++++++++++++++++++++ arch/nano-z80/tools/colorfg.z80 | 105 ++++++++++++++++++++++++++++++++ 4 files changed, 231 insertions(+) create mode 100644 arch/nano-z80/tools/colorbg.z80 create mode 100644 arch/nano-z80/tools/colorfg.z80 diff --git a/arch/nano-z80/build.py b/arch/nano-z80/build.py index 8646ad7..a677ff6 100644 --- a/arch/nano-z80/build.py +++ b/arch/nano-z80/build.py @@ -75,6 +75,8 @@ "mkfs.com": "cpmtools+mkfs", "baudrate.com": "arch/nano-z80/tools+baudrate", "nanoterm.com": "arch/nano-z80/tools+nanoterm", + "colorfg.com": "arch/nano-z80/tools+colorfg", + "colorbg.com": "arch/nano-z80/tools+colorbg", }, ) diff --git a/arch/nano-z80/tools/build.py b/arch/nano-z80/tools/build.py index b1025c6..fb83543 100644 --- a/arch/nano-z80/tools/build.py +++ b/arch/nano-z80/tools/build.py @@ -10,6 +10,25 @@ ], ) +zmac( + name="colorfg", + relocatable=False, + src="./colorfg.z80", + deps=[ + "arch/nano-z80+addresses", + ], +) + +zmac( + name="colorbg", + relocatable=False, + src="./colorbg.z80", + deps=[ + "arch/nano-z80+addresses", + ], +) + + ackprogram( name="nanoterm", srcs=["./nanoterm.c", "./uart.s"], diff --git a/arch/nano-z80/tools/colorbg.z80 b/arch/nano-z80/tools/colorbg.z80 new file mode 100644 index 0000000..bc9f361 --- /dev/null +++ b/arch/nano-z80/tools/colorbg.z80 @@ -0,0 +1,105 @@ + maclib addresses + +IOBANK = 0x7f +IO_SELECT_VID = 0x04 +VID_BG_R = 0x13 +VID_BG_G = 0x14 +VID_BG_B = 0x15 + +CPM_FCB = 0x005c + + cseg + org 0x100 + + ; Read hex color values from command line + ld hl, CPM_FCB + inc hl ; Skip drive letter + ld ix, (bg_color) + ld b,0 +read_col: + ld a, (hl) + call hex_conv + ret c ; Exit if failed + + ; Shift to high nibble + sla a + sla a + sla a + sla a + ; Store in RAM + ld (ix), a + + ; Read second digit + inc hl + ld a, (hl) + call hex_conv + ret c + or a, (ix) + ld (ix), a + inc ix + inc hl + + inc b + ld a,b + cp 3 + jr nz, read_col + + ; Set color + ld ix, (bg_color) + ld a, IO_SELECT_VID + out (IOBANK), a + + ld a, (ix) + out (VID_BG_R), a + ld a, (ix+1) + out (VID_BG_G), a + ld a, (ix+2) + out (VID_BG_B), a + + ret + +; Convert ASCII hex digit to number, print usage if not valid +hex_conv: + cp '0' + jr c, print_usage +hex_conv_a: + cp '9' + jr c, hex_conv_b + jr z, hex_conv_b + jr hex_conv_c +hex_conv_b: + sub 0x30 + or a ; Clear carry flag + ret +hex_conv_c: + cp 'F' + jr c, hex_conv_d + jr z, hex_conv_d + jr print_usage +hex_conv_d: + sub 0x37 + or a ; Clear carry flag + ret + +print_usage: + call print_string_inline + db "colorfg - set foreground color on the nano-z80 computer" + db 13,10 + db "Usage: colorfg RRGGBB. Eg. colorfg FFFFFF for white text" + db 13,10, 0 + scf + ret + +print_string_inline: + pop hl + ld a,(hl) + inc hl + push hl + cp 0 + ret z + ld c, a + call BBASE+0x0c ; CONOUT + jr print_string_inline + +bg_color: + db 0,0,0 diff --git a/arch/nano-z80/tools/colorfg.z80 b/arch/nano-z80/tools/colorfg.z80 new file mode 100644 index 0000000..6a3d119 --- /dev/null +++ b/arch/nano-z80/tools/colorfg.z80 @@ -0,0 +1,105 @@ + maclib addresses + +IOBANK = 0x7f +IO_SELECT_VID = 0x04 +VID_FG_R = 0x10 +VID_FG_G = 0x11 +VID_FG_B = 0x12 + +CPM_FCB = 0x005c + + cseg + org 0x100 + + ; Read hex color values from command line + ld hl, CPM_FCB + inc hl ; Skip drive letter + ld ix, (fg_color) + ld b,0 +read_col: + ld a, (hl) + call hex_conv + ret c ; Exit if failed + + ; Shift to high nibble + sla a + sla a + sla a + sla a + ; Store in RAM + ld (ix), a + + ; Read second digit + inc hl + ld a, (hl) + call hex_conv + ret c + or a, (ix) + ld (ix), a + inc ix + inc hl + + inc b + ld a,b + cp 3 + jr nz, read_col + + ; Set color + ld ix, (fg_color) + ld a, IO_SELECT_VID + out (IOBANK), a + + ld a, (ix) + out (VID_FG_R), a + ld a, (ix+1) + out (VID_FG_G), a + ld a, (ix+2) + out (VID_FG_B), a + + ret + +; Convert ASCII hex digit to number, print usage if not valid +hex_conv: + cp '0' + jr c, print_usage +hex_conv_a: + cp '9' + jr c, hex_conv_b + jr z, hex_conv_b + jr hex_conv_c +hex_conv_b: + sub 0x30 + or a ; Clear carry flag + ret +hex_conv_c: + cp 'F' + jr c, hex_conv_d + jr z, hex_conv_d + jr print_usage +hex_conv_d: + sub 0x37 + or a ; Clear carry flag + ret + +print_usage: + call print_string_inline + db "colorfg - set foreground color on the nano-z80 computer" + db 13,10 + db "Usage: colorfg RRGGBB. Eg. colorfg FFFFFF for white text" + db 13,10, 0 + scf + ret + +print_string_inline: + pop hl + ld a,(hl) + inc hl + push hl + cp 0 + ret z + ld c, a + call BBASE+0x0c ; CONOUT + jr print_string_inline + +fg_color: + db 0,0,0 From 6ed6053ebab37ef99e31cd37df8d580acb517818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Fri, 3 Oct 2025 19:38:39 +0200 Subject: [PATCH 25/34] Changed to 14x4 MB drives --- arch/nano-z80/bios.z80 | 141 ++++++++++++++++++++++++++++- arch/nano-z80/boot.z80 | 6 +- arch/nano-z80/build.py | 8 +- arch/nano-z80/include/nano-z80.lib | 2 +- diskdefs | 11 --- 5 files changed, 148 insertions(+), 20 deletions(-) diff --git a/arch/nano-z80/bios.z80 b/arch/nano-z80/bios.z80 index fc2216e..6f6a5dd 100644 --- a/arch/nano-z80/bios.z80 +++ b/arch/nano-z80/bios.z80 @@ -337,6 +337,46 @@ SELDSKE: ld hl, drive_d_dph dec a ; Test for 3 ret z + + ld hl, drive_e_dph + dec a ; Test for 4 + ret z + + ld hl, drive_f_dph + dec a ; Test for 5 + ret z + + ld hl, drive_g_dph + dec a ; Test for 6 + ret z + + ld hl, drive_h_dph + dec a ; Test for 7 + ret z + + ld hl, drive_i_dph + dec a ; Test for 8 + ret z + + ld hl, drive_j_dph + dec a ; Test for 9 + ret z + + ld hl, drive_k_dph + dec a ; Test for 10 + ret z + + ld hl, drive_l_dph + dec a ; Test for 11 + ret z + + ld hl, drive_m_dph + dec a ; Test for 12 + ret z + + ld hl, drive_n_dph + dec a ; Test for 13 + ret z ld a, b ; Don't change disk ld (BDISK), a @@ -525,6 +565,86 @@ drive_d_dph: dw 0 ; Disk change check vector dw drive_d_bitmap ; Allocation bitmap +drive_e_dph: + dw 0 ; Sector translation vector + dw 0, 0, 0 ; BDOS scratchpad + dw dirbuf ; Directory scratchpad + dw DRVBDPB ; Drive parameter block + dw 0 ; Disk change check vector + dw drive_e_bitmap ; Allocation bitmap + +drive_f_dph: + dw 0 ; Sector translation vector + dw 0, 0, 0 ; BDOS scratchpad + dw dirbuf ; Directory scratchpad + dw DRVBDPB ; Drive parameter block + dw 0 ; Disk change check vector + dw drive_f_bitmap ; Allocation bitmap + +drive_g_dph: + dw 0 ; Sector translation vector + dw 0, 0, 0 ; BDOS scratchpad + dw dirbuf ; Directory scratchpad + dw DRVBDPB ; Drive parameter block + dw 0 ; Disk change check vector + dw drive_g_bitmap ; Allocation bitmap + +drive_h_dph: + dw 0 ; Sector translation vector + dw 0, 0, 0 ; BDOS scratchpad + dw dirbuf ; Directory scratchpad + dw DRVBDPB ; Drive parameter block + dw 0 ; Disk change check vector + dw drive_h_bitmap ; Allocation bitmap + +drive_i_dph: + dw 0 ; Sector translation vector + dw 0, 0, 0 ; BDOS scratchpad + dw dirbuf ; Directory scratchpad + dw DRVBDPB ; Drive parameter block + dw 0 ; Disk change check vector + dw drive_i_bitmap ; Allocation bitmap + +drive_j_dph: + dw 0 ; Sector translation vector + dw 0, 0, 0 ; BDOS scratchpad + dw dirbuf ; Directory scratchpad + dw DRVBDPB ; Drive parameter block + dw 0 ; Disk change check vector + dw drive_j_bitmap ; Allocation bitmap + +drive_k_dph: + dw 0 ; Sector translation vector + dw 0, 0, 0 ; BDOS scratchpad + dw dirbuf ; Directory scratchpad + dw DRVBDPB ; Drive parameter block + dw 0 ; Disk change check vector + dw drive_k_bitmap ; Allocation bitmap + +drive_l_dph: + dw 0 ; Sector translation vector + dw 0, 0, 0 ; BDOS scratchpad + dw dirbuf ; Directory scratchpad + dw DRVBDPB ; Drive parameter block + dw 0 ; Disk change check vector + dw drive_l_bitmap ; Allocation bitmap + +drive_m_dph: + dw 0 ; Sector translation vector + dw 0, 0, 0 ; BDOS scratchpad + dw dirbuf ; Directory scratchpad + dw DRVBDPB ; Drive parameter block + dw 0 ; Disk change check vector + dw drive_m_bitmap ; Allocation bitmap + +drive_n_dph: + dw 0 ; Sector translation vector + dw 0, 0, 0 ; BDOS scratchpad + dw dirbuf ; Directory scratchpad + dw DRVBDPB ; Drive parameter block + dw 0 ; Disk change check vector + dw drive_n_bitmap ; Allocation bitmap + ; Boot disk label DRVADPB dw 64 ; Number of CP/M sectors per track @@ -556,7 +676,26 @@ drive_c_bitmap: ds (DRIVE_B_BLOCKS+7) / 8 drive_d_bitmap: ds (DRIVE_B_BLOCKS+7) / 8 - +drive_e_bitmap: + ds (DRIVE_B_BLOCKS+7) / 8 +drive_f_bitmap: + ds (DRIVE_B_BLOCKS+7) / 8 +drive_g_bitmap: + ds (DRIVE_B_BLOCKS+7) / 8 +drive_h_bitmap: + ds (DRIVE_B_BLOCKS+7) / 8 +drive_i_bitmap: + ds (DRIVE_B_BLOCKS+7) / 8 +drive_j_bitmap: + ds (DRIVE_B_BLOCKS+7) / 8 +drive_k_bitmap: + ds (DRIVE_B_BLOCKS+7) / 8 +drive_l_bitmap: + ds (DRIVE_B_BLOCKS+7) / 8 +drive_m_bitmap: + ds (DRIVE_B_BLOCKS+7) / 8 +drive_n_bitmap: + ds (DRIVE_B_BLOCKS+7) / 8 dirbuf: ds 128 diff --git a/arch/nano-z80/boot.z80 b/arch/nano-z80/boot.z80 index dd62518..839b41a 100644 --- a/arch/nano-z80/boot.z80 +++ b/arch/nano-z80/boot.z80 @@ -18,11 +18,11 @@ db 0x01 ; Image length - db 0x0e + db 0x0f ; RAM page to load to - db 0xe2 + db 0xe0 ; RAM page to boot from - db 0xf8 + db 0xf6 diff --git a/arch/nano-z80/build.py b/arch/nano-z80/build.py index a677ff6..7619b5c 100644 --- a/arch/nano-z80/build.py +++ b/arch/nano-z80/build.py @@ -4,8 +4,8 @@ from third_party.zmac.build import zmac from third_party.ld80.build import ld80 -(cbase, fbase, bbase) = cpm_addresses(name="addresses", bios_size=0x0800) -#bbase = bbase + 0x200 +(cbase, fbase, bbase) = cpm_addresses(name="addresses", bios_size=0x0a00) + zmac(name="boot", src="./boot.z80", relocatable=False) zmac( @@ -38,7 +38,7 @@ objs={ cbase: ["third_party/zcpr1"], fbase: ["third_party/zsdos"], - 0xf800: [".+bios",".+tty",], + bbase: [".+bios",".+tty",], }, ) @@ -50,7 +50,7 @@ outs=["=bootfile.img"], commands=[ "dd if={ins[0]} of={outs[0]} bs=128 count=1 2> /dev/null", - "dd if={ins[1]} of={outs[0]} bs=128 seek=4 skip=450 count=64 2> /dev/null", + "dd if={ins[1]} of={outs[0]} bs=128 seek=4 skip=446 count=64 2> /dev/null", ], label="nanoZ80", ) diff --git a/arch/nano-z80/include/nano-z80.lib b/arch/nano-z80/include/nano-z80.lib index 0962950..f8bd6a1 100644 --- a/arch/nano-z80/include/nano-z80.lib +++ b/arch/nano-z80/include/nano-z80.lib @@ -69,7 +69,7 @@ SD_DATA equ $80 ; Start of data page ($80-$FF) ; Disk definitions DRIVE_A_SIZE = 4*1024 ; kB -DRIVE_A_BLOCKS = DRIVE_A_SIZE / 16 ;/ 2 ; 2 kB blocks +DRIVE_A_BLOCKS = DRIVE_A_SIZE / 16 ; 16 kB blocks DRIVE_B_SIZE = 4*1024 ; kB DRIVE_B_BLOCKS = DRIVE_B_SIZE / 16 ; 16 kB blocks diff --git a/diskdefs b/diskdefs index 404976e..ef5d053 100644 --- a/diskdefs +++ b/diskdefs @@ -91,17 +91,6 @@ diskdef brother-powernote os 2.2 end -# Generic 1 Meg image used for the nanoZ80 -diskdef generic-1m - seclen 512 - tracks 128 - sectrk 16 - blocksize 2048 - maxdir 128 - boottrk 1 - os 2.2 -end - # 4 Meg format used for the nanoZ80 diskdef nanoz80 seclen 512 From 25c53f77c4fb8fcb517afe7c7a099b98aabba4bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= <106430829+venomix666@users.noreply.github.com> Date: Fri, 3 Oct 2025 19:43:27 +0200 Subject: [PATCH 26/34] Update README.md --- arch/nano-z80/README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/nano-z80/README.md b/arch/nano-z80/README.md index ab1a1be..f374bf6 100644 --- a/arch/nano-z80/README.md +++ b/arch/nano-z80/README.md @@ -5,18 +5,19 @@ The [nano-z80](https://github.com/venomix666/nano-z80) is a Z80 based SoC for th What you get with this port: -- 16x2 Mb drives on the SD-card +- 14x4 MB drives on the SD-card - Most of an ADM-3a / Kaypro II terminal emulator supporting 80x30 text -- A TPA of about 60kB +- A TPA of 57 kB - Two serial ports, one on the built in USB UART and on a TTL UART header - A crude but functional terminal program with X-modem file transfer support +- Utilities for changing baudrate and setting the display color - A fully implemented IO-byte which allows using monitor/keyboard or any of serial ports for the console. How to use it ------------- Write `nano-z80.img` to a micro-SD card using `dd` and press `B` in the nano-z80 monitor to boot from the SD-card. -By default, only drive `A:` is formatted. To use the additional drives, run `mkfs B:` up to `mkfs P:` to format the other drives once you have booted to CP/M. +By default, only drive `A:` is formatted. To use the additional drives, run `mkfs B:` up to `mkfs N:` to format the other drives once you have booted to CP/M. UART B (the one on the TTL UART header) supports changing baudrates, this can be done either using the `baudrate.com` tool or from inside `nanoterm`. @@ -43,7 +44,7 @@ As the monitor is diplayed both on the USB UART and the HDMI-output/USB-keyboard Who? ---- -This port and the nano-z80 project was made by [Henrik Löfgren](https://github.com/venomix666/). +CP/Mish was written David Given, and is covered under the terms of the whole CP/Mish project. See the documentation in the project root for more information. This port and the nano-z80 project was made by [Henrik Löfgren](https://github.com/venomix666/). From daefafc00298e82d814aefc42afe82bb1c47e768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Fri, 3 Oct 2025 19:56:46 +0200 Subject: [PATCH 27/34] Added cls tool --- arch/nano-z80/build.py | 3 ++- arch/nano-z80/tools/build.py | 9 +++++++++ arch/nano-z80/tools/cls.z80 | 10 ++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 arch/nano-z80/tools/cls.z80 diff --git a/arch/nano-z80/build.py b/arch/nano-z80/build.py index 7619b5c..b3fa6d6 100644 --- a/arch/nano-z80/build.py +++ b/arch/nano-z80/build.py @@ -77,6 +77,7 @@ "nanoterm.com": "arch/nano-z80/tools+nanoterm", "colorfg.com": "arch/nano-z80/tools+colorfg", "colorbg.com": "arch/nano-z80/tools+colorbg", - }, + "cls.com" : "arch/nano-z80/tools+cls", + }, ) diff --git a/arch/nano-z80/tools/build.py b/arch/nano-z80/tools/build.py index fb83543..ae1fb1d 100644 --- a/arch/nano-z80/tools/build.py +++ b/arch/nano-z80/tools/build.py @@ -28,6 +28,15 @@ ], ) +zmac( + name="cls", + relocatable=False, + src="./cls.z80", + deps=[ + "arch/nano-z80+addresses", + ], +) + ackprogram( name="nanoterm", diff --git a/arch/nano-z80/tools/cls.z80 b/arch/nano-z80/tools/cls.z80 new file mode 100644 index 0000000..32d5c46 --- /dev/null +++ b/arch/nano-z80/tools/cls.z80 @@ -0,0 +1,10 @@ + maclib addresses + + cseg + org 0x100 + + ld a, 0x1a + ld c, a + call BBASE+0x0c ; CONOUT + ret + From 79ba3dc08fb17193c231e796a9f02881ad9559fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Fri, 3 Oct 2025 20:25:48 +0200 Subject: [PATCH 28/34] Fix usage message for colorbg --- arch/nano-z80/tools/colorbg.z80 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/nano-z80/tools/colorbg.z80 b/arch/nano-z80/tools/colorbg.z80 index bc9f361..5669b1e 100644 --- a/arch/nano-z80/tools/colorbg.z80 +++ b/arch/nano-z80/tools/colorbg.z80 @@ -83,9 +83,9 @@ hex_conv_d: print_usage: call print_string_inline - db "colorfg - set foreground color on the nano-z80 computer" + db "colorbg - set background color on the nano-z80 computer" db 13,10 - db "Usage: colorfg RRGGBB. Eg. colorfg FFFFFF for white text" + db "Usage: colorbg RRGGBB. Eg. colorfg FFFFFF for white background" db 13,10, 0 scf ret From 0b49a6a3f715d0abc794f4a8186691b5f1db7f4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Fri, 3 Oct 2025 20:31:37 +0200 Subject: [PATCH 29/34] Optimized delay loop --- arch/nano-z80/tty.z80 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/nano-z80/tty.z80 b/arch/nano-z80/tty.z80 index f94d334..b0cf200 100644 --- a/arch/nano-z80/tty.z80 +++ b/arch/nano-z80/tty.z80 @@ -91,7 +91,7 @@ video_init: ld a,0 video_init_delay: inc a - cp a, 50 + cp a, 25 jr nz, video_init_delay ret From a3e6f9c70a3f121e32bffbd5f93e6fa7d9bd494e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= <106430829+venomix666@users.noreply.github.com> Date: Fri, 3 Oct 2025 20:48:21 +0200 Subject: [PATCH 30/34] Update README.md --- arch/nano-z80/README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/nano-z80/README.md b/arch/nano-z80/README.md index f374bf6..916c682 100644 --- a/arch/nano-z80/README.md +++ b/arch/nano-z80/README.md @@ -21,6 +21,11 @@ By default, only drive `A:` is formatted. To use the additional drives, run `mkf UART B (the one on the TTL UART header) supports changing baudrates, this can be done either using the `baudrate.com` tool or from inside `nanoterm`. +If you put the SD-card in a SD-card reader on a Linux machine, the diskdefs file in the +cpmish root allows you to read and write files to the A: drive, e.g.: + + cpmcp -f nanoz80 -i /dev/sdf 0:oncpm onlinux + Modifying the IO byte at boot -------------- The IO is mapped in the following way: @@ -28,7 +33,7 @@ The IO is mapped in the following way: - TTY/LPT: UART header on carrier board - PTP/PTR/UC1: USB UART on Tang Nano 20k board -The default value is 0x81, so that the HDMI output and USB keyboard is used for the console. It can be modified according to the table below before booting by first loading to image from the SD-card using the `L` command in the monitor and then writing to address 0xF833 by running for instance `WF833,83` to redirect the console to the USB UART, and then booting by running `JF800`. +The default value is 0x81, so that the HDMI output and USB keyboard is used for the console. It can be modified according to the table below before booting by first loading to image from the SD-card using the `L` command in the monitor and then writing to address 0xF833 by running for instance `WF633,83` to redirect the console to the USB UART, and then booting by running `JF600`. As the monitor is diplayed both on the USB UART and the HDMI-output/USB-keyboard by default, this can be useful when running with no external display connected. ``` From 6b5fde5519584feafe2b5d482cec9e3f89cf6d1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Sat, 4 Oct 2025 13:44:41 +0200 Subject: [PATCH 31/34] Fix register usage in color setting tools --- arch/nano-z80/tools/colorbg.z80 | 22 +++++++++++++--------- arch/nano-z80/tools/colorfg.z80 | 22 +++++++++++++--------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/arch/nano-z80/tools/colorbg.z80 b/arch/nano-z80/tools/colorbg.z80 index 5669b1e..0e252f8 100644 --- a/arch/nano-z80/tools/colorbg.z80 +++ b/arch/nano-z80/tools/colorbg.z80 @@ -14,7 +14,7 @@ CPM_FCB = 0x005c ; Read hex color values from command line ld hl, CPM_FCB inc hl ; Skip drive letter - ld ix, (bg_color) + ld de, bg_color ld b,0 read_col: ld a, (hl) @@ -27,16 +27,18 @@ read_col: sla a sla a ; Store in RAM - ld (ix), a + ld (de), a ; Read second digit inc hl ld a, (hl) call hex_conv ret c - or a, (ix) - ld (ix), a - inc ix + ld c, a + ld a, (de) + or a, c + ld (de), a + inc de inc hl inc b @@ -45,15 +47,17 @@ read_col: jr nz, read_col ; Set color - ld ix, (bg_color) + ld de, bg_color ld a, IO_SELECT_VID out (IOBANK), a - ld a, (ix) + ld a, (de) out (VID_BG_R), a - ld a, (ix+1) + inc de + ld a, (de) out (VID_BG_G), a - ld a, (ix+2) + inc de + ld a, (de) out (VID_BG_B), a ret diff --git a/arch/nano-z80/tools/colorfg.z80 b/arch/nano-z80/tools/colorfg.z80 index 6a3d119..af8575d 100644 --- a/arch/nano-z80/tools/colorfg.z80 +++ b/arch/nano-z80/tools/colorfg.z80 @@ -14,7 +14,7 @@ CPM_FCB = 0x005c ; Read hex color values from command line ld hl, CPM_FCB inc hl ; Skip drive letter - ld ix, (fg_color) + ld de, fg_color ld b,0 read_col: ld a, (hl) @@ -27,16 +27,18 @@ read_col: sla a sla a ; Store in RAM - ld (ix), a + ld (de), a ; Read second digit inc hl ld a, (hl) call hex_conv ret c - or a, (ix) - ld (ix), a - inc ix + ld c, a + ld a, (de) + or a, c + ld (de), a + inc de inc hl inc b @@ -45,15 +47,17 @@ read_col: jr nz, read_col ; Set color - ld ix, (fg_color) + ld de, fg_color ld a, IO_SELECT_VID out (IOBANK), a - ld a, (ix) + ld a, (de) out (VID_FG_R), a - ld a, (ix+1) + inc de + ld a, (de) out (VID_FG_G), a - ld a, (ix+2) + inc de + ld a, (de) out (VID_FG_B), a ret From 0c5ea1c04b727091c82c85973ab53a4d06756574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Sat, 4 Oct 2025 14:03:34 +0200 Subject: [PATCH 32/34] Delays... --- arch/nano-z80/tty.z80 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/nano-z80/tty.z80 b/arch/nano-z80/tty.z80 index b0cf200..524d4c5 100644 --- a/arch/nano-z80/tty.z80 +++ b/arch/nano-z80/tty.z80 @@ -91,7 +91,7 @@ video_init: ld a,0 video_init_delay: inc a - cp a, 25 + cp a, 37 jr nz, video_init_delay ret From 850f91e6050e1fc9797058fcce99dec7270431bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Thu, 9 Oct 2025 08:22:36 +0200 Subject: [PATCH 33/34] Remove ugly delay loop and add timeout to tty busy flag check instead --- arch/nano-z80/tty.z80 | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/nano-z80/tty.z80 b/arch/nano-z80/tty.z80 index 524d4c5..aedab20 100644 --- a/arch/nano-z80/tty.z80 +++ b/arch/nano-z80/tty.z80 @@ -84,15 +84,16 @@ tty_clear_to_eos: out (VID_CLEAR_TO_EOS), a ret ;jp tty_update_cursor -; This is soooo ugly, but it works for now +; Add a timeout as just checking the busy flag gets stuck sometimes +; for unknows reasons video_init: ld a, IO_SELECT_VID out (IO_BANK), a - ld a,0 + ld b,37 video_init_delay: - inc a - cp a, 37 - jr nz, video_init_delay + in a, (VID_BUSY) + ret z + djnz video_init_delay ret From 1986b91aba9676ea2514fb63bfb1c84dee6ab056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20L=C3=B6fgren?= Date: Fri, 10 Oct 2025 08:57:49 +0200 Subject: [PATCH 34/34] Added tool for changing the arrow key configuration --- arch/nano-z80/README.md | 2 + arch/nano-z80/build.py | 1 + arch/nano-z80/include/nano-z80.lib | 1 + arch/nano-z80/tools/arrowkey.z80 | 96 ++++++++++++++++++++++++++++++ arch/nano-z80/tools/build.py | 8 +++ 5 files changed, 108 insertions(+) create mode 100644 arch/nano-z80/tools/arrowkey.z80 diff --git a/arch/nano-z80/README.md b/arch/nano-z80/README.md index 916c682..734ed53 100644 --- a/arch/nano-z80/README.md +++ b/arch/nano-z80/README.md @@ -21,6 +21,8 @@ By default, only drive `A:` is formatted. To use the additional drives, run `mkf UART B (the one on the TTL UART header) supports changing baudrates, this can be done either using the `baudrate.com` tool or from inside `nanoterm`. +The USB keyboard arrow key configuration defaults to ADM3A key codes. This can be changed to WordStar or EMACS/MINCE key codes with the `arrowkey.com` tool. + If you put the SD-card in a SD-card reader on a Linux machine, the diskdefs file in the cpmish root allows you to read and write files to the A: drive, e.g.: diff --git a/arch/nano-z80/build.py b/arch/nano-z80/build.py index b3fa6d6..e219fc6 100644 --- a/arch/nano-z80/build.py +++ b/arch/nano-z80/build.py @@ -78,6 +78,7 @@ "colorfg.com": "arch/nano-z80/tools+colorfg", "colorbg.com": "arch/nano-z80/tools+colorbg", "cls.com" : "arch/nano-z80/tools+cls", + "arrowkey.com" : "arch/nano-z80/tools+arrowkey", }, ) diff --git a/arch/nano-z80/include/nano-z80.lib b/arch/nano-z80/include/nano-z80.lib index f8bd6a1..fa96c1d 100644 --- a/arch/nano-z80/include/nano-z80.lib +++ b/arch/nano-z80/include/nano-z80.lib @@ -51,6 +51,7 @@ USB_GAME_BUT2 equ $07 ; USB gamepad buttons reg 2 USB_NEW_REP equ $08 ; USB new report available USB_DEV_TYPE equ $09 ; USB device type USB_ERROR equ $0a ; USB error +USB_ARROWKEY equ $0b ; USB keyboard arrow key configuration KB_AVAIL equ $74 ; Keyboard character available, non-banked KB_CHAR equ $75 ; Keyboard character, non-banked diff --git a/arch/nano-z80/tools/arrowkey.z80 b/arch/nano-z80/tools/arrowkey.z80 new file mode 100644 index 0000000..b9e1660 --- /dev/null +++ b/arch/nano-z80/tools/arrowkey.z80 @@ -0,0 +1,96 @@ + maclib addresses + +IOBANK = 0x7f +IO_SELECT_USB = 0x02 +USB_ARROWKEY = 0x0b + + + cseg + org 0x100 + + call print_string_inline + db "nano-z80 arrow key configuration tool" + db 13,10 + db 13,10 + db "Current arrow key setting: " + db 0 + ; Print the current setting + ld a, IO_SELECT_USB + out (IOBANK), a + in a,(USB_ARROWKEY) + + cp 0 + jr nz, checkws + call print_string_inline + db "ADM3A" + db 0 + jr check_done +checkws: + cp 1 + jr nz, checkemacs + call print_string_inline + db "WordStar" + db 0 + jr check_done +checkemacs: + cp 2 + jr nz, check_3 + call print_string_inline + db "EMACS/MINCE" + db 0 + jr check_done +check_3: + ; No check needed + call print_string_inline + db "ADM3A (redundant)" + db 0 +check_done: + call print_string_inline + db 13,10,13,10 + db "Please select new configuration:" + db 13,10 + db "1: ADM3A" + db 13,10 + db "2: WordStar" + db 13,10 + db "3: EMACS/MINCE" + db 13,10,0 + + ; Get input + call BBASE+0x09 ; CONIN + cp '1' + jr c, illegal_input + cp '4' + jr c, accepted_input + jr illegal_input +accepted_input: + dec a + ld b, a + ld a, IO_SELECT_USB + out (IOBANK), a + ld a, b + out (USB_ARROWKEY), a + call print_string_inline + db 13,10 + db "Configuration updated" + db 0 + ret + +illegal_input: + call print_string_inline + db 13,10 + db "Illegal selection" + db 0 + ret +print_string_inline: + pop hl + ld a,(hl) + inc hl + push hl + cp 0 + ret z + ld c, a + call BBASE+0x0c ; CONOUT + jr print_string_inline + + diff --git a/arch/nano-z80/tools/build.py b/arch/nano-z80/tools/build.py index ae1fb1d..f3e843a 100644 --- a/arch/nano-z80/tools/build.py +++ b/arch/nano-z80/tools/build.py @@ -37,6 +37,14 @@ ], ) +zmac( + name="arrowkey", + relocatable=False, + src="./arrowkey.z80", + deps=[ + "arch/nano-z80+addresses", + ], +) ackprogram( name="nanoterm",