From 0a5cf0d9430b9d858a5b1b5c1a335f70a205f2dd Mon Sep 17 00:00:00 2001 From: Neil McPhail Date: Sat, 26 Apr 2025 23:30:03 +0100 Subject: [PATCH] Add example project, devcontainer config and debugger config --- .devcontainer/devcontainer.json | 21 ++++ .vscode/launch.json | 110 +++++++++++++++++++ Makefile | 10 ++ README.md | 8 ++ loader.asm | 25 +++++ main.asm | 22 ++++ print.asm | 180 ++++++++++++++++++++++++++++++++ 7 files changed, 376 insertions(+) create mode 100644 .devcontainer/devcontainer.json create mode 100644 .vscode/launch.json create mode 100644 Makefile create mode 100644 loader.asm create mode 100644 main.asm create mode 100644 print.asm diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..1bf8f2f --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,21 @@ +{ + "name": "ZX Spectrum dev tools", + "image": "boarstone.mcphail.uk/mcphail/speccydev", + "remoteUser": "ubuntu", + "runArgs": [ + "--network=host" + ], + "customizations": { + "vscode": { + "extensions": [ + "maziac.dezog", + "maziac.asm-code-lens", + "maziac.z80-instruction-set", + "maziac.hex-hover-converter", + "maziac.sna-fileviewer", + "maziac.nex-fileviewer", + "ms-vscode.makefile-tools" + ] + } + } +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..c203a6b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,110 @@ +{ + "configurations": [ + { + "type": "dezog", + "request": "launch", + "name": "Simulator - ZX81 56k RAM", + "remoteType": "zsim", + "zsim": { + "visualMemory": true, + "preset": "zx81", + "memoryModel": "ZX81-56K" + }, + "sjasmplus": [ + { + "path": "myprog.sld" + } + ], + "commandsAfterLaunch": [], + "history": { + "reverseDebugInstructionCount": 1000000, + "spotCount": 10, + "codeCoverageEnabled": true + }, + "startAutomatically": false, + "rootFolder": "${workspaceFolder}", + "load": "myprog.p" + }, + { + "type": "dezog", + "request": "launch", + "name": "Simulator - ZX128K Spectrum", + "remoteType": "zsim", + "zsim": { + "visualMemory": true, + "preset": "spectrum", + "memoryModel": "ZX128K" + }, + "sjasmplus": [ + { + "path": "myprog.sld" + } + ], + "commandsAfterLaunch": [], + "history": { + "reverseDebugInstructionCount": 1000000, + "spotCount": 10, + "codeCoverageEnabled": true + }, + "startAutomatically": false, + "rootFolder": "${workspaceFolder}", + "load": "myprog.sna", + "topOfStack": "0x5d58" + }, + { + "type": "dezog", + "request": "launch", + "name": "CSpect", + "remoteType": "cspect", + "sjasmplus": [ + { + "path": "myprog.sld" + } + ], + "commandsAfterLaunch": [], + "history": { + "reverseDebugInstructionCount": 1000000, + "spotCount": 10, + "codeCoverageEnabled": false + }, + "startAutomatically": false, + "rootFolder": "${workspaceFolder}", + "load": "myprog.sna", + "topOfStack": "0x5d58", + "cspect": { + "hostname": "host.docker.internal" + }, + "linux": { + "cspect": { + "hostname": "localhost" + } + } + }, + { + "type": "dezog", + "request": "launch", + "name": "Simulator - ZX48K Spectrum", + "remoteType": "zsim", + "zsim": { + "visualMemory": true, + "preset": "spectrum", + "memoryModel": "ZX48K" + }, + "sjasmplus": [ + { + "path": "myprog.sld" + } + ], + "commandsAfterLaunch": [], + "history": { + "reverseDebugInstructionCount": 1000000, + "spotCount": 10, + "codeCoverageEnabled": true + }, + "startAutomatically": false, + "rootFolder": "${workspaceFolder}", + "load": "myprog.sna", + "topOfStack": "0x5d58" + } + ] +} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9d7a9c7 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +myprog.sna myprog.tap myprog.sld: main.asm loader.asm print.asm + sjasmplus --sld=myprog.sld --fullpath main.asm + +clean: + rm -f *.tap + rm -f *.sna + rm -f *.sld + rm -rf .tmp/ + +.PHONY: clean diff --git a/README.md b/README.md index d57453a..1d8b650 100644 --- a/README.md +++ b/README.md @@ -1 +1,9 @@ Development tools for the ZX Spectrum + +The Dockerfile is the basis of the devcontainer. + +The devcontainer contains various assemblers etc. + +Build the example project by running `make` from the terminal or the VSCode extension. + +Debug in the build in simulator or in CSpect externally (example CSpect invocation on Windows would be `CSpect.exe -w2 -debug -remote`). diff --git a/loader.asm b/loader.asm new file mode 100644 index 0000000..0e87d3c --- /dev/null +++ b/loader.asm @@ -0,0 +1,25 @@ + MODULE basic_loader + ORG #5c00 +basic_start: + db 0, 0 ; line number + dw line_length +line_start: + db #fd, '0', #0e, 0, 0 ; CLEAR + dw code_start_addr - 1 + db 0, ':' + db #ef, '"' ; LOAD " + db "code" + db '"', #af, ':' ; name"CODE + db #f5, #c0 ; PRINT USR + db '0', #0e, 0, 0 + dw code_run_addr + db 0, #0d + +line_length EQU $ - line_start +basic_length EQU $ - basic_start + + EMPTYTAP "myprog.tap" + SAVETAP "myprog.tap", BASIC, "myprog", basic_start, basic_length, 0 + SAVETAP "myprog.tap", CODE, "code", code_start_addr, code_length + + ENDMODULE diff --git a/main.asm b/main.asm new file mode 100644 index 0000000..2013849 --- /dev/null +++ b/main.asm @@ -0,0 +1,22 @@ +code_start_addr EQU #8000 + ORG code_start_addr + + MODULE main +@code_run_addr: + ld a, 57 + ld bc, 64 + ld hl, 0 + call print_string + db "Hello, world!", 0 + ret + + ENDMODULE + + INCLUDE print.asm + +code_length EQU $ - code_start_addr + + DEVICE ZXSPECTRUM48 + SLDOPT COMMENT WPMEM, LOGPOINT, ASSERTION + SAVESNA "myprog.sna", code_run_addr + INCLUDE loader.asm diff --git a/print.asm b/print.asm new file mode 100644 index 0000000..31a538a --- /dev/null +++ b/print.asm @@ -0,0 +1,180 @@ +ink_black EQU 0 +ink_blue EQU 1 +ink_red EQU 2 +ink_magenta EQU 3 +ink_green EQU 4 +ink_cyan EQU 5 +ink_yellow EQU 6 +ink_white EQU 7 +black EQU ink_black +blue EQU ink_blue +red EQU ink_red +magenta EQU ink_magenta +green EQU ink_green +cyan EQU ink_cyan +yellow EQU ink_yellow +white EQU ink_white +paper_black EQU 0 +paper_blue EQU 8 +paper_red EQU 16 +paper_magenta EQU 24 +paper_green EQU 32 +paper_cyan EQU 40 +paper_yellow EQU 48 +paper_white EQU 56 +bright EQU 64 +flash EQU 128 +attr_list_end EQU flash | bright | ink_black | paper_black + + MODULE print +bm_start EQU #4000 +attr_area EQU #5800 +bm_len EQU 6144 +attr_len EQU 768 +CHARS EQU #5c36 +char_posn: + dw #4000 + + MODULE print_string +; Set HL to be row and column and follow the call with a null-terminated string +; All registers preserved +; char_posn will have been moved to after string, but HL will still have coordinates of string start +@print_string: + call set_char_posn + ex (sp), hl + push af +loop: + ld a, (hl) + inc hl + or a + jr z, exit + call print_char + jr loop +exit: + pop af + ex (sp), hl + ret + ENDMODULE + + MODULE print_char +; Prints the single character from the A register +; All registers preserved +; char_posn will point to next square +@print_char: + push hl + push de + push af + ld h, 0 + ld l, a + add hl, hl + add hl, hl + add hl, hl + ld d, h + ld e, l + ld hl, (print.CHARS) + add hl, de + ld d, h + ld e, l + ld hl, (print.char_posn) + push bc + ld b, 8 +loop: + ld a, (de) + ld (hl), a + inc h + inc de + djnz loop + ld hl, (print.char_posn) + inc l + jr nz, update_char_posn + ld a, #50 + cp h + jr nz, next_third + ld hl, print.bm_start + jr update_char_posn +next_third: + ld a, 8 + add a, h + ld h, a +update_char_posn: + ld (print.char_posn), hl + pop bc + pop af + pop de + pop hl + ret + ENDMODULE + + MODULE set_char_posn +; Pass row and column, in that order, in HL +@set_char_posn: + push hl + push af + ld a, h + ; check for top third + ld h, %01000000 + sub 8 + jr c, set_column + ; check for middle third + ld h, %01001000 + sub 8 + jr c, set_column + ; must be bottom third + ld h, %01010000 + sub 8 +set_column: + ; restore the row offset of the third and shift it into upper 3 bits of L + add a, 8 + sla a + sla a + sla a + sla a + sla a + or l + ld l, a + ld (print.char_posn), hl + pop af + pop hl + ret + ENDMODULE + + MODULE set_attributes +; Row and column in HL +; db list of attributes follows call +; terminate with attr_list_end byte (bright flashing black on black) +@set_attributes: + ex de, hl + ex (sp), hl + push de + push hl + ld h, 0 + ld l, d + ld d, h + add hl, hl + add hl, hl + add hl, hl + add hl, hl + add hl, hl + add hl, de + ld de, print.attr_area + add hl, de + pop de + ex de, hl + push af +loop: + ld a, (hl) + inc hl + cp attr_list_end + jr z, exit + ld (de), a + inc de + jr loop +exit: + pop af + pop de + ex (sp), hl + ex de, hl + ret + ENDMODULE + + ENDMODULE