├── blobs ├── font64.bin └── font80.bin ├── .gitignore ├── zcpr ├── Makefile └── zcpr.asm ├── bios ├── display.asm ├── console.asm ├── uart.asm ├── keyboard.asm ├── bios.asm ├── mmc.asm ├── offload.asm ├── display │ ├── speccy.asm │ └── timex.asm └── disk.asm ├── .vscode ├── extensions.json ├── launch.json └── tasks.json ├── LICENSE ├── rom.asm ├── software ├── esprst.z80 ├── dial.z80 └── zmp-overlay │ └── zmoiobyt.z80 ├── main.asm ├── README.MD └── cpm.asm /blobs/font64.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihirash/cpm-uno/HEAD/blobs/font64.bin -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .tmp/ 2 | cpm.bin 3 | cpm.sys 4 | *.com 5 | test.hdd 6 | *.sna 7 | *.sld 8 | zcpr.bin 9 | *.hex 10 | *.rom -------------------------------------------------------------------------------- /zcpr/Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | ../zcpr.bin: zcpr.asm 4 | zxcc MAC -$< -$$PO 5 | zxcc MLOAD25 -$@=zcpr.hex 6 | rm ../zcpr.bin || true 7 | mv zcpr.bin ../ 8 | 9 | -------------------------------------------------------------------------------- /bios/display.asm: -------------------------------------------------------------------------------- 1 | ifdef TIMEX_SCR 2 | DISPLAY "USING TIMEX SCREEN" 3 | include "display/timex.asm" 4 | endif 5 | 6 | ifdef SPECCY_SCR 7 | DISPLAY "USING SPECCY SCREEN" 8 | include "display/speccy.asm" 9 | endif -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "maziac.asm-code-lens", 4 | "maziac.dezog", 5 | "maziac.hex-hover-converter", 6 | "maziac.z80-instruction-set", 7 | "maziac.sna-fileviewer", 8 | "maziac.nex-fileviewer", 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /bios/console.asm: -------------------------------------------------------------------------------- 1 | module console 2 | 3 | status: 4 | ld a, (IOBYTE) 5 | and 3 6 | jp nz, keyboard.read_status 7 | jp uart.status 8 | in: 9 | ld a, (IOBYTE) 10 | and 3 11 | jp nz, keyboard.read 12 | jp uart.read 13 | 14 | out: 15 | ld a, (IOBYTE) 16 | and 3 17 | jp nz, display.write 18 | jp uart.write 19 | endmodule -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | By using this CP/M 2.2 port for ZXUno you accepting that you against and not supporting russian war aggresy in Ukraine, Georgia and other countries. 2 | 3 | You can freely use/modify binary distribution and/or source code only in case accepting first paragraph of this agreement. 4 | 5 | In case development of derivative products, please keep information about authors of original code/data. 6 | 7 | If you want support me - better donate to Ukrainian refugees. They need it more really. -------------------------------------------------------------------------------- /rom.asm: -------------------------------------------------------------------------------- 1 | DEVICE ZXSPECTRUM128 2 | org 0x0000 3 | main: 4 | di 5 | ld bc, #7ffd : ld a, 6 : out (c),a 6 | ld hl, page6 7 | ld de, #c000 8 | ld bc,page6_len 9 | ldir 10 | 11 | 12 | ld bc, #7ffd : ld a, 3 : out (c),a 13 | ld hl, cpm 14 | ld de, cpm_start 15 | ld bc, cpm_size 16 | ldir 17 | 18 | jp cpm_start 19 | page6: 20 | DISP #8000 21 | include "bios/offload.asm" 22 | ENT 23 | page6_len = $ - page6 24 | 25 | cpm: 26 | DISP $d5b0 27 | DISPLAY "CP/M starts here: ", $ 28 | cpm_start: 29 | jp BOOT 30 | include "cpm.asm" 31 | cpm_size = $ - cpm_start 32 | ENT 33 | savebin "cpm.rom", 0, #4000 -------------------------------------------------------------------------------- /software/esprst.z80: -------------------------------------------------------------------------------- 1 | ORG 100H 2 | TTY EQU 0 3 | CRT EQU 1 4 | BDOS EQU 5 5 | 6 | START: 7 | ; Step 1 8 | LD DE, MSG1 9 | CALL PRINTS 10 | CALL SET_TTY 11 | LD DE, CMD1 12 | CALL PRINTS 13 | EI 14 | HALT 15 | HALT 16 | HALT 17 | HALT 18 | ; Step 2 19 | CALL SET_CRT 20 | LD DE, MSG2 21 | CALL PRINTS 22 | CALL SET_TTY 23 | LD DE, CMD2 24 | CALL PRINTS 25 | 26 | CALL SET_CRT 27 | RET 28 | 29 | PRINTS: 30 | LD C,9 31 | JP BDOS 32 | 33 | SET_CRT: 34 | LD E,CRT 35 | JR SET_IO 36 | SET_TTY: 37 | LD E,TTY 38 | SET_IO: 39 | LD C,8 40 | JP BDOS 41 | 42 | MSG1 DEFB "Switching to command mode", 13, 10, "$" 43 | MSG2 DEFB "Resetting chip",13,10,"$" 44 | 45 | CMD1 DEFB "+++$" 46 | CMD2 DEFB "AT+RST",13,10,"$" 47 | -------------------------------------------------------------------------------- /main.asm: -------------------------------------------------------------------------------- 1 | DEVICE ZXSPECTRUM128 2 | SLDOPT COMMENT WPMEM, LOGPOINT, ASSERTION 3 | 4 | 5 | DEFINE SPECCY_SCR 6 | DEFINE DIVMMC 7 | ; DEFINE TIMEX_SCR 8 | ; DEFINE ZXUNO 9 | ; DEFINE ZC 10 | 11 | org 0x8000 12 | stack_top: 13 | main: 14 | di 15 | ld bc, #7ffd : ld a, 6 : out (c),a 16 | ld hl, page6 17 | ld de, #c000 18 | ld bc,page6_len 19 | ldir 20 | 21 | 22 | ld bc, #7ffd : ld a, 3 : out (c),a 23 | ld hl, cpm 24 | ld de, cpm_start 25 | ld bc, cpm_size 26 | ldir 27 | 28 | jp cpm_start 29 | page6: 30 | DISP #8000 31 | include "bios/offload.asm" 32 | ENT 33 | page6_len = $ - page6 34 | 35 | cpm: 36 | DISP $d5b0 37 | DISPLAY "CP/M starts here: ", $ 38 | cpm_start: 39 | jp BOOT 40 | include "cpm.asm" 41 | cpm_size = $ - cpm_start 42 | ENT 43 | 44 | save3dos "cpm.sys", main, $-main 45 | savesna "test.sna", main -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "dezog", 6 | "request": "launch", 7 | "name": "ZEsarUX", 8 | "remoteType": "zrcp", 9 | "sjasmplus": [ 10 | { 11 | "path": "debug.sld" 12 | } 13 | ], 14 | "history": { 15 | "reverseDebugInstructionCount": 1000000, 16 | "spotCount": 10, 17 | }, 18 | "startAutomatically": true, 19 | "commandsAfterLaunch": [ 20 | "-logpoint enable", 21 | "-assertion enable", 22 | "-wpmem enable" 23 | ], 24 | "rootFolder": "${workspaceFolder}", 25 | "topOfStack": "stack_top", 26 | "load": "test.sna", 27 | "smallValuesMaximum": 513, 28 | "tmpDir": ".tmp" 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "sjasmplus", 6 | "type": "shell", 7 | "command": "sjasmplus", 8 | "args": [ 9 | "--sld=debug.sld", 10 | "--fullpath", 11 | "main.asm" 12 | ], 13 | "problemMatcher": { 14 | "owner": "sjasmplus", 15 | "fileLocation": [ 16 | "relative", 17 | "${workspaceRoot}" 18 | ], 19 | "pattern": { 20 | "regexp": "^(.*)\\((\\d+)\\):\\s+(warning|error):\\s+(.*)$", 21 | "file": 1, 22 | "line": 2, 23 | "severity": 3, 24 | "message": 4 25 | } 26 | }, 27 | "group": { 28 | "kind": "build", 29 | "isDefault": true 30 | } 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /blobs/font80.bin: -------------------------------------------------------------------------------- 1 | 8DlD|TD88|T|Dl|8(|||88|88ll88||88<<~~fBBf~~$$~~fZZf~~ 8ll88ll88<$< ``<$<$,l`T8l8T 08<80  < 8||8llllll ",13,10,"$" 94 | 95 | PRINTS 96 | LD C, 09H 97 | JP BDOS 98 | 99 | SET_TTY: 100 | IF USE_MODEM 101 | PUSH DE 102 | LD C, 8 103 | LD E, TTY 104 | CALL BDOS 105 | POP DE 106 | ENDIF 107 | RET 108 | 109 | SET_CRT: 110 | IF USE_MODEM 111 | PUSH AF 112 | LD C, 8 113 | LD E, CRT 114 | CALL BDOS 115 | POP AF 116 | ENDIF 117 | RET 118 | 119 | AT_CMD: 120 | CALL SET_TTY 121 | CALL PRINTS 122 | CALL OK_ERR 123 | CALL SET_CRT 124 | RET 125 | 126 | OK_ERR: 127 | CALL READ_RESP 128 | ; CHECK FOR RESPONSES 129 | CALL IS_OK 130 | LD A, 0FFH 131 | RET Z 132 | 133 | CALL IS_ERR 134 | LD A, 00H 135 | RET Z 136 | JR OK_ERR 137 | 138 | IS_OK: 139 | LD HL, R_OK 140 | LD DE, DATA 141 | JP STRCMP 142 | 143 | IS_ERR: 144 | LD HL, R_ERR 145 | LD DE, DATA 146 | JP STRCMP 147 | 148 | STRCMP: 149 | LD A,(HL) 150 | LD B, A 151 | LD A, (DE) 152 | CP B 153 | RET NZ 154 | OR A 155 | RET Z 156 | 157 | INC HL 158 | INC DE 159 | JR STRCMP 160 | 161 | READ_RESP: 162 | ; CLEAN UP BUFFER 163 | LD HL, DATA-1 164 | LD DE, DATA 165 | LD BC, 80 166 | XOR A 167 | LD (HL), A 168 | LDIR 169 | ; READ 170 | LD DE, DATA 171 | READ_LOOP 172 | PUSH DE 173 | READZERO 174 | LD C, 6 175 | LD E, 0FFH 176 | CALL BDOS 177 | AND A 178 | JR Z, READZERO 179 | POP DE 180 | CP 13 181 | RET Z 182 | CP 10 183 | RET Z 184 | LD (DE), A 185 | INC DE 186 | JR READ_LOOP 187 | 188 | 189 | C_AT DEFB "ATE0",13,10,"$" 190 | 191 | C_MODE DEFB "AT+CIPMODE=1",13,10,"$" 192 | 193 | C_CON1 DEFB 'AT+CIPSTART="TCP","$' 194 | C_CON2 DEFB '",$' 195 | C_CON3 DEFB 13,10,'$' 196 | 197 | C_SND DEFB "AT+CIPSEND",13,10,"$" 198 | 199 | ; AWAITED RESPONSES 200 | R_OK DEFB "OK",0 201 | R_ERR DEFB "ERROR",0 202 | 203 | ; RESPONSE BUFFER 204 | DATA: 205 | DEFS 80 206 | 207 | HOST: 208 | DEFS 40 209 | PORT: 210 | DEFS 6 211 | DEFB "HERE ENDS ALL MY DATA" 212 | -------------------------------------------------------------------------------- /bios/mmc.asm: -------------------------------------------------------------------------------- 1 | module drive 2 | ifdef DIVMMC 3 | mmc_en = #e7 4 | mmc_data = #eb 5 | slot1 = #fe 6 | slot0 = #ff 7 | endif 8 | 9 | ifdef ZC 10 | mmc_en = #77 11 | mmc_data = #57 12 | slot1 = #1 13 | slot0 = #0 14 | endif 15 | 16 | CMD_12 EQU 0x4C ;STOP_TRANSMISSION 17 | CMD_17 EQU 0x51 ;READ_SINGLE_BLOCK 18 | CMD_18 EQU 0x52 ;READ_MULTIPLE_BLOCK 19 | CMD_24 EQU 0x58 ;WRITE_BLOCK 20 | CMD_25 EQU 0x59 ;WRITE_MULTIPLE_BLOCK 21 | CMD_55 EQU 0x77 ;APP_CMD 22 | CMD_58 EQU 0x7A ;READ_OCR 23 | CMD_59 EQU 0x7B ;CRC_ON_OFF 24 | ACMD_41 EQU 0x69 ;SD_SEND_OP_COND 25 | 26 | macro mmc_cs_en 27 | push af 28 | ld a, slot1 : out (mmc_en), a 29 | pop af 30 | endm 31 | 32 | macro mmc_cs_dis 33 | push af 34 | ld a, slot0 : out (mmc_en), a 35 | pop af 36 | endm 37 | 38 | init: 39 | mmc_cs_dis 40 | ld bc, mmc_data 41 | ld de, #20FF ; Just flush all data from SD buffer 42 | .init_loop 43 | out (c), e 44 | dec d 45 | jr nz, .init_loop 46 | .zaw001 47 | ld hl, CMD00 : call outcom : call in_oout 48 | dec a 49 | jr nz, .zaw001 ; If response not eq 1 - retry 50 | ld hl, CMD08 : call outcom : call in_oout 51 | dup 4 52 | in h, (c) : nop ; We don't care about result 53 | edup 54 | ld hl, 0 55 | bit 2, a 56 | jr nz, .zaw006 57 | ld h, #40 ; Looks like it's SDHC 58 | .zaw006 59 | ld a, CMD_55 : call out_com : call in_oout 60 | in (c) : in (c) 61 | ld a, ACMD_41 : out (c), a : nop 62 | out (c), h : nop 63 | out (c), l : nop 64 | out (c), l : nop 65 | out (c), l : nop 66 | ld a, #ff : out (c), a : call in_oout 67 | and a 68 | jr nz, .zaw006 69 | .zaw004 ; Disable CRC 70 | ld a, CMD_59 : call out_com 71 | call in_oout : and a 72 | jr nz, .zaw004 73 | .zaw005 ; Force 512b block 74 | ld hl, CMD16 : call outcom 75 | call in_oout : and a 76 | jr nz, .zaw005 77 | 78 | ld a, CMD_58 : ld bc, mmc_data 79 | call out_com : call in_oout 80 | in a, (c) : nop 81 | in h, (c) : nop 82 | in h, (c) : nop 83 | in h, (c) : nop 84 | and #40 85 | ld (blsize), a 86 | mmc_cs_dis 87 | ret 88 | 89 | secm200: 90 | push hl 91 | push af 92 | ld h, b 93 | ld l, c 94 | ld bc, mmc_data 95 | mmc_cs_en 96 | ld a, 00 97 | blsize = $ - 1 98 | or a 99 | jr nz, .secn200 100 | ex de, hl 101 | add hl, hl 102 | ex de, hl 103 | adc hl, hl 104 | ld h, l 105 | ld l, d 106 | ld d, e 107 | ld e, 0 108 | .secn200 109 | pop af 110 | in (c) : in (c) 111 | out (c), a : nop 112 | out (c), h : nop 113 | out (c), l : nop 114 | out (c), d : nop 115 | out (c), e : nop 116 | ld a, #ff : out (c), a 117 | pop hl 118 | ret 119 | 120 | ;; BCDE - LBA addr 121 | ;; HL - dma buff 122 | read: 123 | ld a, CMD_17 : call secm200 124 | .rd1 125 | call in_oout 126 | cp #fe 127 | jr nz, .rd1 128 | ld bc, mmc_data 129 | inir : nop 130 | inir : nop 131 | in a, (c) : nop 132 | in a, (c) : nop 133 | call in_oout 134 | mmc_cs_dis 135 | ret 136 | 137 | write: 138 | ld a, CMD_24 : call secm200 139 | .wait1 140 | call in_oout 141 | inc a 142 | jr nz, .wait1 143 | ld a, #fe : ld bc, mmc_data : out (c), a : nop 144 | ld b, #80 : otir : nop 145 | ld b, #80 : otir : nop 146 | ld b, #80 : otir : nop 147 | ld b, #80 : otir : nop 148 | 149 | ld a, #ff : out (c),a : nop 150 | ld a, #ff : out (c),a : nop 151 | .wait2 152 | call in_oout 153 | inc a 154 | jr nz, .wait2 155 | 156 | mmc_cs_dis 157 | ret 158 | 159 | outcom: 160 | mmc_cs_en 161 | ld bc, #600+mmc_data ; 6 is loop counter 162 | otir 163 | ret 164 | 165 | 166 | out_com: 167 | push af 168 | mmc_cs_en 169 | pop af 170 | ld bc, mmc_data 171 | in (c) 172 | in (c) 173 | out (c), a 174 | xor a 175 | out (c), a : nop 176 | out (c), a : nop 177 | out (c), a : nop 178 | out (c), a : nop 179 | dec a : out (c), a 180 | ret 181 | 182 | in_oout: 183 | di 184 | push de 185 | ld de, #20ff 186 | ld b, mmc_data 187 | .in_wait 188 | in a, (c) 189 | cp e 190 | jr nz, .in_exit 191 | .in_next 192 | dec d 193 | jr nz, .in_wait 194 | .in_exit 195 | pop de 196 | ret 197 | 198 | 199 | CMD00 db 0x40,0x00,0x00,0x00,0x00,0x95 ;GO_IDLE_STATE 200 | CMD08 db 0x48,0x00,0x00,0x01,0xAA,0x87 ;SEND_IF_COND 201 | CMD16 db 0x50,0x00,0x00,0x02,0x00,0xFF ;SET_BLOCKEN 202 | 203 | buffer ds 512 204 | endmodule -------------------------------------------------------------------------------- /bios/offload.asm: -------------------------------------------------------------------------------- 1 | module display 2 | font: 3 | ifdef TIMEX_SCR 4 | incbin "blobs/font80.bin" 5 | endif 6 | 7 | ifdef SPECCY_SCR 8 | incbin "blobs/font64.bin" 9 | endif 10 | 11 | table_addr_scr 12 | defw #4000,#4100,#4200,#4300,#4400,#4500,#4600,#4700 13 | defw #4020,#4120,#4220,#4320,#4420,#4520,#4620,#4720 14 | defw #4040,#4140,#4240,#4340,#4440,#4540,#4640,#4740 15 | defw #4060,#4160,#4260,#4360,#4460,#4560,#4660,#4760 16 | defw #4080,#4180,#4280,#4380,#4480,#4580,#4680,#4780 17 | defw #40a0,#41a0,#42a0,#43a0,#44a0,#45a0,#46a0,#47a0 18 | defw #40c0,#41c0,#42c0,#43c0,#44c0,#45c0,#46c0,#47c0 19 | defw #40e0,#41e0,#42e0,#43e0,#44e0,#45e0,#46e0,#47e0 20 | 21 | defw #4800,#4900,#4a00,#4b00,#4c00,#4d00,#4e00,#4f00 22 | defw #4820,#4920,#4a20,#4b20,#4c20,#4d20,#4e20,#4f20 23 | defw #4840,#4940,#4a40,#4b40,#4c40,#4d40,#4e40,#4f40 24 | defw #4860,#4960,#4a60,#4b60,#4c60,#4d60,#4e60,#4f60 25 | defw #4880,#4980,#4a80,#4b80,#4c80,#4d80,#4e80,#4f80 26 | defw #48a0,#49a0,#4aa0,#4ba0,#4ca0,#4da0,#4ea0,#4fa0 27 | defw #48c0,#49c0,#4ac0,#4bc0,#4cc0,#4dc0,#4ec0,#4fc0 28 | defw #48e0,#49e0,#4ae0,#4be0,#4ce0,#4de0,#4ee0,#4fe0 29 | 30 | defw #5000,#5100,#5200,#5300,#5400,#5500,#5600,#5700 31 | defw #5020,#5120,#5220,#5320,#5420,#5520,#5620,#5720 32 | defw #5040,#5140,#5240,#5340,#5440,#5540,#5640,#5740 33 | defw #5060,#5160,#5260,#5360,#5460,#5560,#5660,#5760 34 | defw #5080,#5180,#5280,#5380,#5480,#5580,#5680,#5780 35 | defw #50a0,#51a0,#52a0,#53a0,#54a0,#55a0,#56a0,#57a0 36 | defw #50c0,#51c0,#52c0,#53c0,#54c0,#55c0,#56c0,#57c0 37 | defw #50e0,#51e0,#52e0,#53e0,#54e0,#55e0,#56e0,#57e0 38 | endmodule 39 | 40 | module keyboard 41 | 42 | Keyboard_Map: DB 0x00,"z","x","c","v" 43 | DB "a","s","d","f","g" 44 | DB "q","w","e","r","t" 45 | DB "1","2","3","4","5" 46 | DB "0","9","8","7","6" 47 | DB "p","o","i","u","y" 48 | DB CR,"l","k","j","h" 49 | DB " ",0x00,"m","n","b" 50 | 51 | 52 | Keyboard_Map_CS: DB 0x00,"Z","X","C","V" 53 | DB "A","S","D","F","G" 54 | DB "Q","W","E","R","T" 55 | DB BS,CAPS,0x00,0x00,0x08 56 | DB DEL,TAB,0x0c,0x0b,0x0a 57 | DB "P","O","I","U","Y" 58 | DB CR,"L","K","J","H" 59 | DB ESC,0x00,"M","N","B" 60 | 61 | Keyboard_Map_SS: DB 0x00,":","$","?","/" 62 | DB '~','|','\','{','}' 63 | DB 0x00,'{','}',"<",">" 64 | DB "!","@","#","$","%" 65 | DB "_",")","(","'","&" 66 | DB "\"",";",0x00,']','[' 67 | DB CR,"=","+","-","^" 68 | DB " ",0x00,".",",","*" 69 | 70 | Keyboard_Map_CSSS: 71 | DB 0x00,CNTRLZ,CNTRLX,CNTRLC,0x16 72 | DB 0x01,CNTRLS,0x04,0x06,0x07 73 | DB 0x11,0x17,CNTRLE,CNTRLR,0x14 74 | DB DEL,0x00,0x00,0x00,0x00 75 | DB BS,0x00,TAB,0x00,0x00 76 | DB CNTRLP,0x0f,0x09,CNTRLU,0x19 77 | DB CR,0x0c,0x0b,0x0a,0x08 78 | DB " ",0x00,0x0d,0x0e,0x02 79 | Keyboard_Map_CL: DB 0x00,"Z","X","C","V" 80 | DB "A","S","D","F","G" 81 | DB "Q","W","E","R","T" 82 | DB "1","2","3","4","5" 83 | DB "0","9","8","7","6" 84 | DB "P","O","I","U","Y" 85 | DB CR,"L","K","J","H" 86 | DB " ",0x00,"M","N","B" 87 | 88 | Keyboard_Map_CL_CS: DB 0x00,"Z","X","C","V" 89 | DB "A","S","D","F","G" 90 | DB "Q","W","E","R","T" 91 | DB BS,CAPS,0x00,0x00,0x08 92 | DB DEL,TAB,0x0c,0x0b,0x0a 93 | DB "P","O","I","U","Y" 94 | DB CR,"L","K","J","H" 95 | DB ESC,0x00,"M","N","B" 96 | 97 | Keyboard_Map_CL_SS: DB 0x00,":","$","?","/" 98 | DB '~','|','\','{','}' 99 | DB 0x00,'{','}',"<",">" 100 | DB "!","@","#","$","%" 101 | DB "_",")","(","'","&" 102 | DB "\"",";",0x00,']','[' 103 | DB CR,"=","+","-","^" 104 | DB " ",0x00,".",",","*" 105 | 106 | Keyboard_Map_CL_CSSS: 107 | DB 0x00,CNTRLZ,CNTRLX,CNTRLC,0x16 108 | DB 0x01,CNTRLS,0x04,0x06,0x07 109 | DB 0x11,0x17,CNTRLE,CNTRLR,0x14 110 | DB DEL,0x00,0x00,0x00,0x00 111 | DB BS,0x00,TAB,0x00,0x00 112 | DB CNTRLP,0x0f,0x09,CNTRLU,0x19 113 | DB CR,0x0c,0x0b,0x0a,0x08 114 | DB " ",0x00,0x0d,0x0e,0x02 115 | endmodule 116 | ccp_backup: 117 | ds FBASE-CBASE 118 | display "CCP_BACKUP_size: ",$-ccp_backup 119 | 120 | display $c000-$ 121 | -------------------------------------------------------------------------------- /bios/display/speccy.asm: -------------------------------------------------------------------------------- 1 | module display 2 | 3 | init: 4 | ld a, 3 : ld bc, #7ffd : out (c),a 5 | ld a, %00000101 : ld bc, #1ffd : out (c),a 6 | 7 | ld a, 016O 8 | ld hl, #5800 9 | ld de, #5801 10 | ld bc, 767 11 | ld (hl), a 12 | ldir 13 | 14 | call cls 15 | call cursor 16 | ld a, %00000001 : ld bc, #1ffd : out (c),a 17 | ret 18 | 19 | cls: 20 | ld a, 1 : out (#fe), a 21 | xor a 22 | ld hl,#4000 23 | ld de,#4001 24 | ld bc,#17ff 25 | ld (hl),a 26 | ldir 27 | home: 28 | or a 29 | sbc hl, hl 30 | ld (coords), hl 31 | ret 32 | 33 | write: 34 | di 35 | localStack 36 | ld a, c 37 | push af 38 | ld a, %00000101 : ld bc, #1ffd : out (c),a 39 | pop af 40 | ; A - char 41 | call tty_putC 42 | ld a, %00000001 : ld bc, #1ffd : out (c),a 43 | usualStack 44 | ei 45 | ret 46 | 47 | putC: 48 | push af 49 | ld a, %00000101 : ld bc, #1ffd : out (c),a 50 | pop af 51 | call tty_putC 52 | ld a, %00000001 : ld bc, #1ffd : out (c),a 53 | ret 54 | 55 | tty_putC: 56 | push af 57 | call cursor 58 | ld a, (is_esc) : and a : jr nz, handleEsc 59 | pop af 60 | call _tty_putC 61 | jp cursor 62 | 63 | handleEsc: 64 | pop bc 65 | ld a, (is_esc) 66 | cp 2 : jr nc, .load 67 | ld a, '=' : cp b : jr z, .prepare_load 68 | .not_valid 69 | xor a : ld (is_esc), a 70 | jp cursor 71 | .load 72 | ld a, (is_esc) 73 | ld hl, coords 74 | cp 3 : jr z, .load_coord 75 | inc hl 76 | .load_coord 77 | ld a, b 78 | sub 32 79 | ld (hl), a 80 | ld a, (is_esc) : cp 3 : jr z, .not_valid 81 | .prepare_load 82 | ld hl, is_esc 83 | inc (hl) 84 | jp cursor 85 | 86 | handleBS: 87 | ld hl, (coords) 88 | ld a, l 89 | and a 90 | jr z, .prev_line 91 | dec a 92 | ld l, a 93 | .exit 94 | ld (coords), hl 95 | ret 96 | .prev_line 97 | ld a, h 98 | and a 99 | jr z, .exit 100 | dec h 101 | ld l, 63 102 | jr .exit 103 | 104 | cursor_up: 105 | ld a, (coords + 1) 106 | and a 107 | ret z 108 | dec a 109 | ld (coords + 1),a 110 | ret 111 | 112 | cursor_down: 113 | ld hl, (coords) 114 | inc h 115 | ld (coords), hl 116 | jp scrollCheck 117 | 118 | _esc: 119 | ld a, 1 : ld (is_esc),a 120 | ret 121 | 122 | clearline: 123 | ld de, (coords) 124 | .loop 125 | ld a, 80 126 | cp e : ret z 127 | push de 128 | call posit 129 | xor a 130 | 131 | dup 8 132 | ld (de), a : inc d 133 | edup 134 | 135 | pop de 136 | inc e 137 | jr .loop 138 | 139 | 140 | _tty_putC: 141 | cp 1 : jp z, home 142 | cp 8 : jp z, handleBS 143 | cp 12 : jp z, cls 144 | cp 13 : jp z, .cr 145 | cp 10 : jp z, .lf 146 | cp 20 : jp z, cursor_up 147 | cp 21 : jp z, cursor_down 148 | cp 22 : jp z, handleBS 149 | cp 23 : jp z, .cursor_right 150 | cp 24 : jp z, clearline 151 | cp 26 : jp z, cls 152 | cp 27 : jr z, _esc 153 | call drawC 154 | 155 | .cursor_right 156 | ld hl, coords 157 | inc (hl) 158 | ld a, (hl) : cp 64 : jr nc, .crlf 159 | ret 160 | .crlf: 161 | ld hl, coords : xor a : ld (hl), a 162 | jr .lf 163 | .cr: 164 | ld hl, coords 165 | xor a : ld (hl), a 166 | ret 167 | .lf 168 | ld hl, coords+1 169 | inc (hl) 170 | scrollCheck: 171 | ld a, (coords+1) 172 | cp 24 : ret c 173 | ld a, 23 174 | ld (coords+1), a 175 | scroll: 176 | ld hl, table_addr_scr 177 | ld b, 184 178 | .do 179 | push bc 180 | ld e, (hl) : inc hl 181 | ld d, (hl) : inc hl 182 | push hl 183 | ld bc, 14 184 | add hl, bc 185 | ld c, (hl) : inc hl 186 | ld b, (hl) 187 | 188 | ld h, b 189 | ld l, c 190 | 191 | ld bc, 32 192 | ldir 193 | 194 | pop hl 195 | pop bc 196 | djnz .do 197 | 198 | ld b, 8 199 | .bottom 200 | push bc 201 | ld e,(hl) 202 | inc hl 203 | ld a,(hl) 204 | ld d, a 205 | inc hl 206 | 207 | push hl 208 | ld h,d 209 | ld l,e 210 | inc de 211 | ld (hl),0 212 | ld bc,31 213 | ldir 214 | 215 | pop hl 216 | pop bc 217 | djnz .bottom 218 | ret 219 | 220 | cursor: 221 | or a 222 | ld de, (coords) 223 | rr e 224 | jp c, .right 225 | 226 | call posit 227 | 228 | ld hl, #0700 229 | add hl, de 230 | ex de, hl 231 | ld a, (de) 232 | xor %11110000 233 | ld (de), a 234 | ret 235 | .right 236 | call posit 237 | ld hl, #0700 238 | add hl, de 239 | ex de, hl 240 | 241 | ld a, (de) 242 | xor %00001111 243 | ld (de), a 244 | ret 245 | 246 | posit: 247 | ld a, d 248 | and 7 249 | rrca : rrca : rrca 250 | or e 251 | ld e, a 252 | ld a, d 253 | and 24 254 | or #40 255 | ld d, a 256 | ret 257 | 258 | ; a - character 259 | drawC: 260 | ld l, a 261 | ld h, 0 262 | add hl, hl ; *2 263 | add hl, hl ; *4 264 | add hl, hl ; *8 265 | ld bc, font 266 | add hl, bc 267 | ld de, (coords) 268 | rr e 269 | jp c, .right 270 | call posit 271 | ld b, 8 272 | .left 273 | ld a, (hl) 274 | and %11110000 275 | ld c, a 276 | ld a, (de) 277 | and %00001111 278 | or c 279 | ld (de), a 280 | inc d 281 | inc hl 282 | djnz .left 283 | ret 284 | .right 285 | call posit 286 | ld b, 8 287 | .draw_right 288 | ld a, (hl) 289 | and %00001111 290 | ld c, a 291 | ld a, (de) 292 | and %11110000 293 | or c 294 | ld (de), a 295 | inc d 296 | inc hl 297 | djnz .draw_right 298 | ret 299 | 300 | 301 | coords dw 0 302 | is_esc db 0 303 | endmodule -------------------------------------------------------------------------------- /bios/disk.asm: -------------------------------------------------------------------------------- 1 | module disk 2 | set_dma: 3 | ld hl, bc 4 | ld (dma_addr), hl 5 | ret 6 | 7 | seldsk: 8 | ld a, (cur_drive) : ld (.drive), a 9 | ld hl, 0 10 | ld a, (volumes_found) 11 | cp c 12 | jr c, .exit 13 | ld a, c 14 | ld (cur_drive), a 15 | ld hl, dpbase 16 | and a : ret z 17 | .loop 18 | ld de, dpbase1 - dpbase 19 | add hl, de 20 | dec a 21 | jr nz ,.loop 22 | ret 23 | .exit 24 | ld a, 0 25 | .drive = $ - 1 26 | ld (TDRIVE), a 27 | ld (cur_drive), a 28 | ld hl, 0 29 | ret 30 | 31 | settrk: 32 | ld hl, bc 33 | ld (disk_trk), hl 34 | ret 35 | 36 | setsec: 37 | ld hl, bc 38 | ld (disk_sec), bc 39 | ret 40 | 41 | home: 42 | ld hl, 0 43 | ld (disk_sec), hl 44 | ld (disk_trk), hl 45 | xor a 46 | ret 47 | 48 | checksect: 49 | call calc_offset 50 | ld hl, (cur_sect) 51 | ld a, h : cp b : jr nz, .do_read 52 | ld a, l : cp c : jr nz, .do_read 53 | ld hl, (cur_sect + 2) 54 | ld a, h : cp d : jr nz, .do_read 55 | ld a, l : cp e : jr nz, .do_read 56 | ret 57 | .do_read 58 | ld (cur_sect), bc 59 | ld (cur_sect + 2), de 60 | ld hl, sec_buffer 61 | call drive.read 62 | ret 63 | 64 | read: 65 | call checksect 66 | 67 | ld a, (disk_sec) 68 | and 3 69 | ld hl, sec_buffer 70 | ld de, 128 71 | jr z, .copy 72 | .offset 73 | add hl, de 74 | dec a 75 | jr nz, .offset 76 | .copy 77 | ld de, (dma_addr) 78 | ld bc, 128 79 | ldir 80 | ei 81 | xor a 82 | ret 83 | 84 | write: 85 | call checksect 86 | 87 | ld a, (disk_sec) 88 | and 3 89 | ld hl, sec_buffer 90 | ld de, 128 91 | jr z, .copy 92 | .offset 93 | add hl, de 94 | dec a 95 | jr nz, .offset 96 | .copy 97 | ex de, hl 98 | ld hl, (dma_addr) 99 | ld bc, 128 100 | ldir 101 | 102 | ld bc, (cur_sect) 103 | ld de, (cur_sect + 2) 104 | ld hl, sec_buffer 105 | call drive.write 106 | xor a 107 | ret 108 | 109 | sectran: 110 | ld hl, bc 111 | ret 112 | 113 | calc_offset: 114 | ld a, (cur_drive) 115 | ld ix, drives_offsets 116 | and a : jr z, .calc 117 | ld bc, 3 118 | .offset 119 | add ix, bc 120 | dec a 121 | jr nz, .offset 122 | .calc 123 | ld hl,(disk_trk) 124 | ld de,(disk_sec) 125 | 126 | srl d 127 | rr e 128 | srl d 129 | rr e 130 | 131 | 132 | ld b, 0 133 | ld c, (ix + 2) 134 | add hl,bc 135 | srl l 136 | jr nc, .sec1 137 | ld d, 1 138 | .sec1 139 | ld a, h 140 | and 1 141 | jr z, .sec2 142 | ld a, l 143 | or #80 144 | ld l, a 145 | .sec2 146 | ld h, 0 147 | ld b, (ix + 1) 148 | ld c, (ix + 0) 149 | add hl, bc 150 | call chs_to_sector 151 | 152 | ld bc, hl 153 | ret 154 | 155 | dma_addr dw 0 156 | disk_sec dw 0 157 | disk_trk dw 0 158 | cur_drive db 0 159 | 160 | IDEDOS_PE_START_CYL_LO equ 0x11 161 | IDEDOS_PE_START_CYL_HI equ 0x12 162 | IDEDOS_PE_START_HEAD equ 0x13 163 | IDEDOS_PE_END_CYL_LO equ 0x14 164 | IDEDOS_PE_END_CYL_HI equ 0x15 165 | IDEDOS_PE_END_HEAD equ 0x16 166 | 167 | init: 168 | di 169 | call load_part_table : jp nz, .error1 170 | ld hl, (part_table_offset) : push hl 171 | ld a,0 : ld (cur_drive),a : ld iy, partition_1 : call find_partition 172 | pop hl : ld (part_table_offset), hl : push hl 173 | ld a,1 : ld (cur_drive),a : ld iy, partition_2 : call find_partition : jr nz, .notFound 174 | pop hl : ld (part_table_offset), hl 175 | ld a,2 : ld (cur_drive),a : ld iy, partition_3 : call find_partition 176 | ret 177 | .notFound: 178 | pop hl 179 | ret 180 | .error1 181 | ld hl, .error1msg 182 | call bios.bios_print 183 | ret 184 | .error1msg db 13, 10, "[ERROR] Didn't found partition table", 13, 10, 0 185 | 186 | find_partition: 187 | ld bc, 0 : ld de, (part_table_offset) : ld hl, sec_buffer 188 | call drive.read 189 | 190 | ld ix, sec_buffer 191 | ld b, 8 ; Partitions per sector 192 | .loop 193 | ld a, (ix + #10) ; Partition type 194 | and a : jp z, .unused 195 | ld a, (ix + 0) : cp (iy + 0) : jp nz, .wrong ; C 196 | ld a, (ix + 1) : cp (iy + 1) : jp nz, .wrong ; P 197 | ld a, (ix + 2) : cp (iy + 2) : jp nz, .wrong ; M 198 | ld a, (ix + 3) : cp (iy + 3) : jp nz, .wrong ; . 199 | ld a, (ix + 4) : cp (iy + 4) : jp nz, .wrong ; A 200 | ld a, (ix + 5) : cp (iy + 5) : jr nz, .wrong ; 201 | ld a, (ix + 6) : cp (iy + 6) : jr nz, .wrong 202 | ld a, (ix + 7) : cp (iy + 7) : jr nz, .wrong 203 | ld a, (ix + 8) : cp (iy + 8) : jr nz, .wrong 204 | ld a, (ix + 9) : cp (iy + 9) : jr nz, .wrong 205 | ld a, (ix +10) : cp (iy +10) : jr nz, .wrong 206 | ld a, (ix +11) : cp (iy +11) : jr nz, .wrong 207 | ld a, (ix +12) : cp (iy +12) : jr nz, .wrong 208 | ld a, (ix +13) : cp (iy +13) : jr nz, .wrong 209 | ld a, (ix +14) : cp (iy +14) : jr nz, .wrong 210 | ld a, (ix +15) : cp (iy +15) : jr nz, .wrong 211 | ;; Found partition 212 | ld l, (ix + IDEDOS_PE_START_CYL_LO) 213 | ld h, (ix + IDEDOS_PE_START_CYL_HI) 214 | ld a, (cur_drive) : ld iy, drives_offsets 215 | and a : jr z, .write_offset 216 | 217 | ld bc, 3 218 | .offset_loop 219 | add iy, bc 220 | dec a 221 | jr nz, .offset_loop 222 | .write_offset 223 | ld (iy + 0), l 224 | ld (iy + 1), h 225 | ld a, (ix + IDEDOS_PE_START_HEAD) 226 | ld (iy + 2), a 227 | ld hl, volumes_found : inc (hl) 228 | xor a 229 | ret 230 | .wrong 231 | ld de, #40 232 | add ix, de 233 | dec b 234 | jp nz, .loop 235 | ld hl, (part_table_offset) : inc hl : ld (part_table_offset), hl 236 | jp find_partition 237 | .unused 238 | or 1 239 | ret 240 | 241 | load_part_table: 242 | ld bc, 0 243 | ld de, 0 244 | ld hl, sec_buffer 245 | call drive.read 246 | call find_part_table 247 | ret z 248 | ld bc, 0 249 | ld de, 128 250 | ld (part_table_offset), de 251 | ld hl, sec_buffer 252 | call drive.read 253 | call find_part_table 254 | ret 255 | 256 | 257 | find_part_table: 258 | ld ix, sec_buffer 259 | ld a, (ix + #00) : cp 'P' : ret nz 260 | ld a, (ix + #01) : cp 'L' : ret nz 261 | ld a, (ix + #02) : cp 'U' : ret nz 262 | ld a, (ix + #10) : cp #01 : ret nz ; Partition table 263 | ret 264 | 265 | 266 | chs_to_sector: 267 | ; HL - cylinder 268 | ; D - head 269 | ; E - sector 270 | ; returns: 271 | ; HL:DE - 32-bit sector number (MSB .. LSB) 272 | ; corrupts: 273 | ; flags, A, BC 274 | ld a, d 275 | and 1 ; assuming two heads 276 | jr z, _chs_to_sector_carry_on 277 | ld a, e 278 | or 0x80 ; with the assumption that sector number is always below 128 279 | ld e, a 280 | _chs_to_sector_carry_on: 281 | push hl 282 | pop bc 283 | ld h, 0 284 | ld l, b 285 | ld d, c 286 | ret 287 | 288 | part_table_offset dw 0 289 | 290 | cur_sect ds 4 291 | sec_buffer ds 512 292 | 293 | volumes_found db #ff 294 | drives_offsets dw 12 295 | partition_1 db 'CPM.A ' 296 | partition_2 db 'CPM.B ' 297 | partition_3 db 'CPM.C ' 298 | dpbase: 299 | defw 0000h, 0000h 300 | defw 0000h, 0000h 301 | defw dirbf, dpblk 302 | defw 0000h, all00 303 | dpbase1: 304 | defw 0000h, 0000h 305 | defw 0000h, 0000h 306 | defw dirbf, dpblk 307 | defw 0000h, all01 308 | dpbase2: 309 | defw 0000h, 0000h 310 | defw 0000h, 0000h 311 | defw dirbf, dpblk 312 | defw 0000h, all02 313 | ; 314 | dpblk: ;disk parameter block for all disks. 315 | defw #0200 ;sectors per track 316 | defm #05 ;block shift factor 317 | defm #1f ;block mask 318 | defm #01 ;null mask 319 | defw #07ff ;disk size-1 320 | defw #01ff ;directory max 321 | defm 240 ;alloc 0 322 | defm 0 ;alloc 1 323 | defw 0 ;check size - don't care about it - SD card isn't removable 324 | defw #0 ;track offset 325 | 326 | 327 | dirbf ds #80 328 | all00 ds 240 329 | all01 ds 240 330 | all02 ds 240 331 | endmodule -------------------------------------------------------------------------------- /bios/display/timex.asm: -------------------------------------------------------------------------------- 1 | module display 2 | 3 | init: 4 | di 5 | ld a, %00110110 : out (#ff), a 6 | ld a, 3 : ld bc, #7ffd : out (c),a 7 | clrscr: 8 | di 9 | ld a, %00000101 : ld bc, #1ffd : out (c),a 10 | 11 | ld hl,#4000 12 | ld de,#4001 13 | ld bc,6143 14 | xor a 15 | ld (hl),a 16 | ldir 17 | ld hl,#4000 18 | ld de,#6000 19 | ld bc, 6144 20 | ldir 21 | ld (coords), bc 22 | call draw_cursor 23 | ld a, %00000001 : ld bc, #1ffd : out (c),a 24 | ret 25 | 26 | 27 | find_screen: 28 | ld a, e 29 | srl a 30 | ld e,a 31 | ld b, #60 32 | jr c, .proc 33 | ld b, #40 34 | .proc 35 | LD A,D 36 | AND 7 37 | RRCA 38 | RRCA 39 | RRCA 40 | OR E 41 | LD E,A 42 | LD A,D 43 | AND 24 44 | OR b 45 | LD D,A 46 | ret 47 | 48 | 49 | handleBS: 50 | ld hl, (coords) 51 | ld a, l 52 | and a 53 | jr z, .prev_line 54 | dec a 55 | ld l, a 56 | .exit 57 | ld (coords), hl 58 | jp draw_cursor 59 | .prev_line 60 | ld a, h 61 | and a 62 | jr z, .exit 63 | dec h 64 | ld l, #80 65 | jr .exit 66 | 67 | write: 68 | di 69 | localStack 70 | ld a,c 71 | ; A - char 72 | call tty_putC 73 | usualStack 74 | ei 75 | ret 76 | putC: 77 | tty_putC: 78 | di 79 | push af 80 | call draw_cursor 81 | ld a, (.is_esc) : and a : jr nz,.handle_esc 82 | pop af 83 | 84 | cp 1 : jp z, home 85 | cp 8 : jp z, handleBS 86 | cp 12 : jp z, cls 87 | cp 13 : jp z, carridge_return 88 | 89 | cp 10 : jp z, cursor_down 90 | cp 20 : jp z, cursor_up 91 | cp 21 : jp z, cursor_down 92 | cp 22 : jp z, handleBS 93 | cp 23 : jp z, cursor_right 94 | cp 24 : jp z, clearline 95 | cp 26 : jp z, cls 96 | cp 27 : jr z, .esc 97 | call _putC 98 | jp draw_cursor 99 | .esc 100 | ld a, 1 : ld (.is_esc),a 101 | jp draw_cursor 102 | .handle_esc 103 | pop bc 104 | ld a,(.is_esc) 105 | cp 2 : jr nc, .load 106 | ld a,'=' : cp b : jr z, .prepare_load 107 | .not_esc 108 | xor a : ld (.is_esc), a 109 | jp draw_cursor 110 | 111 | .load 112 | ld a, (.is_esc) 113 | ld hl, coords 114 | cp 3 : jr z, .loadX 115 | inc hl 116 | .loadX 117 | ld a,b 118 | sub 32 119 | ld (hl), a 120 | 121 | ld a, (.is_esc) : cp 3 : jr z, .not_esc 122 | .prepare_load 123 | ld hl, .is_esc 124 | inc (hl) 125 | jp draw_cursor 126 | 127 | .is_esc dw 0 128 | 129 | clearline: 130 | ld de, (coords) 131 | .loop 132 | di 133 | ld a, %00000101 : ld bc, #1ffd : out (c),a 134 | 135 | 136 | ld a, 80 137 | cp e : jp z, draw_cursor 138 | push de 139 | call find_screen : xor a 140 | dup 8 141 | ld (de),a 142 | inc d 143 | edup 144 | pop de 145 | 146 | inc e 147 | jr .loop 148 | 149 | 150 | cursor_up: 151 | ld a, (coords + 1) 152 | and a 153 | ret z 154 | dec a 155 | ld (coords + 1),a 156 | jp draw_cursor 157 | 158 | home: 159 | ld hl, 0 160 | ld (coords), hl 161 | jp draw_cursor 162 | 163 | cls: 164 | di 165 | ld a, %00000101 : ld bc, #1ffd : out (c),a 166 | 167 | ld hl,#4000 168 | ld de,#4001 169 | ld bc,6143 170 | xor a 171 | ld (hl),a 172 | ldir 173 | ld hl,#4000 174 | ld de,#6000 175 | ld bc, 6144 176 | ldir 177 | ld (coords), bc 178 | ld a, %00000001 : ld bc, #1ffd : out (c),a 179 | jp draw_cursor 180 | 181 | carridge_return: 182 | ld hl, coords 183 | xor a 184 | ld (hl),a 185 | jp draw_cursor 186 | 187 | 188 | cursor_down: 189 | di 190 | ld a, %00000101 : ld bc, #1ffd : out (c),a 191 | ld hl, (coords) 192 | inc h 193 | ld (coords), hl 194 | call scrollCheck 195 | jp draw_cursor 196 | _putC: 197 | push af 198 | call find_screen 199 | 200 | di 201 | ld a, %00000101 : ld bc, #1ffd : out (c),a 202 | pop af 203 | call drawC 204 | ld a, %00000001 : ld bc, #1ffd : out (c),a 205 | cursor_right: 206 | ld hl, (coords) 207 | inc l 208 | ld a, l 209 | cp 80 210 | jr nc, .cr 211 | .ok 212 | ld (coords), hl 213 | ret 214 | .cr 215 | ld hl, (coords) 216 | inc h 217 | ld l, 0, (coords), hl 218 | scrollCheck: 219 | ld a, h 220 | cp 24 221 | jp c, .exit 222 | ld h, 23 223 | ld (coords), hl 224 | call scroll 225 | .exit 226 | ld a, %00000001 : ld bc, #1ffd : out (c),a 227 | ret 228 | 229 | draw_cursor: 230 | di 231 | push af, bc 232 | ld a, %00000101 : ld bc, #1ffd : out (c),a 233 | ld hl, (coords) 234 | ld b, l 235 | call drawC.calc 236 | ld de, hl 237 | ld (drawC.rot_tmp), a 238 | call find_screen 239 | ex hl, de 240 | 241 | ld ix, hl 242 | ld a, h 243 | bit 5, a 244 | jr z, .ok 245 | inc l 246 | .ok 247 | xor #20 : ld h, a 248 | ld iy, hl 249 | ld a, (drawC.rot_tmp) 250 | call drawC.rotate_mask 251 | ld a, h : xor #ff : ld h,a 252 | ld a, l : xor #ff : ld l,a 253 | 254 | ld b, 8 255 | .loop 256 | ld a,(ix) : xor h : ld (ix),a 257 | ld a,(iy) : xor l : ld (iy),a 258 | inc ixh 259 | inc iyh 260 | djnz .loop 261 | 262 | ld a, %00000001 : ld bc, #1ffd : out (c),a 263 | pop bc, af 264 | ret 265 | 266 | 267 | drawC: 268 | ld (.char_tmp), a 269 | ld hl, (coords) 270 | ld b, l 271 | call .calc 272 | ld d, h 273 | ld e, l 274 | ld (.rot_tmp), a 275 | call display.find_screen 276 | push de 277 | call .get_char 278 | 279 | pop hl 280 | .print0 281 | ld ix, hl 282 | ld a, h 283 | bit 5, a 284 | jr z, .ok 285 | inc l 286 | .ok 287 | xor #20 : ld h, a 288 | ld iy, hl 289 | ld a, (.rot_tmp) 290 | call .rotate_mask 291 | ld a, (.rot_tmp) 292 | jp basic_draw 293 | .calc 294 | ld l,0 295 | ld a, b : and a : ret z 296 | ld ix, 0 297 | ld de,6 298 | 1 add ix, de 299 | djnz 1b 300 | ld de, -8 301 | 2 ld a, ixh 302 | and a 303 | jr nz, 3f 304 | ld a, ixl 305 | cp 8 306 | ret c 307 | 3 308 | add ix, de 309 | inc l 310 | jr 2b 311 | ret 312 | 313 | .rotate_mask 314 | ld hl, #03ff 315 | and a : ret z 316 | .rot_loop 317 | ex af, af 318 | ld a,l 319 | rrca 320 | rr h 321 | rr l 322 | ex af, af 323 | dec a 324 | jr nz, .rot_loop 325 | ret 326 | .get_char: 327 | ld a, (.char_tmp) 328 | ld l, a 329 | ld h, 0 330 | add hl, hl 331 | add hl, hl 332 | add hl, hl 333 | ld bc, font 334 | add hl, bc 335 | ex hl, de 336 | ret 337 | .char_tmp db 0 338 | .rot_tmp db 0 339 | ; A - rotation counter 340 | ; DE - font PTR 341 | ; HL - mask 342 | ; IX - left half on screen 343 | ; IY - right half on screen 344 | basic_draw: 345 | ld (.rot_cnt),a 346 | 347 | ld a, l 348 | ld (.mask1), a 349 | ld a, h 350 | ld (.mask2), a 351 | ld b, 8 352 | .printIt 353 | ld a, (de) 354 | ld h, a 355 | ld l, 0 356 | ld a, 0 357 | .rot_cnt = $ - 1 358 | and a : jr z, .skiprot 359 | .rot 360 | ex af, af 361 | ld a,l 362 | rrca 363 | rr h 364 | rr l 365 | ex af, af 366 | dec a 367 | jr nz, .rot 368 | .skiprot 369 | ld a, (iy) 370 | and #0f 371 | .mask1 = $ - 1 372 | or l 373 | ld (iy), a 374 | ld a, (ix) 375 | and #fc 376 | .mask2 = $ -1 377 | or h 378 | ld (ix), a 379 | inc ixh 380 | inc iyh 381 | inc de 382 | djnz .printIt 383 | ret 384 | 385 | scroll 386 | di 387 | ld a, %00000101 : ld bc, #1ffd : out (c),a 388 | ld hl,table_addr_scr 389 | ld b,184 390 | .pass1 391 | push bc 392 | 393 | ld e,(hl) 394 | inc hl 395 | ld d,(hl) 396 | inc hl 397 | 398 | push hl 399 | 400 | ld bc,14 401 | add hl,bc 402 | ld c,(hl) 403 | inc hl 404 | ld b,(hl) 405 | 406 | ld h,b 407 | ld l,c 408 | 409 | ld bc,32 410 | ldir 411 | 412 | pop hl 413 | pop bc 414 | djnz .pass1 415 | 416 | ld b,8 417 | .pass2 418 | push bc 419 | 420 | ld e,(hl) 421 | inc hl 422 | ld d,(hl) 423 | inc hl 424 | 425 | push hl 426 | 427 | ld h,d 428 | ld l,e 429 | inc de 430 | ld (hl),0 431 | ld bc,31 432 | ldir 433 | 434 | pop hl 435 | pop bc 436 | djnz .pass2 437 | ld hl,table_addr_scr 438 | ld b,184 439 | .pass3 440 | push bc 441 | 442 | ld e,(hl) 443 | inc hl 444 | ld a,(hl) 445 | or #20 446 | ld d, a 447 | inc hl 448 | 449 | push hl 450 | 451 | ld bc,14 452 | add hl,bc 453 | ld c,(hl) 454 | inc hl 455 | ld a,(hl) 456 | or #20 457 | ld b, a 458 | 459 | ld h,b 460 | ld l,c 461 | 462 | ld bc,32 463 | ldir 464 | 465 | pop hl 466 | pop bc 467 | djnz .pass3 468 | 469 | ld b,8 470 | .pass4 471 | push bc 472 | 473 | ld e,(hl) 474 | inc hl 475 | ld a,(hl) 476 | or #20 477 | ld d, a 478 | inc hl 479 | 480 | push hl 481 | 482 | ld h,d 483 | ld l,e 484 | inc de 485 | ld (hl),0 486 | ld bc,31 487 | ldir 488 | 489 | pop hl 490 | pop bc 491 | djnz .pass4 492 | ld a, %00000001 : ld bc, #1ffd : out (c),a 493 | ei 494 | ret 495 | 496 | 497 | coords dw 0 498 | 499 | endmodule -------------------------------------------------------------------------------- /software/zmp-overlay/zmoiobyt.z80: -------------------------------------------------------------------------------- 1 | ;----------------------------------------------------------------------------- 2 | ; 3 | ; IOByte overlay for ZMP (Z-Modem Program) 4 | ; 5 | ; Name ZMOIOBYT.Z80 6 | ; 7 | ; Dated Sep 6, 2022 8 | ; 9 | ; Written by - 10 | ; Aleksandr Sharikhin 11 | ;----------------------------------------------------------------------------- 12 | ; 13 | ; 14 | ; System-dependent code overlay for ZMODEM 15 | ; 16 | ; 17 | ; 18 | ; Insert your own code as necessary in this file. Code contained herein 19 | ; has been written in Z80 code for use with M80 or SLR. Assemble as follows: 20 | ; 21 | ; SLR ZMOIOBYT/h 22 | ; MLOAD ZMP.COM=ZMPX.COM,ZMOIOBYT.HEX 23 | ; or 24 | ; M80 =ZMOIOBYT.Z80 25 | ; RELHEX ZMOIOBYT 26 | ; MLOAD ZMP.COM=ZMPX.COM,ZMOIOBYT.HEX 27 | ; 28 | ; 29 | ; (Don't use L80 without changing the source for assembly as a 30 | ; cseg file.) 31 | ; 32 | ;----------------------------------------------------------------------------- 33 | ; 34 | ; 35 | ; Notes on modifying this file: 36 | ; 37 | ; C requires that functions do not change either index register (IX or IY). 38 | ; If your overlay requires either of these to be changed, ensure they are 39 | ; restored to the original values on return. 40 | ; Since collecting parameters from C functions can be tricky, only change 41 | ; the parts marked 'Insert your own code here'. Do NOT modify the jump 42 | ; table at the start. Do NOT modify the entry/exit sections of each 43 | ; function. Do NOT pass 'GO'. Do NOT collect $200. 44 | ; Apart from defining modem functions, this file also defines terminal 45 | ; characteristics. Examples provided are for ADM-3A (with a few of my own 46 | ; additions). Modify to suit your own terminal. An inline print routine 47 | ; is provided for printing strings in the usual way: usage is 48 | ; 49 | ; call print 50 | ; db 'required string',0 51 | ; 52 | ;----------------------------------------------------------------------------- 53 | ; 54 | ; 55 | ; Don't forget to set your clock speed at the clkspd variable. 56 | ; 57 | ; 58 | ; If you find your overlay exceeds the maximum size (currently 0400h), 59 | ; you will have to contact me for another version. If too many people need 60 | ; to do it, we haven't allowed enough room. 61 | ; 62 | ; Ron Murray 15/8/88 63 | ; 64 | ; 65 | ; 66 | ;--------------------------------------------------------------------------- 67 | 68 | false equ 0 69 | true equ not false 70 | 71 | iobyte_console equ 1 72 | iobyte_modem equ 0 73 | 74 | ;------------------------------------------------------------------------------ 75 | 76 | ; User-set variables: 77 | 78 | clkspd equ 56 ; Processor clock speed in MHz 79 | debug equ false ; to allow debugging of overlay with Z8E etc. 80 | 81 | ;Set the following two equates to the drive and user area which will contain 82 | ; ZMP's .OVR files, .CFG file, .FON file and .HLP file. Set both to zero 83 | ; (null) to locate them on the drive from which ZMP was invoked. 84 | 85 | overdrive equ 'A' ; Drive to find overlay files on ('A'-'P') 86 | overuser equ 0 ; User area to find files 87 | 88 | ;------------------------------------------------------------------------------ 89 | 90 | 91 | ; NOT user-set variables 92 | 93 | userdef equ 0145h ; origin of this overlay 94 | ; This address should not change with 95 | ; subsequent revisions. 96 | mspeed equ 03ch ; location of current baud rate. 97 | ovsize equ 0400h ; max size of this overlay 98 | 99 | .z80 ; use z80 code 100 | aseg ; absolute 101 | 102 | if debug 103 | org 100h ; so you can debug it with cebug, zsid, etc 104 | else 105 | org userdef 106 | endif 107 | 108 | 109 | esc equ 1bh 110 | ctrlq equ 11h 111 | cr equ 0dh 112 | lf equ 0ah 113 | bdos equ 5 114 | TTY EQU 0 115 | CRT EQU 1 116 | 117 | codebgn equ $ 118 | 119 | ;Jump table for the overlay: do NOT change this 120 | jump_tab: 121 | jp scrnpr ; screen print 122 | jp mrd ; modem read with timeout 123 | jp mchin ; get a character from modem 124 | jp mchout ; send a character to the modem 125 | jp mordy ; test for tx buffer empty 126 | jp mirdy ; test for character received 127 | jp sndbrk ; send break 128 | jp cursadd ; cursor addressing 129 | jp cls ; clear screen 130 | jp invon ; inverse video on 131 | jp invoff ; inverse video off 132 | jp hide ; hide cursor 133 | jp show ; show cursor 134 | jp savecu ; save cursor position 135 | jp rescu ; restore cursor position 136 | jp mint ; service modem interrupt 137 | jp invec ; initialise interrupt vectors 138 | jp dinvec ; de-initialise interrupt vectors 139 | jp mdmerr ; test uart flags for error 140 | jp dtron ; turn DTR on 141 | jp dtroff ; turn DTR OFF 142 | jp init ; initialise uart 143 | jp wait ; wait seconds 144 | jp mswait ; wait milliseconds 145 | jp userin ; user-defined entry routine 146 | jp userout ; user-defined exit routine 147 | jp getvars ; get system variables 148 | jp setport ; set port (0 or 1) 149 | 150 | ; Spare jumps for compatibility with future versions 151 | jp spare ; spare for later use 152 | jp spare ; spare for later use 153 | jp spare ; spare for later use 154 | jp spare ; spare for later use 155 | jp spare ; spare for later use 156 | jp spare ; spare for later use 157 | 158 | bios_const: 159 | defb 03ch 160 | defw 0 161 | bios_conin: 162 | defb 03ch 163 | defw 0 164 | bios_conout: 165 | defb 03ch 166 | defw 0 167 | 168 | ; 169 | ; Main code starts here 170 | ; 171 | ;Screen print function 172 | scrnpr: 173 | 174 | ; <== End of your own code 175 | spare: 176 | ret 177 | 178 | ; User-defined entry routine: leave empty if not needed 179 | userin: 180 | ld de, 3 181 | ld hl,(01) ;WBOOT address 182 | add hl, de 183 | ld de, bios_const 184 | ld bc, 9 185 | ldir 186 | ret 187 | 188 | ; User-defined exit routine: leave empty if not needed 189 | userout: 190 | ret 191 | 192 | 193 | ;Get a character from the modem: return in HL 194 | mchin: 195 | push bc 196 | ; <== Insert your own code here 197 | ld a,iobyte_modem 198 | ld (3),a 199 | 200 | call bios_conin 201 | ld l,a 202 | 203 | ld a,iobyte_console 204 | ld (3),a 205 | ; <== End of your own code 206 | 207 | ld a,l ; put in HL 208 | ld h,0 209 | or a ; set/clear Z 210 | pop bc 211 | ret 212 | 213 | ;Send a character to the modem 214 | mchout: 215 | ld hl,2 ; get the character 216 | add hl,sp 217 | ld a,(hl) 218 | ; <== Insert your own code here 219 | ld c, a 220 | 221 | ld a,iobyte_modem 222 | ld (3),a 223 | 224 | call bios_conout 225 | 226 | ld a,iobyte_console 227 | ld (3),a 228 | 229 | ; <== End of your own code 230 | ret ; done 231 | 232 | ;Test for output ready: return TRUE (1) in HL if ok 233 | mordy: 234 | ld hl,1 235 | ld a,l 236 | ; <== End of your own code 237 | or a 238 | ret 239 | 240 | ;Test for character at modem: return TRUE (1) in HL if so 241 | mirdy: 242 | ld a,iobyte_modem 243 | ld (3),a 244 | 245 | call bios_const 246 | and 1 247 | ld l,a 248 | 249 | ld a,iobyte_console 250 | ld (3),a 251 | 252 | ld h,0 253 | ld a,l 254 | or a 255 | ret 256 | 257 | ;Send a break to the modem: leave empty if your system can't do it 258 | sndbrk: 259 | ; <== Insert your own code here 260 | 261 | ; <== End of your own code 262 | ret 263 | ; 264 | ;Test UART flags for error: return TRUE (1) in HL if error. 265 | mdmerr: 266 | ; <== Insert your own code here 267 | ld hl, 0 268 | ; <== End of your own code 269 | ld a,l ; set/clear Z 270 | or a 271 | ret 272 | 273 | 274 | 275 | ;Turn DTR ON 276 | dtron: 277 | ; <== Insert your own code here 278 | 279 | ; <== End of your own code 280 | ret 281 | 282 | 283 | 284 | ;Turn DTR OFF 285 | dtroff: 286 | ; <== Insert your own code here 287 | 288 | 289 | ; <== End of your own code 290 | ret 291 | 292 | 293 | 294 | ;Initialise the uart 295 | 296 | init: 297 | 298 | ld hl,2 ; get parameters 299 | add hl,sp 300 | ex de,hl 301 | call getparm ; in HL 302 | ld (brate),hl ; baud rate 303 | call getparm 304 | ld (parity),hl ; parity 305 | call getparm 306 | ld (data),hl ; data bits (BINARY 7 or 8) 307 | call getparm 308 | ld (stop),hl ; stop bits (BINARY 1 or 2) 309 | 310 | 311 | ; <== Insert your own code here 312 | ld a,12 ; using values below 313 | ld (mspeed),a ; don't forget to load mspeed with the 314 | ; current brate value if the new rate is 315 | ; valid. See table of values below. 316 | ; <== End of your own code 317 | ret 318 | ;-------------------------------------------------------------------------- 319 | 320 | stop: dw 1 ; stop bits 321 | parity: dw 'N' ; parity 322 | data: dw 8 ; data bits 323 | brate: dw 12 ; baud rate: 324 | 325 | ;-------------------------------------------------------------------------- 326 | ;Values of brate for each baud rate 327 | ; 328 | ; baud rate brate 329 | ; 330 | ; 110 0 331 | ; 300 1 332 | ; 450 2 333 | ; 600 3 334 | ; 710 4 335 | ; 1200 5 336 | ; 2400 6 337 | ; 4800 7 338 | ; 9600 8 339 | ; 19200 9 340 | ; 38400 10 341 | ; 57600 11 342 | ; 76800 12 343 | 344 | ; 345 | ; Set the port. ZMP supplies either 0 or 1 as a parameter. You're on your 346 | ; own here -- your system is bound to be different from any other! You may 347 | ; implement a software switch on all the modem-dependent routines, or perhaps 348 | ; you can have one or two centralised routines for accessing the UARTs and 349 | ; modify the code from this routine to select one or the other. (Who said 350 | ; there was anything wrong with self-modifying code?). If you have only one 351 | ; UART port, or if you don't want to go through all the hassles, just have 352 | ; this routine returning with no changes made. Note that ZMP calls this 353 | ; routine with both values for the port on initialisation. 354 | ; 355 | setport: 356 | 357 | ; <== End of your own code 358 | ret 359 | 360 | port: ds 1 361 | 362 | 363 | ;**************************************************************************** 364 | ;Video terminal sequences: these are for ADM-3A: Modify as you wish 365 | ;Cursor addressing: 366 | cursadd: 367 | ld hl,2 ; get parameters 368 | add hl,sp 369 | ex de,hl 370 | call getparm ; in HL 371 | ld (row),hl ; row 372 | call getparm 373 | ld (col),hl ; column 374 | ; <== Insert your own code here 375 | ; using values in row and col 376 | call print 377 | db esc,'=',0 ; ADM-3A leadin 378 | ld a,(row) ; row first 379 | add a,' ' ; add offset 380 | call cout 381 | ld a,(col) ; sane for column 382 | add a,' ' 383 | call cout 384 | ; <== end of your own code 385 | ret 386 | 387 | row: ds 2 ; row 388 | col: ds 2 ; column 389 | 390 | 391 | ;Clear screen: 392 | cls: 393 | call print 394 | db 1ah,0 395 | ret 396 | 397 | ;Inverse video on: 398 | invon: 399 | 400 | ret 401 | 402 | ;Inverse video off: 403 | invoff: 404 | 405 | ret 406 | 407 | ;Turn off cursor: 408 | hide: 409 | 410 | ret 411 | 412 | ;Turn on cursor: 413 | show: 414 | 415 | ret 416 | 417 | ;Save cursor position: 418 | savecu: 419 | ret 420 | 421 | ;Restore cursor position: 422 | rescu: 423 | ret 424 | 425 | ;**************************************************************************** 426 | 427 | ;Service modem interrupt: 428 | mint: 429 | ret ; my system doesn't need this 430 | 431 | ;Initialise interrupt vectors: 432 | invec: 433 | ret ; ditto 434 | 435 | ;De-initialise interrupt vectors: 436 | dinvec: 437 | ret ; ditto 438 | 439 | ;****************** End of user-defined code ******************************** 440 | ; Do not change anything below here. 441 | 442 | ;Modem character test for 100 ms 443 | mrd: 444 | push bc ; save bc 445 | ld bc,200 ; set limit 446 | mrd1: 447 | call mirdy ; char at modem? 448 | jr nz,mrd2 ; yes, exit 449 | ld hl,1 ; else wait 1ms 450 | call waithlms 451 | dec bc ; loop till done 452 | ld a,b 453 | or c 454 | jr nz,mrd1 455 | ld hl,0 ; none there, result=0 456 | xor a 457 | mrd2: 458 | pop bc 459 | ret 460 | 461 | ; Inline print routine: destroys A and HL 462 | 463 | print: 464 | ex (sp),hl ; get address of string 465 | ploop: 466 | ld a,(hl) ; get next 467 | inc hl ; bump pointer 468 | or a ; done if zero 469 | jr z,pdone 470 | call cout ; else print 471 | jr ploop ; and loop 472 | pdone: 473 | ex (sp),hl ; restore return address 474 | ret ; and quit 475 | 476 | ; 477 | ;Output a character in A to the console 478 | ; 479 | cout: 480 | push bc ; save regs 481 | push de 482 | push hl 483 | ld e,a ; character to E 484 | ld c,2 485 | call bdos ; print it 486 | pop hl 487 | pop de 488 | pop bc 489 | ret 490 | 491 | ;Wait(seconds) 492 | wait: 493 | ld hl,2 494 | add hl,sp 495 | ex de,hl ; get delay size 496 | call getparm 497 | ; fall thru to.. 498 | ;Wait seconds in HL 499 | waithls: 500 | push bc ; save bc 501 | push de ; de 502 | push ix ; and ix 503 | ld ix,0 ; then point ix to 0 504 | ; so we don't upset memory-mapped i/o 505 | 506 | ;Calculate values for loop constants. Need to have two loops to avoid 507 | ; 16-bit overflow with clock speeds above 9 MHz. 508 | 509 | outerval equ (clkspd / 10) + 1 510 | innerval equ (6667 / outerval) * clkspd 511 | 512 | wait10: 513 | ld b,outerval 514 | 515 | wait11: 516 | ld de,innerval 517 | 518 | wait12: 519 | bit 0,(ix) ; time-wasters 520 | bit 0,(ix) 521 | bit 0,(ix) ; 20 T-states each 522 | bit 0,(ix) 523 | bit 0,(ix) 524 | bit 0,(ix) 525 | dec de 526 | ld a,e 527 | ld a,d 528 | or e 529 | jr nz,wait12 ; 150 T-states per inner loop 530 | djnz wait11 ; decrement outer loop 531 | dec hl ; ok, decrement count in hl 532 | ld a,h 533 | or l 534 | jr nz,wait10 535 | pop ix ; done -- restore ix 536 | pop de ; de 537 | pop bc ; and bc 538 | ret 539 | 540 | ;Wait milliseconds 541 | mswait: 542 | ld hl,2 543 | add hl,sp 544 | ex de,hl ; get delay size 545 | call getparm 546 | ; fall thru to.. 547 | ;Wait milliseconds in HL 548 | waithlms: 549 | push de 550 | w1ms0: 551 | ld de,39 * clkspd 552 | w1ms1: 553 | dec de 554 | ld a,d 555 | or e 556 | jr nz,w1ms1 557 | dec hl 558 | ld a,h 559 | or l 560 | jr nz,w1ms0 561 | pop de 562 | ret 563 | 564 | ;Get next parameter from (de) into hl 565 | getparm: 566 | ex de,hl ; get address into hl 567 | ld e,(hl) ; get lo 568 | inc hl 569 | ld d,(hl) ; then hi 570 | inc hl ; bump for next 571 | ex de,hl ; result in hl, address still in de 572 | ret 573 | 574 | ;Get address of user-defined variables 575 | 576 | getvars: 577 | ld hl,uservars 578 | ret 579 | 580 | uservars: 581 | dw overdrive ; .OVR etc. drive/user 582 | dw overuser 583 | 584 | 585 | if ($ - codebgn) gt ovsize 586 | toobig: jp errval ; Overlay too large! 587 | endif 588 | 589 | end 590 | -------------------------------------------------------------------------------- /zcpr/zcpr.asm: -------------------------------------------------------------------------------- 1 | TITLE 'ZCPR Version 1.0' 2 | ; 3 | ; CP/M Z80 Command Processor Replacement (CPR) Version 1.0 4 | ; CCPZ CREATED AND CUSTOMIZED FOR ARIES-II BY RLC 5 | ; FURTHER MODIFIED BY RGF AS V2.0 6 | ; FURTHER MODIFIED BY RLC AS V2.1 7 | ; FURTHER MODIFIED BY KBP AS V2.2 8 | ; FURTHER MODIFIED BY RLC AS V2.4 (V2.3 skipped) 9 | ; FURTHER MODIFIED BY RLC AS V2.5 10 | ; FURTHER MODIFIED BY RLC AS V2.6 11 | ; FURTHUR MODIFIED BY SBB AS V2.7 12 | ; FURTHER MODIFIED BY RLC AS V2.8 13 | ; FURTHER MODIFIED BY RLC AS V2.9 14 | ; FURTHER MODIFIED BY RLC AS V3.0 15 | ; FURTHER MODIFIED BY RLC AS V3.1 16 | ; FURTHER MODIFIED BY RLC AS V4.0 17 | ; ZCPR VERSION 1.0 CREATED FROM CCPZ VERSION 4.0 BY RLC IN 18 | ; A COORDINATED EFFORT WITH CCP-GROUP 19 | ; 20 | ; ZCPR is a group effort by CCP-GROUP, whose active membership involved 21 | ; in this project consists of the following: 22 | ; RLC - Richard Conn 23 | ; RGF - Ron Fowler 24 | ; KBP - Keith Peterson 25 | ; FJW - Frank Wancho 26 | ; The following individual also provided a contribution: 27 | ; SBB - Steve Bogolub 28 | ; 29 | ; 30 | ;******** Structure Notes ******** 31 | ; 32 | ; This CPR is divided into a number of major sections. The following 33 | ; is an outline of these sections and the names of the major routines 34 | ; located therein. 35 | ; 36 | ; Section Function/Routines 37 | ; ------- ----------------- 38 | ; 39 | ; -- Opening Comments, Equates, and Macro Definitions 40 | ; 41 | ; 0 JMP Table into CPR 42 | ; 43 | ; 1 Buffers 44 | ; 45 | ; 2 CPR Starting Modules 46 | ; CPR1 CPR RESTRT RSTCPR RCPRNL 47 | ; PRNNF 48 | ; 49 | ; 3 Utilities 50 | ; CRLF CONOUT CONIN LCOUT LSTOUT 51 | ; READF READ BDOSB PRINTC PRINT 52 | ; GETDRV DEFDMA DMASET RESET BDOSJP 53 | ; LOGIN OPENF OPEN GRBDOS CLOSE 54 | ; SEARF SEAR1 SEARN SUBKIL DELETE 55 | ; RESETUSR GETUSR SETUSR 56 | ; 57 | ; 4 CPR Utilities 58 | ; SETUD SETU0D UCASE REDBUF CNVBUF 59 | ; BREAK USRNUM ERROR SDELM ADVAN 60 | ; SBLANK ADDAH NUMBER NUMERR HEXNUM 61 | ; DIRPTR SLOGIN DLOGIN COMLOG SCANER 62 | ; CMDSER 63 | ; 64 | ; 5 CPR-Resident Commands and Functions 65 | ; 5A DIR DIRPR FILLQ 66 | ; 5B ERA 67 | ; 5C LIST 68 | ; 5D TYPE PAGER 69 | ; 5E SAVE 70 | ; 5F REN 71 | ; 5G USER 72 | ; 5H DFU 73 | ; 5I JUMP 74 | ; 5J GO 75 | ; 5K COM CALLPROG ERRLOG ERRJMP 76 | ; 5L GET MEMLOAD PRNLE 77 | ; 78 | ; 79 | FALSE EQU 0 80 | TRUE EQU NOT FALSE 81 | ; 82 | ; CUSTOMIZATION EQUATES 83 | ; 84 | ; The following equates may be used to customize this CPR for the user's 85 | ; system and integration technique. The following constants are provided: 86 | ; 87 | ; REL - TRUE if integration is to be done via MOVCPM 88 | ; - FALSE if integration is to be done via DDT and SYSGEN 89 | ; 90 | ; BASE - Base Address of user's CP/M system (normally 0 for DR version) 91 | ; This equate allows easy modification by non-standard CP/M (eg,H89) 92 | ; 93 | ; CPRLOC - Base Page Address of CPR; this value can be obtained by running 94 | ; the BDOSLOC program on your system, or by setting the 95 | ; MSIZE and BIOSEX equates to the system memory size in 96 | ; K-bytes and the "extra" memory required by your BIOS 97 | ; in K-bytes. BIOSEX is zero if your BIOS is normal size, 98 | ; and can be negative if your BIOS is in PROM or in 99 | ; non-contiguous memory. 100 | ; 101 | ; RAS - Remote-Access System; setting this equate to TRUE disables 102 | ; certain CPR commands that are considered harmful in a Remote- 103 | ; Access environment; use under Remote-Access Systems (RBBS) for 104 | ; security purposes 105 | ; 106 | REL EQU FALSE ;SET TO TRUE FOR MOVCPM INTEGRATION 107 | ; 108 | BASE EQU 0 ;BASE OF CP/M SYSTEM (SET FOR STANDARD CP/M) 109 | ; 110 | IF REL 111 | CPRLOC EQU 0 ;MOVCPM IMAGE 112 | ELSE 113 | ; 114 | ; If REL is FALSE, the value of CPRLOC may be set in one 115 | ; of two ways. The first way is to set MSIZE and BIOSEX 116 | ; as described above using the following three lines: 117 | ; 118 | ;MSIZE EQU 56 ;SIZE OF MEM IN K-BYTES 119 | ;BIOSEX EQU 0 ;EXTRA # K-BYTES IN BIOS 120 | ;CPRLOC EQU 3400H+(MSIZE-20-BIOSEX)*1024 ;CPR ORIGIN 121 | ; 122 | ; The second way is to obtain the origin of your current 123 | ; CPR using BDSLOC or its equivalent, then merely set CPRLOC 124 | ; to that value as as in the following line: 125 | ; 126 | ;CPRLOC EQU 0BD00H ;FILL IN WITH BDOSLOC SUPPLIED VALUE 127 | CPRLOC EQU 0D5b3H ;WW - CUSTOMIZED FOR ROMWBW 128 | ; 129 | ; Note that you should only use one method or the other. 130 | ; Do NOT define CPRLOC twice! 131 | ; 132 | ; The following gives the required offset to load the CPR into the 133 | ; CP/M SYSGEN Image through DDT (the Roffset command); Note that this 134 | ; value conforms with the standard value presented in the CP/M reference 135 | ; manuals, but it may not necessarily conform with the location of the 136 | ; CPR in YOUR CP/M system; several systems (Morrow Designs, P&T, Heath 137 | ; Org-0 to name a few) have the CPR located at a non-standard address in 138 | ; the SYSGEN Image 139 | ; 140 | ;CPRR EQU 0980H-CPRLOC ;DDT LOAD OFFSET 141 | CPRR EQU 1100H-CPRLOC ;DDT LOAD OFFSET FOR MORROW DESIGNS 142 | ENDIF 143 | ; 144 | RAS EQU FALSE ;SET TO TRUE IF CPR IS FOR A REMOTE-ACCESS SYSTEM 145 | ; 146 | ; The following is presented as an option, but is not generally user-customiz- 147 | ; able. A basic design choice had to be made in the design of ZCPR concerning 148 | ; the execution of SUBMIT files. The original CCP had a problem in this sense 149 | ; in that it ALWAYS looked for the SUBMIT file from drive A: and the SUBMIT 150 | ; program itself (SUBMIT.COM) would place the $$$.SUB file on the currently 151 | ; logged-in drive, so when the user was logged into B: and he issued a SUBMIT 152 | ; command, the $$$.SUB was placed on B: and did not execute because the CCP 153 | ; looked for it on A: and never found it. 154 | ; After much debate it was decided to have ZCPR perform the same type of 155 | ; function as CCP (look for the $$$.SUB file on A:), but the problem with 156 | ; SUBMIT.COM still exists. Hence, RGF designed SuperSUB and RLC took his 157 | ; SuperSUB and designed SUB from it; both programs are set up to allow the 158 | ; selection at assembly time of creating the $$$.SUB on the logged-in drive 159 | ; or on drive A:. 160 | ; A final definition of the Indirect Command File ($$$.SUB or SUBMIT 161 | ; File) is presented as follows: 162 | ; "An Indirect Command File is one which contains 163 | ; a series of commands exactly as they would be 164 | ; entered from a CP/M Console. The SUBMIT Command 165 | ; (or SUB Command) reads this files and transforms 166 | ; it for processing by the ZCPR (the $$$.SUB File). 167 | ; ZCPR will then execute the commands indicated 168 | ; EXACTLY as if they were typed at the Console." 169 | ; Hence, to permit this to happen, the $$$.SUB file must always 170 | ; be present on a specific drive, and A: is the choice for said drive. 171 | ; With this facility engaged as such, Indirect Command Files like: 172 | ; DIR 173 | ; A: 174 | ; DIR 175 | ; can be executed, even though the currently logged-in drive is changed 176 | ; during execution. If the $$$.SUB file was present on the currently 177 | ; logged-in drive, the above series of commands would not work since the 178 | ; ZCPR would be looking for $$$.SUB on the logged-in drive, and switching 179 | ; logged-in drives without moving the $$$.SUB file as well would cause 180 | ; processing to abort. 181 | ; 182 | SUBA equ FALSE ; Set to TRUE to have $$$.SUB always on A: 183 | ; Set to FALSE to have $$$.SUB on the logged-in drive 184 | ; 185 | ; The following flag enables extended processing for user-program supplied 186 | ; command lines. This is for Command Level 3 of ZCPR. Under the CCPZ Version 187 | ; 4.0 philosophy, three command levels exist: 188 | ; (1) that command issued by the user from his console at the '>' prompt 189 | ; (2) that command issued by a $$$.SUB file at the '$' prompt 190 | ; (3) that command issued by a user program by placing the command into 191 | ; CIBUFF and setting the character count in CBUFF 192 | ; Setting CLEVEL3 to TRUE enables extended processing of the third level of 193 | ; ZCPR command. All the user program need do is to store the command line and 194 | ; set the character count; ZCPR will initialize the pointers properly, store 195 | ; the ending zero properly, and capitalize the command line for processing. 196 | ; Once the command line is properly stored, the user executes the command line 197 | ; by reentering the ZCPR through CPRLOC [NOTE: The C register MUST contain 198 | ; a valid User/Disk Flag (see location 4) at this time.] 199 | ; 200 | CLEVEL3 equ TRUE ;ENABLE COMMAND LEVEL 3 PROCESSING 201 | ; 202 | ; 203 | ;*** TERMINAL AND 'TYPE' CUSTOMIZATION EQUATES 204 | ; 205 | NLINES EQU 24 ;NUMBER OF LINES ON CRT SCREEN 206 | WIDE EQU TRUE ;TRUE IF WIDE DIR DISPLAY 207 | FENCE EQU '|' ;SEP CHAR BETWEEN DIR FILES 208 | ; 209 | PGDFLT EQU TRUE ;SET TO FALSE TO DISABLE PAGING BY DEFAULT 210 | PGDFLG EQU 'P' ;FOR TYPE COMMAND: PAGE OR NOT (DEP ON PGDFLT) 211 | ; THIS FLAG REVERSES THE DEFAULT EFFECT 212 | ; 213 | MAXUSR EQU 15 ;MAXIMUM USER NUMBER ACCESSABLE 214 | ; 215 | SYSFLG EQU 'A' ;FOR DIR COMMAND: LIST $SYS AND $DIR 216 | ; 217 | SOFLG EQU 'S' ;FOR DIR COMMAND: LIST $SYS FILES ONLY 218 | ; 219 | SUPRES EQU FALSE ;SUPRESSES USER # REPORT FOR USER 0 220 | ; 221 | DEFUSR EQU 0 ;DEFAULT USER NUMBER FOR COM FILES 222 | ; 223 | SPRMPT EQU '$' ;CPR PROMPT INDICATING SUBMIT COMMAND 224 | CPRMPT EQU '>' ;CPR PROMPT INDICATING USER COMMAND 225 | ; 226 | NUMBASE EQU 'H' ;CHARACTER USED TO SWITCH FROM DEFAULT 227 | ; NUMBER BASE 228 | ; 229 | SECTFLG EQU 'S' ;OPTION CHAR FOR SAVE COMMAND TO SAVE SECTORS 230 | ; 231 | ; END OF CUSTOMIZATION SECTION 232 | ; 233 | CR EQU 0DH 234 | LF EQU 0AH 235 | TAB EQU 09H 236 | ; 237 | WBOOT EQU BASE+0000H ;CP/M WARM BOOT ADDRESS 238 | UDFLAG EQU BASE+0004H ;USER NUM IN HIGH NYBBLE, DISK IN LOW 239 | BDOS EQU BASE+0005H ;BDOS FUNCTION CALL ENTRY PT 240 | TFCB EQU BASE+005CH ;DEFAULT FCB BUFFER 241 | TBUFF EQU BASE+0080H ;DEFAULT DISK I/O BUFFER 242 | TPA EQU BASE+0100H ;BASE OF TPA 243 | ; 244 | ; 245 | ; MACROS TO PROVIDE Z80 EXTENSIONS 246 | ; MACROS INCLUDE: 247 | ; 248 | $-MACRO ;FIRST TURN OFF THE EXPANSIONS 249 | ; 250 | ; JR - JUMP RELATIVE 251 | ; JRC - JUMP RELATIVE IF CARRY 252 | ; JRNC - JUMP RELATIVE IF NO CARRY 253 | ; JRZ - JUMP RELATIVE IF ZERO 254 | ; JRNZ - JUMP RELATIVE IF NO ZERO 255 | ; DJNZ - DECREMENT B AND JUMP RELATIVE IF NO ZERO 256 | ; LDIR - MOV @HL TO @DE FOR COUNT IN BC 257 | ; LXXD - LOAD DOUBLE REG DIRECT 258 | ; SXXD - STORE DOUBLE REG DIRECT 259 | ; 260 | ; 261 | ; 262 | ; @GENDD MACRO USED FOR CHECKING AND GENERATING 263 | ; 8-BIT JUMP RELATIVE DISPLACEMENTS 264 | ; 265 | @GENDD MACRO ?DD ;;USED FOR CHECKING RANGE OF 8-BIT DISPLACEMENTS 266 | IF (?DD GT 7FH) AND (?DD LT 0FF80H) 267 | DB 100H ;Displacement Range Error on Jump Relative 268 | ELSE 269 | DB ?DD 270 | ENDIF 271 | ENDM 272 | ; 273 | ; 274 | ; Z80 MACRO EXTENSIONS 275 | ; 276 | JR MACRO ?N ;;JUMP RELATIVE 277 | DB 18H 278 | @GENDD ?N-$-1 279 | ENDM 280 | ; 281 | JRC MACRO ?N ;;JUMP RELATIVE ON CARRY 282 | DB 38H 283 | @GENDD ?N-$-1 284 | ENDM 285 | ; 286 | JRNC MACRO ?N ;;JUMP RELATIVE ON NO CARRY 287 | DB 30H 288 | @GENDD ?N-$-1 289 | ENDM 290 | ; 291 | JRZ MACRO ?N ;;JUMP RELATIVE ON ZERO 292 | DB 28H 293 | @GENDD ?N-$-1 294 | ENDM 295 | ; 296 | JRNZ MACRO ?N ;;JUMP RELATIVE ON NO ZERO 297 | DB 20H 298 | @GENDD ?N-$-1 299 | ENDM 300 | ; 301 | DJNZ MACRO ?N ;;DECREMENT B AND JUMP RELATIVE ON NO ZERO 302 | DB 10H 303 | @GENDD ?N-$-1 304 | ENDM 305 | ; 306 | LDIR MACRO ;;LDIR 307 | DB 0EDH,0B0H 308 | ENDM 309 | ; 310 | LDED MACRO ?N ;;LOAD DE DIRECT 311 | DB 0EDH,05BH 312 | DW ?N 313 | ENDM 314 | ; 315 | LBCD MACRO ?N ;;LOAD BC DIRECT 316 | DB 0EDH,4BH 317 | DW ?N 318 | ENDM 319 | ; 320 | SDED MACRO ?N ;;STORE DE DIRECT 321 | DB 0EDH,53H 322 | DW ?N 323 | ENDM 324 | ; 325 | SBCD MACRO ?N ;;STORE BC DIRECT 326 | DB 0EDH,43H 327 | DW ?N 328 | ENDM 329 | ; 330 | ; END OF Z80 MACRO EXTENSIONS 331 | ; 332 | ; 333 | ;**** Section 0 **** 334 | ; 335 | ORG CPRLOC 336 | ; 337 | ; ENTRY POINTS INTO ZCPR 338 | ; If the ZCPR is entered at location CPRLOC (at the JMP to CPR), then 339 | ; the default command in CIBUFF will be processed. If the ZCPR is entered 340 | ; at location CPRLOC+3 (at the JMP to CPR1), then the default command in 341 | ; CIBUFF will NOT be processed. 342 | ; NOTE: Entry into ZCPR in this way is permitted under ZCPR Version 4.0, 343 | ; but in order for this to work, CIBUFF and CBUFF MUST be initialized properly 344 | ; AND the C register MUST contain a valid User/Disk Flag (see Location 4: the 345 | ; most significant nybble contains the User Number and the least significant 346 | ; nybble contains the Disk Number). 347 | ; Some user programs (such as SYNONYM3) attempt to use the default 348 | ; command facility. Under the original CPR, it was necessary to initialize 349 | ; the pointer after the reserved space for the command buffer to point to 350 | ; the first byte of the command buffer. Under Version 4.x of ZCPR, this is 351 | ; no longer the case. The CIBPTR (Command Input Buffer PoinTeR) is located 352 | ; to be compatable with such programs (provided they determine the buffer 353 | ; length from the byte at MBUFF [CPRLOC + 6]), but under Version 4.x of ZCPR 354 | ; this is no longer necessary. ZCPR Version 4.x automatically initializes 355 | ; this buffer pointer in all cases. 356 | ; 357 | ENTRY: 358 | JMP CPR ; Process potential default command 359 | JMP CPR1 ; Do NOT process potential default command 360 | ; 361 | ;**** Section 1 **** 362 | ; BUFFERS ET AL 363 | ; 364 | ; INPUT COMMAND LINE AND DEFAULT COMMAND 365 | ; The command line to be executed is stored here. This command line 366 | ; is generated in one of three ways: 367 | ; (1) by the user entering it through the BDOS READLN function at 368 | ; the du> prompt [user input from keyboard] 369 | ; (2) by the SUBMIT File Facility placing it there from a $$$.SUB 370 | ; file 371 | ; (3) by an external program or user placing the required command 372 | ; into this buffer 373 | ; In all cases, the command line is placed into the buffer starting at 374 | ; CIBUFF. This command line is terminated by the last character (NOT Carriage 375 | ; Return), and a character count of all characters in the command line 376 | ; up to and including the last character is placed into location CBUFF 377 | ; (immediately before the command line at CIBUFF). The placed command line 378 | ; is then parsed, interpreted, and the indicated command is executed. 379 | ; If CLEVEL3 is permitted, a terminating zero is placed after the command 380 | ; (otherwise the user program has to place this zero) and the CIBPTR is 381 | ; properly initialized (otherwise the user program has to init this ptr). 382 | ; If the command is placed by a user program, entering at CPRLOC is enough 383 | ; to have the command processed. Again, under CCPZ Version 4.0, it is not 384 | ; necessary to store the pointer to CIBUFF in CIBPTR; ZCPR will do this for 385 | ; the calling program if CLEVEL3 is made TRUE. 386 | ; WARNING: The command line must NOT exceed BUFLEN characters in length. 387 | ; For user programs which load this command, the value of BUFLEN can be 388 | ; obtained by examining the byte at MBUFF (CPRLOC + 6). 389 | ; 390 | BUFLEN EQU 80 ;MAXIMUM BUFFER LENGTH 391 | MBUFF: 392 | DB BUFLEN ;MAXIMUM BUFFER LENGTH 393 | CBUFF: 394 | DB 0 ;NUMBER OF VALID CHARS IN COMMAND LINE 395 | CIBUFF: 396 | DB ' ' ;DEFAULT (COLD BOOT) COMMAND 397 | CIBUF: 398 | DB 0 ;COMMAND STRING TERMINATOR 399 | DS BUFLEN-($-CIBUFF)+1 ;TOTAL IS 'BUFLEN' BYTES 400 | ; 401 | CIBPTR: 402 | DW CIBUFF ;POINTER TO COMMAND INPUT BUFFER 403 | CIPTR: 404 | DW CIBUF ;CURRENT POINTER 405 | ; 406 | DS 26 ;STACK AREA 407 | STACK EQU $ ;TOP OF STACK 408 | ; 409 | ; FILE TYPE FOR COMMAND 410 | ; 411 | COMMSG: 412 | DB 'COM' 413 | ; 414 | ; SUBMIT FILE CONTROL BLOCK 415 | ; 416 | SUBFCB: 417 | IF SUBA ;IF $$$.SUB ON A: 418 | DB 1 ;DISK NAME SET TO DEFAULT TO DRIVE A: 419 | ENDIF 420 | ; 421 | IF NOT SUBA ;IF $$$.SUB ON CURRENT DRIVE 422 | DB 0 ;DISK NAME SET TO DEFAULT TO CURRENT DRIVE 423 | ENDIF 424 | ; 425 | DB '$$$' ;FILE NAME 426 | DB ' ' 427 | DB 'SUB' ;FILE TYPE 428 | DB 0 ;EXTENT NUMBER 429 | DB 0 ;S1 430 | SUBFS2: 431 | DS 1 ;S2 432 | SUBFRC: 433 | DS 1 ;RECORD COUNT 434 | DS 16 ;DISK GROUP MAP 435 | SUBFCR: 436 | DS 1 ;CURRENT RECORD NUMBER 437 | ; 438 | ; COMMAND FILE CONTROL BLOCK 439 | ; 440 | FCBDN: 441 | DS 1 ;DISK NAME 442 | FCBFN: 443 | DS 8 ;FILE NAME 444 | FCBFT: 445 | DS 3 ;FILE TYPE 446 | DS 1 ;EXTENT NUMBER 447 | DS 2 ;S1 AND S2 448 | DS 1 ;RECORD COUNT 449 | FCBDM: 450 | DS 16 ;DISK GROUP MAP 451 | FCBCR: 452 | DS 1 ;CURRENT RECORD NUMBER 453 | ; 454 | ; OTHER BUFFERS 455 | ; 456 | PAGCNT: 457 | DB NLINES-2 ;LINES LEFT ON PAGE 458 | CHRCNT: 459 | DB 0 ;CHAR COUNT FOR TYPE 460 | QMCNT: 461 | DB 0 ;QUESTION MARK COUNT FOR FCB TOKEN SCANNER 462 | ; 463 | ; CPR BUILT-IN COMMAND TABLE 464 | ; 465 | NCHARS EQU 4 ;NUMBER OF CHARS/COMMAND 466 | ; 467 | ; CPR COMMAND NAME TABLE 468 | ; EACH TABLE ENTRY IS COMPOSED OF THE 4-BYTE COMMAND AND 2-BYTE ADDRESS 469 | ; 470 | CMDTBL: 471 | DB 'DIR ' 472 | DW DIR 473 | DB 'LIST' 474 | DW LIST 475 | DB 'TYPE' 476 | DW TYPE 477 | DB 'USER' 478 | DW USER 479 | DB 'DFU ' 480 | DW DFU 481 | ; 482 | IF NOT RAS ;FOR NON-RAS 483 | DB 'GO ' 484 | DW GO 485 | DB 'ERA ' 486 | DW ERA 487 | DB 'SAVE' 488 | DW SAVE 489 | DB 'REN ' 490 | DW REN 491 | DB 'GET ' 492 | DW GET 493 | DB 'JUMP' 494 | DW JUMP 495 | ENDIF 496 | ; 497 | NCMNDS EQU ($-CMDTBL)/(NCHARS+2) 498 | ; 499 | ; 500 | ;**** Section 2 **** 501 | ; CPR STARTING POINTS 502 | ; 503 | ; START CPR AND DON'T PROCESS DEFAULT COMMAND STORED 504 | ; 505 | CPR1: 506 | XRA A ;SET NO DEFAULT COMMAND 507 | STA CBUFF 508 | ; 509 | ; START CPR AND POSSIBLY PROCESS DEFAULT COMMAND 510 | ; 511 | ; NOTE ON MODIFICATION BY RGF: BDOS RETURNS 0FFH IN 512 | ; ACCUMULATOR WHENEVER IT LOGS IN A DIRECTORY, IF ANY 513 | ; FILE NAME CONTAINS A '$' IN IT. THIS IS NOW USED AS 514 | ; A CLUE TO DETERMINE WHETHER OR NOT TO DO A SEARCH 515 | ; FOR SUBMIT FILE, IN ORDER TO ELIMINATE WASTEFUL SEARCHES. 516 | ; 517 | CPR: 518 | LXI SP,STACK ;RESET STACK 519 | PUSH B 520 | MOV A,C ;C=USER/DISK NUMBER (SEE LOC 4) 521 | RAR ;EXTRACT USER NUMBER 522 | RAR 523 | RAR 524 | RAR 525 | ANI 0FH 526 | MOV E,A ;SET USER NUMBER 527 | CALL SETUSR 528 | CALL RESET ;RESET DISK SYSTEM 529 | STA RNGSUB ;SAVE SUBMIT CLUE FROM DRIVE A: 530 | POP B 531 | MOV A,C ;C=USER/DISK NUMBER (SEE LOC 4) 532 | ANI 0FH ;EXTRACT DEFAULT DISK DRIVE 533 | STA TDRIVE ;SET IT 534 | JRZ NOLOG ;SKIP IF 0...ALREADY LOGGED 535 | CALL LOGIN ;LOG IN DEFAULT DISK 536 | ; 537 | IF NOT SUBA ;IF $$$.SUB IS ON CURRENT DRIVE 538 | STA RNGSUB ;BDOS '$' CLUE 539 | ENDIF 540 | ; 541 | NOLOG: 542 | LXI D,SUBFCB ;CHECK FOR $$$.SUB ON CURRENT DISK 543 | RNGSUB EQU $+1 ;POINTER FOR IN-THE-CODE MODIFICATION 544 | MVI A,0 ;2ND BYTE (IMMEDIATE ARG) IS THE RNGSUB FLAG 545 | ORA A ;SET FLAGS ON CLUE 546 | CMA ;PREPARE FOR COMING 'CMA' 547 | CNZ SEAR1 548 | CMA ;0FFH IS RETURNED IF NO $$$.SUB, SO COMPLEMENT 549 | STA RNGSUB ;SET FLAG (0=NO $$$.SUB) 550 | LDA CBUFF ;EXECUTE DEFAULT COMMAND? 551 | ORA A ;0=NO 552 | JRNZ RS1 553 | ; 554 | ; PROMPT USER AND INPUT COMMAND LINE FROM HIM 555 | ; 556 | RESTRT: 557 | LXI SP,STACK ;RESET STACK 558 | ; 559 | ; PRINT PROMPT (DU>) 560 | ; 561 | CALL CRLF ;PRINT PROMPT 562 | CALL GETDRV ;CURRENT DRIVE IS PART OF PROMPT 563 | ADI 'A' ;CONVERT TO ASCII A-P 564 | CALL CONOUT 565 | CALL GETUSR ;GET USER NUMBER 566 | ; 567 | IF SUPRES ;IF SUPPRESSING USR # REPORT FOR USR 0 568 | ORA A 569 | JRZ RS000 570 | ENDIF 571 | ; 572 | CPI 10 ;USER < 10? 573 | JRC RS00 574 | SUI 10 ;SUBTRACT 10 FROM IT 575 | PUSH PSW ;SAVE IT 576 | MVI A,'1' ;OUTPUT 10'S DIGIT 577 | CALL CONOUT 578 | POP PSW 579 | RS00: 580 | ADI '0' ;OUTPUT 1'S DIGIT (CONVERT TO ASCII) 581 | CALL CONOUT 582 | ; 583 | ; READ INPUT LINE FROM USER OR $$$.SUB 584 | ; 585 | RS000: 586 | CALL REDBUF ;INPUT COMMAND LINE FROM USER (OR $$$.SUB) 587 | ; 588 | ; PROCESS INPUT LINE 589 | ; 590 | RS1: 591 | ; 592 | IF CLEVEL3 ;IF THIRD COMMAND LEVEL IS PERMITTED 593 | CALL CNVBUF ;CAPITALIZE COMMAND LINE, PLACE ENDING 0, 594 | ; AND SET CIBPTR VALUE 595 | ENDIF 596 | ; 597 | CALL DEFDMA ;SET TBUFF TO DMA ADDRESS 598 | CALL GETDRV ;GET DEFAULT DRIVE NUMBER 599 | STA TDRIVE ;SET IT 600 | CALL SCANER ;PARSE COMMAND NAME FROM COMMAND LINE 601 | CNZ ERROR ;ERROR IF COMMAND NAME CONTAINS A '?' 602 | LXI D,RSTCPR ;PUT RETURN ADDRESS OF COMMAND 603 | PUSH D ;ON THE STACK 604 | LDA TEMPDR ;IS COMMAND OF FORM 'D:COMMAND'? 605 | ORA A ;NZ=YES 606 | JNZ COM ; IMMEDIATELY 607 | CALL CMDSER ;SCAN FOR CPR-RESIDENT COMMAND 608 | JNZ COM ;NOT CPR-RESIDENT 609 | MOV A,M ;FOUND IT: GET LOW-ORDER PART 610 | INX H ;GET HIGH-ORDER PART 611 | MOV H,M ;STORE HIGH 612 | MOV L,A ;STORE LOW 613 | PCHL ;EXECUTE CPR ROUTINE 614 | ; 615 | ; ENTRY POINT FOR RESTARTING CPR AND LOGGING IN DEFAULT DRIVE 616 | ; 617 | RSTCPR: 618 | CALL DLOGIN ;LOG IN DEFAULT DRIVE 619 | ; 620 | ; ENTRY POINT FOR RESTARTING CPR WITHOUT LOGGING IN DEFAULT DRIVE 621 | ; 622 | RCPRNL: 623 | CALL SCANER ;EXTRACT NEXT TOKEN FROM COMMAND LINE 624 | LDA FCBFN ;GET FIRST CHAR OF TOKEN 625 | SUI ' ' ;ANY CHAR? 626 | LXI H,TEMPDR 627 | ORA M 628 | JNZ ERROR 629 | JR RESTRT 630 | ; 631 | ; No File Error Message 632 | ; 633 | PRNNF: 634 | CALL PRINTC ;NO FILE MESSAGE 635 | DB 'No Fil','e'+80H 636 | RET 637 | ; 638 | ;**** Section 3 **** 639 | ; I/O UTILITIES 640 | ; 641 | ; OUTPUT CHAR IN REG A TO CONSOLE AND DON'T CHANGE BC 642 | ; 643 | ; 644 | ; OUTPUT 645 | ; 646 | CRLF: 647 | MVI A,CR 648 | CALL CONOUT 649 | MVI A,LF ;FALL THRU TO CONOUT 650 | ; 651 | CONOUT: 652 | PUSH B 653 | MVI C,02H 654 | OUTPUT: 655 | MOV E,A 656 | PUSH H 657 | CALL BDOS 658 | POP H 659 | POP B 660 | RET 661 | ; 662 | CONIN: 663 | MVI C,01H ;GET CHAR FROM CON: WITH ECHO 664 | CALL BDOSB 665 | JMP UCASE ;CAPITALIZE 666 | ; 667 | LCOUT: 668 | PUSH PSW ;OUTPUT CHAR TO CON: OR LST: DEP ON PRFLG 669 | PRFLG EQU $+1 ;POINTER FOR IN-THE-CODE MODIFICATION 670 | MVI A,0 ;2ND BYTE (IMMEDIATE ARG) IS THE PRINT FLAG 671 | ORA A ;0=TYPE 672 | JRZ LC1 673 | POP PSW ;GET CHAR 674 | ; 675 | ; OUTPUT CHAR IN REG A TO LIST DEVICE 676 | ; 677 | LSTOUT: 678 | PUSH B 679 | MVI C,05H 680 | JR OUTPUT 681 | LC1: 682 | POP PSW ;GET CHAR 683 | PUSH PSW 684 | CALL CONOUT ;OUTPUT TO CON: 685 | POP PSW 686 | CPI LF ;CHECK FOR PAGING 687 | JZ PAGER 688 | RET 689 | ; 690 | READF: 691 | LXI D,FCBDN ;FALL THRU TO READ 692 | READ: 693 | MVI C,14H ;FALL THRU TO BDOSB 694 | ; 695 | ; CALL BDOS AND SAVE BC 696 | ; 697 | BDOSB: 698 | PUSH B 699 | CALL BDOS 700 | POP B 701 | ORA A 702 | RET 703 | ; 704 | ; PRINT STRING (ENDING IN 0) PTED TO BY RET ADR;START WITH 705 | ; 706 | PRINTC: 707 | PUSH PSW ;SAVE FLAGS 708 | CALL CRLF ;NEW LINE 709 | POP PSW 710 | ; 711 | PRINT: 712 | XTHL ;GET PTR TO STRING 713 | PUSH PSW ;SAVE FLAGS 714 | CALL PRIN1 ;PRINT STRING 715 | POP PSW ;GET FLAGS 716 | XTHL ;RESTORE HL AND RET ADR 717 | RET 718 | ; 719 | ; PRINT STRING (ENDING IN 0) PTED TO BY HL 720 | ; 721 | PRIN1: 722 | MOV A,M ;GET NEXT BYTE 723 | ORA A ;WW - SET FLAGS 724 | RZ ;WW - DONE IF ZERO 725 | ANI 7FH ;WW - CLEAR HIGH BIT 726 | CALL CONOUT ;PRINT CHAR 727 | MOV A,M ;GET NEXT BYTE AGAIN FOR TEST 728 | INX H ;PT TO NEXT BYTE 729 | ORA A ;SET FLAGS 730 | RM ;DONE IF MSB SET 731 | JR PRIN1 732 | ; 733 | ; BDOS FUNCTION ROUTINES 734 | ; 735 | ; 736 | ; RETURN NUMBER OF CURRENT DISK IN A 737 | ; 738 | GETDRV: 739 | MVI C,19H 740 | JR BDOSJP 741 | ; 742 | ; SET 80H AS DMA ADDRESS 743 | ; 744 | DEFDMA: 745 | LXI D,TBUFF ;80H=TBUFF 746 | DMASET: 747 | MVI C,1AH 748 | JR BDOSJP 749 | ; 750 | RESET: 751 | MVI C,0DH 752 | BDOSJP: 753 | JMP BDOS 754 | ; 755 | LOGIN: 756 | MOV E,A 757 | MVI C,0EH 758 | JR BDOSJP ;SAVE SOME CODE SPACE 759 | ; 760 | OPENF: 761 | XRA A 762 | STA FCBCR 763 | LXI D,FCBDN ;FALL THRU TO OPEN 764 | ; 765 | OPEN: 766 | MVI C,0FH ;FALL THRU TO GRBDOS 767 | ; 768 | GRBDOS: 769 | CALL BDOS 770 | INR A ;SET ZERO FLAG FOR ERROR RETURN 771 | RET 772 | ; 773 | CLOSE: 774 | MVI C,10H 775 | JR GRBDOS 776 | ; 777 | SEARF: 778 | LXI D,FCBDN ;SPECIFY FCB 779 | SEAR1: 780 | MVI C,11H 781 | JR GRBDOS 782 | ; 783 | SEARN: 784 | MVI C,12H 785 | JR GRBDOS 786 | ; 787 | ; CHECK FOR SUBMIT FILE IN EXECUTION AND ABORT IT IF SO 788 | ; 789 | SUBKIL: 790 | LXI H,RNGSUB ;CHECK FOR SUBMIT FILE IN EXECUTION 791 | MOV A,M 792 | ORA A ;0=NO 793 | RZ 794 | MVI M,0 ;ABORT SUBMIT FILE 795 | LXI D,SUBFCB ;DELETE $$$.SUB 796 | ; 797 | DELETE: 798 | MVI C,13H 799 | JR BDOSJP ;SAVE MORE SPACE 800 | ; 801 | ; RESET USER NUMBER IF CHANGED 802 | ; 803 | RESETUSR: 804 | TMPUSR EQU $+1 ;POINTER FOR IN-THE-CODE MODIFICATION 805 | MVI A,0 ;2ND BYTE (IMMEDIATE ARG) IS TMPUSR 806 | MOV E,A ;PLACE IN E 807 | JR SETUSR ;THEN GO SET USER 808 | GETUSR: 809 | MVI E,0FFH ;GET CURRENT USER NUMBER 810 | SETUSR: 811 | MVI C,20H ;SET USER NUMBER TO VALUE IN E (GET IF E=FFH) 812 | JR BDOSJP ;MORE SPACE SAVING 813 | ; 814 | ; END OF BDOS FUNCTIONS 815 | ; 816 | ; 817 | ;**** Section 4 **** 818 | ; CPR UTILITIES 819 | ; 820 | ; SET USER/DISK FLAG TO CURRENT USER AND DEFAULT DISK 821 | ; 822 | SETUD: 823 | CALL GETUSR ;GET NUMBER OF CURRENT USER 824 | ADD A ;PLACE IT IN HIGH NYBBLE 825 | ADD A 826 | ADD A 827 | ADD A 828 | LXI H,TDRIVE ;MASK IN DEFAULT DRIVE NUMBER (LOW NYBBLE) 829 | ORA M ;MASK IN 830 | STA UDFLAG ;SET USER/DISK NUMBER 831 | RET 832 | ; 833 | ; SET USER/DISK FLAG TO USER 0 AND DEFAULT DISK 834 | ; 835 | SETU0D: 836 | TDRIVE EQU $+1 ;POINTER FOR IN-THE-CODE MODIFICATION 837 | MVI A,0 ;2ND BYTE (IMMEDIATE ARG) IS TDRIVE 838 | STA UDFLAG ;SET USER/DISK NUMBER 839 | RET 840 | ; 841 | ; CONVERT CHAR IN A TO UPPER CASE 842 | ; 843 | UCASE: 844 | CPI 61H ;LOWER-CASE A 845 | RC 846 | CPI 7BH ;GREATER THAN LOWER-CASE Z? 847 | RNC 848 | ANI 5FH ;CAPITALIZE 849 | RET 850 | ; 851 | ; INPUT NEXT COMMAND TO CPR 852 | ; This routine determines if a SUBMIT file is being processed 853 | ; and extracts the command line from it if so or from the user's console 854 | ; 855 | REDBUF: 856 | LDA RNGSUB ;SUBMIT FILE CURRENTLY IN EXECUTION? 857 | ORA A ;0=NO 858 | JRZ RB1 ;GET LINE FROM CONSOLE IF NOT 859 | LXI D,SUBFCB ;OPEN $$$.SUB 860 | PUSH D ;SAVE DE 861 | CALL OPEN 862 | POP D ;RESTORE DE 863 | JRZ RB1 ;ERASE $$$.SUB IF END OF FILE AND GET CMND 864 | LDA SUBFRC ;GET VALUE OF LAST RECORD IN FILE 865 | DCR A ;PT TO NEXT TO LAST RECORD 866 | STA SUBFCR ;SAVE NEW VALUE OF LAST RECORD IN $$$.SUB 867 | CALL READ ;DE=SUBFCB 868 | JRNZ RB1 ;ABORT $$$.SUB IF ERROR IN READING LAST REC 869 | LXI D,CBUFF ;COPY LAST RECORD (NEXT SUBMIT CMND) TO CBUFF 870 | LXI H,TBUFF ; FROM TBUFF 871 | LXI B,BUFLEN ;NUMBER OF BYTES 872 | LDIR 873 | LXI H,SUBFS2 ;PT TO S2 OF $$$.SUB FCB 874 | MVI M,0 ;SET S2 TO ZERO 875 | INX H ;PT TO RECORD COUNT 876 | DCR M ;DECREMENT RECORD COUNT OF $$$.SUB 877 | LXI D,SUBFCB ;CLOSE $$$.SUB 878 | CALL CLOSE 879 | JRZ RB1 ;ABORT $$$.SUB IF ERROR 880 | MVI A,SPRMPT ;PRINT SUBMIT PROMPT 881 | CALL CONOUT 882 | LXI H,CIBUFF ;PRINT COMMAND LINE FROM $$$.SUB 883 | CALL PRIN1 884 | CALL BREAK ;CHECK FOR ABORT (ANY CHAR) 885 | ; 886 | IF CLEVEL3 ;IF THIRD COMMAND LEVEL IS PERMITTED 887 | RZ ;IF (NO ABORT), RETURN TO CALLER AND RUN 888 | ENDIF 889 | ; 890 | IF NOT CLEVEL3 ;IF THIRD COMMAND LEVEL IS NOT PERMITTED 891 | JRZ CNVBUF ;IF (NO ABORT), CAPITALIZE COMMAND 892 | ENDIF 893 | ; 894 | CALL SUBKIL ;KILL $$$.SUB IF ABORT 895 | JMP RESTRT ;RESTART CPR 896 | ; 897 | ; INPUT COMMAND LINE FROM USER CONSOLE 898 | ; 899 | RB1: 900 | CALL SUBKIL ;ERASE $$$.SUB IF PRESENT 901 | CALL SETUD ;SET USER AND DISK 902 | MVI A,CPRMPT ;PRINT PROMPT 903 | CALL CONOUT 904 | MVI C,0AH ;READ COMMAND LINE FROM USER 905 | LXI D,MBUFF 906 | CALL BDOS 907 | ; 908 | IF CLEVEL3 ;IF THIRD COMMAND LEVEL IS PERMITTED 909 | JMP SETU0D ;SET CURRENT DISK NUMBER IN LOWER PARAMS 910 | ENDIF 911 | ; 912 | IF NOT CLEVEL3 ;IF THIRD COMMAND LEVEL IS NOT PERMITTED 913 | CALL SETU0D ;SET CURRENT DISK NUMBER IF LOWER PARAMS 914 | ; AND FALL THRU TO CNVBUF 915 | ENDIF 916 | ; 917 | ; CAPITALIZE STRING (ENDING IN 0) IN CBUFF AND SET PTR FOR PARSING 918 | ; 919 | CNVBUF: 920 | LXI H,CBUFF ;PT TO USER'S COMMAND 921 | MOV B,M ;CHAR COUNT IN B 922 | INR B ;ADD 1 IN CASE OF ZERO 923 | CB1: 924 | INX H ;PT TO 1ST VALID CHAR 925 | MOV A,M ;CAPITALIZE COMMAND CHAR 926 | CALL UCASE 927 | MOV M,A 928 | DJNZ CB1 ;CONTINUE TO END OF COMMAND LINE 929 | CB2: 930 | MVI M,0 ;STORE ENDING 931 | LXI H,CIBUFF ;SET COMMAND LINE PTR TO 1ST CHAR 932 | SHLD CIBPTR 933 | RET 934 | ; 935 | ; CHECK FOR ANY CHAR FROM USER CONSOLE;RET W/ZERO SET IF NONE 936 | ; 937 | BREAK: 938 | PUSH D ;SAVE DE 939 | MVI C,11 ;CSTS CHECK 940 | CALL BDOSB 941 | CNZ CONIN ;GET INPUT CHAR 942 | BRKBK: 943 | POP D 944 | RET 945 | ; 946 | ; GET THE REQUESTED USER NUMBER FROM THE COMMAND LINE AND VALIDATE IT. 947 | ; 948 | USRNUM: 949 | CALL NUMBER 950 | CPI MAXUSR+1 951 | RC 952 | ; 953 | ; INVALID COMMAND -- PRINT IT 954 | ; 955 | ERROR: 956 | CALL CRLF ;NEW LINE 957 | LHLD CIPTR ;PT TO BEGINNING OF COMMAND LINE 958 | ERR2: 959 | MOV A,M ;GET CHAR 960 | CPI ' '+1 ;SIMPLE '?' IF OR LESS 961 | JRC ERR1 962 | PUSH H ;SAVE PTR TO ERROR COMMAND CHAR 963 | CALL CONOUT ;PRINT COMMAND CHAR 964 | POP H ;GET PTR 965 | INX H ;PT TO NEXT 966 | JR ERR2 ;CONTINUE 967 | ERR1: 968 | CALL PRINT ;PRINT '?' 969 | DB '?'+80H 970 | CALL SUBKIL ;TERMINATE ACTIVE $$$.SUB IF ANY 971 | JMP RESTRT ;RESTART CPR 972 | ; 973 | ; CHECK TO SEE IF DE PTS TO DELIMITER; IF SO, RET W/ZERO FLAG SET 974 | ; 975 | SDELM: 976 | LDAX D 977 | ORA A ;0=DELIMITER 978 | RZ 979 | CPI ' ' ;ERROR IF < 980 | JRC ERROR 981 | RZ ;=DELIMITER 982 | CPI '=' ;'='=DELIMITER 983 | RZ 984 | CPI 5FH ;UNDERSCORE=DELIMITER 985 | RZ 986 | CPI '.' ;'.'=DELIMITER 987 | RZ 988 | CPI ':' ;':'=DELIMITER 989 | RZ 990 | CPI ';' ;';'=DELIMITER 991 | RZ 992 | CPI '<' ;'<'=DELIMITER 993 | RZ 994 | CPI '>' ;'>'=DELIMITER 995 | RET 996 | ; 997 | ; ADVANCE INPUT PTR TO FIRST NON-BLANK AND FALL THROUGH TO SBLANK 998 | ; 999 | ADVAN: 1000 | LDED CIBPTR 1001 | ; 1002 | ; SKIP STRING PTED TO BY DE (STRING ENDS IN 0) UNTIL END OF STRING 1003 | ; OR NON-BLANK ENCOUNTERED (BEGINNING OF TOKEN) 1004 | ; 1005 | SBLANK: 1006 | LDAX D 1007 | ORA A 1008 | RZ 1009 | CPI ' ' 1010 | RNZ 1011 | INX D 1012 | JR SBLANK 1013 | ; 1014 | ; ADD A TO HL (HL=HL+A) 1015 | ; 1016 | ADDAH: 1017 | ADD L 1018 | MOV L,A 1019 | RNC 1020 | INR H 1021 | RET 1022 | ; 1023 | ; EXTRACT DECIMAL NUMBER FROM COMMAND LINE 1024 | ; RETURN WITH VALUE IN REG A;ALL REGISTERS MAY BE AFFECTED 1025 | ; 1026 | NUMBER: 1027 | CALL SCANER ;PARSE NUMBER AND PLACE IN FCBFN 1028 | LXI H,FCBFN+10 ;PT TO END OF TOKEN FOR CONVERSION 1029 | MVI B,11 ;11 CHARS MAX 1030 | ; 1031 | ; CHECK FOR SUFFIX FOR HEXADECIMAL NUMBER 1032 | ; 1033 | NUMS: 1034 | MOV A,M ;GET CHARS FROM END, SEARCHING FOR SUFFIX 1035 | DCX H ;BACK UP 1036 | CPI ' ' ;SPACE? 1037 | JRNZ NUMS1 ;CHECK FOR SUFFIX 1038 | DJNZ NUMS ;COUNT DOWN 1039 | JR NUM0 ;BY DEFAULT, PROCESS 1040 | NUMS1: 1041 | CPI NUMBASE ;CHECK AGAINST BASE SWITCH FLAG 1042 | JRZ HNUM0 1043 | ; 1044 | ; PROCESS DECIMAL NUMBER 1045 | ; 1046 | NUM0: 1047 | LXI H,FCBFN ;PT TO BEGINNING OF TOKEN 1048 | LXI B,1100H ;C=ACCUMULATED VALUE, B=CHAR COUNT 1049 | ; (C=0, B=11) 1050 | NUM1: 1051 | MOV A,M ;GET CHAR 1052 | CPI ' ' ;DONE IF 1053 | JRZ NUM2 1054 | INX H ;PT TO NEXT CHAR 1055 | SUI '0' ;CONVERT TO BINARY (ASCII 0-9 TO BINARY) 1056 | CPI 10 ;ERROR IF >= 10 1057 | JRNC NUMERR 1058 | MOV D,A ;DIGIT IN D 1059 | MOV A,C ;NEW VALUE = OLD VALUE * 10 1060 | RLC 1061 | RLC 1062 | RLC 1063 | ADD C ;CHECK FOR RANGE ERROR 1064 | JRC NUMERR 1065 | ADD C ;CHECK FOR RANGE ERROR 1066 | JRC NUMERR 1067 | ADD D ;NEW VALUE = OLD VALUE * 10 + DIGIT 1068 | JRC NUMERR ;CHECK FOR RANGE ERROR 1069 | MOV C,A ;SET NEW VALUE 1070 | DJNZ NUM1 ;COUNT DOWN 1071 | ; 1072 | ; RETURN FROM NUMBER 1073 | ; 1074 | NUM2: 1075 | MOV A,C ;GET ACCUMULATED VALUE 1076 | RET 1077 | ; 1078 | ; NUMBER ERROR ROUTINE FOR SPACE CONSERVATION 1079 | ; 1080 | NUMERR: 1081 | JMP ERROR ;USE ERROR ROUTINE - THIS IS RELATIVE PT 1082 | ; 1083 | ; EXTRACT HEXADECIMAL NUMBER FROM COMMAND LINE 1084 | ; RETURN WITH VALUE IN REG A; ALL REGISTERS MAY BE AFFECTED 1085 | ; 1086 | HEXNUM: 1087 | CALL SCANER ;PARSE NUMBER AND PLACE IN FCBFN 1088 | HNUM0: 1089 | LXI H,FCBFN ;PT TO TOKEN FOR CONVERSION 1090 | LXI D,0 ;DE=ACCUMULATED VALUE 1091 | MVI B,11 ;B=CHAR COUNT 1092 | HNUM1: 1093 | MOV A,M ;GET CHAR 1094 | CPI ' ' ;DONE? 1095 | JRZ HNUM3 ;RETURN IF SO 1096 | CPI NUMBASE ;DONE IF NUMBASE SUFFIX 1097 | JRZ HNUM3 1098 | SUI '0' ;CONVERT TO BINARY 1099 | JRC NUMERR ;RETURN AND DONE IF ERROR 1100 | CPI 10 ;0-9? 1101 | JRC HNUM2 1102 | SUI 7 ;A-F? 1103 | CPI 10H ;ERROR? 1104 | JRNC NUMERR 1105 | HNUM2: 1106 | INX H ;PT TO NEXT CHAR 1107 | MOV C,A ;DIGIT IN C 1108 | MOV A,D ;GET ACCUMULATED VALUE 1109 | RLC ;EXCHANGE NYBBLES 1110 | RLC 1111 | RLC 1112 | RLC 1113 | ANI 0F0H ;MASK OUT LOW NYBBLE 1114 | MOV D,A 1115 | MOV A,E ;SWITCH LOW-ORDER NYBBLES 1116 | RLC 1117 | RLC 1118 | RLC 1119 | RLC 1120 | MOV E,A ;HIGH NYBBLE OF E=NEW HIGH OF E, 1121 | ; LOW NYBBLE OF E=NEW LOW OF D 1122 | ANI 0FH ;GET NEW LOW OF D 1123 | ORA D ;MASK IN HIGH OF D 1124 | MOV D,A ;NEW HIGH BYTE IN D 1125 | MOV A,E 1126 | ANI 0F0H ;MASK OUT LOW OF E 1127 | ORA C ;MASK IN NEW LOW 1128 | MOV E,A ;NEW LOW BYTE IN E 1129 | DJNZ HNUM1 ;COUNT DOWN 1130 | ; 1131 | ; RETURN FROM HEXNUM 1132 | ; 1133 | HNUM3: 1134 | XCHG ;RETURNED VALUE IN HL 1135 | MOV A,L ;LOW-ORDER BYTE IN A 1136 | RET 1137 | ; 1138 | ; PT TO DIRECTORY ENTRY IN TBUFF WHOSE OFFSET IS SPECIFIED BY A AND C 1139 | ; 1140 | DIRPTR: 1141 | LXI H,TBUFF ;PT TO TEMP BUFFER 1142 | ADD C ;PT TO 1ST BYTE OF DIR ENTRY 1143 | CALL ADDAH ;PT TO DESIRED BYTE IN DIR ENTRY 1144 | MOV A,M ;GET DESIRED BYTE 1145 | RET 1146 | ; 1147 | ; CHECK FOR SPECIFIED DRIVE AND LOG IT IN IF NOT DEFAULT 1148 | ; 1149 | SLOGIN: 1150 | XRA A ;SET FCBDN FOR DEFAULT DRIVE 1151 | STA FCBDN 1152 | CALL COMLOG ;CHECK DRIVE 1153 | RZ 1154 | JR DLOG5 ;DO LOGIN OTHERWISE 1155 | ; 1156 | ; CHECK FOR SPECIFIED DRIVE AND LOG IN DEFAULT DRIVE IF SPECIFIED<>DEFAULT 1157 | ; 1158 | DLOGIN: 1159 | CALL COMLOG ;CHECK DRIVE 1160 | RZ ;ABORT IF SAME 1161 | LDA TDRIVE ;LOG IN DEFAULT DRIVE 1162 | ; 1163 | DLOG5: JMP LOGIN 1164 | ; 1165 | ; ROUTINE COMMON TO BOTH LOGIN ROUTINES; ON EXIT, Z SET MEANS ABORT 1166 | ; 1167 | COMLOG: 1168 | TEMPDR EQU $+1 ;POINTER FOR IN-THE-CODE MODIFICATION 1169 | MVI A,0 ;2ND BYTE (IMMEDIATE ARG) IS TEMPDR 1170 | ORA A ;0=NO 1171 | RZ 1172 | DCR A ;COMPARE IT AGAINST DEFAULT 1173 | LXI H,TDRIVE 1174 | CMP M 1175 | RET ;ABORT IF SAME 1176 | ; 1177 | ; EXTRACT TOKEN FROM COMMAND LINE AND PLACE IT INTO FCBDN; 1178 | ; FORMAT FCBDN FCB IF TOKEN RESEMBLES FILE NAME AND TYPE (FILENAME.TYP); 1179 | ; ON INPUT, CIBPTR PTS TO CHAR AT WHICH TO START SCAN; 1180 | ; ON OUTPUT, CIBPTR PTS TO CHAR AT WHICH TO CONTINUE AND ZERO FLAG IS RESET 1181 | ; IF '?' IS IN TOKEN 1182 | ; 1183 | ; ENTRY POINTS: 1184 | ; SCANER - LOAD TOKEN INTO FIRST FCB 1185 | ; SCANX - LOAD TOKEN INTO FCB PTED TO BY HL 1186 | ; 1187 | SCANER: 1188 | LXI H,FCBDN ;POINT TO FCBDN 1189 | SCANX: 1190 | XRA A ;SET TEMPORARY DRIVE NUMBER TO DEFAULT 1191 | STA TEMPDR 1192 | CALL ADVAN ;SKIP TO NON-BLANK OR END OF LINE 1193 | SDED CIPTR ;SET PTR TO NON-BLANK OR END OF LINE 1194 | LDAX D ;END OF LINE? 1195 | ORA A ;0=YES 1196 | JRZ SCAN2 1197 | SBI 'A'-1 ;CONVERT POSSIBLE DRIVE SPEC TO NUMBER 1198 | MOV B,A ;STORE NUMBER (A:=0, B:=1, ETC) IN B 1199 | INX D ;PT TO NEXT CHAR 1200 | LDAX D ;SEE IF IT IS A COLON (:) 1201 | CPI ':' 1202 | JRZ SCAN3 ;YES, WE HAVE A DRIVE SPEC 1203 | DCX D ;NO, BACK UP PTR TO FIRST NON-BLANK CHAR 1204 | SCAN2: 1205 | LDA TDRIVE ;SET 1ST BYTE OF FCBDN AS DEFAULT DRIVE 1206 | MOV M,A 1207 | JR SCAN4 1208 | SCAN3: 1209 | MOV A,B ;WE HAVE A DRIVE SPEC 1210 | STA TEMPDR ;SET TEMPORARY DRIVE 1211 | MOV M,B ;SET 1ST BYTE OF FCBDN AS SPECIFIED DRIVE 1212 | INX D ;PT TO BYTE AFTER ':' 1213 | ; 1214 | ; EXTRACT FILENAME FROM POSSIBLE FILENAME.TYP 1215 | ; 1216 | SCAN4: 1217 | XRA A ;A=0 1218 | STA QMCNT ;INIT COUNT OF NUMBER OF QUESTION MARKS IN FCB 1219 | MVI B,8 ;MAX OF 8 CHARS IN FILE NAME 1220 | CALL SCANF ;FILL FCB FILE NAME 1221 | ; 1222 | ; EXTRACT FILE TYPE FROM POSSIBLE FILENAME.TYP 1223 | ; 1224 | MVI B,3 ;PREPARE TO EXTRACT TYPE 1225 | CPI '.' ;IF (DE) DELIMITER IS A '.', WE HAVE A TYPE 1226 | JRNZ SCAN15 ;FILL FILE TYPE BYTES WITH 1227 | INX D ;PT TO CHAR IN COMMAND LINE AFTER '.' 1228 | CALL SCANF ;FILL FCB FILE TYPE 1229 | JR SCAN16 ;SKIP TO NEXT PROCESSING 1230 | SCAN15: 1231 | CALL SCANF4 ;SPACE FILL 1232 | ; 1233 | ; FILL IN EX, S1, S2, AND RC WITH ZEROES 1234 | ; 1235 | SCAN16: 1236 | MVI B,4 ;4 BYTES 1237 | SCAN17: 1238 | INX H ;PT TO NEXT BYTE IN FCBDN 1239 | MVI M,0 1240 | DJNZ SCAN17 1241 | ; 1242 | ; SCAN COMPLETE -- DE PTS TO DELIMITER BYTE AFTER TOKEN 1243 | ; 1244 | SDED CIBPTR 1245 | ; 1246 | ; SET ZERO FLAG TO INDICATE PRESENCE OF '?' IN FILENAME.TYP 1247 | ; 1248 | LDA QMCNT ;GET NUMBER OF QUESTION MARKS 1249 | ORA A ;SET ZERO FLAG TO INDICATE ANY '?' 1250 | RET 1251 | ; 1252 | ; SCANF -- SCAN TOKEN PTED TO BY DE FOR A MAX OF B BYTES; PLACE IT INTO 1253 | ; FILE NAME FIELD PTED TO BY HL; EXPAND AND INTERPRET WILD CARDS OF 1254 | ; '*' AND '?'; ON EXIT, DE PTS TO TERMINATING DELIMITER 1255 | ; 1256 | SCANF: 1257 | CALL SDELM ;DONE IF DELIMITER ENCOUNTERED - FILL 1258 | JRZ SCANF4 1259 | INX H ;PT TO NEXT BYTE IN FCBDN 1260 | CPI '*' ;IS (DE) A WILD CARD? 1261 | JRNZ SCANF1 ;CONTINUE IF NOT 1262 | MVI M,'?' ;PLACE '?' IN FCBDN AND DON'T ADVANCE DE IF SO 1263 | CALL SCQ ;SCANNER COUNT QUESTION MARKS 1264 | JR SCANF2 1265 | SCANF1: 1266 | MOV M,A ;STORE FILENAME CHAR IN FCBDN 1267 | INX D ;PT TO NEXT CHAR IN COMMAND LINE 1268 | CPI '?' ;CHECK FOR QUESTION MARK (WILD) 1269 | CZ SCQ ;SCANNER COUNT QUESTION MARKS 1270 | SCANF2: 1271 | DJNZ SCANF ;DECREMENT CHAR COUNT UNTIL 8 ELAPSED 1272 | SCANF3: 1273 | CALL SDELM ;8 CHARS OR MORE - SKIP UNTIL DELIMITER 1274 | RZ ;ZERO FLAG SET IF DELIMITER FOUND 1275 | INX D ;PT TO NEXT CHAR IN COMMAND LINE 1276 | JR SCANF3 1277 | ; 1278 | ; FILL MEMORY POINTED TO BY HL WITH SPACES FOR B BYTES 1279 | ; 1280 | SCANF4: 1281 | INX H ;PT TO NEXT BYTE IN FCBDN 1282 | MVI M,' ' ;FILL FILENAME PART WITH 1283 | DJNZ SCANF4 1284 | RET 1285 | ; 1286 | ; INCREMENT QUESTION MARK COUNT FOR SCANNER 1287 | ; THIS ROUTINE INCREMENTS THE COUNT OF THE NUMBER OF QUESTION MARKS IN 1288 | ; THE CURRENT FCB ENTRY 1289 | ; 1290 | SCQ: 1291 | LDA QMCNT ;GET COUNT 1292 | INR A ;INCREMENT 1293 | STA QMCNT ;PUT COUNT 1294 | RET 1295 | ; 1296 | ; CMDTBL (COMMAND TABLE) SCANNER 1297 | ; ON RETURN, HL PTS TO ADDRESS OF COMMAND IF CPR-RESIDENT 1298 | ; ON RETURN, ZERO FLAG SET MEANS CPR-RESIDENT COMMAND 1299 | ; 1300 | CMDSER: 1301 | LXI H,CMDTBL ;PT TO COMMAND TABLE 1302 | MVI C,NCMNDS ;SET COMMAND COUNTER 1303 | CMS1: 1304 | LXI D,FCBFN ;PT TO STORED COMMAND NAME 1305 | MVI B,NCHARS ;NUMBER OF CHARS/COMMAND (8 MAX) 1306 | CMS2: 1307 | LDAX D ;COMPARE AGAINST TABLE ENTRY 1308 | CMP M 1309 | JRNZ CMS3 ;NO MATCH 1310 | INX D ;PT TO NEXT CHAR 1311 | INX H 1312 | DJNZ CMS2 ;COUNT DOWN 1313 | LDAX D ;NEXT CHAR IN INPUT COMMAND MUST BE 1314 | CPI ' ' 1315 | JRNZ CMS4 1316 | RET ;COMMAND IS CPR-RESIDENT (ZERO FLAG SET) 1317 | CMS3: 1318 | INX H ;SKIP TO NEXT COMMAND TABLE ENTRY 1319 | DJNZ CMS3 1320 | CMS4: 1321 | INX H ;SKIP ADDRESS 1322 | INX H 1323 | DCR C ;DECREMENT TABLE ENTRY NUMBER 1324 | JRNZ CMS1 1325 | INR C ;CLEAR ZERO FLAG 1326 | RET ;COMMAND IS DISK-RESIDENT (ZERO FLAG CLEAR) 1327 | ; 1328 | ;**** Section 5 **** 1329 | ; CPR-Resident Commands 1330 | ; 1331 | ; 1332 | ;Section 5A 1333 | ;Command: DIR 1334 | ;Function: To display a directory of the files on disk 1335 | ;Forms: 1336 | ; DIR Displays the DIR files 1337 | ; DIR S Displays the SYS files 1338 | ; DIR A Display both DIR and SYS files 1339 | ; 1340 | DIR: 1341 | MVI A,80H ;SET SYSTEM BIT EXAMINATION 1342 | PUSH PSW 1343 | CALL SCANER ;EXTRACT POSSIBLE D:FILENAME.TYP TOKEN 1344 | CALL SLOGIN ;LOG IN DRIVE IF NECESSARY 1345 | LXI H,FCBFN ;MAKE FCB WILD (ALL '?') IF NO FILENAME.TYP 1346 | MOV A,M ;GET FIRST CHAR OF FILENAME.TYP 1347 | CPI ' ' ;IF , ALL WILD 1348 | CZ FILLQ 1349 | CALL ADVAN ;LOOK AT NEXT INPUT CHAR 1350 | MVI B,0 ;SYS TOKEN DEFAULT 1351 | JRZ DIR2 ;JUMP; THERE ISN'T ONE 1352 | CPI SYSFLG ;SYSTEM FLAG SPECIFIER? 1353 | JRZ GOTSYS ;GOT SYSTEM SPECIFIER 1354 | CPI SOFLG ;SYS ONLY? 1355 | JRNZ DIR2 1356 | MVI B,80H ;FLAG SYS ONLY 1357 | GOTSYS: 1358 | INX D 1359 | SDED CIBPTR 1360 | CPI SOFLG ;SYS ONLY SPEC? 1361 | JRZ DIR2 ;THEN LEAVE BIT SPEC UNCHAGNED 1362 | POP PSW ;GET FLAG 1363 | XRA A ;SET NO SYSTEM BIT EXAMINATION 1364 | PUSH PSW 1365 | DIR2: 1366 | POP PSW ;GET FLAG 1367 | DIR2A: 1368 | ;DROP INTO DIRPR TO PRINT DIRECTORY 1369 | ; THEN RESTART CPR 1370 | ; 1371 | ; DIRECTORY PRINT ROUTINE; ON ENTRY, MSB OF A IS 1 (80H) IF SYSTEM FILES EXCL 1372 | ; 1373 | DIRPR: 1374 | MOV D,A ;STORE SYSTEM FLAG IN D 1375 | MVI E,0 ;SET COLUMN COUNTER TO ZERO 1376 | PUSH D ;SAVE COLUMN COUNTER (E) AND SYSTEM FLAG (D) 1377 | MOV A,B ;SYS ONLY SPECIFIER 1378 | STA SYSTST 1379 | CALL SEARF ;SEARCH FOR SPECIFIED FILE (FIRST OCCURRANCE) 1380 | CZ PRNNF ;PRINT NO FILE MSG;REG A NOT CHANGED 1381 | ; 1382 | ; ENTRY SELECTION LOOP; ON ENTRY, A=OFFSET FROM SEARF OR SEARN 1383 | ; 1384 | DIR3: 1385 | JRZ DIR11 ;DONE IF ZERO FLAG SET 1386 | DCR A ;ADJUST TO RETURNED VALUE 1387 | RRC ;CONVERT NUMBER TO OFFSET INTO TBUFF 1388 | RRC 1389 | RRC 1390 | ANI 60H 1391 | MOV C,A ;OFFSET INTO TBUFF IN C (C=OFFSET TO ENTRY) 1392 | MVI A,10 ;ADD 10 TO PT TO SYSTEM FILE ATTRIBUTE BIT 1393 | CALL DIRPTR 1394 | POP D ;GET SYSTEM BIT MASK FROM D 1395 | PUSH D 1396 | ANA D ;MASK FOR SYSTEM BIT 1397 | SYSTST EQU $+1 ;POINTER TO IN-THE-CODE BUFFER SYSTST 1398 | CPI 0 1399 | JRNZ DIR10 1400 | POP D ;GET ENTRY COUNT (= COUNTER) 1401 | MOV A,E ;ADD 1 TO IT 1402 | INR E 1403 | PUSH D ;SAVE IT 1404 | ANI 03H ;OUTPUT IF 4 ENTRIES PRINTED IN LINE 1405 | PUSH PSW 1406 | JRNZ DIR4 1407 | CALL CRLF ;NEW LINE 1408 | JR DIR5 1409 | DIR4: 1410 | CALL PRINT 1411 | ; 1412 | IF WIDE 1413 | DB ' ' ;2 SPACES 1414 | DB FENCE ;THEN FENCE CHAR 1415 | DB ' ',' '+80H ;THEN 2 MORE SPACES 1416 | ENDIF 1417 | ; 1418 | IF NOT WIDE 1419 | DB ' ' ;SPACE 1420 | DB FENCE ;THEN FENCE CHAR 1421 | DB ' '+80H ;THEN SPACE 1422 | ENDIF 1423 | ; 1424 | DIR5: 1425 | MVI B,01H ;PT TO 1ST BYTE OF FILE NAME 1426 | DIR6: 1427 | MOV A,B ;A=OFFSET 1428 | CALL DIRPTR ;HL NOW PTS TO 1ST BYTE OF FILE NAME 1429 | ANI 7FH ;MASK OUT MSB 1430 | CPI ' ' ;NO FILE NAME? 1431 | JRNZ DIR8 ;PRINT FILE NAME IF PRESENT 1432 | POP PSW 1433 | PUSH PSW 1434 | CPI 03H 1435 | JRNZ DIR7 1436 | MVI A,09H ;PT TO 1ST BYTE OF FILE TYPE 1437 | CALL DIRPTR ;HL NOW PTS TO 1ST BYTE OF FILE TYPE 1438 | ANI 7FH ;MASK OUT MSB 1439 | CPI ' ' ;NO FILE TYPE? 1440 | JRZ DIR9 ;CONTINUE IF SO 1441 | DIR7: 1442 | MVI A,' ' ;OUTPUT 1443 | DIR8: 1444 | CALL CONOUT ;PRINT CHAR 1445 | INR B ;INCR CHAR COUNT 1446 | MOV A,B 1447 | CPI 12 ;END OF FILENAME.TYP? 1448 | JRNC DIR9 ;CONTINUE IF SO 1449 | CPI 09H ;END IF FILENAME ONLY? 1450 | JRNZ DIR6 ;PRINT TYP IF SO 1451 | MVI A,'.' ;PRINT DOT BETWEEN FILE NAME AND TYPE 1452 | CALL CONOUT 1453 | JR DIR6 1454 | DIR9: 1455 | POP PSW 1456 | DIR10: 1457 | CALL BREAK ;CHECK FOR ABORT 1458 | JRNZ DIR11 1459 | CALL SEARN ;SEARCH FOR NEXT FILE 1460 | JR DIR3 ;CONTINUE 1461 | DIR11: 1462 | POP D ;RESTORE STACK 1463 | RET 1464 | ; 1465 | ; FILL FCB @HL WITH '?' 1466 | ; 1467 | FILLQ: 1468 | MVI B,11 ;NUMBER OF CHARS IN FN & FT 1469 | FQLP: 1470 | MVI M,'?' ;STORE '?' 1471 | INX H 1472 | DJNZ FQLP 1473 | RET 1474 | ; 1475 | ;Section 5B 1476 | ;Command: ERA 1477 | ;Function: Erase files 1478 | ;Forms: 1479 | ; ERA Erase Specified files and print their names 1480 | ; 1481 | IF NOT RAS ;NOT FOR REMOTE-ACCESS SYSTEM 1482 | ; 1483 | ERA: 1484 | CALL SCANER ;PARSE FILE SPECIFICATION 1485 | CPI 11 ;ALL WILD (ALL FILES = 11 '?')? 1486 | JRNZ ERA1 ;IF NOT, THEN DO ERASES 1487 | CALL PRINTC 1488 | DB 'All','?'+80H 1489 | CALL CONIN ;GET REPLY 1490 | CPI 'Y' ;YES? 1491 | JNZ RESTRT ;RESTART CPR IF NOT 1492 | CALL CRLF ;NEW LINE 1493 | ERA1: 1494 | CALL SLOGIN ;LOG IN SELECTED DISK IF ANY 1495 | XRA A ;PRINT ALL FILES (EXAMINE SYSTEM BIT) 1496 | MOV B,A ;NO SYS-ONLY OPT TO DIRPR 1497 | CALL DIRPR ;PRINT DIRECTORY OF ERASED FILES 1498 | LXI D,FCBDN ;DELETE FILE SPECIFIED 1499 | CALL DELETE 1500 | RET ;REENTER CPR 1501 | ; 1502 | ENDIF ;RAS 1503 | ; 1504 | ;Section 5C 1505 | ;Command: LIST 1506 | ;Function: Print out specified file on the LST: Device 1507 | ;Forms: 1508 | ; LIST Print file (NO Paging) 1509 | ; 1510 | LIST: 1511 | MVI A,0FFH ;TURN ON PRINTER FLAG 1512 | JR TYPE0 1513 | ; 1514 | ;Section 5D 1515 | ;Command: TYPE 1516 | ;Function: Print out specified file on the CON: Device 1517 | ;Forms: 1518 | ; TYPE Print file 1519 | ; TYPE P Print file with paging flag 1520 | ; 1521 | TYPE: 1522 | XRA A ;TURN OFF PRINTER FLAG 1523 | ; 1524 | ; ENTRY POINT FOR CPR LIST FUNCTION (LIST) 1525 | ; 1526 | TYPE0: 1527 | STA PRFLG ;SET FLAG 1528 | CALL SCANER ;EXTRACT FILENAME.TYP TOKEN 1529 | JNZ ERROR ;ERROR IF ANY QUESTION MARKS 1530 | CALL ADVAN ;GET PGDFLG IF IT'S THERE 1531 | STA PGFLG ;SAVE IT AS A FLAG 1532 | JRZ NOSLAS ;JUMP IF INPUT ENDED 1533 | INX D ;PUT NEW BUF POINTER 1534 | XCHG 1535 | SHLD CIBPTR 1536 | NOSLAS: 1537 | CALL SLOGIN ;LOG IN SELECTED DISK IF ANY 1538 | CALL OPENF ;OPEN SELECTED FILE 1539 | JZ TYPE4 ;ABORT IF ERROR 1540 | CALL CRLF ;NEW LINE 1541 | MVI A,NLINES-1 ;SET LINE COUNT 1542 | STA PAGCNT 1543 | LXI H,CHRCNT ;SET CHAR POSITION/COUNT 1544 | MVI M,0FFH ;EMPTY LINE 1545 | MVI B,0 ;SET TAB CHAR COUNTER 1546 | TYPE1: 1547 | LXI H,CHRCNT ;PT TO CHAR POSITION/COUNT 1548 | MOV A,M ;END OF BUFFER? 1549 | CPI 80H 1550 | JRC TYPE2 1551 | PUSH H ;READ NEXT BLOCK 1552 | CALL READF 1553 | POP H 1554 | JRNZ TYPE3 ;ERROR? 1555 | XRA A ;RESET COUNT 1556 | MOV M,A 1557 | TYPE2: 1558 | INR M ;INCREMENT CHAR COUNT 1559 | LXI H,TBUFF ;PT TO BUFFER 1560 | CALL ADDAH ;COMPUTE ADDRESS OF NEXT CHAR FROM OFFSET 1561 | MOV A,M ;GET NEXT CHAR 1562 | ANI 7FH ;MASK OUT MSB 1563 | CPI 1AH ;END OF FILE (^Z)? 1564 | RZ ;RESTART CPR IF SO 1565 | ; 1566 | ; OUTPUT CHAR TO CON: OR LST: DEVICE WITH TABULATION 1567 | ; 1568 | CPI CR ;RESET TAB COUNT? 1569 | JRZ TABRST 1570 | CPI LF ;RESET TAB COUNT? 1571 | JRZ TABRST 1572 | CPI TAB ;TAB? 1573 | JRZ LTAB 1574 | CALL LCOUT ;OUTPUT CHAR 1575 | INR B ;INCREMENT CHAR COUNT 1576 | JR TYPE2L 1577 | TABRST: 1578 | CALL LCOUT ;OUTPUT OR 1579 | MVI B,0 ;RESET TAB COUNTER 1580 | JR TYPE2L 1581 | LTAB: 1582 | MVI A,' ' ; 1583 | CALL LCOUT 1584 | INR B ;INCR POS COUNT 1585 | MOV A,B 1586 | ANI 7 1587 | JRNZ LTAB 1588 | ; 1589 | ; CONTINUE PROCESSING 1590 | ; 1591 | TYPE2L: 1592 | CALL BREAK ;CHECK FOR ABORT 1593 | JRZ TYPE1 ;CONTINUE IF NO CHAR 1594 | CPI 'C'-'@' ;^C? 1595 | RZ ;RESTART IF SO 1596 | JR TYPE1 1597 | TYPE3: 1598 | DCR A ;NO ERROR? 1599 | RZ ;RESTART CPR 1600 | TYPE4: 1601 | JMP ERRLOG 1602 | ; 1603 | ; PAGING ROUTINES 1604 | ; PAGER COUNTS DOWN LINES AND PAUSES FOR INPUT (DIRECT) IF COUNT EXPIRES 1605 | ; PAGSET SETS LINES/PAGE COUNT 1606 | ; 1607 | PAGER: 1608 | PUSH H 1609 | LXI H,PAGCNT ;COUNT DOWN 1610 | DCR M 1611 | JRNZ PGBAK ;JUMP IF NOT END OF PAGE 1612 | MVI M,NLINES-2 ;REFILL COUNTER 1613 | ; 1614 | PGFLG EQU $+1 ;POINTER TO IN-THE-CODE BUFFER PGFLG 1615 | MVI A,0 ;0 MAY BE CHANGED BY PGFLG EQUATE 1616 | CPI PGDFLG ;PAGE DEFAULT OVERRIDE OPTION WANTED? 1617 | ; 1618 | IF PGDFLT ;IF PAGING IS DEFAULT 1619 | JRZ PGBAK ; PGDFLG MEANS NO PAGING, PLEASE 1620 | ELSE ;IF PAGING NOT DEFAULT 1621 | JRNZ PGBAK ; PGDFLG MEANS PLEASE PAGINATE 1622 | ENDIF 1623 | ; 1624 | CALL CONIN ;GET CHAR TO CONTINUE 1625 | CPI 'C'-'@' ;^C 1626 | JZ RSTCPR ;RESTART CPR 1627 | PGBAK: 1628 | POP H ;RESTORE HL 1629 | RET 1630 | ; 1631 | ;Section 5E 1632 | ;Command: SAVE 1633 | ;Function: To save the contents of the TPA onto disk as a file 1634 | ;Forms: 1635 | ; SAVE 1636 | ; Save specified number of pages (start at 100H) 1637 | ; from TPA into specified file; is in DEC 1639 | ; SAVE S 1640 | ; Like SAVE above, but numeric argument specifies 1641 | ; number of sectors rather than pages 1642 | ; 1643 | IF NOT RAS ;NOT FOR REMOTE-ACCESS SYSTEM 1644 | ; 1645 | SAVE: 1646 | CALL NUMBER ;EXTRACT NUMBER FROM COMMAND LINE 1647 | MOV L,A ;HL=PAGE COUNT 1648 | MVI H,0 1649 | PUSH H ;SAVE PAGE COUNT 1650 | CALL EXTEST ;TEST FOR EXISTENCE OF FILE AND ABORT IF SO 1651 | MVI C,16H ;BDOS MAKE FILE 1652 | CALL GRBDOS 1653 | POP H ;GET PAGE COUNT 1654 | JRZ SAVE3 ;ERROR? 1655 | XRA A ;SET RECORD COUNT FIELD OF NEW FILE'S FCB 1656 | STA FCBCR 1657 | CALL ADVAN ;LOOK FOR 'S' FOR SECTOR OPTION 1658 | INX D ;PT TO AFTER 'S' TOKEN 1659 | CPI SECTFLG 1660 | JRZ SAVE0 1661 | DCX D ;NO 'S' TOKEN, SO BACK UP 1662 | DAD H ;DOUBLE IT FOR HL=SECTOR (128 BYTES) COUNT 1663 | SAVE0: 1664 | SDED CIBPTR ;SET PTR TO BAD TOKEN OR AFTER GOOD TOKEN 1665 | LXI D,TPA ;PT TO START OF SAVE AREA (TPA) 1666 | SAVE1: 1667 | MOV A,H ;DONE WITH SAVE? 1668 | ORA L ;HL=0 IF SO 1669 | JRZ SAVE2 1670 | DCX H ;COUNT DOWN ON SECTORS 1671 | PUSH H ;SAVE PTR TO BLOCK TO SAVE 1672 | LXI H,128 ;128 BYTES PER SECTOR 1673 | DAD D ;PT TO NEXT SECTOR 1674 | PUSH H ;SAVE ON STACK 1675 | CALL DMASET ;SET DMA ADDRESS FOR WRITE (ADDRESS IN DE) 1676 | LXI D,FCBDN ;WRITE SECTOR 1677 | MVI C,15H ;BDOS WRITE SECTOR 1678 | CALL BDOSB ;SAVE BC 1679 | POP D ;GET PTR TO NEXT SECTOR IN DE 1680 | POP H ;GET SECTOR COUNT 1681 | JRNZ SAVE3 ;WRITE ERROR? 1682 | JR SAVE1 ;CONTINUE 1683 | SAVE2: 1684 | LXI D,FCBDN ;CLOSE SAVED FILE 1685 | CALL CLOSE 1686 | INR A ;ERROR? 1687 | JRNZ SAVE4 1688 | SAVE3: 1689 | CALL PRNLE ;PRINT 'NO SPACE' ERROR 1690 | SAVE4: 1691 | CALL DEFDMA ;SET DMA TO 0080 1692 | RET ;RESTART CPR 1693 | ; 1694 | ; Test File in FCB for existence, ask user to delete if so, and abort if he 1695 | ; choses not to 1696 | ; 1697 | EXTEST: 1698 | CALL SCANER ;EXTRACT FILE NAME 1699 | JNZ ERROR ;'?' IS NOT PERMITTED 1700 | CALL SLOGIN ;LOG IN SELECTED DISK 1701 | CALL SEARF ;LOOK FOR SPECIFIED FILE 1702 | LXI D,FCBDN ;PT TO FILE FCB 1703 | RZ ;OK IF NOT FOUND 1704 | PUSH D ;SAVE PTR TO FCB 1705 | CALL PRINTC 1706 | DB 'Delete File','?'+80H 1707 | CALL CONIN ;GET RESPONSE 1708 | POP D ;GET PTR TO FCB 1709 | CPI 'Y' ;KEY ON YES 1710 | JNZ RSTCPR ;RESTART IF NO 1711 | PUSH D ;SAVE PTR TO FCB 1712 | CALL DELETE ;DELETE FILE 1713 | POP D ;GET PTR TO FCB 1714 | RET 1715 | ; 1716 | ENDIF ;RAS 1717 | ; 1718 | ;Section 5F 1719 | ;Command: REN 1720 | ;Function: To change the name of an existing file 1721 | ;Forms: 1722 | ; REN = Perform function 1723 | ; 1724 | IF NOT RAS ;NOT FOR REMOTE-ACCESS SYSTEM 1725 | ; 1726 | REN: 1727 | CALL EXTEST ;TEST FOR FILE EXISTENCE AND RETURN 1728 | ; IF FILE DOESN'T EXIST; ABORT IF IT DOES 1729 | LDA TEMPDR ;SAVE CURRENT DEFAULT DISK 1730 | PUSH PSW ;SAVE ON STACK 1731 | REN0: 1732 | LXI H,FCBDN ;SAVE NEW FILE NAME 1733 | LXI D,FCBDM 1734 | LXI B,16 ;16 BYTES 1735 | LDIR 1736 | CALL ADVAN ;ADVANCE CIBPTR 1737 | CPI '=' ;'=' OK 1738 | JRNZ REN4 1739 | REN1: 1740 | XCHG ;PT TO CHAR AFTER '=' IN HL 1741 | INX H 1742 | SHLD CIBPTR ;SAVE PTR TO OLD FILE NAME 1743 | CALL SCANER ;EXTRACT FILENAME.TYP TOKEN 1744 | JRNZ REN4 ;ERROR IF ANY '?' 1745 | POP PSW ;GET OLD DEFAULT DRIVE 1746 | MOV B,A ;SAVE IT 1747 | LXI H,TEMPDR ;COMPARE IT AGAINST CURRENT DEFAULT DRIVE 1748 | MOV A,M ;MATCH? 1749 | ORA A 1750 | JRZ REN2 1751 | CMP B ;CHECK FOR DRIVE ERROR 1752 | MOV M,B 1753 | JRNZ REN4 1754 | REN2: 1755 | MOV M,B 1756 | XRA A 1757 | STA FCBDN ;SET DEFAULT DRIVE 1758 | LXI D,FCBDN ;RENAME FILE 1759 | MVI C,17H ;BDOS RENAME FCT 1760 | CALL GRBDOS 1761 | RNZ 1762 | REN3: 1763 | CALL PRNNF ;PRINT NO FILE MSG 1764 | REN4: 1765 | JMP ERRLOG 1766 | ; 1767 | ENDIF ;RAS 1768 | ; 1769 | ;Section 5G 1770 | ;Command: USER 1771 | ;Function: Change current USER number 1772 | ;Forms: 1773 | ; USER Select specified user number; is in DEC 1774 | ; 1775 | USER: 1776 | CALL USRNUM ;EXTRACT USER NUMBER FROM COMMAND LINE 1777 | MOV E,A ;PLACE USER NUMBER IN E 1778 | CALL SETUSR ;SET SPECIFIED USER 1779 | RSTJMP: 1780 | JMP RCPRNL ;RESTART CPR 1781 | ; 1782 | ;Section 5H 1783 | ;Command: DFU 1784 | ;Function: Set the Default User Number for the command/file scanner 1785 | ; (MEMLOAD) 1786 | ;Forms: 1787 | ; DFU Select Default User Number; is in DEC 1788 | ; 1789 | DFU: 1790 | CALL USRNUM ;GET USER NUMBER 1791 | STA DFUSR ;PUT IT AWAY 1792 | JR RSTJMP ;RESTART CPR (NO DEFAULT LOGIN) 1793 | ; 1794 | ;Section 5I 1795 | ;Command: JUMP 1796 | ;Function: To Call the program (subroutine) at the specified address 1797 | ; without loading from disk 1798 | ;Forms: 1799 | ; JUMP Call at ; is in HEX 1800 | ; 1801 | IF NOT RAS ;NOT FOR REMOTE-ACCESS SYSTEM 1802 | ; 1803 | JUMP: 1804 | CALL HEXNUM ;GET LOAD ADDRESS IN HL 1805 | JR CALLPROG ;PERFORM CALL 1806 | ; 1807 | ENDIF ;RAS 1808 | ; 1809 | ;Section 5J 1810 | ;Command: GO 1811 | ;Function: To Call the program in the TPA without loading 1812 | ; loading from disk. Same as JUMP 100H, but much 1813 | ; more convenient, especially when used with 1814 | ; parameters for programs like STAT. Also can be 1815 | ; allowed on remote-access systems with no problems. 1816 | ; 1817 | ;Form: 1818 | ; GO 1819 | ; 1820 | IF NOT RAS ;ONLY IF RAS 1821 | ; 1822 | GO: LXI H,TPA ;Always to TPA 1823 | JR CALLPROG ;Perform call 1824 | ; 1825 | ENDIF ;END OF GO FOR RAS 1826 | ; 1827 | ;Section 5K 1828 | ;Command: COM file processing 1829 | ;Function: To load the specified COM file from disk and execute it 1830 | ;Forms: 1831 | ; 1832 | ; 1833 | COM: 1834 | LDA FCBFN ;ANY COMMAND? 1835 | CPI ' ' ;' ' MEANS COMMAND WAS 'D:' TO SWITCH 1836 | JRNZ COM1 ;NOT , SO MUST BE TRANSIENT OR ERROR 1837 | LDA TEMPDR ;LOOK FOR DRIVE SPEC 1838 | ORA A ;IF ZERO, JUST BLANK 1839 | JZ RCPRNL 1840 | DCR A ;ADJUST FOR LOG IN 1841 | STA TDRIVE ;SET DEFAULT DRIVE 1842 | CALL SETU0D ;SET DRIVE WITH USER 0 1843 | CALL LOGIN ;LOG IN DRIVE 1844 | JMP RCPRNL ;RESTART CPR 1845 | COM1: 1846 | LDA FCBFT ;FILE TYPE MUST BE BLANK 1847 | CPI ' ' 1848 | JNZ ERROR 1849 | LXI H,COMMSG ;PLACE DEFAULT FILE TYPE (COM) INTO FCB 1850 | LXI D,FCBFT ;COPY INTO FILE TYPE 1851 | LXI B,3 ;3 BYTES 1852 | LDIR 1853 | LXI H,TPA ;SET EXECUTION/LOAD ADDRESS 1854 | PUSH H ;SAVE FOR EXECUTION 1855 | CALL MEMLOAD ;LOAD MEMORY WITH FILE SPECIFIED IN CMD LINE 1856 | POP H ;GET EXECUTION ADDRESS 1857 | RNZ ;RETURN (ABORT) IF LOAD ERROR 1858 | ; 1859 | ; CALLPROG IS THE ENTRY POINT FOR THE EXECUTION OF THE LOADED 1860 | ; PROGRAM;ON ENTRY TO THIS ROUTINE, HL MUST CONTAIN THE EXECUTION 1861 | ; ADDRESS OF THE PROGRAM (SUBROUTINE) TO EXECUTE 1862 | ; 1863 | CALLPROG: 1864 | SHLD EXECADR ;PERFORM IN-LINE CODE MODIFICATION 1865 | CALL DLOGIN ;LOG IN DEFAULT DRIVE 1866 | CALL SCANER ;SEARCH COMMAND LINE FOR NEXT TOKEN 1867 | LXI H,TEMPDR ;SAVE PTR TO DRIVE SPEC 1868 | PUSH H 1869 | MOV A,M ;SET DRIVE SPEC 1870 | STA FCBDN 1871 | LXI H,FCBDN+10H ;PT TO 2ND FILE NAME 1872 | CALL SCANX ;SCAN FOR IT AND LOAD IT INTO FCBDN+16 1873 | POP H ;SET UP DRIVE SPECS 1874 | MOV A,M 1875 | STA FCBDM 1876 | XRA A 1877 | STA FCBCR 1878 | LXI D,TFCB ;COPY TO DEFAULT FCB 1879 | LXI H,FCBDN ;FROM FCBDN 1880 | LXI B,33 ;SET UP DEFAULT FCB 1881 | LDIR 1882 | LXI H,CIBUFF 1883 | COM4: 1884 | MOV A,M ;SKIP TO END OF 2ND FILE NAME 1885 | ORA A ;END OF LINE? 1886 | JRZ COM5 1887 | CPI ' ' ;END OF TOKEN? 1888 | JRZ COM5 1889 | INX H 1890 | JR COM4 1891 | ; 1892 | ; LOAD COMMAND LINE INTO TBUFF 1893 | ; 1894 | COM5: 1895 | MVI B,0 ;SET CHAR COUNT 1896 | LXI D,TBUFF+1 ;PT TO CHAR POS 1897 | COM6: 1898 | MOV A,M ;COPY COMMAND LINE TO TBUFF 1899 | STAX D 1900 | ORA A ;DONE IF ZERO 1901 | JRZ COM7 1902 | INR B ;INCR CHAR COUNT 1903 | INX H ;PT TO NEXT 1904 | INX D 1905 | JR COM6 1906 | ; 1907 | ; RUN LOADED TRANSIENT PROGRAM 1908 | ; 1909 | COM7: 1910 | MOV A,B ;SAVE CHAR COUNT 1911 | STA TBUFF 1912 | CALL CRLF ;NEW LINE 1913 | CALL DEFDMA ;SET DMA TO 0080 1914 | CALL SETUD ;SET USER/DISK 1915 | ; 1916 | ; EXECUTION (CALL) OF PROGRAM (SUBROUTINE) OCCURS HERE 1917 | ; 1918 | EXECADR EQU $+1 ;CHANGE ADDRESS FOR IN-LINE CODE MODIFICATION 1919 | CALL TPA ;CALL TRANSIENT 1920 | CALL DEFDMA ;SET DMA TO 0080, IN CASE 1921 | ;PROG CHANGED IT ON US 1922 | CALL SETU0D ;SET USER 0/DISK 1923 | CALL LOGIN ;LOGIN DISK 1924 | JMP RESTRT ;RESTART CPR 1925 | ; 1926 | ; TRANSIENT LOAD ERROR 1927 | ; 1928 | COM8: 1929 | POP H ;CLEAR RETURN ADDRESS 1930 | CALL RESETUSR ;RESET CURRENT USER NUMBER 1931 | ; RESET MUST BE DONE BEFORE LOGIN 1932 | ERRLOG: 1933 | CALL DLOGIN ;LOG IN DEFAULT DISK 1934 | ERRJMP: 1935 | JMP ERROR 1936 | ; 1937 | ;Section 5L 1938 | ;Command: GET 1939 | ;Function: To load the specified file from disk to the specified address 1940 | ;Forms: 1941 | ; GET Load the specified file at the specified page; 1942 | ; is in HEX 1943 | ; 1944 | IF NOT RAS ;NOT FOR REMOTE-ACCESS SYSTEM 1945 | ; 1946 | GET: 1947 | CALL HEXNUM ;GET LOAD ADDRESS IN HL 1948 | PUSH H ;SAVE ADDRESS 1949 | CALL SCANER ;GET FILE NAME 1950 | POP H ;RESTORE ADDRESS 1951 | JRNZ ERRJMP ;MUST BE UNAMBIGUOUS 1952 | ; 1953 | ; FALL THRU TO MEMLOAD 1954 | ; 1955 | ENDIF ;RAS 1956 | ; 1957 | ; LOAD MEMORY WITH THE FILE WHOSE NAME IS SPECIFIED IN THE COMMAND LINE 1958 | ; ON INPUT, HL CONTAINS STARTING ADDRESS TO LOAD 1959 | ; 1960 | MEMLOAD: 1961 | CALL MLOAD ;USER MEMORY LOAD SUBROUTINE 1962 | PUSH PSW ;SAVE RETURN STATUS 1963 | CALL RESETUSR ;RESET USER NUMBER 1964 | POP PSW ;GET RETURN STATUS 1965 | RET 1966 | 1967 | ; 1968 | ; MEMORY LOAD SUBROUTINE 1969 | ; EXIT POINTS ARE A SIMPLE RETURN WITH THE ZERO FLAG SET IF NO ERROR, 1970 | ; A SIMPLE RETURN WITH THE ZERO FLAG RESET (NZ) IF MEMORY FULL, OR A JMP TO 1971 | ; COM8 IF COM FILE NOT FOUND 1972 | ; 1973 | MLOAD: 1974 | SHLD LOADADR ;SET LOAD ADDRESS 1975 | CALL GETUSR ;GET CURRENT USER NUMBER 1976 | STA TMPUSR ;SAVE IT FOR LATER 1977 | STA TSELUSR ;TEMP USER TO SELECT 1978 | ; 1979 | ; MLA is a reentry point for a non-standard CP/M Modification 1980 | ; This is the return point for when the .COM (or GET) file is not found the 1981 | ; first time, Drive A: is selected for a second attempt 1982 | ; 1983 | MLA: 1984 | CALL SLOGIN ;LOG IN SPECIFIED DRIVE IF ANY 1985 | CALL OPENF ;OPEN COMMAND.COM FILE 1986 | JRNZ MLA1 ;FILE FOUND - LOAD IT 1987 | ; 1988 | ; ERROR ROUTINE TO SELECT USER 0 IF ALL ELSE FAILS 1989 | ; 1990 | DFUSR EQU $+1 ;MARK IN-THE-CODE VARIABLE 1991 | MVI A,DEFUSR ;GET DEFAULT USER 1992 | TSELUSR EQU $+1 ;MARK IN-THE-CODE VARIABLE 1993 | CPI DEFUSR ;SAME? 1994 | JRZ MLA0 ;JUMP IF 1995 | STA TSELUSR ;ELSE PUT DOWN NEW ONE 1996 | MOV E,A 1997 | CALL SETUSR ;GO SET NEW USER NUMBER 1998 | JR MLA ;AND TRY AGAIN 1999 | ; 2000 | ; ERROR ROUTINE TO SELECT DRIVE A: IF DEFAULT WAS ORIGINALLY SELECTED 2001 | ; 2002 | MLA0: 2003 | LXI H,TEMPDR ;GET DRIVE FROM CURRENT COMMAND 2004 | XRA A ;A=0 2005 | ORA M 2006 | JNZ COM8 ;ERROR IF ALREADY DISK A: 2007 | MVI M,1 ;SELECT DRIVE A: 2008 | JR MLA 2009 | ; 2010 | ; FILE FOUND -- PROCEED WITH LOAD 2011 | ; 2012 | MLA1: 2013 | LOADADR EQU $+1 ;MEMORY LOAD ADDRESS (IN-LINE CODE MOD) 2014 | LXI H,TPA ;SET START ADDRESS OF MEMORY LOAD 2015 | ML2: 2016 | MVI A,ENTRY/256-1 ;GET HIGH-ORDER ADR OF JUST BELOW CPR 2017 | CMP H ;ARE WE GOING TO OVERWRITE THE CPR? 2018 | JRC PRNLE ;ERROR IF SO 2019 | PUSH H ;SAVE ADDRESS OF NEXT SECTOR 2020 | XCHG ;... IN DE 2021 | CALL DMASET ;SET DMA ADDRESS FOR LOAD 2022 | LXI D,FCBDN ;READ NEXT SECTOR 2023 | CALL READ 2024 | POP H ;GET ADDRESS OF NEXT SECTOR 2025 | JRNZ ML3 ;READ ERROR OR EOF? 2026 | LXI D,128 ;MOVE 128 BYTES PER SECTOR 2027 | DAD D ;PT TO NEXT SECTOR IN HL 2028 | JR ML2 2029 | ; 2030 | ML3: 2031 | DCR A ;LOAD COMPLETE 2032 | RZ ;OK IF ZERO, ELSE FALL THRU TO PRNLE 2033 | ; 2034 | ; LOAD ERROR 2035 | ; 2036 | PRNLE: 2037 | CALL PRINTC 2038 | DB 'Ful','l'+80H 2039 | MVI A,1 ;SET NON-ZERO TO INDICATE ERROR 2040 | ORA A ;SET FLAG 2041 | RET 2042 | ; 2043 | END 2044 |  -------------------------------------------------------------------------------- /cpm.asm: -------------------------------------------------------------------------------- 1 | ;************************************************************** 2 | ;* 3 | ;* C P / M version 2 . 2 4 | ;* 5 | ;* Reconstructed from memory image on February 27, 1981 6 | ;* 7 | ;* by Clark A. Calkins 8 | ;* 9 | ;************************************************************** 10 | ; 11 | ; Set memory limit here. This is the amount of contigeous 12 | ; ram starting from 0000. CP/M will reside at the end of this space. 13 | ; 14 | IOBYTE EQU 3 ;i/o definition byte. 15 | TDRIVE EQU 4 ;current drive name and user number. 16 | ENTRY EQU 5 ;entry point for the cp/m bdos. 17 | TFCB EQU 5CH ;default file control block. 18 | TBUFF EQU 80H ;i/o buffer and command line storage. 19 | TBASE EQU 100H ;transiant program storage area. 20 | ; 21 | ; Set control character equates. 22 | ; 23 | CNTRLC EQU 3 ;control-c 24 | CNTRLE EQU 05H ;control-e 25 | BS EQU 08H ;backspace 26 | TAB EQU 09H ;tab 27 | LF EQU 0AH ;line feed 28 | FF EQU 0CH ;form feed 29 | CR EQU 0DH ;carriage return 30 | CNTRLP EQU 10H ;control-p 31 | CNTRLR EQU 12H ;control-r 32 | CNTRLS EQU 13H ;control-s 33 | CNTRLU EQU 15H ;control-u 34 | CNTRLX EQU 18H ;control-x 35 | CNTRLZ EQU 1AH ;control-z (end-of-file mark) 36 | DEL EQU 7FH ;rubout 37 | ; 38 | ; Set origin for CP/M 39 | ; 40 | ; 41 | CBASE: 42 | display "CCP: ", $ 43 | incbin "zcpr.bin" 44 | ;************************************************************** 45 | ;* 46 | ;* B D O S E N T R Y 47 | ;* 48 | ;************************************************************** 49 | ; 50 | FBASE: JP FBASE1 51 | ; 52 | ; Bdos error table. 53 | ; 54 | BADSCTR:DEFW ERROR1 ;bad sector on read or write. 55 | BADSLCT:DEFW ERROR2 ;bad disk select. 56 | RODISK: DEFW ERROR3 ;disk is read only. 57 | ROFILE: DEFW ERROR4 ;file is read only. 58 | ; 59 | ; Entry into bdos. (DE) or (E) are the parameters passed. The 60 | ; function number desired is in register (C). 61 | ; 62 | FBASE1: EX DE,HL ;save the (DE) parameters. 63 | LD (PARAMS),HL 64 | EX DE,HL 65 | LD A,E ;and save register (E) in particular. 66 | LD (EPARAM),A 67 | LD HL,0 68 | LD (STATUS),HL ;clear return status. 69 | ADD HL,SP 70 | LD (USRSTACK),HL ;save users stack pointer. 71 | LD SP,STKAREA ;and set our own. 72 | XOR A ;clear auto select storage space. 73 | LD (AUTOFLAG),A 74 | LD (AUTO),A 75 | LD HL,GOBACK ;set return address. 76 | PUSH HL 77 | LD A,C ;get function number. 78 | CP NFUNCTS ;valid function number? 79 | RET NC 80 | LD C,E ;keep single register function here. 81 | LD HL,FUNCTNS ;now look thru the function table. 82 | LD E,A 83 | LD D,0 ;(DE)=function number. 84 | ADD HL,DE 85 | ADD HL,DE ;(HL)=(start of table)+2*(function number). 86 | LD E,(HL) 87 | INC HL 88 | LD D,(HL) ;now (DE)=address for this function. 89 | LD HL,(PARAMS) ;retrieve parameters. 90 | EX DE,HL ;now (DE) has the original parameters. 91 | JP (HL) ;execute desired function. 92 | ; 93 | ; BDOS function jump table. 94 | ; 95 | NFUNCTS EQU 41 ;number of functions in followin table. 96 | ; 97 | FUNCTNS:DEFW WBOOT,GETCON,OUTCON,GETRDR,PUNCH,LIST,DIRCIO,GETIOB 98 | DEFW SETIOB,PRTSTR,RDBUFF,GETCSTS,GETVER,RSTDSK,SETDSK,OPENFIL 99 | DEFW CLOSEFIL,GETFST,GETNXT,DELFILE,READSEQ,WRTSEQ,FCREATE 100 | DEFW RENFILE,GETLOG,GETCRNT,PUTDMA,GETALOC,WRTPRTD,GETROV,SETATTR 101 | DEFW GETPARM,GETUSER,RDRANDOM,WTRANDOM,FILESIZE,SETRAN,LOGOFF,RTN 102 | DEFW RTN,WTSPECL 103 | ; 104 | ; Bdos error message section. 105 | ; 106 | ERROR1: LD HL,BADSEC ;bad sector message. 107 | CALL PRTERR ;print it and get a 1 char responce. 108 | CP CNTRLC ;re-boot request (control-c)? 109 | JP Z,0 ;yes. 110 | RET ;no, return to retry i/o function. 111 | ; 112 | ERROR2: LD HL,BADSEL ;bad drive selected. 113 | JP ERROR5 114 | ; 115 | ERROR3: LD HL,DISKRO ;disk is read only. 116 | JP ERROR5 117 | ; 118 | ERROR4: LD HL,FILERO ;file is read only. 119 | ; 120 | ERROR5: CALL PRTERR 121 | JP 0 ;always reboot on these errors. 122 | ; 123 | BDOSERR:DEFB 'Bdos Err On ' 124 | BDOSDRV:DEFB ' : $' 125 | BADSEC: DEFB 'Bad Sector$' 126 | BADSEL: DEFB 'Select$' 127 | FILERO: DEFB 'File ' 128 | DISKRO: DEFB 'R/O$' 129 | ; 130 | ; Print bdos error message. 131 | ; 132 | PRTERR: PUSH HL ;save second message pointer. 133 | CALL OUTCRLF ;send (cr)(lf). 134 | LD A,(ACTIVE) ;get active drive. 135 | ADD A,'A' ;make ascii. 136 | LD (BDOSDRV),A ;and put in message. 137 | LD BC,BDOSERR ;and print it. 138 | CALL PRTMESG 139 | POP BC ;print second message line now. 140 | CALL PRTMESG 141 | ; 142 | ; Get an input character. We will check our 1 character 143 | ; buffer first. This may be set by the console status routine. 144 | ; 145 | GETCHAR:LD HL,CHARBUF ;check character buffer. 146 | LD A,(HL) ;anything present already? 147 | LD (HL),0 ;...either case clear it. 148 | OR A 149 | RET NZ ;yes, use it. 150 | JP CONIN ;nope, go get a character responce. 151 | ; 152 | ; Input and echo a character. 153 | ; 154 | GETECHO:CALL GETCHAR ;input a character. 155 | CALL CHKCHAR ;carriage control? 156 | RET C ;no, a regular control char so don't echo. 157 | PUSH AF ;ok, save character now. 158 | LD C,A 159 | CALL OUTCON ;and echo it. 160 | POP AF ;get character and return. 161 | RET 162 | ; 163 | ; Check character in (A). Set the zero flag on a carriage 164 | ; control character and the carry flag on any other control 165 | ; character. 166 | ; 167 | CHKCHAR:CP CR ;check for carriage return, line feed, backspace, 168 | RET Z ;or a tab. 169 | CP LF 170 | RET Z 171 | CP TAB 172 | RET Z 173 | CP BS 174 | RET Z 175 | CP ' ' ;other control char? Set carry flag. 176 | RET 177 | ; 178 | ; Check the console during output. Halt on a control-s, then 179 | ; reboot on a control-c. If anything else is ready, clear the 180 | ; zero flag and return (the calling routine may want to do 181 | ; something). 182 | ; 183 | CKCONSOL: LD A,(CHARBUF) ;check buffer. 184 | OR A ;if anything, just return without checking. 185 | JP NZ,CKCON2 186 | CALL CONST ;nothing in buffer. Check console. 187 | AND 01H ;look at bit 0. 188 | RET Z ;return if nothing. 189 | CALL CONIN ;ok, get it. 190 | CP CNTRLS ;if not control-s, return with zero cleared. 191 | JP NZ,CKCON1 192 | CALL CONIN ;halt processing until another char 193 | CP CNTRLC ;is typed. Control-c? 194 | JP Z,0 ;yes, reboot now. 195 | XOR A ;no, just pretend nothing was ever ready. 196 | RET 197 | CKCON1: LD (CHARBUF),A ;save character in buffer for later processing. 198 | CKCON2: LD A,1 ;set (A) to non zero to mean something is ready. 199 | RET 200 | ; 201 | ; Output (C) to the screen. If the printer flip-flop flag 202 | ; is set, we will send character to printer also. The console 203 | ; will be checked in the process. 204 | ; 205 | OUTCHAR:LD A,(OUTFLAG) ;check output flag. 206 | OR A ;anything and we won't generate output. 207 | JP NZ,OUTCHR1 208 | PUSH BC 209 | CALL CKCONSOL ;check console (we don't care whats there). 210 | POP BC 211 | PUSH BC 212 | CALL CONOUT ;output (C) to the screen. 213 | POP BC 214 | PUSH BC 215 | LD A,(PRTFLAG) ;check printer flip-flop flag. 216 | OR A 217 | CALL NZ,LIST ;print it also if non-zero. 218 | POP BC 219 | OUTCHR1:LD A,C ;update cursors position. 220 | LD HL,CURPOS 221 | CP DEL ;rubouts don't do anything here. 222 | RET Z 223 | INC (HL) ;bump line pointer. 224 | CP ' ' ;and return if a normal character. 225 | RET NC 226 | DEC (HL) ;restore and check for the start of the line. 227 | LD A,(HL) 228 | OR A 229 | RET Z ;ingnore control characters at the start of the line. 230 | LD A,C 231 | CP BS ;is it a backspace? 232 | JP NZ,OUTCHR2 233 | DEC (HL) ;yes, backup pointer. 234 | RET 235 | OUTCHR2:CP LF ;is it a line feed? 236 | RET NZ ;ignore anything else. 237 | LD (HL),0 ;reset pointer to start of line. 238 | RET 239 | ; 240 | ; Output (A) to the screen. If it is a control character 241 | ; (other than carriage control), use ^x format. 242 | ; 243 | SHOWIT: LD A,C 244 | CALL CHKCHAR ;check character. 245 | JP NC,OUTCON ;not a control, use normal output. 246 | PUSH AF 247 | LD C,'^' ;for a control character, preceed it with '^'. 248 | CALL OUTCHAR 249 | POP AF 250 | OR '@' ;and then use the letter equivelant. 251 | LD C,A 252 | ; 253 | ; Function to output (C) to the console device and expand tabs 254 | ; if necessary. 255 | ; 256 | OUTCON: LD A,C 257 | CP TAB ;is it a tab? 258 | JP NZ,OUTCHAR ;use regular output. 259 | OUTCON1:LD C,' ' ;yes it is, use spaces instead. 260 | CALL OUTCHAR 261 | LD A,(CURPOS) ;go until the cursor is at a multiple of 8 262 | 263 | AND 07H ;position. 264 | JP NZ,OUTCON1 265 | RET 266 | ; 267 | ; Echo a backspace character. Erase the prevoius character 268 | ; on the screen. 269 | ; 270 | BACKUP: CALL BACKUP1 ;backup the screen 1 place. 271 | LD C,' ' ;then blank that character. 272 | CALL CONOUT 273 | BACKUP1:LD C,BS ;then back space once more. 274 | JP CONOUT 275 | ; 276 | ; Signal a deleted line. Print a '#' at the end and start 277 | ; over. 278 | ; 279 | NEWLINE:LD C,'#' 280 | CALL OUTCHAR ;print this. 281 | CALL OUTCRLF ;start new line. 282 | NEWLN1: LD A,(CURPOS) ;move the cursor to the starting position. 283 | LD HL,STARTING 284 | CP (HL) 285 | RET NC ;there yet? 286 | LD C,' ' 287 | CALL OUTCHAR ;nope, keep going. 288 | JP NEWLN1 289 | ; 290 | ; Output a (cr) (lf) to the console device (screen). 291 | ; 292 | OUTCRLF:LD C,CR 293 | CALL OUTCHAR 294 | LD C,LF 295 | JP OUTCHAR 296 | ; 297 | ; Print message pointed to by (BC). It will end with a '$'. 298 | ; 299 | PRTMESG:LD A,(BC) ;check for terminating character. 300 | CP '$' 301 | RET Z 302 | INC BC 303 | PUSH BC ;otherwise, bump pointer and print it. 304 | LD C,A 305 | CALL OUTCON 306 | POP BC 307 | JP PRTMESG 308 | ; 309 | ; Function to execute a buffered read. 310 | ; 311 | RDBUFF: LD A,(CURPOS) ;use present location as starting one. 312 | LD (STARTING),A 313 | LD HL,(PARAMS) ;get the maximum buffer space. 314 | LD C,(HL) 315 | INC HL ;point to first available space. 316 | PUSH HL ;and save. 317 | LD B,0 ;keep a character count. 318 | RDBUF1: PUSH BC 319 | PUSH HL 320 | RDBUF2: CALL GETCHAR ;get the next input character. 321 | AND 7FH ;strip bit 7. 322 | POP HL ;reset registers. 323 | POP BC 324 | CP CR ;en of the line? 325 | JP Z,RDBUF17 326 | CP LF 327 | JP Z,RDBUF17 328 | CP BS ;how about a backspace? 329 | JP NZ,RDBUF3 330 | LD A,B ;yes, but ignore at the beginning of the line. 331 | OR A 332 | JP Z,RDBUF1 333 | DEC B ;ok, update counter. 334 | LD A,(CURPOS) ;if we backspace to the start of the line, 335 | LD (OUTFLAG),A ;treat as a cancel (control-x). 336 | JP RDBUF10 337 | RDBUF3: CP DEL ;user typed a rubout? 338 | JP NZ,RDBUF4 339 | LD A,B ;ignore at the start of the line. 340 | OR A 341 | JP Z,RDBUF1 342 | LD A,BS ;ok, echo the prevoius character. 343 | DEC B ;and reset pointers (counters). 344 | LD A,(CURPOS) ;if we backspace to the start of the line, 345 | LD (OUTFLAG),A ;treat as a cancel (control-x). 346 | JP RDBUF10 347 | RDBUF4: CP CNTRLE ;physical end of line? 348 | JP NZ,RDBUF5 349 | PUSH BC ;yes, do it. 350 | PUSH HL 351 | CALL OUTCRLF 352 | XOR A ;and update starting position. 353 | LD (STARTING),A 354 | JP RDBUF2 355 | RDBUF5: CP CNTRLP ;control-p? 356 | JP NZ,RDBUF6 357 | PUSH HL ;yes, flip the print flag filp-flop byte. 358 | LD HL,PRTFLAG 359 | LD A,1 ;PRTFLAG=1-PRTFLAG 360 | SUB (HL) 361 | LD (HL),A 362 | POP HL 363 | JP RDBUF1 364 | RDBUF6: CP CNTRLX ;control-x (cancel)? 365 | JP NZ,RDBUF8 366 | POP HL 367 | RDBUF7: LD A,(STARTING) ;yes, backup the cursor to here. 368 | LD HL,CURPOS 369 | CP (HL) 370 | JP NC,RDBUFF ;done yet? 371 | DEC (HL) ;no, decrement pointer and output back up one space. 372 | CALL BACKUP 373 | JP RDBUF7 374 | RDBUF8: CP CNTRLU ;cntrol-u (cancel line)? 375 | JP NZ,RDBUF9 376 | CALL NEWLINE ;start a new line. 377 | POP HL 378 | JP RDBUFF 379 | RDBUF9: CP CNTRLR ;control-r? 380 | JP NZ,RDBUF14 381 | RDBUF10:PUSH BC ;yes, start a new line and retype the old one. 382 | CALL NEWLINE 383 | POP BC 384 | POP HL 385 | PUSH HL 386 | PUSH BC 387 | RDBUF11:LD A,B ;done whole line yet? 388 | OR A 389 | JP Z,RDBUF12 390 | INC HL ;nope, get next character. 391 | LD C,(HL) 392 | DEC B ;count it. 393 | PUSH BC 394 | PUSH HL 395 | CALL SHOWIT ;and display it. 396 | POP HL 397 | POP BC 398 | JP RDBUF11 399 | RDBUF12:PUSH HL ;done with line. If we were displaying 400 | LD A,(OUTFLAG) ;then update cursor position. 401 | OR A 402 | JP Z,RDBUF2 403 | LD HL,CURPOS ;because this line is shorter, we must 404 | SUB (HL) ;back up the cursor (not the screen however) 405 | LD (OUTFLAG),A ;some number of positions. 406 | RDBUF13:CALL BACKUP ;note that as long as (OUTFLAG) is non 407 | LD HL,OUTFLAG ;zero, the screen will not be changed. 408 | DEC (HL) 409 | JP NZ,RDBUF13 410 | JP RDBUF2 ;now just get the next character. 411 | ; 412 | ; Just a normal character, put this in our buffer and echo. 413 | ; 414 | RDBUF14:INC HL 415 | LD (HL),A ;store character. 416 | INC B ;and count it. 417 | RDBUF15:PUSH BC 418 | PUSH HL 419 | LD C,A ;echo it now. 420 | CALL SHOWIT 421 | POP HL 422 | POP BC 423 | LD A,(HL) ;was it an abort request? 424 | CP CNTRLC ;control-c abort? 425 | LD A,B 426 | JP NZ,RDBUF16 427 | CP 1 ;only if at start of line. 428 | JP Z,0 429 | RDBUF16:CP C ;nope, have we filled the buffer? 430 | JP C,RDBUF1 431 | RDBUF17:POP HL ;yes end the line and return. 432 | LD (HL),B 433 | LD C,CR 434 | JP OUTCHAR ;output (cr) and return. 435 | ; 436 | ; Function to get a character from the console device. 437 | ; 438 | GETCON: CALL GETECHO ;get and echo. 439 | JP SETSTAT ;save status and return. 440 | ; 441 | ; Function to get a character from the tape reader device. 442 | ; 443 | GETRDR: CALL READER ;get a character from reader, set status and return. 444 | JP SETSTAT 445 | ; 446 | ; Function to perform direct console i/o. If (C) contains (FF) 447 | ; then this is an input request. If (C) contains (FE) then 448 | ; this is a status request. Otherwise we are to output (C). 449 | ; 450 | DIRCIO: LD A,C ;test for (FF). 451 | INC A 452 | JP Z,DIRC1 453 | INC A ;test for (FE). 454 | JP Z,CONST 455 | JP CONOUT ;just output (C). 456 | DIRC1: CALL CONST ;this is an input request. 457 | OR A 458 | JP Z,GOBACK1 ;not ready? Just return (directly). 459 | CALL CONIN ;yes, get character. 460 | JP SETSTAT ;set status and return. 461 | ; 462 | ; Function to return the i/o byte. 463 | ; 464 | GETIOB: LD A,(IOBYTE) 465 | JP SETSTAT 466 | ; 467 | ; Function to set the i/o byte. 468 | ; 469 | SETIOB: LD HL,IOBYTE 470 | LD (HL),C 471 | RET 472 | ; 473 | ; Function to print the character string pointed to by (DE) 474 | ; on the console device. The string ends with a '$'. 475 | ; 476 | PRTSTR: EX DE,HL 477 | LD C,L 478 | LD B,H ;now (BC) points to it. 479 | JP PRTMESG 480 | ; 481 | ; Function to interigate the console device. 482 | ; 483 | GETCSTS:CALL CKCONSOL 484 | ; 485 | ; Get here to set the status and return to the cleanup 486 | ; section. Then back to the user. 487 | ; 488 | SETSTAT:LD (STATUS),A 489 | RTN: RET 490 | ; 491 | ; Set the status to 1 (read or write error code). 492 | ; 493 | IOERR1: LD A,1 494 | JP SETSTAT 495 | ; 496 | OUTFLAG:DEFB 0 ;output flag (non zero means no output). 497 | STARTING: DEFB 2 ;starting position for cursor. 498 | CURPOS: DEFB 0 ;cursor position (0=start of line). 499 | PRTFLAG:DEFB 0 ;printer flag (control-p toggle). List if non zero. 500 | CHARBUF:DEFB 0 ;single input character buffer. 501 | ; 502 | ; Stack area for BDOS calls. 503 | ; 504 | USRSTACK: DEFW 0 ;save users stack pointer here. 505 | ; 506 | DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 507 | DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 508 | STKAREA EQU $ ;end of stack area. 509 | ; 510 | USERNO: DEFB 0 ;current user number. 511 | ACTIVE: DEFB 0 ;currently active drive. 512 | PARAMS: DEFW 0 ;save (DE) parameters here on entry. 513 | STATUS: DEFW 0 ;status returned from bdos function. 514 | ; 515 | ; Select error occured, jump to error routine. 516 | ; 517 | SLCTERR:LD HL,BADSLCT 518 | ; 519 | ; Jump to (HL) indirectly. 520 | ; 521 | JUMPHL: LD E,(HL) 522 | INC HL 523 | LD D,(HL) ;now (DE) contain the desired address. 524 | EX DE,HL 525 | JP (HL) 526 | ; 527 | ; Block move. (DE) to (HL), (C) bytes total. 528 | ; 529 | DE2HL: INC C ;is count down to zero? 530 | DE2HL1: DEC C 531 | RET Z ;yes, we are done. 532 | LD A,(DE) ;no, move one more byte. 533 | LD (HL),A 534 | INC DE 535 | INC HL 536 | JP DE2HL1 ;and repeat. 537 | ; 538 | ; Select the desired drive. 539 | ; 540 | SELECT: LD A,(ACTIVE) ;get active disk. 541 | LD C,A 542 | CALL SELDSK ;select it. 543 | LD A,H ;valid drive? 544 | OR L ;valid drive? 545 | RET Z ;return if not. 546 | ; 547 | ; Here, the BIOS returned the address of the parameter block 548 | ; in (HL). We will extract the necessary pointers and save them. 549 | ; 550 | LD E,(HL) ;yes, get address of translation table into (DE). 551 | INC HL 552 | LD D,(HL) 553 | INC HL 554 | LD (SCRATCH1),HL ;save pointers to scratch areas. 555 | INC HL 556 | INC HL 557 | LD (SCRATCH2),HL ;ditto. 558 | INC HL 559 | INC HL 560 | LD (SCRATCH3),HL ;ditto. 561 | INC HL 562 | INC HL 563 | EX DE,HL ;now save the translation table address. 564 | LD (XLATE),HL 565 | LD HL,DIRBUF ;put the next 8 bytes here. 566 | LD C,8 ;they consist of the directory buffer 567 | CALL DE2HL ;pointer, parameter block pointer, 568 | LD HL,(DISKPB) ;check and allocation vectors. 569 | EX DE,HL 570 | LD HL,SECTORS ;move parameter block into our ram. 571 | LD C,15 ;it is 15 bytes long. 572 | CALL DE2HL 573 | LD HL,(DSKSIZE) ;check disk size. 574 | LD A,H ;more than 256 blocks on this? 575 | LD HL,BIGDISK 576 | LD (HL),0FFH ;set to samll. 577 | OR A 578 | JP Z,SELECT1 579 | LD (HL),0 ;wrong, set to large. 580 | SELECT1:LD A,0FFH ;clear the zero flag. 581 | OR A 582 | RET 583 | ; 584 | ; Routine to home the disk track head and clear pointers. 585 | ; 586 | HOMEDRV:CALL HOME ;home the head. 587 | XOR A 588 | LD HL,(SCRATCH2) ;set our track pointer also. 589 | LD (HL),A 590 | INC HL 591 | LD (HL),A 592 | LD HL,(SCRATCH3) ;and our sector pointer. 593 | LD (HL),A 594 | INC HL 595 | LD (HL),A 596 | RET 597 | ; 598 | ; Do the actual disk read and check the error return status. 599 | ; 600 | DOREAD: CALL READ 601 | JP IORET 602 | ; 603 | ; Do the actual disk write and handle any bios error. 604 | ; 605 | DOWRITE:CALL WRITE 606 | IORET: OR A 607 | RET Z ;return unless an error occured. 608 | LD HL,BADSCTR ;bad read/write on this sector. 609 | JP JUMPHL 610 | ; 611 | ; Routine to select the track and sector that the desired 612 | ; block number falls in. 613 | ; 614 | TRKSEC: LD HL,(FILEPOS) ;get position of last accessed file 615 | LD C,2 ;in directory and compute sector #. 616 | CALL SHIFTR ;sector #=file-position/4. 617 | LD (BLKNMBR),HL ;save this as the block number of interest. 618 | LD (CKSUMTBL),HL ;what's it doing here too? 619 | ; 620 | ; if the sector number has already been set (BLKNMBR), enter 621 | ; at this point. 622 | ; 623 | TRKSEC1:LD HL,BLKNMBR 624 | LD C,(HL) ;move sector number into (BC). 625 | INC HL 626 | LD B,(HL) 627 | LD HL,(SCRATCH3) ;get current sector number and 628 | LD E,(HL) ;move this into (DE). 629 | INC HL 630 | LD D,(HL) 631 | LD HL,(SCRATCH2) ;get current track number. 632 | LD A,(HL) ;and this into (HL). 633 | INC HL 634 | LD H,(HL) 635 | LD L,A 636 | TRKSEC2:LD A,C ;is desired sector before current one? 637 | SUB E 638 | LD A,B 639 | SBC A,D 640 | JP NC,TRKSEC3 641 | PUSH HL ;yes, decrement sectors by one track. 642 | LD HL,(SECTORS) ;get sectors per track. 643 | LD A,E 644 | SUB L 645 | LD E,A 646 | LD A,D 647 | SBC A,H 648 | LD D,A ;now we have backed up one full track. 649 | POP HL 650 | DEC HL ;adjust track counter. 651 | JP TRKSEC2 652 | TRKSEC3:PUSH HL ;desired sector is after current one. 653 | LD HL,(SECTORS) ;get sectors per track. 654 | ADD HL,DE ;bump sector pointer to next track. 655 | JP C,TRKSEC4 656 | LD A,C ;is desired sector now before current one? 657 | SUB L 658 | LD A,B 659 | SBC A,H 660 | JP C,TRKSEC4 661 | EX DE,HL ;not yes, increment track counter 662 | POP HL ;and continue until it is. 663 | INC HL 664 | JP TRKSEC3 665 | ; 666 | ; here we have determined the track number that contains the 667 | ; desired sector. 668 | ; 669 | TRKSEC4:POP HL ;get track number (HL). 670 | PUSH BC 671 | PUSH DE 672 | PUSH HL 673 | EX DE,HL 674 | LD HL,(OFFSET) ;adjust for first track offset. 675 | ADD HL,DE 676 | LD B,H 677 | LD C,L 678 | CALL SETTRK ;select this track. 679 | POP DE ;reset current track pointer. 680 | LD HL,(SCRATCH2) 681 | LD (HL),E 682 | INC HL 683 | LD (HL),D 684 | POP DE 685 | LD HL,(SCRATCH3) ;reset the first sector on this track. 686 | LD (HL),E 687 | INC HL 688 | LD (HL),D 689 | POP BC 690 | LD A,C ;now subtract the desired one. 691 | SUB E ;to make it relative (1-# sectors/track). 692 | LD C,A 693 | LD A,B 694 | SBC A,D 695 | LD B,A 696 | LD HL,(XLATE) ;translate this sector according to this table. 697 | EX DE,HL 698 | CALL SECTRN ;let the bios translate it. 699 | LD C,L 700 | LD B,H 701 | JP SETSEC ;and select it. 702 | ; 703 | ; Compute block number from record number (SAVNREC) and 704 | ; extent number (SAVEXT). 705 | ; 706 | GETBLOCK: LD HL,BLKSHFT ;get logical to physical conversion. 707 | LD C,(HL) ;note that this is base 2 log of ratio. 708 | LD A,(SAVNREC) ;get record number. 709 | GETBLK1:OR A ;compute (A)=(A)/2^BLKSHFT. 710 | RRA 711 | DEC C 712 | JP NZ,GETBLK1 713 | LD B,A ;save result in (B). 714 | LD A,8 715 | SUB (HL) 716 | LD C,A ;compute (C)=8-BLKSHFT. 717 | LD A,(SAVEXT) 718 | GETBLK2:DEC C ;compute (A)=SAVEXT*2^(8-BLKSHFT). 719 | JP Z,GETBLK3 720 | OR A 721 | RLA 722 | JP GETBLK2 723 | GETBLK3:ADD A,B 724 | RET 725 | ; 726 | ; Routine to extract the (BC) block byte from the fcb pointed 727 | ; to by (PARAMS). If this is a big-disk, then these are 16 bit 728 | ; block numbers, else they are 8 bit numbers. 729 | ; Number is returned in (HL). 730 | ; 731 | EXTBLK: LD HL,(PARAMS) ;get fcb address. 732 | LD DE,16 ;block numbers start 16 bytes into fcb. 733 | ADD HL,DE 734 | ADD HL,BC 735 | LD A,(BIGDISK) ;are we using a big-disk? 736 | OR A 737 | JP Z,EXTBLK1 738 | LD L,(HL) ;no, extract an 8 bit number from the fcb. 739 | LD H,0 740 | RET 741 | EXTBLK1:ADD HL,BC ;yes, extract a 16 bit number. 742 | LD E,(HL) 743 | INC HL 744 | LD D,(HL) 745 | EX DE,HL ;return in (HL). 746 | RET 747 | ; 748 | ; Compute block number. 749 | ; 750 | COMBLK: CALL GETBLOCK 751 | LD C,A 752 | LD B,0 753 | CALL EXTBLK 754 | LD (BLKNMBR),HL 755 | RET 756 | ; 757 | ; Check for a zero block number (unused). 758 | ; 759 | CHKBLK: LD HL,(BLKNMBR) 760 | LD A,L ;is it zero? 761 | OR H 762 | RET 763 | ; 764 | ; Adjust physical block (BLKNMBR) and convert to logical 765 | ; sector (LOGSECT). This is the starting sector of this block. 766 | ; The actual sector of interest is then added to this and the 767 | ; resulting sector number is stored back in (BLKNMBR). This 768 | ; will still have to be adjusted for the track number. 769 | ; 770 | LOGICAL:LD A,(BLKSHFT) ;get log2(physical/logical sectors). 771 | LD HL,(BLKNMBR) ;get physical sector desired. 772 | LOGICL1:ADD HL,HL ;compute logical sector number. 773 | DEC A ;note logical sectors are 128 bytes long. 774 | JP NZ,LOGICL1 775 | LD (LOGSECT),HL ;save logical sector. 776 | LD A,(BLKMASK) ;get block mask. 777 | LD C,A 778 | LD A,(SAVNREC) ;get next sector to access. 779 | AND C ;extract the relative position within physical block. 780 | OR L ;and add it too logical sector. 781 | LD L,A 782 | LD (BLKNMBR),HL ;and store. 783 | RET 784 | ; 785 | ; Set (HL) to point to extent byte in fcb. 786 | ; 787 | SETEXT: LD HL,(PARAMS) 788 | LD DE,12 ;it is the twelth byte. 789 | ADD HL,DE 790 | RET 791 | ; 792 | ; Set (HL) to point to record count byte in fcb and (DE) to 793 | ; next record number byte. 794 | ; 795 | SETHLDE:LD HL,(PARAMS) 796 | LD DE,15 ;record count byte (#15). 797 | ADD HL,DE 798 | EX DE,HL 799 | LD HL,17 ;next record number (#32). 800 | ADD HL,DE 801 | RET 802 | ; 803 | ; Save current file data from fcb. 804 | ; 805 | STRDATA:CALL SETHLDE 806 | LD A,(HL) ;get and store record count byte. 807 | LD (SAVNREC),A 808 | EX DE,HL 809 | LD A,(HL) ;get and store next record number byte. 810 | LD (SAVNXT),A 811 | CALL SETEXT ;point to extent byte. 812 | LD A,(EXTMASK) ;get extent mask. 813 | AND (HL) 814 | LD (SAVEXT),A ;and save extent here. 815 | RET 816 | ; 817 | ; Set the next record to access. If (MODE) is set to 2, then 818 | ; the last record byte (SAVNREC) has the correct number to access. 819 | ; For sequential access, (MODE) will be equal to 1. 820 | ; 821 | SETNREC:CALL SETHLDE 822 | LD A,(MODE) ;get sequential flag (=1). 823 | CP 2 ;a 2 indicates that no adder is needed. 824 | JP NZ,STNREC1 825 | XOR A ;clear adder (random access?). 826 | STNREC1:LD C,A 827 | LD A,(SAVNREC) ;get last record number. 828 | ADD A,C ;increment record count. 829 | LD (HL),A ;and set fcb's next record byte. 830 | EX DE,HL 831 | LD A,(SAVNXT) ;get next record byte from storage. 832 | LD (HL),A ;and put this into fcb as number of records used. 833 | RET 834 | ; 835 | ; Shift (HL) right (C) bits. 836 | ; 837 | SHIFTR: INC C 838 | SHIFTR1:DEC C 839 | RET Z 840 | LD A,H 841 | OR A 842 | RRA 843 | LD H,A 844 | LD A,L 845 | RRA 846 | LD L,A 847 | JP SHIFTR1 848 | ; 849 | ; Compute the check-sum for the directory buffer. Return 850 | ; integer sum in (A). 851 | ; 852 | CHECKSUM: LD C,128 ;length of buffer. 853 | LD HL,(DIRBUF) ;get its location. 854 | XOR A ;clear summation byte. 855 | CHKSUM1:ADD A,(HL) ;and compute sum ignoring carries. 856 | INC HL 857 | DEC C 858 | JP NZ,CHKSUM1 859 | RET 860 | ; 861 | ; Shift (HL) left (C) bits. 862 | ; 863 | SHIFTL: INC C 864 | SHIFTL1:DEC C 865 | RET Z 866 | ADD HL,HL ;shift left 1 bit. 867 | JP SHIFTL1 868 | ; 869 | ; Routine to set a bit in a 16 bit value contained in (BC). 870 | ; The bit set depends on the current drive selection. 871 | ; 872 | SETBIT: PUSH BC ;save 16 bit word. 873 | LD A,(ACTIVE) ;get active drive. 874 | LD C,A 875 | LD HL,1 876 | CALL SHIFTL ;shift bit 0 into place. 877 | POP BC ;now 'or' this with the original word. 878 | LD A,C 879 | OR L 880 | LD L,A ;low byte done, do high byte. 881 | LD A,B 882 | OR H 883 | LD H,A 884 | RET 885 | ; 886 | ; Extract the write protect status bit for the current drive. 887 | ; The result is returned in (A), bit 0. 888 | ; 889 | GETWPRT:LD HL,(WRTPRT) ;get status bytes. 890 | LD A,(ACTIVE) ;which drive is current? 891 | LD C,A 892 | CALL SHIFTR ;shift status such that bit 0 is the 893 | LD A,L ;one of interest for this drive. 894 | AND 01H ;and isolate it. 895 | RET 896 | ; 897 | ; Function to write protect the current disk. 898 | ; 899 | WRTPRTD:LD HL,WRTPRT ;point to status word. 900 | LD C,(HL) ;set (BC) equal to the status. 901 | INC HL 902 | LD B,(HL) 903 | CALL SETBIT ;and set this bit according to current drive. 904 | LD (WRTPRT),HL ;then save. 905 | LD HL,(DIRSIZE) ;now save directory size limit. 906 | INC HL ;remember the last one. 907 | EX DE,HL 908 | LD HL,(SCRATCH1) ;and store it here. 909 | LD (HL),E ;put low byte. 910 | INC HL 911 | LD (HL),D ;then high byte. 912 | RET 913 | ; 914 | ; Check for a read only file. 915 | ; 916 | CHKROFL:CALL FCB2HL ;set (HL) to file entry in directory buffer. 917 | CKROF1: LD DE,9 ;look at bit 7 of the ninth byte. 918 | ADD HL,DE 919 | LD A,(HL) 920 | RLA 921 | RET NC ;return if ok. 922 | LD HL,ROFILE ;else, print error message and terminate. 923 | JP JUMPHL 924 | ; 925 | ; Check the write protect status of the active disk. 926 | ; 927 | CHKWPRT:CALL GETWPRT 928 | RET Z ;return if ok. 929 | LD HL,RODISK ;else print message and terminate. 930 | JP JUMPHL 931 | ; 932 | ; Routine to set (HL) pointing to the proper entry in the 933 | ; directory buffer. 934 | ; 935 | FCB2HL: LD HL,(DIRBUF) ;get address of buffer. 936 | LD A,(FCBPOS) ;relative position of file. 937 | ; 938 | ; Routine to add (A) to (HL). 939 | ; 940 | ADDA2HL:ADD A,L 941 | LD L,A 942 | RET NC 943 | INC H ;take care of any carry. 944 | RET 945 | ; 946 | ; Routine to get the 's2' byte from the fcb supplied in 947 | ; the initial parameter specification. 948 | ; 949 | GETS2: LD HL,(PARAMS) ;get address of fcb. 950 | LD DE,14 ;relative position of 's2'. 951 | ADD HL,DE 952 | LD A,(HL) ;extract this byte. 953 | RET 954 | ; 955 | ; Clear the 's2' byte in the fcb. 956 | ; 957 | CLEARS2:CALL GETS2 ;this sets (HL) pointing to it. 958 | LD (HL),0 ;now clear it. 959 | RET 960 | ; 961 | ; Set bit 7 in the 's2' byte of the fcb. 962 | ; 963 | SETS2B7:CALL GETS2 ;get the byte. 964 | OR 80H ;and set bit 7. 965 | LD (HL),A ;then store. 966 | RET 967 | ; 968 | ; Compare (FILEPOS) with (SCRATCH1) and set flags based on 969 | ; the difference. This checks to see if there are more file 970 | ; names in the directory. We are at (FILEPOS) and there are 971 | ; (SCRATCH1) of them to check. 972 | ; 973 | MOREFLS:LD HL,(FILEPOS) ;we are here. 974 | EX DE,HL 975 | LD HL,(SCRATCH1) ;and don't go past here. 976 | LD A,E ;compute difference but don't keep. 977 | SUB (HL) 978 | INC HL 979 | LD A,D 980 | SBC A,(HL) ;set carry if no more names. 981 | RET 982 | ; 983 | ; Call this routine to prevent (SCRATCH1) from being greater 984 | ; than (FILEPOS). 985 | ; 986 | CHKNMBR:CALL MOREFLS ;SCRATCH1 too big? 987 | RET C 988 | INC DE ;yes, reset it to (FILEPOS). 989 | LD (HL),D 990 | DEC HL 991 | LD (HL),E 992 | RET 993 | ; 994 | ; Compute (HL)=(DE)-(HL) 995 | ; 996 | SUBHL: LD A,E ;compute difference. 997 | SUB L 998 | LD L,A ;store low byte. 999 | LD A,D 1000 | SBC A,H 1001 | LD H,A ;and then high byte. 1002 | RET 1003 | ; 1004 | ; Set the directory checksum byte. 1005 | ; 1006 | SETDIR: LD C,0FFH 1007 | ; 1008 | ; Routine to set or compare the directory checksum byte. If 1009 | ; (C)=0ffh, then this will set the checksum byte. Else the byte 1010 | ; will be checked. If the check fails (the disk has been changed), 1011 | ; then this disk will be write protected. 1012 | ; 1013 | CHECKDIR: LD HL,(CKSUMTBL) 1014 | EX DE,HL 1015 | LD HL,(ALLOC1) 1016 | CALL SUBHL 1017 | RET NC ;ok if (CKSUMTBL) > (ALLOC1), so return. 1018 | PUSH BC 1019 | CALL CHECKSUM ;else compute checksum. 1020 | LD HL,(CHKVECT) ;get address of checksum table. 1021 | EX DE,HL 1022 | LD HL,(CKSUMTBL) 1023 | ADD HL,DE ;set (HL) to point to byte for this drive. 1024 | POP BC 1025 | INC C ;set or check ? 1026 | JP Z,CHKDIR1 1027 | CP (HL) ;check them. 1028 | RET Z ;return if they are the same. 1029 | CALL MOREFLS ;not the same, do we care? 1030 | RET NC 1031 | CALL WRTPRTD ;yes, mark this as write protected. 1032 | RET 1033 | CHKDIR1:LD (HL),A ;just set the byte. 1034 | RET 1035 | ; 1036 | ; Do a write to the directory of the current disk. 1037 | ; 1038 | DIRWRITE: CALL SETDIR ;set checksum byte. 1039 | CALL DIRDMA ;set directory dma address. 1040 | LD C,1 ;tell the bios to actually write. 1041 | CALL DOWRITE ;then do the write. 1042 | JP DEFDMA 1043 | ; 1044 | ; Read from the directory. 1045 | ; 1046 | DIRREAD:CALL DIRDMA ;set the directory dma address. 1047 | CALL DOREAD ;and read it. 1048 | ; 1049 | ; Routine to set the dma address to the users choice. 1050 | ; 1051 | DEFDMA: LD HL,USERDMA ;reset the default dma address and return. 1052 | JP DIRDMA1 1053 | ; 1054 | ; Routine to set the dma address for directory work. 1055 | ; 1056 | DIRDMA: LD HL,DIRBUF 1057 | ; 1058 | ; Set the dma address. On entry, (HL) points to 1059 | ; word containing the desired dma address. 1060 | ; 1061 | DIRDMA1:LD C,(HL) 1062 | INC HL 1063 | LD B,(HL) ;setup (BC) and go to the bios to set it. 1064 | JP SETDMA 1065 | ; 1066 | ; Move the directory buffer into user's dma space. 1067 | ; 1068 | MOVEDIR:LD HL,(DIRBUF) ;buffer is located here, and 1069 | EX DE,HL 1070 | LD HL,(USERDMA) ; put it here. 1071 | LD C,128 ;this is its length. 1072 | JP DE2HL ;move it now and return. 1073 | ; 1074 | ; Check (FILEPOS) and set the zero flag if it equals 0ffffh. 1075 | ; 1076 | CKFILPOS: LD HL,FILEPOS 1077 | LD A,(HL) 1078 | INC HL 1079 | CP (HL) ;are both bytes the same? 1080 | RET NZ 1081 | INC A ;yes, but are they each 0ffh? 1082 | RET 1083 | ; 1084 | ; Set location (FILEPOS) to 0ffffh. 1085 | ; 1086 | STFILPOS: LD HL,0FFFFH 1087 | LD (FILEPOS),HL 1088 | RET 1089 | ; 1090 | ; Move on to the next file position within the current 1091 | ; directory buffer. If no more exist, set pointer to 0ffffh 1092 | ; and the calling routine will check for this. Enter with (C) 1093 | ; equal to 0ffh to cause the checksum byte to be set, else we 1094 | ; will check this disk and set write protect if checksums are 1095 | ; not the same (applies only if another directory sector must 1096 | ; be read). 1097 | ; 1098 | NXENTRY:LD HL,(DIRSIZE) ;get directory entry size limit. 1099 | EX DE,HL 1100 | LD HL,(FILEPOS) ;get current count. 1101 | INC HL ;go on to the next one. 1102 | LD (FILEPOS),HL 1103 | CALL SUBHL ;(HL)=(DIRSIZE)-(FILEPOS) 1104 | JP NC,NXENT1 ;is there more room left? 1105 | JP STFILPOS ;no. Set this flag and return. 1106 | NXENT1: LD A,(FILEPOS) ;get file position within directory. 1107 | AND 03H ;only look within this sector (only 4 entries fit). 1108 | LD B,5 ;convert to relative position (32 bytes each). 1109 | NXENT2: ADD A,A ;note that this is not efficient code. 1110 | DEC B ;5 'ADD A's would be better. 1111 | JP NZ,NXENT2 1112 | LD (FCBPOS),A ;save it as position of fcb. 1113 | OR A 1114 | RET NZ ;return if we are within buffer. 1115 | PUSH BC 1116 | CALL TRKSEC ;we need the next directory sector. 1117 | CALL DIRREAD 1118 | POP BC 1119 | JP CHECKDIR 1120 | ; 1121 | ; Routine to to get a bit from the disk space allocation 1122 | ; map. It is returned in (A), bit position 0. On entry to here, 1123 | ; set (BC) to the block number on the disk to check. 1124 | ; On return, (D) will contain the original bit position for 1125 | ; this block number and (HL) will point to the address for it. 1126 | ; 1127 | CKBITMAP: LD A,C ;determine bit number of interest. 1128 | AND 07H ;compute (D)=(E)=(C and 7)+1. 1129 | INC A 1130 | LD E,A ;save particular bit number. 1131 | LD D,A 1132 | ; 1133 | ; compute (BC)=(BC)/8. 1134 | ; 1135 | LD A,C 1136 | RRCA ;now shift right 3 bits. 1137 | RRCA 1138 | RRCA 1139 | AND 1FH ;and clear bits 7,6,5. 1140 | LD C,A 1141 | LD A,B 1142 | ADD A,A ;now shift (B) into bits 7,6,5. 1143 | ADD A,A 1144 | ADD A,A 1145 | ADD A,A 1146 | ADD A,A 1147 | OR C ;and add in (C). 1148 | LD C,A ;ok, (C) ha been completed. 1149 | LD A,B ;is there a better way of doing this? 1150 | RRCA 1151 | RRCA 1152 | RRCA 1153 | AND 1FH 1154 | LD B,A ;and now (B) is completed. 1155 | ; 1156 | ; use this as an offset into the disk space allocation 1157 | ; table. 1158 | ; 1159 | LD HL,(ALOCVECT) 1160 | ADD HL,BC 1161 | LD A,(HL) ;now get correct byte. 1162 | CKBMAP1:RLCA ;get correct bit into position 0. 1163 | DEC E 1164 | JP NZ,CKBMAP1 1165 | RET 1166 | ; 1167 | ; Set or clear the bit map such that block number (BC) will be marked 1168 | ; as used. On entry, if (E)=0 then this bit will be cleared, if it equals 1169 | ; 1 then it will be set (don't use anyother values). 1170 | ; 1171 | STBITMAP: PUSH DE 1172 | CALL CKBITMAP ;get the byte of interest. 1173 | AND 0FEH ;clear the affected bit. 1174 | POP BC 1175 | OR C ;and now set it acording to (C). 1176 | ; 1177 | ; entry to restore the original bit position and then store 1178 | ; in table. (A) contains the value, (D) contains the bit 1179 | ; position (1-8), and (HL) points to the address within the 1180 | ; space allocation table for this byte. 1181 | ; 1182 | STBMAP1:RRCA ;restore original bit position. 1183 | DEC D 1184 | JP NZ,STBMAP1 1185 | LD (HL),A ;and stor byte in table. 1186 | RET 1187 | ; 1188 | ; Set/clear space used bits in allocation map for this file. 1189 | ; On entry, (C)=1 to set the map and (C)=0 to clear it. 1190 | ; 1191 | SETFILE:CALL FCB2HL ;get address of fcb 1192 | LD DE,16 1193 | ADD HL,DE ;get to block number bytes. 1194 | PUSH BC 1195 | LD C,17 ;check all 17 bytes (max) of table. 1196 | SETFL1: POP DE 1197 | DEC C ;done all bytes yet? 1198 | RET Z 1199 | PUSH DE 1200 | LD A,(BIGDISK) ;check disk size for 16 bit block numbers. 1201 | OR A 1202 | JP Z,SETFL2 1203 | PUSH BC ;only 8 bit numbers. set (BC) to this one. 1204 | PUSH HL 1205 | LD C,(HL) ;get low byte from table, always 1206 | LD B,0 ;set high byte to zero. 1207 | JP SETFL3 1208 | SETFL2: DEC C ;for 16 bit block numbers, adjust counter. 1209 | PUSH BC 1210 | LD C,(HL) ;now get both the low and high bytes. 1211 | INC HL 1212 | LD B,(HL) 1213 | PUSH HL 1214 | SETFL3: LD A,C ;block used? 1215 | OR B 1216 | JP Z,SETFL4 1217 | LD HL,(DSKSIZE) ;is this block number within the 1218 | LD A,L ;space on the disk? 1219 | SUB C 1220 | LD A,H 1221 | SBC A,B 1222 | CALL NC,STBITMAP ;yes, set the proper bit. 1223 | SETFL4: POP HL ;point to next block number in fcb. 1224 | INC HL 1225 | POP BC 1226 | JP SETFL1 1227 | ; 1228 | ; Construct the space used allocation bit map for the active 1229 | ; drive. If a file name starts with '$' and it is under the 1230 | ; current user number, then (STATUS) is set to minus 1. Otherwise 1231 | ; it is not set at all. 1232 | ; 1233 | BITMAP: LD HL,(DSKSIZE) ;compute size of allocation table. 1234 | LD C,3 1235 | CALL SHIFTR ;(HL)=(HL)/8. 1236 | INC HL ;at lease 1 byte. 1237 | LD B,H 1238 | LD C,L ;set (BC) to the allocation table length. 1239 | ; 1240 | ; Initialize the bitmap for this drive. Right now, the first 1241 | ; two bytes are specified by the disk parameter block. However 1242 | ; a patch could be entered here if it were necessary to setup 1243 | ; this table in a special mannor. For example, the bios could 1244 | ; determine locations of 'bad blocks' and set them as already 1245 | ; 'used' in the map. 1246 | ; 1247 | LD HL,(ALOCVECT) ;now zero out the table now. 1248 | BITMAP1:LD (HL),0 1249 | INC HL 1250 | DEC BC 1251 | LD A,B 1252 | OR C 1253 | JP NZ,BITMAP1 1254 | LD HL,(ALLOC0) ;get initial space used by directory. 1255 | EX DE,HL 1256 | LD HL,(ALOCVECT) ;and put this into map. 1257 | LD (HL),E 1258 | INC HL 1259 | LD (HL),D 1260 | ; 1261 | ; End of initialization portion. 1262 | ; 1263 | CALL HOMEDRV ;now home the drive. 1264 | LD HL,(SCRATCH1) 1265 | LD (HL),3 ;force next directory request to read 1266 | INC HL ;in a sector. 1267 | LD (HL),0 1268 | CALL STFILPOS ;clear initial file position also. 1269 | BITMAP2:LD C,0FFH ;read next file name in directory 1270 | CALL NXENTRY ;and set checksum byte. 1271 | CALL CKFILPOS ;is there another file? 1272 | RET Z 1273 | CALL FCB2HL ;yes, get its address. 1274 | LD A,0E5H 1275 | CP (HL) ;empty file entry? 1276 | JP Z,BITMAP2 1277 | LD A,(USERNO) ;no, correct user number? 1278 | CP (HL) 1279 | JP NZ,BITMAP3 1280 | INC HL 1281 | LD A,(HL) ;yes, does name start with a '$'? 1282 | SUB '$' 1283 | JP NZ,BITMAP3 1284 | DEC A ;yes, set atatus to minus one. 1285 | LD (STATUS),A 1286 | BITMAP3:LD C,1 ;now set this file's space as used in bit map. 1287 | CALL SETFILE 1288 | CALL CHKNMBR ;keep (SCRATCH1) in bounds. 1289 | JP BITMAP2 1290 | ; 1291 | ; Set the status (STATUS) and return. 1292 | ; 1293 | STSTATUS: LD A,(FNDSTAT) 1294 | JP SETSTAT 1295 | ; 1296 | ; Check extents in (A) and (C). Set the zero flag if they 1297 | ; are the same. The number of 16k chunks of disk space that 1298 | ; the directory extent covers is expressad is (EXTMASK+1). 1299 | ; No registers are modified. 1300 | ; 1301 | SAMEXT: PUSH BC 1302 | PUSH AF 1303 | LD A,(EXTMASK) ;get extent mask and use it to 1304 | CPL ;to compare both extent numbers. 1305 | LD B,A ;save resulting mask here. 1306 | LD A,C ;mask first extent and save in (C). 1307 | AND B 1308 | LD C,A 1309 | POP AF ;now mask second extent and compare 1310 | AND B ;with the first one. 1311 | SUB C 1312 | AND 1FH ;(* only check buts 0-4 *) 1313 | POP BC ;the zero flag is set if they are the same. 1314 | RET ;restore (BC) and return. 1315 | ; 1316 | ; Search for the first occurence of a file name. On entry, 1317 | ; register (C) should contain the number of bytes of the fcb 1318 | ; that must match. 1319 | ; 1320 | FINDFST:LD A,0FFH 1321 | LD (FNDSTAT),A 1322 | LD HL,COUNTER ;save character count. 1323 | LD (HL),C 1324 | LD HL,(PARAMS) ;get filename to match. 1325 | LD (SAVEFCB),HL ;and save. 1326 | CALL STFILPOS ;clear initial file position (set to 0ffffh). 1327 | CALL HOMEDRV ;home the drive. 1328 | ; 1329 | ; Entry to locate the next occurence of a filename within the 1330 | ; directory. The disk is not expected to have been changed. If 1331 | ; it was, then it will be write protected. 1332 | ; 1333 | FINDNXT:LD C,0 ;write protect the disk if changed. 1334 | CALL NXENTRY ;get next filename entry in directory. 1335 | CALL CKFILPOS ;is file position = 0ffffh? 1336 | JP Z,FNDNXT6 ;yes, exit now then. 1337 | LD HL,(SAVEFCB) ;set (DE) pointing to filename to match. 1338 | EX DE,HL 1339 | LD A,(DE) 1340 | CP 0E5H ;empty directory entry? 1341 | JP Z,FNDNXT1 ;(* are we trying to reserect erased entries? *) 1342 | PUSH DE 1343 | CALL MOREFLS ;more files in directory? 1344 | POP DE 1345 | JP NC,FNDNXT6 ;no more. Exit now. 1346 | FNDNXT1:CALL FCB2HL ;get address of this fcb in directory. 1347 | LD A,(COUNTER) ;get number of bytes (characters) to check. 1348 | LD C,A 1349 | LD B,0 ;initialize byte position counter. 1350 | FNDNXT2:LD A,C ;are we done with the compare? 1351 | OR A 1352 | JP Z,FNDNXT5 1353 | LD A,(DE) ;no, check next byte. 1354 | CP '?' ;don't care about this character? 1355 | JP Z,FNDNXT4 1356 | LD A,B ;get bytes position in fcb. 1357 | CP 13 ;don't care about the thirteenth byte either. 1358 | JP Z,FNDNXT4 1359 | CP 12 ;extent byte? 1360 | LD A,(DE) 1361 | JP Z,FNDNXT3 1362 | SUB (HL) ;otherwise compare characters. 1363 | AND 7FH 1364 | JP NZ,FINDNXT ;not the same, check next entry. 1365 | JP FNDNXT4 ;so far so good, keep checking. 1366 | FNDNXT3:PUSH BC ;check the extent byte here. 1367 | LD C,(HL) 1368 | CALL SAMEXT 1369 | POP BC 1370 | JP NZ,FINDNXT ;not the same, look some more. 1371 | ; 1372 | ; So far the names compare. Bump pointers to the next byte 1373 | ; and continue until all (C) characters have been checked. 1374 | ; 1375 | FNDNXT4:INC DE ;bump pointers. 1376 | INC HL 1377 | INC B 1378 | DEC C ;adjust character counter. 1379 | JP FNDNXT2 1380 | FNDNXT5:LD A,(FILEPOS) ;return the position of this entry. 1381 | AND 03H 1382 | LD (STATUS),A 1383 | LD HL,FNDSTAT 1384 | LD A,(HL) 1385 | RLA 1386 | RET NC 1387 | XOR A 1388 | LD (HL),A 1389 | RET 1390 | ; 1391 | ; Filename was not found. Set appropriate status. 1392 | ; 1393 | FNDNXT6:CALL STFILPOS ;set (FILEPOS) to 0ffffh. 1394 | LD A,0FFH ;say not located. 1395 | JP SETSTAT 1396 | ; 1397 | ; Erase files from the directory. Only the first byte of the 1398 | ; fcb will be affected. It is set to (E5). 1399 | ; 1400 | ERAFILE:CALL CHKWPRT ;is disk write protected? 1401 | LD C,12 ;only compare file names. 1402 | CALL FINDFST ;get first file name. 1403 | ERAFIL1:CALL CKFILPOS ;any found? 1404 | RET Z ;nope, we must be done. 1405 | CALL CHKROFL ;is file read only? 1406 | CALL FCB2HL ;nope, get address of fcb and 1407 | LD (HL),0E5H ;set first byte to 'empty'. 1408 | LD C,0 ;clear the space from the bit map. 1409 | CALL SETFILE 1410 | CALL DIRWRITE ;now write the directory sector back out. 1411 | CALL FINDNXT ;find the next file name. 1412 | JP ERAFIL1 ;and repeat process. 1413 | ; 1414 | ; Look through the space allocation map (bit map) for the 1415 | ; next available block. Start searching at block number (BC-1). 1416 | ; The search procedure is to look for an empty block that is 1417 | ; before the starting block. If not empty, look at a later 1418 | ; block number. In this way, we return the closest empty block 1419 | ; on either side of the 'target' block number. This will speed 1420 | ; access on random devices. For serial devices, this should be 1421 | ; changed to look in the forward direction first and then start 1422 | ; at the front and search some more. 1423 | ; 1424 | ; On return, (DE)= block number that is empty and (HL) =0 1425 | ; if no empry block was found. 1426 | ; 1427 | FNDSPACE: LD D,B ;set (DE) as the block that is checked. 1428 | LD E,C 1429 | ; 1430 | ; Look before target block. Registers (BC) are used as the lower 1431 | ; pointer and (DE) as the upper pointer. 1432 | ; 1433 | FNDSPA1:LD A,C ;is block 0 specified? 1434 | OR B 1435 | JP Z,FNDSPA2 1436 | DEC BC ;nope, check previous block. 1437 | PUSH DE 1438 | PUSH BC 1439 | CALL CKBITMAP 1440 | RRA ;is this block empty? 1441 | JP NC,FNDSPA3 ;yes. use this. 1442 | ; 1443 | ; Note that the above logic gets the first block that it finds 1444 | ; that is empty. Thus a file could be written 'backward' making 1445 | ; it very slow to access. This could be changed to look for the 1446 | ; first empty block and then continue until the start of this 1447 | ; empty space is located and then used that starting block. 1448 | ; This should help speed up access to some files especially on 1449 | ; a well used disk with lots of fairly small 'holes'. 1450 | ; 1451 | POP BC ;nope, check some more. 1452 | POP DE 1453 | ; 1454 | ; Now look after target block. 1455 | ; 1456 | FNDSPA2:LD HL,(DSKSIZE) ;is block (DE) within disk limits? 1457 | LD A,E 1458 | SUB L 1459 | LD A,D 1460 | SBC A,H 1461 | JP NC,FNDSPA4 1462 | INC DE ;yes, move on to next one. 1463 | PUSH BC 1464 | PUSH DE 1465 | LD B,D 1466 | LD C,E 1467 | CALL CKBITMAP ;check it. 1468 | RRA ;empty? 1469 | JP NC,FNDSPA3 1470 | POP DE ;nope, continue searching. 1471 | POP BC 1472 | JP FNDSPA1 1473 | ; 1474 | ; Empty block found. Set it as used and return with (HL) 1475 | ; pointing to it (true?). 1476 | ; 1477 | FNDSPA3:RLA ;reset byte. 1478 | INC A ;and set bit 0. 1479 | CALL STBMAP1 ;update bit map. 1480 | POP HL ;set return registers. 1481 | POP DE 1482 | RET 1483 | ; 1484 | ; Free block was not found. If (BC) is not zero, then we have 1485 | ; not checked all of the disk space. 1486 | ; 1487 | FNDSPA4:LD A,C 1488 | OR B 1489 | JP NZ,FNDSPA1 1490 | LD HL,0 ;set 'not found' status. 1491 | RET 1492 | ; 1493 | ; Move a complete fcb entry into the directory and write it. 1494 | ; 1495 | FCBSET: LD C,0 1496 | LD E,32 ;length of each entry. 1497 | ; 1498 | ; Move (E) bytes from the fcb pointed to by (PARAMS) into 1499 | ; fcb in directory starting at relative byte (C). This updated 1500 | ; directory buffer is then written to the disk. 1501 | ; 1502 | UPDATE: PUSH DE 1503 | LD B,0 ;set (BC) to relative byte position. 1504 | LD HL,(PARAMS) ;get address of fcb. 1505 | ADD HL,BC ;compute starting byte. 1506 | EX DE,HL 1507 | CALL FCB2HL ;get address of fcb to update in directory. 1508 | POP BC ;set (C) to number of bytes to change. 1509 | CALL DE2HL 1510 | UPDATE1:CALL TRKSEC ;determine the track and sector affected. 1511 | JP DIRWRITE ;then write this sector out. 1512 | ; 1513 | ; Routine to change the name of all files on the disk with a 1514 | ; specified name. The fcb contains the current name as the 1515 | ; first 12 characters and the new name 16 bytes into the fcb. 1516 | ; 1517 | CHGNAMES: CALL CHKWPRT ;check for a write protected disk. 1518 | LD C,12 ;match first 12 bytes of fcb only. 1519 | CALL FINDFST ;get first name. 1520 | LD HL,(PARAMS) ;get address of fcb. 1521 | LD A,(HL) ;get user number. 1522 | LD DE,16 ;move over to desired name. 1523 | ADD HL,DE 1524 | LD (HL),A ;keep same user number. 1525 | CHGNAM1:CALL CKFILPOS ;any matching file found? 1526 | RET Z ;no, we must be done. 1527 | CALL CHKROFL ;check for read only file. 1528 | LD C,16 ;start 16 bytes into fcb. 1529 | LD E,12 ;and update the first 12 bytes of directory. 1530 | CALL UPDATE 1531 | CALL FINDNXT ;get te next file name. 1532 | JP CHGNAM1 ;and continue. 1533 | ; 1534 | ; Update a files attributes. The procedure is to search for 1535 | ; every file with the same name as shown in fcb (ignoring bit 7) 1536 | ; and then to update it (which includes bit 7). No other changes 1537 | ; are made. 1538 | ; 1539 | SAVEATTR: LD C,12 ;match first 12 bytes. 1540 | CALL FINDFST ;look for first filename. 1541 | SAVATR1:CALL CKFILPOS ;was one found? 1542 | RET Z ;nope, we must be done. 1543 | LD C,0 ;yes, update the first 12 bytes now. 1544 | LD E,12 1545 | CALL UPDATE ;update filename and write directory. 1546 | CALL FINDNXT ;and get the next file. 1547 | JP SAVATR1 ;then continue until done. 1548 | ; 1549 | ; Open a file (name specified in fcb). 1550 | ; 1551 | OPENIT: LD C,15 ;compare the first 15 bytes. 1552 | CALL FINDFST ;get the first one in directory. 1553 | CALL CKFILPOS ;any at all? 1554 | RET Z 1555 | OPENIT1:CALL SETEXT ;point to extent byte within users fcb. 1556 | LD A,(HL) ;and get it. 1557 | PUSH AF ;save it and address. 1558 | PUSH HL 1559 | CALL FCB2HL ;point to fcb in directory. 1560 | EX DE,HL 1561 | LD HL,(PARAMS) ;this is the users copy. 1562 | LD C,32 ;move it into users space. 1563 | PUSH DE 1564 | CALL DE2HL 1565 | CALL SETS2B7 ;set bit 7 in 's2' byte (unmodified). 1566 | POP DE ;now get the extent byte from this fcb. 1567 | LD HL,12 1568 | ADD HL,DE 1569 | LD C,(HL) ;into (C). 1570 | LD HL,15 ;now get the record count byte into (B). 1571 | ADD HL,DE 1572 | LD B,(HL) 1573 | POP HL ;keep the same extent as the user had originally. 1574 | POP AF 1575 | LD (HL),A 1576 | LD A,C ;is it the same as in the directory fcb? 1577 | CP (HL) 1578 | LD A,B ;if yes, then use the same record count. 1579 | JP Z,OPENIT2 1580 | LD A,0 ;if the user specified an extent greater than 1581 | JP C,OPENIT2 ;the one in the directory, then set record count to 0. 1582 | LD A,128 ;otherwise set to maximum. 1583 | OPENIT2:LD HL,(PARAMS) ;set record count in users fcb to (A). 1584 | LD DE,15 1585 | ADD HL,DE ;compute relative position. 1586 | LD (HL),A ;and set the record count. 1587 | RET 1588 | ; 1589 | ; Move two bytes from (DE) to (HL) if (and only if) (HL) 1590 | ; point to a zero value (16 bit). 1591 | ; Return with zero flag set it (DE) was moved. Registers (DE) 1592 | ; and (HL) are not changed. However (A) is. 1593 | ; 1594 | MOVEWORD: LD A,(HL) ;check for a zero word. 1595 | INC HL 1596 | OR (HL) ;both bytes zero? 1597 | DEC HL 1598 | RET NZ ;nope, just return. 1599 | LD A,(DE) ;yes, move two bytes from (DE) into 1600 | LD (HL),A ;this zero space. 1601 | INC DE 1602 | INC HL 1603 | LD A,(DE) 1604 | LD (HL),A 1605 | DEC DE ;don't disturb these registers. 1606 | DEC HL 1607 | RET 1608 | ; 1609 | ; Get here to close a file specified by (fcb). 1610 | ; 1611 | CLOSEIT:XOR A ;clear status and file position bytes. 1612 | LD (STATUS),A 1613 | LD (FILEPOS),A 1614 | LD (FILEPOS+1),A 1615 | CALL GETWPRT ;get write protect bit for this drive. 1616 | RET NZ ;just return if it is set. 1617 | CALL GETS2 ;else get the 's2' byte. 1618 | AND 80H ;and look at bit 7 (file unmodified?). 1619 | RET NZ ;just return if set. 1620 | LD C,15 ;else look up this file in directory. 1621 | CALL FINDFST 1622 | CALL CKFILPOS ;was it found? 1623 | RET Z ;just return if not. 1624 | LD BC,16 ;set (HL) pointing to records used section. 1625 | CALL FCB2HL 1626 | ADD HL,BC 1627 | EX DE,HL 1628 | LD HL,(PARAMS) ;do the same for users specified fcb. 1629 | ADD HL,BC 1630 | LD C,16 ;this many bytes are present in this extent. 1631 | CLOSEIT1: LD A,(BIGDISK) ;8 or 16 bit record numbers? 1632 | OR A 1633 | JP Z,CLOSEIT4 1634 | LD A,(HL) ;just 8 bit. Get one from users fcb. 1635 | OR A 1636 | LD A,(DE) ;now get one from directory fcb. 1637 | JP NZ,CLOSEIT2 1638 | LD (HL),A ;users byte was zero. Update from directory. 1639 | CLOSEIT2: OR A 1640 | JP NZ,CLOSEIT3 1641 | LD A,(HL) ;directories byte was zero, update from users fcb. 1642 | LD (DE),A 1643 | CLOSEIT3: CP (HL) ;if neither one of these bytes were zero, 1644 | JP NZ,CLOSEIT7 ;then close error if they are not the same. 1645 | JP CLOSEIT5 ;ok so far, get to next byte in fcbs. 1646 | CLOSEIT4: CALL MOVEWORD ;update users fcb if it is zero. 1647 | EX DE,HL 1648 | CALL MOVEWORD ;update directories fcb if it is zero. 1649 | EX DE,HL 1650 | LD A,(DE) ;if these two values are no different, 1651 | CP (HL) ;then a close error occured. 1652 | JP NZ,CLOSEIT7 1653 | INC DE ;check second byte. 1654 | INC HL 1655 | LD A,(DE) 1656 | CP (HL) 1657 | JP NZ,CLOSEIT7 1658 | DEC C ;remember 16 bit values. 1659 | CLOSEIT5: INC DE ;bump to next item in table. 1660 | INC HL 1661 | DEC C ;there are 16 entries only. 1662 | JP NZ,CLOSEIT1 ;continue if more to do. 1663 | LD BC,0FFECH ;backup 20 places (extent byte). 1664 | ADD HL,BC 1665 | EX DE,HL 1666 | ADD HL,BC 1667 | LD A,(DE) 1668 | CP (HL) ;directory's extent already greater than the 1669 | JP C,CLOSEIT6 ;users extent? 1670 | LD (HL),A ;no, update directory extent. 1671 | LD BC,3 ;and update the record count byte in 1672 | ADD HL,BC ;directories fcb. 1673 | EX DE,HL 1674 | ADD HL,BC 1675 | LD A,(HL) ;get from user. 1676 | LD (DE),A ;and put in directory. 1677 | CLOSEIT6: LD A,0FFH ;set 'was open and is now closed' byte. 1678 | LD (CLOSEFLG),A 1679 | JP UPDATE1 ;update the directory now. 1680 | CLOSEIT7: LD HL,STATUS ;set return status and then return. 1681 | DEC (HL) 1682 | RET 1683 | ; 1684 | ; Routine to get the next empty space in the directory. It 1685 | ; will then be cleared for use. 1686 | ; 1687 | GETEMPTY: CALL CHKWPRT ;make sure disk is not write protected. 1688 | LD HL,(PARAMS) ;save current parameters (fcb). 1689 | PUSH HL 1690 | LD HL,EMPTYFCB ;use special one for empty space. 1691 | LD (PARAMS),HL 1692 | LD C,1 ;search for first empty spot in directory. 1693 | CALL FINDFST ;(* only check first byte *) 1694 | CALL CKFILPOS ;none? 1695 | POP HL 1696 | LD (PARAMS),HL ;restore original fcb address. 1697 | RET Z ;return if no more space. 1698 | EX DE,HL 1699 | LD HL,15 ;point to number of records for this file. 1700 | ADD HL,DE 1701 | LD C,17 ;and clear all of this space. 1702 | XOR A 1703 | GETMT1: LD (HL),A 1704 | INC HL 1705 | DEC C 1706 | JP NZ,GETMT1 1707 | LD HL,13 ;clear the 's1' byte also. 1708 | ADD HL,DE 1709 | LD (HL),A 1710 | CALL CHKNMBR ;keep (SCRATCH1) within bounds. 1711 | CALL FCBSET ;write out this fcb entry to directory. 1712 | JP SETS2B7 ;set 's2' byte bit 7 (unmodified at present). 1713 | ; 1714 | ; Routine to close the current extent and open the next one 1715 | ; for reading. 1716 | ; 1717 | GETNEXT:XOR A 1718 | LD (CLOSEFLG),A ;clear close flag. 1719 | CALL CLOSEIT ;close this extent. 1720 | CALL CKFILPOS 1721 | RET Z ;not there??? 1722 | LD HL,(PARAMS) ;get extent byte. 1723 | LD BC,12 1724 | ADD HL,BC 1725 | LD A,(HL) ;and increment it. 1726 | INC A 1727 | AND 1FH ;keep within range 0-31. 1728 | LD (HL),A 1729 | JP Z,GTNEXT1 ;overflow? 1730 | LD B,A ;mask extent byte. 1731 | LD A,(EXTMASK) 1732 | AND B 1733 | LD HL,CLOSEFLG ;check close flag (0ffh is ok). 1734 | AND (HL) 1735 | JP Z,GTNEXT2 ;if zero, we must read in next extent. 1736 | JP GTNEXT3 ;else, it is already in memory. 1737 | GTNEXT1:LD BC,2 ;Point to the 's2' byte. 1738 | ADD HL,BC 1739 | INC (HL) ;and bump it. 1740 | LD A,(HL) ;too many extents? 1741 | AND 0FH 1742 | JP Z,GTNEXT5 ;yes, set error code. 1743 | ; 1744 | ; Get here to open the next extent. 1745 | ; 1746 | GTNEXT2:LD C,15 ;set to check first 15 bytes of fcb. 1747 | CALL FINDFST ;find the first one. 1748 | CALL CKFILPOS ;none available? 1749 | JP NZ,GTNEXT3 1750 | LD A,(RDWRTFLG) ;no extent present. Can we open an empty one? 1751 | INC A ;0ffh means reading (so not possible). 1752 | JP Z,GTNEXT5 ;or an error. 1753 | CALL GETEMPTY ;we are writing, get an empty entry. 1754 | CALL CKFILPOS ;none? 1755 | JP Z,GTNEXT5 ;error if true. 1756 | JP GTNEXT4 ;else we are almost done. 1757 | GTNEXT3:CALL OPENIT1 ;open this extent. 1758 | GTNEXT4:CALL STRDATA ;move in updated data (rec #, extent #, etc.) 1759 | XOR A ;clear status and return. 1760 | JP SETSTAT 1761 | ; 1762 | ; Error in extending the file. Too many extents were needed 1763 | ; or not enough space on the disk. 1764 | ; 1765 | GTNEXT5:CALL IOERR1 ;set error code, clear bit 7 of 's2' 1766 | JP SETS2B7 ;so this is not written on a close. 1767 | ; 1768 | ; Read a sequential file. 1769 | ; 1770 | RDSEQ: LD A,1 ;set sequential access mode. 1771 | LD (MODE),A 1772 | RDSEQ1: LD A,0FFH ;don't allow reading unwritten space. 1773 | LD (RDWRTFLG),A 1774 | CALL STRDATA ;put rec# and ext# into fcb. 1775 | LD A,(SAVNREC) ;get next record to read. 1776 | LD HL,SAVNXT ;get number of records in extent. 1777 | CP (HL) ;within this extent? 1778 | JP C,RDSEQ2 1779 | CP 128 ;no. Is this extent fully used? 1780 | JP NZ,RDSEQ3 ;no. End-of-file. 1781 | CALL GETNEXT ;yes, open the next one. 1782 | XOR A ;reset next record to read. 1783 | LD (SAVNREC),A 1784 | LD A,(STATUS) ;check on open, successful? 1785 | OR A 1786 | JP NZ,RDSEQ3 ;no, error. 1787 | RDSEQ2: CALL COMBLK ;ok. compute block number to read. 1788 | CALL CHKBLK ;check it. Within bounds? 1789 | JP Z,RDSEQ3 ;no, error. 1790 | CALL LOGICAL ;convert (BLKNMBR) to logical sector (128 byte). 1791 | CALL TRKSEC1 ;set the track and sector for this block #. 1792 | CALL DOREAD ;and read it. 1793 | JP SETNREC ;and set the next record to be accessed. 1794 | ; 1795 | ; Read error occured. Set status and return. 1796 | ; 1797 | RDSEQ3: JP IOERR1 1798 | ; 1799 | ; Write the next sequential record. 1800 | ; 1801 | WTSEQ: LD A,1 ;set sequential access mode. 1802 | LD (MODE),A 1803 | WTSEQ1: LD A,0 ;allow an addition empty extent to be opened. 1804 | LD (RDWRTFLG),A 1805 | CALL CHKWPRT ;check write protect status. 1806 | LD HL,(PARAMS) 1807 | CALL CKROF1 ;check for read only file, (HL) already set to fcb. 1808 | CALL STRDATA ;put updated data into fcb. 1809 | LD A,(SAVNREC) ;get record number to write. 1810 | CP 128 ;within range? 1811 | JP NC,IOERR1 ;no, error(?). 1812 | CALL COMBLK ;compute block number. 1813 | CALL CHKBLK ;check number. 1814 | LD C,0 ;is there one to write to? 1815 | JP NZ,WTSEQ6 ;yes, go do it. 1816 | CALL GETBLOCK ;get next block number within fcb to use. 1817 | LD (RELBLOCK),A ;and save. 1818 | LD BC,0 ;start looking for space from the start 1819 | OR A ;if none allocated as yet. 1820 | JP Z,WTSEQ2 1821 | LD C,A ;extract previous block number from fcb 1822 | DEC BC ;so we can be closest to it. 1823 | CALL EXTBLK 1824 | LD B,H 1825 | LD C,L 1826 | WTSEQ2: CALL FNDSPACE ;find the next empty block nearest number (BC). 1827 | LD A,L ;check for a zero number. 1828 | OR H 1829 | JP NZ,WTSEQ3 1830 | LD A,2 ;no more space? 1831 | JP SETSTAT 1832 | WTSEQ3: LD (BLKNMBR),HL ;save block number to access. 1833 | EX DE,HL ;put block number into (DE). 1834 | LD HL,(PARAMS) ;now we must update the fcb for this 1835 | LD BC,16 ;newly allocated block. 1836 | ADD HL,BC 1837 | LD A,(BIGDISK) ;8 or 16 bit block numbers? 1838 | OR A 1839 | LD A,(RELBLOCK) ;(* update this entry *) 1840 | JP Z,WTSEQ4 ;zero means 16 bit ones. 1841 | CALL ADDA2HL ;(HL)=(HL)+(A) 1842 | LD (HL),E ;store new block number. 1843 | JP WTSEQ5 1844 | WTSEQ4: LD C,A ;compute spot in this 16 bit table. 1845 | LD B,0 1846 | ADD HL,BC 1847 | ADD HL,BC 1848 | LD (HL),E ;stuff block number (DE) there. 1849 | INC HL 1850 | LD (HL),D 1851 | WTSEQ5: LD C,2 ;set (C) to indicate writing to un-used disk space. 1852 | WTSEQ6: LD A,(STATUS) ;are we ok so far? 1853 | OR A 1854 | RET NZ 1855 | PUSH BC ;yes, save write flag for bios (register C). 1856 | CALL LOGICAL ;convert (BLKNMBR) over to loical sectors. 1857 | LD A,(MODE) ;get access mode flag (1=sequential, 1858 | DEC A ;0=random, 2=special?). 1859 | DEC A 1860 | JP NZ,WTSEQ9 1861 | ; 1862 | ; Special random i/o from function #40. Maybe for M/PM, but the 1863 | ; current block, if it has not been written to, will be zeroed 1864 | ; out and then written (reason?). 1865 | ; 1866 | POP BC 1867 | PUSH BC 1868 | LD A,C ;get write status flag (2=writing unused space). 1869 | DEC A 1870 | DEC A 1871 | JP NZ,WTSEQ9 1872 | PUSH HL 1873 | LD HL,(DIRBUF) ;zero out the directory buffer. 1874 | LD D,A ;note that (A) is zero here. 1875 | WTSEQ7: LD (HL),A 1876 | INC HL 1877 | INC D ;do 128 bytes. 1878 | JP P,WTSEQ7 1879 | CALL DIRDMA ;tell the bios the dma address for directory access. 1880 | LD HL,(LOGSECT) ;get sector that starts current block. 1881 | LD C,2 ;set 'writing to unused space' flag. 1882 | WTSEQ8: LD (BLKNMBR),HL ;save sector to write. 1883 | PUSH BC 1884 | CALL TRKSEC1 ;determine its track and sector numbers. 1885 | POP BC 1886 | CALL DOWRITE ;now write out 128 bytes of zeros. 1887 | LD HL,(BLKNMBR) ;get sector number. 1888 | LD C,0 ;set normal write flag. 1889 | LD A,(BLKMASK) ;determine if we have written the entire 1890 | LD B,A ;physical block. 1891 | AND L 1892 | CP B 1893 | INC HL ;prepare for the next one. 1894 | JP NZ,WTSEQ8 ;continue until (BLKMASK+1) sectors written. 1895 | POP HL ;reset next sector number. 1896 | LD (BLKNMBR),HL 1897 | CALL DEFDMA ;and reset dma address. 1898 | ; 1899 | ; Normal disk write. Set the desired track and sector then 1900 | ; do the actual write. 1901 | ; 1902 | WTSEQ9: CALL TRKSEC1 ;determine track and sector for this write. 1903 | POP BC ;get write status flag. 1904 | PUSH BC 1905 | CALL DOWRITE ;and write this out. 1906 | POP BC 1907 | LD A,(SAVNREC) ;get number of records in file. 1908 | LD HL,SAVNXT ;get last record written. 1909 | CP (HL) 1910 | JP C,WTSEQ10 1911 | LD (HL),A ;we have to update record count. 1912 | INC (HL) 1913 | LD C,2 1914 | ; 1915 | ;* This area has been patched to correct disk update problem 1916 | ;* when using blocking and de-blocking in the BIOS. 1917 | ; 1918 | WTSEQ10:NOP ;was 'dcr c' 1919 | NOP ;was 'dcr c' 1920 | LD HL,0 ;was 'jnz wtseq99' 1921 | ; 1922 | ; * End of patch. 1923 | ; 1924 | PUSH AF 1925 | CALL GETS2 ;set 'extent written to' flag. 1926 | AND 7FH ;(* clear bit 7 *) 1927 | LD (HL),A 1928 | POP AF ;get record count for this extent. 1929 | WTSEQ99:CP 127 ;is it full? 1930 | JP NZ,WTSEQ12 1931 | LD A,(MODE) ;yes, are we in sequential mode? 1932 | CP 1 1933 | JP NZ,WTSEQ12 1934 | CALL SETNREC ;yes, set next record number. 1935 | CALL GETNEXT ;and get next empty space in directory. 1936 | LD HL,STATUS ;ok? 1937 | LD A,(HL) 1938 | OR A 1939 | JP NZ,WTSEQ11 1940 | DEC A ;yes, set record count to -1. 1941 | LD (SAVNREC),A 1942 | WTSEQ11:LD (HL),0 ;clear status. 1943 | WTSEQ12:JP SETNREC ;set next record to access. 1944 | ; 1945 | ; For random i/o, set the fcb for the desired record number 1946 | ; based on the 'r0,r1,r2' bytes. These bytes in the fcb are 1947 | ; used as follows: 1948 | ; 1949 | ; fcb+35 fcb+34 fcb+33 1950 | ; | 'r-2' | 'r-1' | 'r-0' | 1951 | ; |7 0 | 7 0 | 7 0| 1952 | ; |0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0| 1953 | ; | overflow | | extra | extent | record # | 1954 | ; | ______________| |_extent|__number___|_____________| 1955 | ; also 's2' 1956 | ; 1957 | ; On entry, register (C) contains 0ffh if this is a read 1958 | ; and thus we can not access unwritten disk space. Otherwise, 1959 | ; another extent will be opened (for writing) if required. 1960 | ; 1961 | POSITION: XOR A ;set random i/o flag. 1962 | LD (MODE),A 1963 | ; 1964 | ; Special entry (function #40). M/PM ? 1965 | ; 1966 | POSITN1:PUSH BC ;save read/write flag. 1967 | LD HL,(PARAMS) ;get address of fcb. 1968 | EX DE,HL 1969 | LD HL,33 ;now get byte 'r0'. 1970 | ADD HL,DE 1971 | LD A,(HL) 1972 | AND 7FH ;keep bits 0-6 for the record number to access. 1973 | PUSH AF 1974 | LD A,(HL) ;now get bit 7 of 'r0' and bits 0-3 of 'r1'. 1975 | RLA 1976 | INC HL 1977 | LD A,(HL) 1978 | RLA 1979 | AND 1FH ;and save this in bits 0-4 of (C). 1980 | LD C,A ;this is the extent byte. 1981 | LD A,(HL) ;now get the extra extent byte. 1982 | RRA 1983 | RRA 1984 | RRA 1985 | RRA 1986 | AND 0FH 1987 | LD B,A ;and save it in (B). 1988 | POP AF ;get record number back to (A). 1989 | INC HL ;check overflow byte 'r2'. 1990 | LD L,(HL) 1991 | INC L 1992 | DEC L 1993 | LD L,6 ;prepare for error. 1994 | JP NZ,POSITN5 ;out of disk space error. 1995 | LD HL,32 ;store record number into fcb. 1996 | ADD HL,DE 1997 | LD (HL),A 1998 | LD HL,12 ;and now check the extent byte. 1999 | ADD HL,DE 2000 | LD A,C 2001 | SUB (HL) ;same extent as before? 2002 | JP NZ,POSITN2 2003 | LD HL,14 ;yes, check extra extent byte 's2' also. 2004 | ADD HL,DE 2005 | LD A,B 2006 | SUB (HL) 2007 | AND 7FH 2008 | JP Z,POSITN3 ;same, we are almost done then. 2009 | ; 2010 | ; Get here when another extent is required. 2011 | ; 2012 | POSITN2:PUSH BC 2013 | PUSH DE 2014 | CALL CLOSEIT ;close current extent. 2015 | POP DE 2016 | POP BC 2017 | LD L,3 ;prepare for error. 2018 | LD A,(STATUS) 2019 | INC A 2020 | JP Z,POSITN4 ;close error. 2021 | LD HL,12 ;put desired extent into fcb now. 2022 | ADD HL,DE 2023 | LD (HL),C 2024 | LD HL,14 ;and store extra extent byte 's2'. 2025 | ADD HL,DE 2026 | LD (HL),B 2027 | CALL OPENIT ;try and get this extent. 2028 | LD A,(STATUS) ;was it there? 2029 | INC A 2030 | JP NZ,POSITN3 2031 | POP BC ;no. can we create a new one (writing?). 2032 | PUSH BC 2033 | LD L,4 ;prepare for error. 2034 | INC C 2035 | JP Z,POSITN4 ;nope, reading unwritten space error. 2036 | CALL GETEMPTY ;yes we can, try to find space. 2037 | LD L,5 ;prepare for error. 2038 | LD A,(STATUS) 2039 | INC A 2040 | JP Z,POSITN4 ;out of space? 2041 | ; 2042 | ; Normal return location. Clear error code and return. 2043 | ; 2044 | POSITN3:POP BC ;restore stack. 2045 | XOR A ;and clear error code byte. 2046 | JP SETSTAT 2047 | ; 2048 | ; Error. Set the 's2' byte to indicate this (why?). 2049 | ; 2050 | POSITN4:PUSH HL 2051 | CALL GETS2 2052 | LD (HL),0C0H 2053 | POP HL 2054 | ; 2055 | ; Return with error code (presently in L). 2056 | ; 2057 | POSITN5:POP BC 2058 | LD A,L ;get error code. 2059 | LD (STATUS),A 2060 | JP SETS2B7 2061 | ; 2062 | ; Read a random record. 2063 | ; 2064 | READRAN:LD C,0FFH ;set 'read' status. 2065 | CALL POSITION ;position the file to proper record. 2066 | CALL Z,RDSEQ1 ;and read it as usual (if no errors). 2067 | RET 2068 | ; 2069 | ; Write to a random record. 2070 | ; 2071 | WRITERAN: LD C,0 ;set 'writing' flag. 2072 | CALL POSITION ;position the file to proper record. 2073 | CALL Z,WTSEQ1 ;and write as usual (if no errors). 2074 | RET 2075 | ; 2076 | ; Compute the random record number. Enter with (HL) pointing 2077 | ; to a fcb an (DE) contains a relative location of a record 2078 | ; number. On exit, (C) contains the 'r0' byte, (B) the 'r1' 2079 | ; byte, and (A) the 'r2' byte. 2080 | ; 2081 | ; On return, the zero flag is set if the record is within 2082 | ; bounds. Otherwise, an overflow occured. 2083 | ; 2084 | COMPRAND: EX DE,HL ;save fcb pointer in (DE). 2085 | ADD HL,DE ;compute relative position of record #. 2086 | LD C,(HL) ;get record number into (BC). 2087 | LD B,0 2088 | LD HL,12 ;now get extent. 2089 | ADD HL,DE 2090 | LD A,(HL) ;compute (BC)=(record #)+(extent)*128. 2091 | RRCA ;move lower bit into bit 7. 2092 | AND 80H ;and ignore all other bits. 2093 | ADD A,C ;add to our record number. 2094 | LD C,A 2095 | LD A,0 ;take care of any carry. 2096 | ADC A,B 2097 | LD B,A 2098 | LD A,(HL) ;now get the upper bits of extent into 2099 | RRCA ;bit positions 0-3. 2100 | AND 0FH ;and ignore all others. 2101 | ADD A,B ;add this in to 'r1' byte. 2102 | LD B,A 2103 | LD HL,14 ;get the 's2' byte (extra extent). 2104 | ADD HL,DE 2105 | LD A,(HL) 2106 | ADD A,A ;and shift it left 4 bits (bits 4-7). 2107 | ADD A,A 2108 | ADD A,A 2109 | ADD A,A 2110 | PUSH AF ;save carry flag (bit 0 of flag byte). 2111 | ADD A,B ;now add extra extent into 'r1'. 2112 | LD B,A 2113 | PUSH AF ;and save carry (overflow byte 'r2'). 2114 | POP HL ;bit 0 of (L) is the overflow indicator. 2115 | LD A,L 2116 | POP HL ;and same for first carry flag. 2117 | OR L ;either one of these set? 2118 | AND 01H ;only check the carry flags. 2119 | RET 2120 | ; 2121 | ; Routine to setup the fcb (bytes 'r0', 'r1', 'r2') to 2122 | ; reflect the last record used for a random (or other) file. 2123 | ; This reads the directory and looks at all extents computing 2124 | ; the largerst record number for each and keeping the maximum 2125 | ; value only. Then 'r0', 'r1', and 'r2' will reflect this 2126 | ; maximum record number. This is used to compute the space used 2127 | ; by a random file. 2128 | ; 2129 | RANSIZE:LD C,12 ;look thru directory for first entry with 2130 | CALL FINDFST ;this name. 2131 | LD HL,(PARAMS) ;zero out the 'r0, r1, r2' bytes. 2132 | LD DE,33 2133 | ADD HL,DE 2134 | PUSH HL 2135 | LD (HL),D ;note that (D)=0. 2136 | INC HL 2137 | LD (HL),D 2138 | INC HL 2139 | LD (HL),D 2140 | RANSIZ1:CALL CKFILPOS ;is there an extent to process? 2141 | JP Z,RANSIZ3 ;no, we are done. 2142 | CALL FCB2HL ;set (HL) pointing to proper fcb in dir. 2143 | LD DE,15 ;point to last record in extent. 2144 | CALL COMPRAND ;and compute random parameters. 2145 | POP HL 2146 | PUSH HL ;now check these values against those 2147 | LD E,A ;already in fcb. 2148 | LD A,C ;the carry flag will be set if those 2149 | SUB (HL) ;in the fcb represent a larger size than 2150 | INC HL ;this extent does. 2151 | LD A,B 2152 | SBC A,(HL) 2153 | INC HL 2154 | LD A,E 2155 | SBC A,(HL) 2156 | JP C,RANSIZ2 2157 | LD (HL),E ;we found a larger (in size) extent. 2158 | DEC HL ;stuff these values into fcb. 2159 | LD (HL),B 2160 | DEC HL 2161 | LD (HL),C 2162 | RANSIZ2:CALL FINDNXT ;now get the next extent. 2163 | JP RANSIZ1 ;continue til all done. 2164 | RANSIZ3:POP HL ;we are done, restore the stack and 2165 | RET ;return. 2166 | ; 2167 | ; Function to return the random record position of a given 2168 | ; file which has been read in sequential mode up to now. 2169 | ; 2170 | SETRAN: LD HL,(PARAMS) ;point to fcb. 2171 | LD DE,32 ;and to last used record. 2172 | CALL COMPRAND ;compute random position. 2173 | LD HL,33 ;now stuff these values into fcb. 2174 | ADD HL,DE 2175 | LD (HL),C ;move 'r0'. 2176 | INC HL 2177 | LD (HL),B ;and 'r1'. 2178 | INC HL 2179 | LD (HL),A ;and lastly 'r2'. 2180 | RET 2181 | ; 2182 | ; This routine select the drive specified in (ACTIVE) and 2183 | ; update the login vector and bitmap table if this drive was 2184 | ; not already active. 2185 | ; 2186 | LOGINDRV: LD HL,(LOGIN) ;get the login vector. 2187 | LD A,(ACTIVE) ;get the default drive. 2188 | LD C,A 2189 | CALL SHIFTR ;position active bit for this drive 2190 | PUSH HL ;into bit 0. 2191 | EX DE,HL 2192 | CALL SELECT ;select this drive. 2193 | POP HL 2194 | CALL Z,SLCTERR ;valid drive? 2195 | LD A,L ;is this a newly activated drive? 2196 | RRA 2197 | RET C 2198 | LD HL,(LOGIN) ;yes, update the login vector. 2199 | LD C,L 2200 | LD B,H 2201 | CALL SETBIT 2202 | LD (LOGIN),HL ;and save. 2203 | JP BITMAP ;now update the bitmap. 2204 | ; 2205 | ; Function to set the active disk number. 2206 | ; 2207 | SETDSK: LD A,(EPARAM) ;get parameter passed and see if this 2208 | LD HL,ACTIVE ;represents a change in drives. 2209 | CP (HL) 2210 | RET Z 2211 | LD (HL),A ;yes it does, log it in. 2212 | JP LOGINDRV 2213 | ; 2214 | ; This is the 'auto disk select' routine. The firsst byte 2215 | ; of the fcb is examined for a drive specification. If non 2216 | ; zero then the drive will be selected and loged in. 2217 | ; 2218 | AUTOSEL:LD A,0FFH ;say 'auto-select activated'. 2219 | LD (AUTO),A 2220 | LD HL,(PARAMS) ;get drive specified. 2221 | LD A,(HL) 2222 | AND 1FH ;look at lower 5 bits. 2223 | DEC A ;adjust for (1=A, 2=B) etc. 2224 | LD (EPARAM),A ;and save for the select routine. 2225 | CP 1EH ;check for 'no change' condition. 2226 | JP NC,AUTOSL1 ;yes, don't change. 2227 | LD A,(ACTIVE) ;we must change, save currently active 2228 | LD (OLDDRV),A ;drive. 2229 | LD A,(HL) ;and save first byte of fcb also. 2230 | LD (AUTOFLAG),A ;this must be non-zero. 2231 | AND 0E0H ;whats this for (bits 6,7 are used for 2232 | LD (HL),A ;something)? 2233 | CALL SETDSK ;select and log in this drive. 2234 | AUTOSL1:LD A,(USERNO) ;move user number into fcb. 2235 | LD HL,(PARAMS) ;(* upper half of first byte *) 2236 | OR (HL) 2237 | LD (HL),A 2238 | RET ;and return (all done). 2239 | ; 2240 | ; Function to return the current cp/m version number. 2241 | ; 2242 | GETVER: LD A,022H ;version 2.2 2243 | JP SETSTAT 2244 | ; 2245 | ; Function to reset the disk system. 2246 | ; 2247 | RSTDSK: LD HL,0 ;clear write protect status and log 2248 | LD (WRTPRT),HL ;in vector. 2249 | LD (LOGIN),HL 2250 | XOR A ;select drive 'A'. 2251 | LD (ACTIVE),A 2252 | LD HL,TBUFF ;setup default dma address. 2253 | LD (USERDMA),HL 2254 | CALL DEFDMA 2255 | JP LOGINDRV ;now log in drive 'A'. 2256 | ; 2257 | ; Function to open a specified file. 2258 | ; 2259 | OPENFIL:CALL CLEARS2 ;clear 's2' byte. 2260 | CALL AUTOSEL ;select proper disk. 2261 | JP OPENIT ;and open the file. 2262 | ; 2263 | ; Function to close a specified file. 2264 | ; 2265 | CLOSEFIL: CALL AUTOSEL ;select proper disk. 2266 | JP CLOSEIT ;and close the file. 2267 | ; 2268 | ; Function to return the first occurence of a specified file 2269 | ; name. If the first byte of the fcb is '?' then the name will 2270 | ; not be checked (get the first entry no matter what). 2271 | ; 2272 | GETFST: LD C,0 ;prepare for special search. 2273 | EX DE,HL 2274 | LD A,(HL) ;is first byte a '?'? 2275 | CP '?' 2276 | JP Z,GETFST1 ;yes, just get very first entry (zero length match). 2277 | CALL SETEXT ;get the extension byte from fcb. 2278 | LD A,(HL) ;is it '?'? if yes, then we want 2279 | CP '?' ;an entry with a specific 's2' byte. 2280 | CALL NZ,CLEARS2 ;otherwise, look for a zero 's2' byte. 2281 | CALL AUTOSEL ;select proper drive. 2282 | LD C,15 ;compare bytes 0-14 in fcb (12&13 excluded). 2283 | GETFST1:CALL FINDFST ;find an entry and then move it into 2284 | JP MOVEDIR ;the users dma space. 2285 | ; 2286 | ; Function to return the next occurence of a file name. 2287 | ; 2288 | GETNXT: LD HL,(SAVEFCB) ;restore pointers. note that no 2289 | LD (PARAMS),HL ;other dbos calls are allowed. 2290 | CALL AUTOSEL ;no error will be returned, but the 2291 | CALL FINDNXT ;results will be wrong. 2292 | JP MOVEDIR 2293 | ; 2294 | ; Function to delete a file by name. 2295 | ; 2296 | DELFILE:CALL AUTOSEL ;select proper drive. 2297 | CALL ERAFILE ;erase the file. 2298 | JP STSTATUS ;set status and return. 2299 | ; 2300 | ; Function to execute a sequential read of the specified 2301 | ; record number. 2302 | ; 2303 | READSEQ:CALL AUTOSEL ;select proper drive then read. 2304 | JP RDSEQ 2305 | ; 2306 | ; Function to write the net sequential record. 2307 | ; 2308 | WRTSEQ: CALL AUTOSEL ;select proper drive then write. 2309 | JP WTSEQ 2310 | ; 2311 | ; Create a file function. 2312 | ; 2313 | FCREATE:CALL CLEARS2 ;clear the 's2' byte on all creates. 2314 | CALL AUTOSEL ;select proper drive and get the next 2315 | JP GETEMPTY ;empty directory space. 2316 | ; 2317 | ; Function to rename a file. 2318 | ; 2319 | RENFILE:CALL AUTOSEL ;select proper drive and then switch 2320 | CALL CHGNAMES ;file names. 2321 | JP STSTATUS 2322 | ; 2323 | ; Function to return the login vector. 2324 | ; 2325 | GETLOG: LD HL,(LOGIN) 2326 | JP GETPRM1 2327 | ; 2328 | ; Function to return the current disk assignment. 2329 | ; 2330 | GETCRNT:LD A,(ACTIVE) 2331 | JP SETSTAT 2332 | ; 2333 | ; Function to set the dma address. 2334 | ; 2335 | PUTDMA: EX DE,HL 2336 | LD (USERDMA),HL ;save in our space and then get to 2337 | JP DEFDMA ;the bios with this also. 2338 | ; 2339 | ; Function to return the allocation vector. 2340 | ; 2341 | GETALOC:LD HL,(ALOCVECT) 2342 | JP GETPRM1 2343 | ; 2344 | ; Function to return the read-only status vector. 2345 | ; 2346 | GETROV: LD HL,(WRTPRT) 2347 | JP GETPRM1 2348 | ; 2349 | ; Function to set the file attributes (read-only, system). 2350 | ; 2351 | SETATTR:CALL AUTOSEL ;select proper drive then save attributes. 2352 | CALL SAVEATTR 2353 | JP STSTATUS 2354 | ; 2355 | ; Function to return the address of the disk parameter block 2356 | ; for the current drive. 2357 | ; 2358 | GETPARM:LD HL,(DISKPB) 2359 | GETPRM1:LD (STATUS),HL 2360 | RET 2361 | ; 2362 | ; Function to get or set the user number. If (E) was (FF) 2363 | ; then this is a request to return the current user number. 2364 | ; Else set the user number from (E). 2365 | ; 2366 | GETUSER:LD A,(EPARAM) ;get parameter. 2367 | CP 0FFH ;get user number? 2368 | JP NZ,SETUSER 2369 | LD A,(USERNO) ;yes, just do it. 2370 | JP SETSTAT 2371 | SETUSER:AND 1FH ;no, we should set it instead. keep low 2372 | LD (USERNO),A ;bits (0-4) only. 2373 | RET 2374 | ; 2375 | ; Function to read a random record from a file. 2376 | ; 2377 | RDRANDOM: CALL AUTOSEL ;select proper drive and read. 2378 | JP READRAN 2379 | ; 2380 | ; Function to compute the file size for random files. 2381 | ; 2382 | WTRANDOM: CALL AUTOSEL ;select proper drive and write. 2383 | JP WRITERAN 2384 | ; 2385 | ; Function to compute the size of a random file. 2386 | ; 2387 | FILESIZE: CALL AUTOSEL ;select proper drive and check file length 2388 | JP RANSIZE 2389 | ; 2390 | ; Function #37. This allows a program to log off any drives. 2391 | ; On entry, set (DE) to contain a word with bits set for those 2392 | ; drives that are to be logged off. The log-in vector and the 2393 | ; write protect vector will be updated. This must be a M/PM 2394 | ; special function. 2395 | ; 2396 | LOGOFF: LD HL,(PARAMS) ;get drives to log off. 2397 | LD A,L ;for each bit that is set, we want 2398 | CPL ;to clear that bit in (LOGIN) 2399 | LD E,A ;and (WRTPRT). 2400 | LD A,H 2401 | CPL 2402 | LD HL,(LOGIN) ;reset the login vector. 2403 | AND H 2404 | LD D,A 2405 | LD A,L 2406 | AND E 2407 | LD E,A 2408 | LD HL,(WRTPRT) 2409 | EX DE,HL 2410 | LD (LOGIN),HL ;and save. 2411 | LD A,L ;now do the write protect vector. 2412 | AND E 2413 | LD L,A 2414 | LD A,H 2415 | AND D 2416 | LD H,A 2417 | LD (WRTPRT),HL ;and save. all done. 2418 | RET 2419 | ; 2420 | ; Get here to return to the user. 2421 | ; 2422 | GOBACK: LD A,(AUTO) ;was auto select activated? 2423 | OR A 2424 | JP Z,GOBACK1 2425 | LD HL,(PARAMS) ;yes, but was a change made? 2426 | LD (HL),0 ;(* reset first byte of fcb *) 2427 | LD A,(AUTOFLAG) 2428 | OR A 2429 | JP Z,GOBACK1 2430 | LD (HL),A ;yes, reset first byte properly. 2431 | LD A,(OLDDRV) ;and get the old drive and select it. 2432 | LD (EPARAM),A 2433 | CALL SETDSK 2434 | GOBACK1:LD HL,(USRSTACK) ;reset the users stack pointer. 2435 | LD SP,HL 2436 | LD HL,(STATUS) ;get return status. 2437 | LD A,L ;force version 1.4 compatability. 2438 | LD B,H 2439 | RET ;and go back to user. 2440 | ; 2441 | ; Function #40. This is a special entry to do random i/o. 2442 | ; For the case where we are writing to unused disk space, this 2443 | ; space will be zeroed out first. This must be a M/PM special 2444 | ; purpose function, because why would any normal program even 2445 | ; care about the previous contents of a sector about to be 2446 | ; written over. 2447 | ; 2448 | WTSPECL:CALL AUTOSEL ;select proper drive. 2449 | LD A,2 ;use special write mode. 2450 | LD (MODE),A 2451 | LD C,0 ;set write indicator. 2452 | CALL POSITN1 ;position the file. 2453 | CALL Z,WTSEQ1 ;and write (if no errors). 2454 | RET 2455 | ; 2456 | ;************************************************************** 2457 | ;* 2458 | ;* BDOS data storage pool. 2459 | ;* 2460 | ;************************************************************** 2461 | ; 2462 | EMPTYFCB: DEFB 0E5H ;empty directory segment indicator. 2463 | WRTPRT: DEFW 0 ;write protect status for all 16 drives. 2464 | LOGIN: DEFW 0 ;drive active word (1 bit per drive). 2465 | USERDMA:DEFW 080H ;user's dma address (defaults to 80h). 2466 | ; 2467 | ; Scratch areas from parameter block. 2468 | ; 2469 | SCRATCH1: DEFW 0 ;relative position within dir segment for file (0-3). 2470 | SCRATCH2: DEFW 0 ;last selected track number. 2471 | SCRATCH3: DEFW 0 ;last selected sector number. 2472 | ; 2473 | ; Disk storage areas from parameter block. 2474 | ; 2475 | DIRBUF: DEFW 0 ;address of directory buffer to use. 2476 | DISKPB: DEFW 0 ;contains address of disk parameter block. 2477 | CHKVECT:DEFW 0 ;address of check vector. 2478 | ALOCVECT: DEFW 0 ;address of allocation vector (bit map). 2479 | ; 2480 | ; Parameter block returned from the bios. 2481 | ; 2482 | SECTORS:DEFW 0 ;sectors per track from bios. 2483 | BLKSHFT:DEFB 0 ;block shift. 2484 | BLKMASK:DEFB 0 ;block mask. 2485 | EXTMASK:DEFB 0 ;extent mask. 2486 | DSKSIZE:DEFW 0 ;disk size from bios (number of blocks-1). 2487 | DIRSIZE:DEFW 0 ;directory size. 2488 | ALLOC0: DEFW 0 ;storage for first bytes of bit map (dir space used). 2489 | ALLOC1: DEFW 0 2490 | OFFSET: DEFW 0 ;first usable track number. 2491 | XLATE: DEFW 0 ;sector translation table address. 2492 | ; 2493 | ; 2494 | CLOSEFLG: DEFB 0 ;close flag (=0ffh is extent written ok). 2495 | RDWRTFLG: DEFB 0 ;read/write flag (0ffh=read, 0=write). 2496 | FNDSTAT:DEFB 0 ;filename found status (0=found first entry). 2497 | MODE: DEFB 0 ;I/o mode select (0=random, 1=sequential, 2=special random). 2498 | EPARAM: DEFB 0 ;storage for register (E) on entry to bdos. 2499 | RELBLOCK: DEFB 0 ;relative position within fcb of block number written. 2500 | COUNTER:DEFB 0 ;byte counter for directory name searches. 2501 | SAVEFCB:DEFW 0,0 ;save space for address of fcb (for directory searches). 2502 | BIGDISK:DEFB 0 ;if =0 then disk is > 256 blocks long. 2503 | AUTO: DEFB 0 ;if non-zero, then auto select activated. 2504 | OLDDRV: DEFB 0 ;on auto select, storage for previous drive. 2505 | AUTOFLAG: DEFB 0 ;if non-zero, then auto select changed drives. 2506 | SAVNXT: DEFB 0 ;storage for next record number to access. 2507 | SAVEXT: DEFB 0 ;storage for extent number of file. 2508 | SAVNREC:DEFW 0 ;storage for number of records in file. 2509 | BLKNMBR:DEFW 0 ;block number (physical sector) used within a file or logical sect 2510 | LOGSECT:DEFW 0 ;starting logical (128 byte) sector of block (physical sector). 2511 | FCBPOS: DEFB 0 ;relative position within buffer for fcb of file of interest. 2512 | FILEPOS:DEFW 0 ;files position within directory (0 to max entries -1). 2513 | ; 2514 | ; Disk directory buffer checksum bytes. One for each of the 2515 | ; 16 possible drives. 2516 | ; 2517 | CKSUMTBL: DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 2518 | ; 2519 | ; Extra space ? 2520 | ; 2521 | DEFB 0,0,0,0 2522 | ; 2523 | ;********* 2524 | ; 2525 | ;************************************************************** 2526 | ;* 2527 | ;* B I O S J U M P T A B L E 2528 | ;* 2529 | ;************************************************************** 2530 | ; 2531 | include "bios/bios.asm" 2532 | ; 2533 | ;* 2534 | ;****************** E N D O F C P / M ***************** 2535 | ;* 2536 | 2537 | --------------------------------------------------------------------------------