;====================================================== ; SSMPB1 by Martin Eberhard and Mike Douglas ;====================================================== ; SSMPB1 is a menu-driven EPROM Programmer/Reader/ ; Editor program for the Solid State Music PB1 ; EPROM board running in a CP/M 2.x sytstem. ; See the Help screen for a list of supported ; commands. ; ; Based on the configuration of your PB1 board, set ; the programming socket address and the command ; port I/O address in the equates below, PGMADR and ; CMDPORT. ; ; Command Line Scripts ; ; An initial list of commands may be specified on the ; command line that invokes SSMPB1. SSMPB1 commands ; on the command line are separated by commas. These ; commands will be executed before allowing user input ; from the SSMPB1 prompt. Any errors encountered while ; executing a command line script will terminate ; processing the remainder of the script. Example: ; ; SSMPB1 HA MYFILE,EP,QU ; ; This example will load the file MYFILE.HEX into the ; buffer, automatically setting the address offset. It ; will then program the EPROM from the buffer (after ; prompting the user to install the EPROM etc.), and ; then quit to CP/M. ; ; Screen Sizes ; ; SSMPB1 supports both a 16X64 screen and a 24X80 ; (or larger) screen, selected with the SS command. ; At 16X64, the DB command does not display the ASCII ; values of the buffer data, since the screen is not ; wide enough. ; ; You will need a version of CP/M that avoids the ; memory space that you have allocated to the programmer ; and still leaves at least 14K of free RAM starting at ; address 100h. ; ; ; * Assemble with Digital Research's ASM assembler ; * Requires about 14K of RAM, starting at 0100h ; ;====================================================== ; Revision History: ; 1.00 18 May 2019 M. Douglas ; Ported from M. Eberhard's BYTESAVE v1.01. ;====================================================== ;**************************************************** ; Set these equates to match your board configuration ;**************************************************** PGMADR equ 0E000h ;programmer address CMDPORT equ 0A0h ;programmer command port address ;********* ; Defaults ;********* DEFSS equ 1 ;0 means 64X16 screen, ;1 means 80x24 screen DEFAO equ 0 ;default target address offset DEPMS equ 110 ;default 2704/08 prog time (mS) ;min 1, max 255 ;************************* ; Program Equates ;************************* SZ2704 equ 2 ;256-byte pages/2704 SZ2708 equ 4 ;256-byte pages/2708 SZ2716 equ 8 ;256-byte pages/2716 EBUFSZ equ SZ2716 ;EPROM buffer size, in pages LBSIZE equ 80 ;line buffer size MAXDRV equ 16 ;max CP/M drive FCBCLR equ 36 ;# of FCB bytes to clear SECSIZ equ 128 ;sector size (must be 128) SBUFSZ equ 48 ;Sectors per disk read/write ; SECTOR buffer size = SBUFSZ * 128 bytes ; multiples of 16 work well with CP/M. MUST be even. ;****************************************** ; BDOS Function Codes, passed in register C ;****************************************** BCONIN equ 1 ;Read Console to a with echo BCONOT equ 2 ;Write E to Console BPRINT equ 9 ;Print $-terminated line at de BRDCON equ 10 ;Input buffered line at de BCONST equ 11 ;Console input status ;a=-1 if chr, 0 if none BOPEN equ 15 ;Open disk file, FCB at de ; Aa=-1 if not found BCLOSE equ 16 ;Close disk file, FCB at de ; a=-1 if not found BSERCH equ 17 ;Search dir for file, FCB at de ; A=-1 if not found BDELET equ 19 ;delete file, FCB at (de) BREAD equ 20 ;Read next record. 0=OK, 1=EOF BWRITE equ 21 ;Write next record, 0=OK, 1=ERR, ; 2=out of disk space, 0FFH=NO DIR SPC BMAKE equ 22 ;Create file, 0FFH=BAD BSTDMA equ 26 ;Set disk buffer to (de) ;******************************************* ; BDOS Entry Points and low-memory locations ;******************************************* CBASE equ 0 ;CP/M base address WBOOT equ CBASE ;CP/M re-entry BDOS equ CBASE+5 ;CP/M System Call FCB equ CBASE+5CH ;CP/M file control blk FCBDD equ FCB ;Drive Descriptor FCBFN equ FCB+1 ;File name (8 chrs) FCBFT equ FCB+9 ;File Type (3 chrs) FCBEXT equ FCB+12 ;File extent within FCB FCBRCNT equ FCB+15 ;Record cnt within FCB FCBSEC equ FCB+32 ;Sector # within FCB COMBUF equ CBASE+80H ;disk & cmd line buffer USAREA equ CBASE+100H ;User program area ;------------------------------- ;Console line buffer uses COMBUF ;------------------------------- LINBUF equ COMBUF ;***************** ; ASCII Characters ;***************** BS equ 08h LF equ 0Ah CR equ 0Dh DEL equ 7Fh ;============================ ; Initialization ; Calls here use CP/M's stack ;============================ ORG USAREA lda PGMADR ;clear program mode flip-flop ; print banner lxi d,BANNER ;Sign-on msg call CRPRNT ; Initialize all RAM variables mvi a,DEFSS ;default screen size sta SCRNSZ mvi e,0FFh ;fill EPROM buffer call FILBUF ;..with unprogrammed lxi h,DEFAO ;Default address offset shld OFFSET mvi a,DEPMS ;default 2704/8 prog time sta EPMS mvi a,SZ2708 ;init default EPROM size sta EPSIZE mvi a,PGMADR/256 ;page address of program socket sta BSADDR ;see if there was any command line input other than spaces ;set SCRFLG if so lxi d,LINBUF ldax d ;how many bytes of input? sta SCRFLG ora a ;any input? jz CLI2 ;z:no command line script ;copy command line into script buffer inx d ;skip byte count lxi h,SCRBUF ;destination shld SCRPTR ;set pointer too mov b,a ;byte count mvi c,0 call BLKMOV dad b ;compute last address mov m,c CLI2: ;final bit of banner message lxi d,BANR2 ;type ? for help push d ; Fall into PCMAIN ;=============================================== ; Main loop: re-initialize, wait for command ; from the user & dispatch to process command ; ; Entry at MAIN does not print a string ; ; Entry at PRMAIN prints a $-terminated string ; at (sp) and restores the stack. ; ; Entry at PCMAIN prints CR,LF before the string ;=============================================== PCMAIN: call PCRLF ;print CRLF 1st PRMAIN: pop d lxi sp,STACK ;SP=local stack call PRINTF ;print incoming string ;re-initialize a few things MAIN: lxi h,CLRBEG ;zero out some variables lxi b,CLREND-CLRBEG mov e,b ;e=0: fill value call FILBLK lxi h,FCB ;clear FCB mvi c,FCBCLR ;byte count call FILBLK lxi h,FCBFN ;fill FCB name & ext mvi c,8+3 ;..with spaces mvi e,' ' call FILBLK lxi h,SECBUF ;point to beginning of shld SBPNTR ;..disk buffer ;get a line of user input lxi d,PROMPT ;print prompt call CRPRNT lxi h,MAIN ;create return addr push h ;..on the stack ;test for remaining script input lda SCRFLG ora a jz NOSCPT ; get next script item and execute it call TESTQ ;allow script abort lhld SCRPTR ;script pointer lxi d,LINBUF+2 ;command buffer ; copy comma-terminated script command into command buffer SCRLP: mov a,m ;get script chr ora a ;end of line? jz EOS cpi ',' ;seperator? jz EOS stax d call PRINTA ;echo inx d inx h xra a stax d ;potential eol jmp SCRLP EOS: sta SCRFLG ;remember end inx h ;point past comma shld SCRPTR ;save pointer jmp DOSCPT ; no more script input: get user input NOSCPT: mvi c,BRDCON ;get user input lxi d,LINBUF ;give CP/M a buffer mvi a,LBSIZE ;buffer size stax d call GOBDOS ;get input line, which will ;be 0-terminated because ;LINBUF was filled with 0s ; put a null in the line buffer at the end of the input lxi h,LINBUF+1 ;BDOS put byte count here mov a,m inx h add l ;compute last chr addr+1 mov l,a ;will be no carry mvi m,0 ;paste in null ; skip leading blanks, and ignore blank lines DOSCPT: lxi h,LINBUF+2 ;Beginning of user input call SKIPSP ;skip leading spaces ora a ;terminating 0? rz ;null line: try again ;----------------------------------------- ; Search cmd table for match ; go to CMDERR if not in table ; On Entry: ; hl=address of command 1st chr ; On Exit: ; de=addr of pointer to execution routine ; hl=address of next chr on cmd line ;----------------------------------------- call GETUC ;get 2-digit command mov b,a call GETUC mov c,a push h ;LINBUF address on stack call GETUC ;2 chr commands only jnz CMDERR ;..error if more ;search command table for match of b,c lxi h,CMDTAB-1 NXTCMD: inx h ;next entry mov a,m ;1st command chr inx h ora a ;table end? jz CMDERR ;cmd not in table xra b ;compare 1st chr mov d,a ;temp save mov a,m ;2nd command chr inx h mov e,m ;address low byte inx h xra c ;compare 2nd chr ora d ;compare both chrs jnz NXTCMD ;no match: keep looking mov d,m ;de=exec address pop h ;1st chr past command call SKIPSP ;skip spaces to 1st parameter ;z set or hl=address of param ;------------------------------------------- ; Command found. Dispatch to (de) ; with hl pointing to next command line item ; and Z flag set if end of cmd line ;------------------------------------------- push d ;de has execution addr ret ;..go execute there ;=========================================== ; Bogus command return (PCMAIN resets stack) ; On jump to PCMAIN: ; SCRFLG=0 (cancels any scripts) ; 'Huh?' string address is on stack ;=========================================== CMDERR: xra a ;cancel any scripts sta SCRFLG call PCMAIN ;print msg & go to main db 'Huh?$' ;******================================================== ;* MB * Modify Buffer Command ;****** ; allow user to edit buffer contents ; On Entry: ; Z set if end of cmd line already ; hl=address of 1st chr of 1st command paramter, if any ;======================================================== FEDIT: call GETHEX ;get address in de mov a,b ;overflow? cpi 5 ;4 digits max jnc CMDERR mov a,m ;any more chrs? ora a jnz CMDERR lda EPSIZE ;past buffer end? dcr a cmp d jc CMDERR FEDIT0: lxi h,EBUF ;address dad d ;hl=buff address FEDIT1: call PCRADR ;print de=address FEDIT2: mov a,m ;data call PRHEX mvi a,'.' ;separator call PRINTA push h push d ;Loop to get input from console until ; either a space or a Q to quit lxi h,LINBUF push h mvi b,0 ;chr count GLOOP: call CONIUC ;get uppercase cpi 'Q' jz DODONE ;Q quits us cpi BS ;any backspaces? jz GLDEL cpi DEL jz GLDEL cpi ' ' ;space means done jz GLDON jc CMDERR ;below is bogus mov m,a ;save new chr inx h inr b jmp GLOOP ;backspace GLDEL: mov a,b ;can we back up? ora a jz GLOOP ;no: ignore mvi a,BS ;actually BS call PRINTA dcr b ;back up dcx h jmp GLOOP ; Got complete input in LINBUF. Time to parse. GLDON: mvi m,0 ;null termination pop h ;point to LINBUF call GETHEX ;convert to hex mov a,b ;any digits? ora a mov a,e ;get low byte pop d pop h jz FEDIT3 ;no change? mov m,a ;update w/ low byte FEDIT3: inx h ;next buffer address Inx d ;next absolute address mov a,e ;need an address? ani 07h ;every 8 jnz FEDIT2 lda EPSIZE ;end of buffer? cmp d jnz FEDIT1 ;n: keep going lxi d,0 ;wrap to beginning jmp FEDIT0 ;******================================================== ;* DB * Display Buffer Command ;****** ; On Entry: ; Z set if end of cmd line already ; hl=address of 1st chr of 1st command paramter, if any ;======================================================== FBDISP: jnz CMDERR ;no params allowed lxi d,0 ;address count lxi h,EBUF ;buffer addr mvi b,0 ;init checksum FDISP1: call PCRADR ;print address push h ;save for ASCII print push d ;print 16 bytes in hex FDISP2: mov a,m ;data add b ;compute buffer checksm mov b,a mov a,m ;data call PRHEX inx d ;next inx h mvi a,' ' ;spacer call PRINTA mov a,e ani 0Fh ;new line every 16 jnz FDISP2 ;if wide screen, print them again in ASCII pop d pop h FDISP3: mov a,m cpi ' ' ;non-printing? jc FDISP4 cpi DEL ;more non-printing jc FDISP5 FDISP4: mvi a,'.' FDISP5: mov c,a ;temp save lda SCRNSZ ;wide enough screen? ora a ;0 means no mov a,c cnz PRINTA inx d ;next inx h mov a,e ani 0Fh ;new line every 16 jnz FDISP3 ora e ;pause every 256 jnz FDISP1 lda EPSIZE ;past buffer end? cmp d jz FDDONE call ASKQSP ;space to continue, q to quit jmp FDISP1 FDDONE: lxi d,BCSST call CRPRNT mov a,b ;print buffer checksum jmp PRHEX ;******================================================== ;* FB * Buffer Fill Command ;****** ; On Entry: ; Z set if end of cmd line already ; hl=address of 1st chr of 1st command paramter, if any ;======================================================== FFILL: call GETHEX ;get fill value in de mov a,b ;overflow? cpi 3 ;max 2 digits jnc CMDERR call FILBUF jmp DODONE ;done ;******================================================== ;* SE * Display settings ;****** ; On Entry: ; Z set if end of cmd line already ; hl=address of 1st chr of 1st command paramter, if any ;======================================================== FSET: lxi d,PADRST ;display "Programmer address lhld BSADDR-1 ;h=msb of base address mvi l,0 call PSTH16 lxi d,CADRST ;display "Command port address: " call CRPRNT ;print string at de mvi a,CMDPORT ;display command port I/O address call PRHEX call PETYPE ;eprom type lda EPSIZE ;2716's? cpi SZ2716 ;n: no mS prog time cnz PEMS ;mS prog time jmp PAO ;Targ address offset ;******================================================== ;* AO * Set file address offset ;****** ; On Entry: ; Z set if end of cmd line already ; hl=address of 1st chr of 1st command paramter, if any ;======================================================== FSAO: call GETHEX xchg ;result in hl shld OFFSET ; fall into PAO ;===Subroutine============= ; Print File Address Offset ; On exit: ; de=(OFFSET) ;========================== PAO: lxi d,PAOST lhld OFFSET jmp PSTH16 ;******================================================== ;* SS * Set Screen Size (0 means 16X64, 1 means 24X80) ;****** ; On Entry: ; z set if end of cmd line already ; hl=address of 1st chr of 1st command paramter, if any ;======================================================== FSCRSZ: call GETHEX mov a,e cpi 2 jnc CMDERR sta SCRNSZ jmp DODONE ;******================================================== ;* ET * Set EPROM type (2704/2708/2716) ; On Entry: ; Z set if end of cmd line already ; hl=address of 1st chr of 1st command paramter, if any ;======================================================== FSETYP: call GETHEX ;get user input mov a,d cpi 27h ;valid upper byte? jnz FSTERR mov a,e ;get low byte rrc cpi SZ2708 ;2708 requested? jz FSTVAL ;yes, valid type cpi SZ2704 ;2704 requested? jz FSTVAL ;yes, valid type cpi 16h SHR 1 ;2716 requested? mvi a,SZ2716 ;A=2716 FSTERR: jnz CMDERR ;n:error FSTVAL sta EPSIZE ;save the new EPROM type ; fall into PETYPE ;===Subroutine==== ; Print EPROM type ;================= PETYPE: lxi d,EPSST call CRPRNT lda EPSIZE ;will be 2,4,or 8 ora a ;clear carry add a ;make it 04,08, or 16 daa ;fix 16 jmp PRHEX ;print 2 digits ;******================================================== ;* MS * Set mS to program ;****** ; On Entry: ; Z set if end of cmd line already ; hl=address of 1st chr of 1st command paramter, if any ;======================================================== FSMS: call GETDEC ;result in b mov a,b ;input value ora a ;0? jnz FSMS1 ;y:make it the default mvi a,DEPMS FSMS1: sta EPMS ; fall into PEMS ;===Subroutine=================== ; Print programming ms in decimal ;================================ PEMS: lxi d,PEMSST call CRPRNT lda EPMS call PDEC8 lxi d,MSPBST jmp PRINTF ;******====================================== ;* EP * Program the EPROM from the buffer ;****** ; Not that the 2708 specs say that the entire ; EPROM should be programmed sequentially - ; you shouldn't program only part of a 2708. ; ; On Entry: ; ERRCNT=0 ; Z set if end of cmd line already ;============================================ FPROG: call GETAC ;get address & count push b ;byte count push d ;buffer address push h ;EPROM address ;if this is a 2704 or 2708 then warn the user ;about programming less than the entire EPROM lda EPSIZE cpi SZ2716 ;2716? jnc PARTOK ;y: partial EPROM ok cmp b ;entire EPROM? push d cnz PEASK ;n: warn, ask to continue pop d PARTOK: ;blank-check the EPROM mvi d,0FFh ;erased state PBCLUP: mov a,d ;recover result ana m ;accumulate errors mov d,a ;save result inx h dcx b mov a,b ;done? ora c jnz PBCLUP inr d ;d=FF? cnz NBASK ;program the EPROM when the user is ready lxi d,RDYST call CRPRNT call ASKYN lxi d,PROGST call CRPRNT pop h ;hl=EPROM address pop d ;de=buffer address pop b ;byte count lda EPSIZE ;2716s? cpi SZ2716 jz PONCE ;y: one pass only ;------------------------------------------------ ;program a 2708 - requires EPMS mS of programming ; bc = byte coiunt ; de = buffer start address (source) ; hl = EPROM start address (destination) ;------------------------------------------------ mvi a,1 ;select 2708 socket out CMDPORT lda EPMS ;EPMS= requested mS PROGLP: push psw ;save mS counter ;loop for 1 mS EPROM programming ;Each programming pulse on the PB1 is 500us-700us mvi a,2 ;2x500 uS is 1000 uS MSLOOP: push psw ;save prog cycle count call BLKMOV ;leaves registers intact pop psw ;prog cycle count dcr a jnz MSLOOP ;loop for 1 mS total ;loop the specified number of mS call PACIFY ;print dot, check for Q pop psw ;ms counter dcr a jnz PROGLP ;loop specified # of mS ; verify the EPROM. jmp VERIFY ;go verify ;--------------------------------------- ;Program a 2716 -takes just one pass ; Print a pacifier every 64 bytes ; bc = byte count ; de = buffer start address (source) ; hl = EPROM start address (destination) ;--------------------------------------- PONCE: mvi a,2 ;select 2716 socket out CMDPORT push b push d push h P1LOOP: ldax d ;program a byte mov m,a inx d ;bump pointers inx h dcx b mov a,c ani 3Fh ;need a pacifier? cz PACIFY ;..every 64 bytes mov a,c ora b ;done? jnz P1LOOP pop h pop d pop b jmp VERIFY ;go verify ;******============================= ;* EC * Compare EPROM to buffer ;****** ; On Entry: ; ERRCNT=0 ; Z set if end of cmd line already ;=================================== FCOMP: call GETAC ;get address & count ;hl = EPROM address ;de = buffer address ;bc = byte count ; fall into VERIFY ;===Local Subroutine==== ;Verify EPROM ; On Entry: ; hl = EPROM address ; de = buffer address ; bc = byte count ;======================= VERIFY: mov a,m ;clear program flip-flop push d lxi d,VERST ;Verifying... call CRPRNT pop d FCLUP: ldax d ;read from buffer cmp m ;compare to EPROM cnz CMPERR inx h inx d dcx b mov a,c ora b jnz FCLUP ; fall into PERCNT ;===Local Subroutine=== ; Print error count ;====================== PERCNT: lxi d,TOTEST call CRPRNT lhld ERRCNT jmp PDEC16 ;******============================= ;* EB * Check for blank EPROM ;****** ; On Entry: ; ERRCNT=0 ; Z set if end of cmd line already ;=================================== FBLANK: call GETAC ;get address & count ;hl = EPROM address ;de = buffer address ;bc = byte count FBLUP: mov a,m inr a ;test for ff cnz BLAERR inx h dcx b mov a,c ora b jnz FBLUP jmp PERCNT ;print error count ;******============================= ;* ER * Read EPROM into buffer ;****** ; On Entry: ; Z set if end of cmd line already ;=================================== FREAD: call GETAC ;get address & count ;hl = EPROM address ;de = buffer address ;bc = byte count ;move block xchg call BLKMOV jmp DODONE ;******=============================== ;* ES * Display EPROM Checksum ;****** ; On Entry: ; Z set if end of cmd line already ;===================================== FCKSUM: call GETAC ;get address & count ;hl = EPROM address ;de = buffer address ;bc = byte count xra a ;init checksum FCKLP: add m ;compute EPROM checksum inx h dcx b mov d,a ;save checksum mov a,b ora c mov a,d ;recover checksum jnz FCKLP push psw ;save result lxi d,EPCST call CRPRNT pop psw ;recover checksum jmp PRHEX ;..and print it ;******================================================== ;* HS * Save EBUF contents as Intel hex file on disk ;****** ; On Entry: ; ERRCNT=0 ; SBBCNT=0 ; SBPNTR=SECBUF ; Z set if end of cmd line already ; hl=address of 1st chr of 1st command paramter, if any ;======================================================== FHSAVE: lxi d,HEXST ;look for a .hex file call GETFN ;get filename from user ;see if file already exists lxi d,FCB ;FCB has file to open mvi c,BSERCH ;FILE Search function call GOBDOS inr a ;-1 means not there cnz FEXIST ; create & open the file lxi d,FCB ;FCB has file to open mvi c,BMAKE ;FILE Create function call GOBDOS inr a ;-1 means open failure jz FOFAIL ; Create & send hex records lhld OFFSET ;file address lxi d,EBUF ;data source lda EPSIZE rlc rlc rlc rlc mov b,a ;b=# of 16-byte records WRECLP: push b ;save b=record count mvi a,':' ;record start call WRBYTE lxi b,16*256 ;byte cnt, init checksm mov a,b ;byte count call WRHEX mov a,h ;address high byte call WRHEX ;write A as hex mov a,l ;adress low byte call WRHEX xra a ;record type 0 call WRHEX WRLUP: ldax d ;write data call WRHEX inx d inx h dcr b jnz WRLUP xra a ;compute checksum sub c call WRHEX mvi a,CR ;CRLF at end call WRBYTE mvi a,LF call WRBYTE pop b ;more records? dcr b jnz WRECLP ; append EOF record mvi a,':' ;EOF record like this mvi b,11 ;':0000000000' WEOFLP: call WRBYTE mvi a,'0' dcr b jnz WEOFLP ; flush out tail end of disk buffer lhld SBBCNT mov a,l ;need to flush? ora h jz NOFLSH ; pad to an even sector ani 7Fh ;need to pad? jz FLNOW ;not if full sector PADLP: mvi a,' ' ;pad with spaces call WRBYTE inx h mov a,l ani 7Fh ;even sector? jnz PADLP FLNOW: call WFLUSH ; close the file NOFLSH: lxi d,FCB mvi c,BCLOSE call GOBDOS inr a ;error? jnz DODONE ;no: done call ERRABT db 'File close failed$' ;-------------------- ; local error routine ;--------------------- FOFAIL: call ERRABT db 'File open failed$' ;---Local subroutine---------- ; The file already exists. ; Ask if it should be deleted. ;----------------------------- FEXIST: lxi d,EXISTS call CRPRNT call ASKYN lxi d,FCB ;delete the old file mvi c,BDELET jmp GOBDOS ;******================================================== ;* BL * Load binary file from disk into EBUF ;****** load address is set by AO ; On Entry: ; ERRCNT=0 ; SBBCNT=0 ; SBPNTR=SECBUF ; Z set if end of cmd line already ; hl=address of 1st chr of 1st command paramter, if any ; On Exit: ; EBUF has binary file ; byte count message printed ;======================================================== FBLOAD: lxi d,BINST ;file must by type BIN call GETFN ;get filename from user call OPNFIL ; open the file call PAO ;print offset ;de=OFFSET on ret lxi b,0 ;byte count FBLOOP: lda EPSIZE ;overflow? dcr a cmp d jc FBOVER call RDBYTE ;get a data byte jc FBDONE lxi h,EBUF ;compute buffer dest dad d mov m,a ;stash chr inx b ;bump byte count inx d ;next address jmp FBLOOP FBOVER: lxi d,EBOST ;buffer overflow call CRPRNT FBDONE: push b jmp PRBC ;print byte count, done ;******================================================== ;* HA * Load Intel hex file from disk into EBUF ;****** Set address offset according to the 1st record ; ignore all bytes outside EPROM address range ; On Entry: ; ERRCNT=0 ; SBBCNT=0 ; SBPNTR=SECBUF ; Z set if end of cmd line already ; hl=address of 1st chr of 1st command paramter, if any ; Register usage during load: ; b: scratch ; c: Reord byte count ; d: record checksum ; e: record type ; hl: memory address ; (sp): buffer byte count ; On Exit: ; EBUF has received records ; byte count message printed ;======================================================== FALOAD: mvi a,1 ;set auto-offset sta AUTOFS ;fall into FHLOAD ;******================================================== ;* HL * Load Intel hex file from disk into EBUF ;****** ignore all bytes outside EPROM address range ; On Entry: ; ERRCNT=0 ; SBBCNT=0 ; SBPNTR=SECBUF ; Z set if end of cmd line already ; hl=address of 1st chr of 1st command paramter, if any ; Register usage during load: ; b: scratch ; c: Record byte count ; d: record checksum ; e: record type ; hl: memory address ; (sp): buffer byte count ; On Exit: ; EBUF has received records ; byte count message printed ;======================================================== FHLOAD: lxi d,HEXST ;file must by type HEX call GETFN ;get filename from user call OPNFIL ;open the file ;begin loading file lxi h,0 ;clear byte count push h ; eat all chrs until record-start colon GETCO: call RDBYTE jc EOFERR ;unexpected EOF? cpi ':' jnz GETCO ; record byte count to c, restart checksum call GETBYT mov c,b ;byte count mov d,b ;start checksum ; record address to hl call GETBYT mov h,b ;address high byte CALL GETBYT mov l,b ;address low byte ; auto-offset? lda AUTOFS ora a cnz SETOFF ; offset address: hl:=hl-(OFFSET) push d xchg ;de=address from record lhld OFFSET ;user-set addr offset mov a,e ;hl:=de-hl sub l mov l,a mov a,d sbb h mov h,a ;hl=offset address pop d ; record type to e call GETBYT mov e,b ; 0-byte record? this is an alternate EOF. ; don't get data for such records mov a,c ;0-byte record? ora a jnz BYTLP ;N: go get data mvi e,1 ;make it type 1 jmp GCKSM ; loop to get record data and put it in buffer. ; ignore data outside the buffer. BYTLP: call GETBYT ;result in b lda EPSIZE ;overflow? dcr a cmp h ;beyond buffer? jc NOWRIT ;y: don't write or cnt push h ;compute buffer address push d lxi d,EBUF ;point into the buffer dad d ;hl = address in buffer mov m,b ;write data to buffer pop d pop h xthl ;bump written-byte cnt inx h xthl NOWRIT: inx h ;next byte in record dcr c jnz BYTLP ;check checksum GCKSM: call GETBYT ;get checksum byte jnz CSMERR ;checksum error ; record done. Check record type mov a,e ora a ;was it type 0? jz GETCO ;Y: get another record dcr a ;Type 1 is EOF jnz RTERR ;any other is an error ; The file is now in the buffer. (sp)=byte count. ;---------------------------- ; report number of bytes read ; Also used by BL command ;---------------------------- PRBC: call PCRLF pop h ;written-byte count call PDEC16 ;decimal byte count lxi d,LOADST jmp PRINTF ;return from there CSMERR: call ERRABT db 'Checksum fail$' RTERR: call ERRABT db 'Bad record type$' ;*****===================== ;* ? * Display help screen ;***** ;========================== FHELP1: ; 1234567890123456789012345678901234567890123456789012345678901234 db ' == SSM-PB1 Programmer Commands ==' db CR,LF db CR,LF db 'EPROM COMMANDS BUFFER COMMANDS' db CR,LF db ' EB [ []] Blank-Check EPROM DB Display Buffer' db CR,LF db ' EC [ []] Compare EPROM to Buffer FB Fill Buffer' db CR,LF db ' EP [ []] Prog EPROM from Buffer MB Modify Buffer' db CR,LF db ' ER [ []] Read EPROM into Buffer (Space skips, Q quits)' db CR,LF db ' ES Compute EPROM Checksum' db CR,LF db CR,LF db 'FILE <-> BUFFER TRANSFER COMMANDS PROGRAMMER SETTINGS' db CR,LF db ' AO Set Address Offset ET <2704/2708/2716>' db CR,LF db ' BL [.BIN] Load Bin File MS <1-255> mSec Prog Time' db CR,LF db ' HA [.HEX] Load Hex, Auto-Offset' db CR,LF db ' HL [.HEX] Load Hex File' db CR,LF db ' HS [.HEX] Save to Hex File' db '$' ;*************** ;* ENTRY POINT * ;*************** FHELP: lxi d,FHELP1 ;print 1st half of help screen call PRINTF lda SCRNSZ ;will it all fit onscreen? ora a ;0 means no cz ASKQSP ;n: wait for space from user call PCMAIN ;print 2nd half of help screen db CR,LF db 'OTHER COMMANDS' db CR,LF db ' SE Display Settings QU Quit to CP/M' db CR,LF db ' SS <0/1> Screen Size 16X64/24X80 ? Print Help Screen' db CR,LF,CR,LF db ' No value for EPROM byte count goes to the end of the EPROM.' db CR,LF db ' No value for EPROM address starts at EPROM address 0.' db '$' ;===Subroutine================== ;Fill EBUF ; On Entry: ; e = fill value ; Trashes a,bc,hl ;=============================== FILBUF: lxi b,EBUFSZ*256 ;size of buffer lxi h,EBUF ; fall into FILBLK ;===Subroutine================== ;Fill block ; On Entry: ; bc = number of bytes to fill ; e = fill value ; hl = starting address ; ; On Exit: ; bc=0 ; Trashes a,hl ;=============================== FILBLK: mov m,e ;loop to fill buffer inx h dcx b mov a,b ora c jnz FILBLK ret ;===Subroutine=========================================== ; Set OFFSET according to the address ; in the 1st record. ; On entry: ; hl=address of 1st chr of 1st command paramter, if any ; On Exit: ; OFFSET=1K page address of hl ; a trashed, all others preserved ;======================================================== SETOFF: push d push h lxi d,FADDST ;announce address call PSTH16 ;de=address mov a,d ;compute page address ani 0FCh ;1K pages mov h,a mvi l,0 shld OFFSET ;save new offset call PAO ;announce file offset pop h pop d xra a ;clear auto-offset mode sta AUTOFS ret ;===Subroutine===================================== ; Get 2 hex digits from the disk file, combine them ; into a byte, and add the result to the checksum ; ON ENTRY: ; d = Checksum ; ON EXIT: ; b=Data byte ; a=d=Updated checksum ; z set if checksum is 0 ; All other registers preserved ;================================================== GETBYT: call RDHEX ;get high nibble jnc ASCERR ;error if invalid hex add a ;do high nibble add a add a add a mov b,a call RDHEX ;get low nibble jnc ASCERR ;error if invalid hex ora b ;combine nibbles mov b,a ;save for return add d ;do checksum mov d,a ret ; ASCII chr error - not a valid hex digit ASCERR: call ERRABT db 'Invalid hex chr$' EOFERR: call ERRABT ;unexpected EOF db 'Unexpected EOF$' ;===Subroutine========================== ; Read and convert hex digit into A<3:0> ; On Exit: ; c cleared if bad hex digit ; a=binary result ;======================================= RDHEX: call RDBYTE jc EOFERR ;unexpected EOF? ; fall into HEXCON ;=====Subroutine================= ; Convert a from hex to binary ; carry cleared if bogus hex ; z cleared and c set if good hex ; All regs preserved ;================================ HEXCON: sui '0' ;remove ASCII bias cpi 10 rc ;if 0-9 then we're done sui 'A'-'9'-1 ;subtract gap to 'A' cpi 10 ;between ASCII 9 and A? cmc ;so carry means good rnc ;error exit cpi 10H ;above ASCII F? ret ;c set if okay ;===Subroutine======================================== ;Read one disk byte from SECBUF ; Load from disk if SECBUF is empty ; For speed, this routine buffers up to SBUFSZ blocks ; (sectors) into SECBUF each time it reads from disk. ; On Entry: ; SBBCNT = number of remaining bytes in SECBUF ; SBPNTR = address of next byte to send ; On Exit: ; SBBCNT, SBPNTR updated ; carry set if EOF ; chr in a ;===================================================== RDBYTE: push b push d push h lhld SBBCNT ;decrement byte count mov a,h ora l ;empty? go read disk ;also clears carry cz RDFILL ;empty? get more jc RDBDON ;eof: done with c set dcx h ;dec the byte counter shld SBBCNT lhld SBPNTR ;now get the byte mov a,m ;result in a for ret inx h ;and bump pointer shld SBPNTR RDBDON: pop h pop d pop b ret ;value in a, cy valid ;===Subroutine======================================== ; Read as many sectors from the disk as possible into ; SECBUF ; On Entry: ; On Exit: ; ABORT if EOF ; hl = number of data bytes in SECBUF ; SBPNTR = SECBUF ; carry set if EOF and no data ;===================================================== RDFILL: lda EOFLG ;Have we seen the EOF? ora a stc ;carry means no data rnz mvi b,SBUFSZ ;B=free buffs in SECBUF lxi d,SECBUF ;de=address in SECBUF RSECLP: mvi c,BSTDMA ;Set BDOS DMA address call GOBDOS ;trashes no registers xchg ;pointer to hl, free de lxi d,FCB ;Disk sect. into SECBUF mvi c,BREAD call GOBDOS ;trashes no registers ora a ;Read ok? jnz RSCOND ;No: go find out why lxi d,SECSIZ ;next sector dad d xchg ;Result goes in de dcr b jnz RSECLP ;go until all space used jmp RBFULL ;No: done reading ;------------------------------------------------ ;Unsuccessful read either because of EOF or error ;------------------------------------------------ RSCOND: dcr a ;EOF? jnz DSKERR ;---------------------------------------------- ;Encountered the EOF. Set EOFLG so we will stop ;---------------------------------------------- inr a ;a=1 sta EOFLG ;SET EOF FLAG ; fall into RBFULL ;--------------------------------------- ;Receive buffers are all full, or we got ;an EOF from CP/M. compute & save byte ;count, point SBPNTR to the 1st byte, ;clear carry ; On Entry: ; b = remaining sector space in SECBUF ; On Exit: ; hl = # of bytes read ;--------------------------------------- RBFULL: lxi h,SECBUF ;Point SBPNTR to start shld SBPNTR ;..of SECBUF mvi a,SBUFSZ ;compute # sectors read sub b ;b=remaining space rar ;compute # bytes read mov h,a ;=sec countx128 mvi a,0 ;don't mess up carry rar ;rotate carry in mov l,a ;hl=byte count ret ;carry is cleared DSKERR: call ERRABT db 'Disk error$' ;=====Subroutine================ ;Write A as 2 hex digits to disk ;and compute checksum in c ;On Entry ; A = value to print ;Trashes A ;=============================== WRHEX: push psw ;save digit add c ;compute checksum mov c,a pop psw ;recover digit push psw ;save low nibble rrc ;hi nibble 1st rrc rrc rrc call WHXNIB pop psw ;recover low nibble ;fall into PHXNIB ;------------------------------------ ;local subroutine to write hex nibble ;On Entry: ; Nibble in low 4 of A ;Trashes a,C ;------------------------------------ WHXNIB: ani 0Fh ;get low cpi 0Ah ;A-F? jc WHX1 adi 'A'-'9'-1 WHX1: adi '0' ;fall into WRBYTE ;===Subroutine================== ; Write one byte to disk buffer ; flush the buffer if full ;=============================== WRBYTE: push h push b push d lhld SBPNTR ;put the byte mov m,a inx h ;bump the pointer shld SBPNTR lhld SBBCNT ;and the count inx h mov a,h ;full? ;SBUFSZ must be even. cpi SBUFSZ/2 cz WFLUSH shld SBBCNT pop d pop b pop h ret ;===Subroutine======================================= ; Write all data in SECBUF to disk ; On Entry: ; hl=number of bytes to transfer, a multiple of 128 ; a=h ; On Exit: ; hl=0 (new SBBCNT) ; Trashes all other registers ;==================================================== WFLUSH: mov a,l ;compute # of sectors ral ;msb into carry mov a,h adc a ;a=sector count rz ;Return w/ Z set if end mov b,a ;Sector count in b lxi d,SECBUF ;de=start of sect data WFLOOP: mvi c,BSTDMA ;BDOS SET DMA function call GOBDOS ;de = DMA address xchg ;pointer to hl, free de shld SBPNTR ;reset pointer lxi d,FCB ;Write from buf to disk mvi c,BWRITE call GOBDOS ora a ;return 0 if okay jnz WRERR ;oops, error ;hl = address in SECBUF lxi d,SECSIZ ;de=sector size dad d ;(hl)=next sector data xchg ;addr to de for BSTDMA dcr b jnz WFLOOP ;until all sectors sent lxi h,0 ;new BYTECNT for ret ret WRERR: call ERRABT db 'Write fault$' ;===Subroutine============================================ ; Get file name from input line and install it in the FCB ; make sure it is the requested type ; On Entry: ; Z set if end of cmd line already ; de=address of desired file-type string ; hl=address of 1st chr of 1st command parameter, if any ; On exit: ; Trashes all regs ;========================================================= GETFN: push d ;desired file type string lxi d,FCBDD ;drive descriptor within FCB inx h ;was a drive letter included? mov a,m dcx h cpi ':' cz GETDR ;y: go get the drive number ;get the file name inx d ;file name within FCB lxi b,9*256 + '.' ;8 chrs for a file name call GETNAM ;..terminate on period ;get file extension, if there is one (carry set if '.' found) lxi d,FCBFT lxi b,4*256 + 0 ;3 chrs for extension cc GETNAM ;..and don't hunt for .'. ;if no file type given, then set it to the desired type pop d ;desired file type string lxi h,FCBFT lxi b,3 ;move/test 3 bytes mov a,m cpi ' ' ;any file type? cz BLKMOV ;saves all regs but a ;make sure desired file type at de matches FCBFT push d ;desired type for error msg GFNLP: ldax d xra m ora b mov b,a inx d inx h dcr c jnz GFNLP pop d ;desired type for error msg ora a ;any mismatches? rz ;n: done ;wrong file type - error push d ;desired type on stack lxi d,WFTST ;error: file must be call CRPRNT jmp PRMAIN ;string is on stack ;===Subroutine============================================= ;Get file name or extension and write it into the FCB at de ; On Entry: ; b = # of characters for the name + 1 ; c = end chr in addition to space (0 for none) ; de = destination address for the name ; hl = next chr in input buffer ; On Exit: ; carry set if chr matched c ; a=terminating chr (null, space, or period) ; hl=address of input chr after last one used ; trashes de ;========================================================== GETNAM: call GETUC ;get input chr, z set if rz ;..none or space cmp c stc ;in case of match rz dcr b jz CMDERR ;error if too many chrs stax d ;new chr into FCB inx d jmp GETNAM ;***Local Subroutine************************** ;Get Drive Descriptor and save it in the FCB ; On entry: ; b=remaining bytes in LINBUF ; hl=address of next input chr ; (hl+1) is know to be a colon ; de=address for drive descriptor in the FCB ; ON Exit: ; (de)=drive descriptor from input ; hl points to chr after colon ;********************************************** GETDR: call GETUC ;get drive descriptor inx h ;skip colon sui 'A' cpi MAXDRV+1 jnc CMDERR inr a ;since 0 means default stax d ;drive descriptor into FCB ret ;===Subroutine========== ; Open disk file ; On Entry: ; de points to filename ; trashes a,c ; Abort if error ;======================= OPNFIL: lxi d,FCB ;FCB has file to open mvi c,BOPEN ;FILE OPEN function call GOBDOS inr a ;-1 means open failure rnz call ERRABT db 'File not found$' ;===Subroutine================================== ; Get address and byte count from user ; On Entry: ; z set if no parameters ; hl=address of 1st chr after cmd ; On Exit: ; bc = byte count ; de=start address in EBUF buffer ; hl=start address in EPROM ; If no given start address, then default to 0 ; If no given count, then default to the ; size of the EPROM. ; If user-supplied count is beyond the ; end of the EPROM, clip it to the end ;=============================================== GETAC: call GETHEX ;get de=start address ;returns 0 if no param ;Error if the provided start address is ;greater than the last EPROM address lda EPSIZE ;past EPROM end? dcr a cmp d jc CMDERR ;y: huh? ;get the byte count. If none is given, or if the ;value is 0, then set it to FFFF, so it will ;get clipped in the next step push d ;save start address call GETHEX ;get de=byte count mov a,d ora e ;zero or no value? jnz GETAC1 dcr d ;de=FFFF: force clip GETAC1: ;if the provided byte count goes past the end of the ;EPROM, then clip it to exactly the EPROM end. ;first, compute remaining EPROM bytes ;hl := EPSIZE-start ;(sp) = start, de=byte count pop h ;recover start push h ;save start xra a ;low byte of EPSIZE sub l mov l,a lda EPSIZE ;now the high byte sbb h mov h,a ;hl=remaining bytes ;next, compare count to remaining bytes: hl-de mov a,l sub e mov a,h sbb d ;carry set if de>hl jnc NOCLIP ;clip byte count hl, (which goes just to the end ;of the EPROM) and announce it lxi d,CLIPST ;'count adjusted to' ;hl=value call PSTH16 ;print string and hex ;returns value in de NOCLIP: xchg ;put count into hl ;compute actual RAM address for the buffer ;RAM address = start address + EBUF xthl ;count on stack, get start push h ;save start onto stack lxi d,EBUF dad d ;hl=buffer address xchg ;de=buffer address for ret ;compute actual RAM address for the EPROM ;RAM address = start address + (BSADDR) pop h ;get start again lda BSADDR add h mov h,a ;hl=EPROM address for ret pop b ;bc=count for ret ret ;====Subroutine===================== ; Print error address and found data ; hl=EPROM address ; (hl)=found (EPROM) data ;=================================== BLAERR: push d push h ;get EPROM data while EPROM bank is still active mov a,m ;get bad data push psw ;save bad data ;compute & print EPROM address = hl-(BSADDR) ;note that the low byte of (BSADDR) = 0 xchg ;de=offending address lxi h,BSADDR mov a,d sub m mov h,a mov l,e ;hl=address in EPROM lxi d,ERRAST ;error at: call PSTH16 ;string in de, addr in hl ; print found data lxi d,FNDST ;. found: call PRINTF pop psw ;recover bad data call PRHEX ;..and print it lhld ERRCNT ;remember error inx h shld ERRCNT call TESTQ ;user abort? pop h pop d ret ;===Subroutine====================================== ; Print error address, found data, and expected data ; On Entry: ; hl=EPROM address ; (hl)=found (EPROM) data ; (de)=expected (Buffer) data ; On Exit: ; a trashed ; SCRFLG=0 (cancels any scripts) ; all regs preserved ;=================================================== CMPERR: call BLAERR ;prnt addr & EPROM data ;..and select OSBANK push d lxi d,EXPST ;Expected: call PRINTF pop d xra a ;cancel any script sta SCRFLG ldax d ;get expected data jmp PRHEX ;print value & ret ;===Subroutine==================== ; Print ERROR: message ; On Entry: ; message address on stack ; On jump to PRMAIN: ; SCRFLG=0 (cancels any scripts) ; message address on stack ;================================= ERRABT: xra a ;cancel any scripts sta SCRFLG lxi d,ERRST ;error: call CRPRNT jmp PRMAIN ;string address on stack ;===Subroutine= ;Print CR, LF ;trashes a ;============== PCRLF: push d lxi d,CRLFST call PRINTF pop d ret ;=====Subroutine======================== ; Print CR,LF,$-terminated message at de ; trashes a,de ;======================================= CRPRNT: call PCRLF ;fall into PRINTF ;=====Subroutine================== ; Print $-terminated message at de ; trashes a,de ;================================= PRINTF: push b mvi c,BPRINT call GOBDOS pop b ret ;===Subroutine====================== ; Print string at de, then hl as hex ; on exit: ; de has value ; trashes a,hl ;=================================== PSTH16: call CRPRNT ;print string at de lxi d,COLSPC ;then a colon & a space call PRINTF xchg ;print value in hl ; fall into PHEXDE ;=====Subroutine========== ;print de as 4 hex digits ;trashes a ;========================= PHEXDE: mov a,d ;high byte call PRHEX mov a,e ;low byte ; fall into PRHEX ;=====Subroutine======== ;Print A as 2 hex digits ;On Entry ; A = value to print ;Trashes a ;======================= PRHEX: push psw ;save digit rrc ;hi nibble 1st rrc rrc rrc call PHXNIB pop psw ;recover lo nibble ;fall into PHXNIB ;------------------------------------ ;local subroutine to print hex nibble ;On Entry: ; Nibble in low 4 of A ;Trashes a ;------------------------------------ PHXNIB: ani 0Fh ;get low cpi 0Ah ;A-F? jc PHX1 adi 'A'-'9'-1 PHX1: adi '0' ;fall into PRINTA ;===Subroutine========== ; Print A on the console ; Trashes a ;======================= PRINTA: push b push d mov e,a mvi c,BCONOT call GOBDOS pop d pop b ret ;===Subroutine==================== ;print CRLF, then de as an ;address, then a colon and a space ;trashes a,de ;================================= PCRADR: call PCRLF ;new line call PHEXDE ;4 digit address mvi a,':' call PRINTA mvi a,' ' jmp PRINTA ;===Subroutine================= ; Incomplete EPROM ; Ask whether to continue ;============================== PEASK: lxi d,PWST jmp EASKYN ;===Subroutine================= ; Non-blank EPROM ; Ask whether to continue ;============================== NBASK: lxi d,NBEST EASKYN: call CRPRNT lxi d,CONTST ;continue? jmp ASKCON ;===Subroutine================ ;Ask user Y or N ;On Entry: de = initial string ;Trashes a,c,de ;============================= ASKYN: lxi d,ASKST ;(Y/N)? ASKCON: call PRINTF call CONIUC ;get uppercase cpi 'Y' rz call PCMAIN ;abort if not Y db 'Abort$' ;=====Subroutine=================== ; Print A as decimal on the console ; Trashes all registers ;================================== PDEC8: mov l,a mvi h,0 ;fall into PDEC16 ;=====Subroutine=================== ;Print hl as decimal on the console ; Trashes all registers ;================================== PDEC16: mvi d,0 ;Suppress leading 0's lxi b,-10000 call DECDIG lxi b,-1000 call DECDIG lxi b,-100 call DECDIG lxi b,-10 call DECDIG mov a,l ;last digit is simple jmp DECDG0 ;with leading 0's ;------------------------------------------------ ; Local subroutine to divide hl by power of 10 in ; bc and print result, unless it's a leading 0. ; On Entry: ; hl=Dividend ; bc=divisor (a negative power of 10) ; d=0 if all prior digits were 0 ; On Exit: ; Quotent is printed, unless it's a leading 0 ; hl=remainder ; d=0 iff this and all prior digits are 0 ;------------------------------------------------ DECDIG: mvi a,0FFh ;will go 1 too many times push d ;leading zero state DIGLP: mov d,h ;de gets prev value mov e,l inr a dad b ;subtract power of 10 jc DIGLP xchg ;hl has remainder pop d ;leading 0 state mov e,a ;e has digit to print ora d ;leading 0 to suppress? rz ;yes: digit is done mov d,a ;save for next digit mov a,e ;make ASCII DECDG0: adi '0' jmp PRINTA ;=====Subroutine========================= ; get decimal parameter < 256 from user ; On Entry: ; hl points to first chr of a parameter ; On Exit: ; b=value, 0 if nothing entered ; hl points to first chr of next param ; Rude jump to CMDERR if bogus chr ;======================================== GETDEC: mvi b,0 ;initial value GDLOOP: call GET1 ;get a digit jz SKIPSP ;exit if none left sui '0' ;de-ASCII cpi 10 ;carry means below 10 jnc GPERR push psw ;save new digit mov a,b ;compute b=b*10 rlc ;*2 rlc ;*4 add b ;*5 rlc ;*10 mov b,a pop psw ;recover 1st digit add b ;combine digits mov b,a ;result into b jnc GDLOOP ;overflow means error ; fall into GPERR GPERR: jmp CMDERR ;bogus: rude abort ;=====Subroutine========================== ; get a multidigit hex value from the user ; Allow lowercase for a-f ; On Entry: ; hl points to first hex digit ; On Exit: ; b=number of digits found ; de=value ; hl points to next input param ; Rude exit if bogus ;========================================= GETHEX: lxi d,0 ;initial value mov b,d ;b counts digits GHLUP: call GETUC ;get an input digit jz SKIPSP ;no more? then exit call HEXCON ;convert to binary jnc GPERR inr b ;bump digit count xchg ;shift prev digits left 4 dad h dad h dad h dad h xchg add e ;combine new digit mov e,a jmp GHLUP ;keep going until gone ;=====Subroutine===================== ; Skip over spaces in cmd line ; On Entry: ; hl=address in cmd line ; On Exit: ; a=next chr on line ; hl=address of first chr past space ; Z set and a=0 if no params found ;==================================== SKIPSP: mov a,m ora a ;end of cmd already? rz ;Y: ret with z set cpi ' ' ;space? rnz ;n: ret with z clear inx h ;y: skip it jmp SKIPSP ;=====Subroutine================================ ;Get 1 chr from input line, convert to uppecase ; On Entry: ; hl = address of next input chr ; On Exit: ; Z set if end of input (null) or space ; uppercase chr in a and hl advanced otherwise ;=============================================== GETUC: call GET1 rz cpi 'a' ;lowercase letter? rc ;n: done, z cleared ani 'a' XOR 'A' XOR 0FFh ;make uppercase ret ;z cleared ;=====Subroutine================================ ;Get 1 chr from input line ; On Entry: ; hl = address of next input chr ; On Exit: ; Z set if end of input (null) or space ; chr in a and hl advanced otherwise ;=============================================== GET1: mov a,m ;any chrs? ora a rz ;no:Z set cpi ' ' ;no digit? rz inx h ret ;=====Subroutine======================= ; Programming pacifier: ; Print a dot, then test for Q pressed. ; Rude jump to PRMAIN if Q or q ; z flag set if no chr waiting ; a=waiting chr AND 0BFh, if not Q or q ; (a=0 if chr is a space) ;====================================== PACIFY: mvi a,'.' ;print pacifier call PRINTA ; fall into TESTQ ;=====Subroutine======================= ; Test for Q pressed. ; Rude jump to PRMAIN if Q or q ; z flag set if no chr waiting ; a=waiting chr AND 0BFh, if not Q or q ; (a=0 if chr is a space) ;====================================== TESTQ: push b mvi c,BCONST call GOBDOS ora a mvi a,0FFh ;disambiguate space mvi c,BCONIN cnz GOBDOS pop b ani 'q' XOR 'Q' XOR 0FFh cpi 'Q' rnz xra a ;cancel script sta SCRFLG mov a,m ;clear program flip-flop ; Fall into DODONE DODONE: call PCMAIN db 'Done$' ;=====Subroutine=================== ;Get console chr, convert to ;uppercase, and echo ; On exit: ; chr is in a ; trashes c ;================================== CONIUC: mvi c,BCONIN call GOBDOS cpi 'a' ;need to fix case? rc ;n: done ani 'a' XOR 'A' XOR 0FFh ret ;=====Subroutine=============== ; Move block ; On Entry: ; de = source address ; hl = destination address ; bc = byte count ; On Exit: ; a=0 ; all other regs preserved ;============================== BLKMOV: push b push d push h BMLOOP: ldax d mov m,a inx h inx d dcx b mov a,b ora c jnz BMLOOP pop h pop d pop b ret ;=====Subroutine=================== ;Issue command to BDOS ; On exit: ; a is as BDOS returned it ; all other registers preserved ;================================== GOBDOS: push b push d push h call BDOS pop h pop d pop b ret ;=====Subroutine=================== ;Ask if we should continue ; quit to MAIN if Q ;================================== ASKQSP: push d lxi d,SCONST ;space to continue call CRPRNT WAITQS: call TESTQ ora a ;a=0 if space jnz WAITQS ;keep waiting pop d ret ;====================================== ; Command Table ; 4 bytes/entry ; Bytes 1 & 2 are the command ; Bytes 3 & 4 are the execution address ;======================================= ; EPROM Commands CMDTAB: DB 'EB' ;Blank check EPROM DW FBLANK DB 'EC' ;EPROM compare DW FCOMP DB 'EP' ;Program EPROM DW FPROG DB 'ER' ;REad EPROM to buffer DW FREAD DB 'ES' ;Checksum EPROM DW FCKSUM ; Buffer Comands DB 'DB' ;Display buffer DW FBDISP DB 'FB' ;fill buffer DW FFILL DB 'MB' ;Modify buffer DW FEDIT ; Programmer Options DB 'MS' ;set programming ms DW FSMS db 'ET' ;set 2704/2708 dw FSETYP ; File Commands DB 'AO' ;set address offset DW FSAO db 'HA' ;load hex, auto-offset dw FALOAD DB 'HL' ;Load hex file DW FHLOAD DB 'HS' ;Save hex file DW FHSAVE DB 'BL' ;load binary file DW FBLOAD ; Others DB 'SE' ;display settings DW FSET DB 'QU' ;quit DW WBOOT DB '?',0 ;help DW FHELP DB 'SS' ;Screen size DW FSCRSZ DB 0 ;Table End ;==================== ;$-terminated strings ;==================== BANNER: db '=============================================',CR,LF db '= SSM-PB1 EPROM Programmer/Reader/Editor =',CR,LF db '= Ver 1.00, May 2019, M Eberhard, M Douglas =',CR,LF db '=============================================',CR,LF db '$' BANR2: db CR,LF,'Type ? for help' ;fall into CRLFST CRLFST: db CR,LF,'$' PROMPT: db '>$' NBEST: db 'EPROM range is not blank$' PWST: db 'Programming partial EPROM not advised$' CONTST: db '. Continue?' ;fall into ASKST ASKST: db ' (Y/N)? $' SCONST: db ' -Space to continue, Q to quit-$' RDYST: db 'EPROM Power on, ready$' PROGST: db 'Programming$' VERST: db 'Verifying$' EPSST: db 'EPROM Type: 27$' PADRST: db 'Programmer address$' CADRST: db 'Command port address' ;continues to COLSPC COLSPC: db ': $' PAOST: db 'Target address offset$' PEMSST: db '2704/08 programming time: $' MSPBST: db ' mS/Byte$' TOTEST: db 'Total errors: $' ERRST: db 'Error: $' ERRAST: db 'Error at$' FNDST: db '. Found: $' EXPST: db ', expected: $' EXISTS: db 'File exists. Overwrite$' BCSST: db 'Buffer checksum: $' EPCST: db 'EPROM checksum: $' LOADST: db ' bytes loaded into buffer$' EBOST: db 'Error: Buffer overflow$' ADJUST: db 'Address adjusted to EPROM boundary$' OVCST: db 'Caution: EP will overwrite this program!$' CLIPST: db 'Byte count$' FADDST: db 'First record address$' WFTST: db 'Error: Must be .$' HEXST: db 'HEX$' BINST: db 'BIN$' ;-------------------------------- ;System Variables cleared at MAIN ;-------------------------------- CLRBEG: AUTOFS: ds 1 ;auto-offset mode EOFLG: ds 1 ;EOF encountered ERRCNT: ds 2 ;error count SBBCNT: ds 2 ;Count of bytes in SECBUF CLREND equ $ ;--------------------- ;More System Variables ;--------------------- SBPNTR: ds 2 ;Points to next byte in SECBUF BSADDR: ds 1 ;base address of EPROM, high byte EPSIZE: ds 1 ;256-byte pages/EPROM SCRNSZ: ds 1 ;Screen size: 0=16X64, 1=24X80 EPMS: ds 1 ;mS of EPROM programming OFFSET: ds 2 ;16-bit Hex Rec Address Offset SCRPTR: ds 2 ;points to next script byte SCRFLG: ds 1 ;nz means script present ;----------- ;Local stack ;----------- ds 64 ;room for stack STACK equ $ ;top of stack ;--------------------- ;Buffer for EPROM data ;--------------------- EBUF: ds EBUFSZ*256 EBUFND: equ $ ;------------------ ;Disk Sector Buffer ;------------------ SECBUF: ds SBUFSZ*SECSIZ ;------------- ;Script buffer ;------------- SCRBUF ds LBSIZE RAMEND equ $ END