; ROMAN.MAC ; ; Converts decimal numbers to Roman numerals. ; Vers equ 03 SubVers equ 'a' ; ; Version 0.3a -- April 7, 1990 -- Gene Pizzetta ; Fixed minor bug, discovered by Howard Goldstein, that caused ; strange behavior if a character less than '0' was entered. ; ; Version 0.3 -- April 1, 1990 -- Gene Pizzetta ; Converted to ZCPR3 utility. Extensively modified screen displays. ; Added command line mode and usage message. ; ; Version 0.2a -- February 13, 1983 -- D. McLanahan, Marlow, NH ; Modified for CP/M. ; ; Version 0.2 -- October 10, 1978 -- M. Pedder ; From SIG/M disk volume 75 ; ; Original code copyright (c) 1978 M. Pedder. ; ; Definitions . . . ; WBoot equ 00h ; warm boot Bdos equ 05h ; BDOS entry DmaBuf equ 80h ; default DMA buffer ; CtrlC equ 03h CR equ 0Dh LF equ 0Ah ; MACLIB Z80 ; ext z3init,gzmtop,getefcb,cin,cout,epstr,phl4hc ; ; TYP3HDR.MAC, Version 1.1 -- Extended Intel Mnemonics ; This code has been modified as suggested by Charles Irvine so that ; it will function correctly with interrupts enabled. ; Extended Intel mnemonics by Gene Pizzetta, April 30, 1989. ; ; This is header code that can be used at the beginning of a type-3-environment ; program so that it will abort with an error message when not loaded to the ; correct address (such as when a user tries to run it under CP/M or Z30). ; Entry: jr start0 ; Must use relative jump db 0 ; Filler db 'Z3ENV',3 ; Type-3 environment Z3EAdr: dw 0 ; Filled in by Z33 dw entry ; Intended load address ; start0: lxi h,0 ; Point to warmboot entry mov a,m ; Save the byte there di ; Protect against interrupts mvi m,0c9h ; Replace warmboot with a return opcode rst 0 ; Call address 0, pushing RETADDR onto stack retaddr: mov m,a ; Restore byte at 0 dcx sp ; Get stack pointer to point dcx sp ; ..to the value of RETADDR pop h ; Get it into HL and restore stack ei ; We can allow interrupts again lxi d,retaddr ; This is where we should be xra a ; Clear carry flag push h ; Save address again dsbc de ; Subtract -- we should have 0 now pop h ; Restore value of RETADDR jz Start ; If addresses matched, begin real code ; lxi d,notz33msg-retaddr ; Offset to message dad d xchg ; Switch pointer to message into DE mvi c,9 jmp 0005h ; Return via BDOS print string function ; notz33msg: db 'Not Z33+$' ; Abort message if not Z33-compatible ; ; Messages . . . ; MsgSOn: db 'ROMAN Version ' db Vers/10+'0','.',Vers mod 10+'0',SubVers db ' (loaded at ',0 MsgSO1: db 'h)',CR,LF,0 MsgUse: db 'Converts decimal numbers to Roman numerals.',CR,LF db 'Syntax:',CR,LF,' ',0 MsgNam: db 'ROMAN',0 MsgUs1: db ' {decimal number}',CR,LF db 'Decimal number may be from 1-3999. If no number',CR,LF db 'is given, ROMAN will enter interactive mode.',0 MsgInf: db CR,LF,'Enter number (1-3999). ^C to exit.',CR,LF,0 MsgSpc: db ' ',0 MsgPpt: db CR,LF,'>> ',0 Msg2Lg: db ' Too Large!',0 MsgInv: db ' Invalid character!',0 RomNum: db 0,0,'IVXLCDM' ; ; Start of program . . . ; Start: lhld Z3EAdr ; set up environment call Z3init sspd OldStk ; save old stack pointer call gzmtop ; get top of memory sphl ; ..and set new stack lxi d,DmaBuf ; see if there's a command tail ldax d ora a jrz NoTail inx d call EatSpc ani 7Fh ; strip parity bit cpi 0 jrz NoTail cpi '/' ; request for help? jz Help ; (yes) lxi h,MsgSpc ; space over call epstr lxi b,DecBuf ; point to decimal buffer lxi h,DecCnt ; point to decimal counter mvi m,0 ; zero counter GetCLp: cpi ' ' jrz GetCL1 cpi 0 jrz GetCL1 call Echo ; check and echo each character jp Exit inx d ldax d jr GetCLp ; GetCL1: mvi a,' ' ; print space, equal, space call cout mvi a,'=' call cout mvi a,' ' call cout mvi a,0 ; mark end of string stax b call Encode ; output Roman jmp Exit ; NoTail: lxi h,MsgSOn ; print sign-on message call epstr lxi h,Entry ; print load address call phl4hc lxi h,MsgSO1 call epstr lxi h,MsgInf ; print interactive instructions call epstr Ready: call PrtPpt ; print prompt call Input ; get number from keyboard jrc Ready call Encode ; encode and output it jr Ready ; ; PrtPpt -- set up and print interactive prompt ; PrtPpt: lxi h,MsgPpt ; print prompt call epstr lxi b,DecBuf ; point to decimal buffer lxi h,DecCnt ; point to counter mvi m,0 ; clear counter ret ; ; Input -- get input from console into DecBuf, checking validity ; Input: call cin ani 7Fh ; strip parity bit cpi CtrlC ; abort? jrz Exit ; (yes) cpi CR ; is it a carriage return? cnz Echo ; (no, echo it to screen) jm Input ; (continue) rc ; (error) mvi a,' ' ; print a space, equal, space call cout mvi a,'=' call cout mvi a,' ' call cout mvi a,0 ; mark end of string stax b ora a ; and clear flag ret ; Exit: lspd OldStk ; restore old stack pointer ret ; ; Help -- print help message and exit ; Help: lxi h,MsgSOn ; print sign-on call epstr lxi h,Entry ; print load address call phl4hc lxi h,MsgSO1 call epstr lxi h,MsgUse ; print usage call epstr call getefcb ; get program name jrz NoEFcb ; (no external fcb) mvi b,8 CNamLp: inx hl mov a,m ani 7Fh cpi ' ' cnz cout djnz CNamLp jr Help2 ; NoEFcb: lxi h,MsgNam call epstr Help2: lxi h,MsgUs1 call epstr jr Exit ; Echo: call cout ; echo character cpi '0' ; is it zero or more? jm InvChr ; (no) cpi ':' ; is it nine or less? jp InvChr ; (no) inr m ; count it stax b ; store it inx b ; advance pointer mov a,m ; check count cpi 5 ; is it less than 5 rm ; (yes) lxi h,Msg2Lg ; print "too large" jr OutMsg ; InvChr: lxi h,MsgInv ; print "invalid character" OutMsg: call epstr ora a ; reset sign flag stc ; set error flag ret ; ; Encode -- encodes numbers and then outputs Roman numerals ; Encode: lxi b,DecBuf ; point to decimal buffer lxi d,RomNum ; point to Roman numerals lxi h,DecCnt ; point to decimal counter mov a,m ; check count cpi 4 ; is it 4? jrnz Cont ; (no, continue) ldax b ; get first character cpi '4' ; is it a "4"? jm Cont ; (no, continue) lxi h,Msg2Lg ; print "too large" call epstr ret ; Cont: mov l,m ; get count to HL mvi h,0 dad h ; double it dad d ; add to list xchg ; and restore list lxi h,RomBuf ; point to Roman character buffer ; ; encode character stream in DecBuf ; Encod1: ldax b ; get a character inx b ; update pointer cpi 0 ; is it end marker? rz ; (yep) cpi '9' ; is it a nine? jrz Nine ; (it is) cpi '5' ; is it five or more? jp Five ; (you bet) cpi '4' ; is it four? jrz Four ; (uh-huh) EncAgn: cpi '0' ; a zero? jrnz One ; (nope) EncExt: dcx d ; decrement Roman pointer dcx d lda DecCnt ; decrement count dcr a sta DecCnt jrnz Encod1 ; (loop until zero) mvi a,0 ; mark end call Store ; ..and store it jr ROut ; we're done ; ; ; One -- 1 = I, 10 = X, 100 = C, 1000 = M or more ; One: push psw ; save number ldax d ; get Roman character call Store ; store it pop psw ; get back number dcr a ; subtract one jr EncAgn ; ..and try again ; ; Four -- 4 = IV, 40 = XL, 400 = CD ; Four: ldax d ; get Roman character I, X, or C call Store ; store it inx d ; get next Roman character V, L, or D ldax d call Store ; store it dcx d ; restore pointer jr EncExt ; ..and exit ; ; Five -- 5 = V, 50 = L, 500 = D or more ; Five: push psw ; save number inx d ; move pointer ldax d ; get Roman character V, L, or D call Store ; store it dcx d ; restore pointer pop psw ; get back number sui 05h ; subtract five jr EncAgn ; ..and try again ; ; Nine -- 9 = IX, 90 = XC, OR 900 = CM ; Nine: ldax d ; get Roman character I, X, or C call Store ; store it inx d ; move pointer inx d ldax d ; get Roman character X, C, or M call Store ; store it dcx d ; restore pointer dcx d jr EncExt ; ..and exit ; ; ; Store -- store Roman character in RomBuf for output ; Store: mov m,a ; store data in buffer inx h ; move pointer ret ; ; ROut -- Roman output to console ; ROut: lxi h,RomBuf ; point to Roman buffer call epstr ; and output it ret ; ; EatSpc -- eats spaces ; EatSpc: ldax d cpi ' ' ; space? inx d jrz EatSpc ; (yes) dcx d ret ; DSEG ; ; Workspace . . . ; DecBuf: ds 5 ; decimal buffer RomBuf: ds 16 ; Roman buffer DecCnt: ds 1 ; decimal count OldStk: ds 2 ; old stack pointer ; end