OK, in the previous part, I did a lot of investigating and work to accomplish...nothing (that is, I figured out how to downline load a program that had a HALT instruction as its only code, which then halted). While nothing is one of my all time favorite things, bar none, it's not what is needed here. With no OS, no debugger and no system calls available, I would need a way to print messages out on the console. A glimp in the MicroVAX1 architecture manual shows me that there are internal processor registers (^X22 and ^X23) that work like "pseudo DL11" registers, If I deposit a character in the internal register that works like the DL11 write data register, it gets printed on the console. There's another register that has a ready bit in it, so you know when it's digested a character and is ready for another one.
char: .ascii /A /
ploop:
mfpr #^X22,r3 ;get the status of console
tstb r3 ;see if it's busy (just like a CSR, nicht wahr?)
bgeq ploop ;if busy, come back around and check again
mtpr char,#^X23 ;jam longword containing byte into an internal register
Just like for a DL11, only with processor registers instead of CSR and data registers.
Note that you test the status byte bit 7 to see if it's busy or not, just like the CSR on a DL11.
From writing bytes, it's a short step to writing a string...
;+
; pstr - print string
; r0 - len to print
; r1 - addr of thing to print
;-
pstr:
pushr #^m<r0,r1,r2,r3>
pdoone: movzbl (r1)+,r2 ;get next byte to print
phloop: mfpr #^X22,r3 ;get the status of console
tstb r3 ;see if it's busy (just like a CSR)
bgeq phloop ;if busy, come back around and check again
mtpr r2,#^X23 ;jam byte into an internal register
sobgtr r0,pdoone ;well, that byte's done - got any more?
popr #^m<r0,r1,r2,r3>
rsb
And a routine to print a CR LF, so we can have multiple lines...
pcrlf:
pushr #^m<r0,r1>
movl #2,r0 ;we're printing two characters
moval crlf,r1 ;a CR and an LF
jsb pstr ;do it
popr #^m<r0,r1>
rsb ;and we're done
It turns out that for this project I didn't need to enter anything on the console, so I didn't write routines to support that - but it too would be pretty much like programming a DL11 to read input, using internal registers ^X20 and ^X21 for status and data buffer.
Now, I can hear what you're all thinking..."But Gleason, this will print out ASCII strings - what do you do when you need to print out binary data?" I'm glad you all asked that question. I wrote a convert binary to hex ASCII routine.
;+
; cvtbtha - convert binary to HEX ascii
; R0 - # of bytes to convert
; R1 - addr of binary bytes
; R2 - addr of ascii buffer
;-
cvtbtha:
pushr #^M<r0,r1,r2,r3>
hloo:
movzbl (r1)+,r3 ;get next byte
movw hextab[r3],(r2)+ ;look up two byte ASCII value in table
sobgeq r0,hloo ;any more to do?
popr #^m<r0,r,r2,r3>
Simple, nicht wahr? But it requires a data table. My old pal Dr. Bob pointed out that a precomputed table can often significantly reduce the amount of code required. Here's the table
;+
; Hex translation table. A few bytes of table can save a lot of code...
;-
hextab::
.ascii /000102030405060708090A0B0C0D0E0F/
.ascii /101112131415161718191A1B1C1D1E1F/
.ascii /202122232425262728292A2B2C2D2E2F/
.ascii /303132333435363738393A3B3C3D3E3F/
.ascii /404142434445464748494A4B4C4D4E4F/
.ascii /505152535455565758595A5B5C5D5E5F/
.ascii /606162636465666768696A6B6C6D6E6F/
.ascii /707172737475767778797A7B7C7D7E7F/
.ascii /808182838485868788898A8B8C8D8E8F/
.ascii /909192939495969798999A9B9C9D9E9F/
.ascii /A0A1A2A3A4A5A6A7A8A9AAABACADAEAF/
.ascii /B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF/
.ascii /C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF/
.ascii /D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF/
.ascii /E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF/
.ascii /F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF/
That gives me the basics. As an aid to coding, I wanted a way to print out a message in one line of code, without having to allocate data for a string and deal with calling the print routine. Something like
mess <this is a diag message>
Mess, being short for message, not a judgement on the esthetics of the code.
This was easy to do, although it did mix data in with the code, something I normally don't like to do. But, this whole project is nothing normal.
Here's how I did it. I wrote a macro, mess.
.macro mess string,?a,?b ;enclose mess in <>
pushr #^m<r0,r1>
brb b ;branch around the deposited text
a: .ascii /string/
slen = . - a ;we'll need its length
.even
b:
moval a,r1 ;addr to use
movl #slen,r0 ;and how long
jsb pstr
jsb pcrlf
popr #^m,r0,r1>
.endm mess
pcrlf:
pushl r0 ;save what ya use
pushl r1
movl #2,r0 ;we're printing two characters
moval crlf,r1 ;a CR and an LF
jsb pstr ;do it
popl r1 ;ok, put 'em back
popl r0
rsb ;and we're done
Mess saves a couple of registers, and then jumps around the following bytes where the ASCII string is stored. Then it calls pstr using the above string as arguments. After that it calls pcrlf, which prints out a carriage return and a line feed. That's needed often enough by itself that it got its own routine.
Well, that's enough of this for now. Next part will discuss setting up the System Control Block.
No comments:
Post a Comment
Comments?