Getting an MVME320B (Motorola VME HD controller) working - Page 1
Forum section: electronics / general

author: LittleFrog
author_rank: Contributor
timestamp: May 02, 2024, 04:39:20 pm
content: HiI'm reaching out to see if anyone has experience of the Motorola MVME320B disk controller card.This is quite a long posting, but the bones of it are: I have hooked everything up and to all intents and purposes, the disk controller does not show any sign of life on the HD interface ports no matter what I do. I can see evidence that the disk controller is not dead on the VME bus side.The detail is as follows;My hardware set up is a VME rack containing two cards;MVME110 processor card with a 68000 running 110Bug V4.1. It's fitted with 32K bytes static RAMMVME320B disk controller card with V4.2 firmware. This is the later version of the card which does all the disk I/O through ribbon cables out the front of the rack.The processor is in the left most slot and the disk controller is directly to the right of that.I am only trying to connect a hard disk, I've not attempted anything with floppy disks.I don't have any instructions for 110Bug but reviewing the source, I can see that there are basically two families of disk related commands;VersaDos boot through the "B*" commands. These have options on what to do after the boot blocks are loadedLow level interaction with the disk controller through the "IO*" commandsI don't have a valid VersaDos image so my investigations only relate to the low level commands.Actually I don't even have a real hard disk. I'm using one of these;https://www.pdp8online.com/mfm/mfm.shtmlthis has the advantage that you get some diagnostics while it's running and I can create just about any topology of disk that I want.The processor card serial port is connected to a Terminal Server like this;https://amzn.eu/d/3c6rNQYwhich comes in handy as I'll explain in a minute. Basically I can run puTTY across my LAN and at the same time send sequences of keystrokes to the processor using a little program I wrote.A characteristic of VME cards of this era is JUMPERS! They're everywhere. Also there seems to be a sensitivity to hardware/firmware revision status plus of course, the card(s) might be faulty. Anyway back to jumpers, I used the information here to set the jumpers;https://bitsavers.computerhistory.org/pdf/motorola/versados/MVMEVDOS_D2_VMEHwSwConf_May85.pdfThis works well for the processor card but unfortunately the disk controller card information relates to an earlier version of the card and the jumper layout has totally changed.So what I did was write down all the settings for the earlier version of the disk controller in the guide. Then I used the old disk controller user manual here;http://www.bitsavers.org/pdf/motorola/VME/MVME320/MVME320_D1_Disk_Controller_Users_Manual_Feb85.pdfto figure out what each jumper does and what it's set to.Then I went to the user manual for the new version of the card which is here;http://www.bitsavers.org/pdf/motorola/VME/MVME320/MVME320B_D1_Disk_Controller_Users_Manual_Jan88.pdfand using the information from the old card, I made sure that my settings are correct. I was heartened to see that actually there are no changes required from "default" which is good (I think).There are two problems with this processThere are many more jumpers on the new card so I just them to "default". I don't think this is a problem because most of the new jumpers relate to Floppy Disk options which I'm not too bothered about.In the User Manual for the older card, there is a section on "MVME320 backplane settings". This does not exist in the new card manual. Am I missing something to do with the backplane? Do I need to set something here too? I've assumed that the old manual is referring to the breakout panel, an MVME702 I think it's called. Anyway this mystery table is attached as a photo if you take a look.So I turn everything on and set my HD emulator running. It starts without error. I think it works because I've used it on PC's without a problem. I have a crude Logic Analyser connected to the HD control signals and although it's crude, I know that if anything moved, I'd at least see a trigger trace on the LA screen. As I implied back at the top of this posting, it's always showing "Everything high"; in other words, fairly dead.Because I don't have any documentation for 110Bug, I don't really understand what each command does. There is Bug documentation on cards like the MVME177 but the functions of each IO* command is massivly expanded and so is of limited use. I'm not good enough to figure out what the commands do by reading the source (sorry). I find it amazing that the code could contain so much annotation without actually explaining what user function is provides anyway the authors will have assumed the reader had the User Manual....I have figured out that in general, all the disk related command seem to take two arguments representing the HD ID and the controller. My HD is a number 0 and my controller is a number 4. I got this from the card user manuals.So first of all we have IOT and IOP. I think IOT teaches the controller about the geometry of the connected drives and IOP enables direct physical interaction with the drive. So getting to actually try something, if I enter;Code:IOTorCode:IOT 0,4orCode:IOPorCode:IOP 0,4The bug hangs and after about 20 seconds, the Fault LED lights up on the processor card. The only way out is a reset.This leaves us with IOC which enables the user to set up a control block in RAM and then (I'm assuming) point the disk controller at it and let it carry out the command.I don't know, but I'm assuming that the difference between IOC and IOP is that the former uses a RAM based control block (called an ECA in the manual) where as the IOP command enables you to poke the disk controller registers directly. That's just a guess of course.So if I enter;Code:IOCThen there is a delay of about 60 seconds and I get a prompt to enter the drive number, then the controller number. I enter 0 and 4.I am then presented with a PARTIAL memory dump of the ECA and I'm asked if I'm sure. I say "Y" and the bug goes dead for about 30 seconds and finally returns a PACK error.It's interesting that the ECA is longer than the memory dump. Don't know why this should be but it's missing the geometry section at the end. Not really a problem but interesting.When I do a dump of the ECA following the command running, I can see that the main status is $07 which is "Card busy". I don't think any other part of the ECA is altered.This is really good because I'd like to think that the disk controller tried to do something and timed out.Turning to the memory locations used by the disk controller card, if I dump the odd addresses, I see values. If I dump even addresses, I get bus errors. If I remove the card and try again, all memory addresses I try to dump give bus errors. This gives me hope that the disk controller is not totally dead.Returning to the ECA, loading it by hand is error prone so I wrote a little loader program that sends keystrokes to the serial port. This is a dumb process, but it does seem to work. Here's a list of the ECA I set up;Code:# Start loading the ECA as per MVME320 User Manual pg C-9# Commands described pg 6-1# Zap the buffer back to 00bf 110 170 00# Zap my sector buffer (not that I need one for the command I'm using)bf 1000 1800 00# Fill the ECAm 110# Command code (Recalibrate to track 0) $0000# Main status $0100# Extended status (Should be 00 before command) $020000# Max retries $040a# Actual retries $0500# DMA type $0600# Command options $0700# Buffer address (Hi word) $080000# Buffer address (Lo word)1000# Buffer length requested $0C0001# Buffer length transferred $0E0000# Cylinder number $100000# Head number00# Sector number01# Current cylinder position $140002# Reserved $1600000000000000000000# Pre index gap $2000# Post index gap00# Sync byte count00# Post ID gap00# Post data gap36# Address mark count03# Sector length code01# Fill bytee5# Reserved $28000000000000# Drive type - Soft sectored, Hard disk with CRC $2E03# Number of surfaces $2F04# Sectors per track $300a# Stepping rate $3106# Head settling time $3246# Head load time $3346# Seek type $3402# Reserved (Phase count) $3500# Low write current $360050# Precomp cylinder $380050# ECC remainder $3A000000000000# Appended EEC remainder $40000000000000# Reserved $4600000000# MVME320 working area $4A000000000000000000000000# Reserved for the controller $560ff0# Get out of data entry mode.# Present a dump of the commanndmd 110 5f# Actually send the command to the MVME320# ioc 0,4and here's the program that loads it (via that Terminal Server I mentioned before).Code:# Send a series of keystrokes to an MVME110 serial port via a terminal server connected# on the LAN. The keystrokes are held in a file, e.g. keystrokes.key# D. Waine 27/04/24# In order that this program function, you need to have the TS and the MVME110 switched on (!)# then start a terminal session using puTTY to the TS. Run this program and the# key strokes will load.import sysimport osimport socketfrom time import sleep# Location of the TS on the LANterminalAddress = '192.168.0.3'terminalPort = 8234if len (sys.argv) != 2:print ('Incorrect number of arguments. You need to supply the input .key file without an extension')sys.exit (2)filename = sys.argv fileExtension = '.key'inputFile = filename + fileExtensionlineCounter = 0outputCounter = 0# Open the input fileprint ('Opening input file: ', inputFile)with open(inputFile) as inFile:print ('Opening output stream to MVME110')sock = socket.socket()sock.connect((terminalAddress, terminalPort))print ('Connected')sleep (0.1)# Read it a record at a timefor line in inFile:# Trim the input line of CR/LF & any spacesinputLine = line.strip()# Bump the correct input record type counterlineCounter += 1if inputLine  != '#':sock.send ((inputLine + '\r').encode())sleep (0.1)outputCounter += 1sock.close()print ('Input records: ', lineCounter)print ('Sent records: ', outputCounter)If anyone can shine a light on any of this, I'd be grateful.Thanks for your attention.Dave

author: marcopolo
author_rank: Regular Contributor
timestamp: May 07, 2024, 08:04:13 pm
content: IOT is normally an interactive command used to define disk parameters.Take a look at the MVME101 documentation, which describes how to use the MVME319.

author: LittleFrog
author_rank: Contributor
timestamp: May 08, 2024, 07:12:09 am
content: Good morning MP, thanks for the really useful tip.

author: LittleFrog
author_rank: Contributor
timestamp: May 13, 2024, 07:00:40 pm
content: HiJust wanted to provide an update.... The MVME320 uses DMA to transfer data to/from connected disks. The only RAM I have in my system right now is on the MVME110 processor board. Reading a general document about the Motorola product line which you can find here;http://www.bitsavers.org/components/motorola/_dataBooks/1984_Motorola_16_32_Bit_Microcomputer_System_Components.pdfIn the chapter about the MVME110 (pg. 1-20, "Local memory", para 2) it says that;Local on-board RAM is not accessible from the VMEbusFurther, reviewing the source of the 110Bug, in program BO320.SA (the MVME320 driver routines), here's a fragment of code starting at line 416 with comments;Code:MOVE.L ENDON,D1 END OF ONBOARD RAM ADDRESSSUB.L D2,D1 CANNOT USE ONBOARD RAM WITH VME320BLT.S SETBUFR OK?MOVE.L #'RAM ',D1 No... Setup Reason for error messageBRA ERRORG . and exitI take the code and comments to indicate that if you're trying to transfer data to/from a RAM address that's on the MVME110, exit with a "RAM" error.Putting all that together, I think I need VME bus RAM to get anywhere.Dave

author: marcopolo
author_rank: Regular Contributor
timestamp: May 13, 2024, 07:18:22 pm
content: Try to to find an MVME202 or 222 DRAM board.

author: LittleFrog
author_rank: Contributor
timestamp: May 13, 2024, 08:49:11 pm
content: Hi MarcoThanks for the tip. I have managed to find several people who might have an MVME221 (static RAM) but I have no User Manual and as there are a mass of jumpers (too many for me to work out), I will look for a D-RAM alternative as you mention (manual on BitSavers).

author: marcopolo
author_rank: Regular Contributor
timestamp: May 14, 2024, 07:42:04 am
content: Quote from: LittleFrog on May 13, 2024, 08:49:11 pmHi Dave,Are you sure it's an MVME221 and not an MVME211?

author: LittleFrog
author_rank: Contributor
timestamp: May 14, 2024, 03:43:36 pm
content: Sorry. It's an MVME211 as you say.I've worked out some of the jumper settings but others (like base address and chip size) are beyond me.Kind regards,

author: marcopolo
author_rank: Regular Contributor
timestamp: May 14, 2024, 03:51:09 pm
content: Hello,I have this documentation on MVME211 jumpersRegards,Marc

author: LittleFrog
author_rank: Contributor
timestamp: May 15, 2024, 07:57:57 am
content: Morning Marco. Amazing. I'm so grateful. Thanks.

author: LittleFrog
author_rank: Contributor
timestamp: May 20, 2024, 03:05:10 pm
content: Having obtained jumper information (thanks Marco), I have been able to reconfigure the MVME211 so it BASICALLY works with my set up. I thought this might be useful for others if I documented my experience here...First off, I don't have a lot of experience with 68K, so there may well be some school boy errors in here. It's very likely that I've misunderstood something and so treat this information with suspicion and assume that there's at least one serious error (but I don't know where).Furthermore, there is a serious problem with testing the board after config. I explain at the bottom of this post.I'm using revision P of the MVME211. This maybe important because some of my comments refer to component positioning which might have changed.I'm trying to fit 16 off 32K * 8 RAM devices (HM62256ALP-10). This is going to be configured as two address contiguous banks of 8 off devices, each bank having 256K bytes.We're going to being using a base address for the whole board of $040000. The first bank will have a base address of $040000 and the second bank will have an address base of $080000Referring to the PDF attached to one of the previous posts, pg 2.Looking at the jumper pin signal information in the centre of the page, I think it's correct. These jumpers control what signals are fed through to memory device pins whose function varies depending on the device being used. It would be useful to know the function of each jumper group. This is what I found with a continuity tester;J1/J3 control what signal is fed to pin 26J6/J7 control what signal is fed to pin 1J12/J13 control what signal is fed to pin 27J2/J4 control what signal is fed to pin 28J14/J15 control what signal is fed to pin 23Given that I am trying to use HM62256ALP-10 devices, there is a massive problem with the above.... If you look at the memory device data sheet, A14 is on pin 1. In the case of a 68000, we need to connect A15 to this pin. As you can see from pg.2 of the PDF, J6/J7 only allow;A16+5VDCR/Wto be connected. For this reason, I traced out where to find A15 so I could "hot wire" the correct signal into pin 1. Results as follows;A15 is on buss connector P1, row C, pin 23This goes to an 'LS373, U22, pin 13This signal pops out on U22, pin 12This then can be found on jumper pack J8/J16, pin 8So the upshot is, don't fit any links to J6/J7 but hot wire J8 pin 8 to any pin on the top row of J6 and J7 (the top row pins are linked on the PCB).In general, Motorola documentation shows the jumpers in the correct orientation (e.g. Rotationally correct when viewing the PCB with connector P1 top-right). There is a major gotcha with J2/J4.The information is wrong, I think there are two faults and unfortunately there is no easy way of describing this because you can't just rotate or mirror the diagram.The jumpers are fitted left-right on the PCB, not north-south. Getting this wrong will short the battery and main +5VDC supplies.In summary, if you look at the PCB;The pins nearer the top of the PCB are +5VBThe pins nearer the bottom of the PCB are for +5VDCBack on pg.2 of the document, With the above in mind, and assuming you're going to use +5VDC to supply the memory, "most" the example settings at the top of the page are correct but J2/J4 should be rotated 90 degrees anti clockwise and you should also re-number the pins (look at the PCB to see what I mean).I said "most" because the example of 16K * 8 MCM63128 is actually correctly orientated (but the pins are still incorrectly numbered).I'm assuming that jumper information for the 2 types of 8K * 8 RAM devices was unintentionally left blank. It doesn't make sense as-is.Finally on pg.2, I used the "logical equivalent number 4" setting which is for 32K * 8 devices.Moving on to pg.3 of the document...The RAM devices I'm using have an access time of 100nS (-10 part code) and so I selected "110nS" on J21/J23. This might not be the right thing to do, but it seems to work.OK. Now the hard part. I found the base address setting really difficult to understand. Here are some comments/thoughts;All the address selection jumpers are shown rotated through 180 degrees. For example, J9/J17 GND signal is on the RIGHT of the jumper pack when the PCB is viewed in the normal orientation not the left.The pairs of jumper packs are shown left/right reversed. In other words, the diagrams show J9/J17 to the left of J8/J16. This is incorrect when viewing the PCB in the normal way. Actually J8/J16 are on the left, J9/J17 are on the right.I can see why the documentation does this, but it's something to bare in mind. (Showing the jumpers this way round makes it easier to read the address bit pattern).Taking the above into account, it's a shame that the "memory device capacity" table shows the addresss lines MSB to the right (it would work better if it was MSB to the left). Again, not a show stopper, but something to keep in mind when working all this out...In that same table, there maybe an error at the intersection of "8K * 8" and A15. The table shows X but I think it should be S.[This is where I make a fool of myself] The biggest problem/error/call-it-what-you-will is the use of bold "BANK 1" and "BANK 2" at the top of each example. This is just totally wrong because (for example) looking at "J9/J17", these are the address selectors for bank 1 and bank 2 respectivly. More generally, jumpers for bank 1 and bank 2 are mixed up across each example. The bold "Bank 1" and "Bank 2" just should not be there.By way of a further demonstration of my ability to make a fool of myself, here's another one... Just below the bold "Bank" text, there's an area called "Boundary (Bytes)". I think this is "Boundary (Words)". There are two reasons why I say this. Firstly the board is advertised as being able to handle up to 1MB. See the following link (pg. 52);https://bitsavers.computerhistory.org/pdf/motorola/versados/MVMEVDOS_D2_VMEHwSwConf_May85.pdfand as that table only shows up to 256K bytes a bank, you could only get 512K on the board. Secondly, if we assume that the text "256K" is correct, then we only need A18 to decode each bank if we're talking about words (not bytes).With all that ranting out of the way, here's how I worked out the base address jumper settings;Say we want bank 1 to be at address $040000 and we're using 8 off 32K * 8 RAM devices which is 256K as previously stated.Consider the "Memory Device Capacity" table. Locate the row corrisponding to the device size you're using. So for me, it's "A A A A S S X" which is better read reversed as follows "X S S A A A A". Just to be clear, we're talking about;Code:A18 A17 A16 A15 A14 A13 A12X S S A A A AWhat this means is that;A15 - A12 should be connected to the memory devices (nothing for you to do unless you need to fix a hardware problem as I described above)A17 & A16 are decoded on the PCB (nothing for you to do)A18 needs setting on the JumpersJust to explain in a little more detail; the two S bits of the address enable one-of-four pairs of devices to be selected. These are the four odd and even pairs of devices in each coloum on the PCB.When I said "A18 needs setting on the jumpers", more accurately I should have said "A18 and above need setting on the jumpers".Rather than consider all the jumpers in a big bad old mess of jumper-meyhem, just think about J9 & J8. Use the functional desciption and diagram provided top left of pg.3 but ignore their settings and let's start with no jumpers fitted.Don't fit any jumpers for address bits that contain either an S or A.For all the high address bits that don't contain an S or A, you need to configure the bit pattern... In our case we have (remembering that I want the base address to be $040000);Code:A23 A22 A21 A20 A19 A18 A17 A16 A15 A14 A13 A120 0 0 0 0 1 0 0 0 0 0 0Before moving to the next step, I want to talk about something else... The three example address base configurations provided on pg.3 don't explain how much memory is on the board being configured. I think it's 8K words. The reason for this is that so far we've only been talking about configuring J8 & J9.We're about to start talking about J10 & J11. It's simple; these two jumper blocks are a bit-flip of J8 & J9 EXCEPT that you don't fit any jumpers for addresses within the block. Reviewing the three example settings, we can see that in all of them, the bit-flip rule works for all the jumpers apart from the bottom two bits. That's why I think the bank is 8K words wide.ImportantI don't know if this bit-flip guide is correct; it's just what I observe looking at the supplied information.So coming back to our example and using the bit pattern above, here's all four jumper settings (1 = fit)Here's the block base address;Code:J9 (don't forget that 12 does not count; it's GND)12 10 08 06 04 020 0 0 0 0 0J814 12 10 08 06 04 021 0 0 0 0 0 0Now here's the block base address bit-flipped BUT ONLY FOR BITS OUTSIDE THE RANGE OF THE BLOCKCode:J11 (Don't forget the 12 does not count: it's GND)12 10 08 06 04 020 1 1 1 1 1J1014 12 10 08 06 04 020 0 0 0 0 0 0OK. So that's block 1 sorted. Moving on to block 2, we want a base address of $080000. Here's the settings without the woffle;Code:J1712 10 08 06 04 020 0 0 0 0 1J1614 12 10 08 06 04 020 0 0 0 0 0 0J1912 10 08 06 04 020 1 1 1 1 0J1814 12 10 08 06 04 021 0 0 0 0 0 0There's one final step. All the above is "address bit pattern" friendly not "PCB layout" friendly. Here's the same information as above but turned around to make transcribing to the PCB jumpers easier;Code:J8 J90 0 0 0 0 0 1 0 0 0 0 0 0J10 J110 0 0 0 0 0 0 1 1 1 1 1 0J16 J170 0 0 0 0 0 0 1 0 0 0 0 0J18 J190 0 0 0 0 0 1 0 1 1 1 1 0The MVME110Bug program I'm using has a Block Test (BT) function. Using this shows errors with repeatable faults which is discouraging to say the least.However if I change the boundaries of the BT function (i.e. Different start and end addresses that still include the "faulty" address from above), the fault goes away.As an aside, other bug commands don't work, so either there's something wrong with the code or my card; not sure which!Furthermore, I've written my own memory test program which is fairly simple, but it runs without error for days. I am currently improving this to check that if I assert a single bit in the entire memory block, then only that very bit is high; all the rest are low. Right now, the program only checks that it's possible to write / read 00 and FF to every location using byte or word accesses. Shown below for information, sorry if coding style makes you weep.Code:; Simple memory test. Tests 64K byte blocks of RAM.; Assumes code running on a MVME110 with 110-Bug Version 4.1; There are four tests herein;; write-read $00 using byte access; write-read $FF using byte access; write-read $0000 using word access; write-read $FFFF using word access; You can set the number of 64K byte blocks to test using "blocks"; You can repeat all the above tests a set number of times using "repeats"; D Waine 9/05/24codeStart equ $1000 ; Start of this routineramStart equ $40000 ; Base addresss of RAM to testblocks equ $8 ; Number of 64K blocks to testrepeats equ $2org codeStartmove.l #$2000,A7 ; Init the stack; Output welcome stringmove.l #testStart,A5move.l #testEnd,A6trap #15dc.w 2; Set up a counter for number of repeats of all tests to domove.l #repeats,D1sub.l #1,D1 ; Make loop count correct for dbramove.l #0,D0 ; Counts number of times done all testsbigLoop; Simple all zeros memory write/read test using byte accessmove.l #ramStart,A0move.l #blocks,D3sub.l #1,D3 ; Make loop count correct for dbrablock00BLoopmove.l #$ffff,D2test00BLoop move.b #$00,(A0) ; write 00move.b (A0)+,D4 ; Read test bytecmpi.b #$00,D4 ; test for non zerobne errIn00BTest ; If byte read back in <>0, we have an errordbra D2,test00BLoopdbra D3,block00BLoop; Simple all bits set memory write/read test using byte accessmove.l #ramStart,A0move.l #blocks,D3sub.l #1,D3 ; Make loop count correct for dbrablockFFBLoopmove.l #$ffff,D2testFFBLoop move.b #$ff,(A0) ; write FFmove.b (A0)+,D4 ; Read test bytecmpi.b #$ff,D4 ; test for non matchbne errInFFBTest ; If byte read back in <>0, we have an errordbra D2,testFFBLoopdbra D3,blockFFBLoop; Simple all zeros memory write/read test using word accessmove.l #ramStart,A0move.l #blocks,D3sub.l #1,D3 ; Make loop count correct for dbrablock00WLoopmove.l #$7fff,D2 ; Becausee we're working on words, we only need $8000 loopstest00WLoop move.w #$0000,(A0) ; write 00move.w (A0)+,D4 ; Read test bytecmpi.w #$0000,D4 ; test for non zerobne errIn00WTest ; If word read back in <>0, we have an errordbra D2,test00WLoopdbra D3,block00WLoop; Simple all bits set memory write/read test using word accessmove.l #ramStart,A0move.l #blocks,D3sub.l #1,D3 ; Make loop count correct for dbrablockFFWLoopmove.l #$7fff,D2 ; Becausee we're working on words, we only need $8000 loopstestFFWLoop move.w #$ffff,(A0) ; write FFFFmove.w (A0)+,D4 ; Read test bytecmpi.w #$ffff,D4 ; test for non zerobne errInFFWTest ; If word read back in <>0, we have an errordbra D2,testFFWLoopdbra D3,blockFFWLoop; Bump the test repeats counter and print it outadd.l #1,D0 ; Bump the test countermove.l #testStart,A5 ; Point at RAM buffer to print D0bsr bin2Asc32Bitsmove.l A5,A6move.l #testStart,A5trap #15dc.w 2; Go round for another repeat if we need todbra D1,bigLoop; Print bye bye messagemove.l #byeStart,A5move.l #byeEnd,A6trap #15dc.w 2; Return to 110BugexitToBug trap #15dc.w 0errIn00BTestmove.l #err00BStart,A5move.l #err00BEnd,A6trap #15dc.w 2bra exitToBugerrInFFBTestmove.l #errFFBStart,A5move.l #errFFBEnd,A6trap #15dc.w 2bra exitToBugerrIn00WTestmove.l #err00WStart,A5move.l #err00WEnd,A6trap #15dc.w 2bra exitToBugerrInFFWTestmove.l #errFFWStart,A5move.l #errFFWEnd,A6trap #15dc.w 2bra exitToBugerrInBitTestNonZeromove.l #errBit1Start,A5move.l #errBit1End,A6trap #15dc.w 2bra exitToBugerrInBitTestBitPatternmove.l #errBit2Start,A5move.l #errBit2End,A6trap #15dc.w 2bra exitToBugerr00BStart dc.b "Error in all zero byte access test, exiting to MVME110-BUG"err00BEnd dc.b 00dc.b $0d,$0aerrFFBStart dc.b "Error in all bits set byte access test, exiting to MVME110-BUG"errFFBEnd dc.b 00dc.b $0d,$0aerr00WStart dc.b "Error in all zero word access test, exiting to MVME110-BUG"err00WEnd dc.b 00dc.b $0d,$0aerrFFWStart dc.b "Error in all bits set word access test, exiting to MVME110-BUG"errFFWEnd dc.b 00dc.b $0d,$0aerrBit1Startdc.b "Error in bit test: location <>0, exiting to MVME110-BUG"errBit1End dc.b 00dc.b $0d,$0aerrBit2Startdc.b "Error in bit test: wrong bit pattern, exiting to MVME110-BUG"errBit2End dc.b 00dc.b $0d,$0atestStart dc.b 'MVME211 Test program', $0d, $0a, 'D. Waine 12/05/24 Version 0.1'testEnd dc.b 00dc.b $0d,$0abyeStart dc.b "Exiting to MVME110-BUG"byeEnd dc.b 00dc.b $0d,$0a; Series of routines to convert D0 from binary to ASCII which is stored at (A5). Bump; A5 before we return. The first routine is the core; all the others just rotate the nibble; we're currently considering into the correct placealign 2bin2Asc4Bitsmove.w D0,-(A7) ; Save D0and.l #$0F,D0 ; remove everything above the bottom 4 bitsadd.b #$30,D0 ; Turn into ASCIIcmpi.b #$3A,D0 ; Is result greater than "9"?bmi.s lessThanAadd.b #7,D0 ; hex over 9 look like a letterlessThanA move.b D0,(A5)+ ; Store result in ASCII buffermove.w (A7)+,D0 ; Restore D0rtsbin2Asc8Bitsmove.l D0,-(A7) ; Save the whole of D0ror.l #4,D0 ; Get the high part of the byte into the bottombsr bin2Asc4Bits ; And convert itmove.l (A7),D0 ; Recover the number without moving the stackbsr bin2Asc4Bitsmove.l (A7)+,D0rtsbin2Asc16Bitsmove.l D0,-(A7)ror.l #8,D0 ; Get the hi byte in the lo bytebsr bin2Asc8Bits ; Convert what was the hi byte of the wordrol.l #8,D0 ; Restore the lo byte to the correct placebsr bin2Asc8Bitsmove.l (A7)+,D0rtsbin2Asc32Bitsmove.l D0,-(A7)swap D0 ; Get the hi word in the lo part (but save the lo part)bsr bin2Asc16Bitsswap D0bsr bin2Asc16Bitsmove.l (A7)+,D0rtsend codeStartAs previously advised, I'm sure that there will be errors/misunderstandings with the above. I'd appreciate any observations/corrections.

author: LittleFrog
author_rank: Contributor
timestamp: May 24, 2024, 06:38:12 pm
content: HiJust wanted to finish the MVME211 memory card configuration and test topic off.....Turns out that I am so stupid, I did not follow my own instructions and effectivly I mis-configured the hot-wiring of pin 1 of the RAM chips. When I went through the instructions in my last mail slowly, I realised that I'd made a mistake and when this was rectified, the MVME110-Bug memory test (BT) and my memory test program both run fine;Code:MVME110 4.1> bt 40000 bfffePhysical Address=00040000 000BFFFEMVME110 4.1> bt 40000 bfffePhysical Address=00040000 000BFFFEMVME110 4.1> bt 40000 bffffePhysical Address=00040000 000BFFFEMy memory test is not very good but here's the final version which includes the ability to set a single walking bit that works its way through the whole of the test area checking that the rest of RAM is still $0000. Right now, I JMP over this part of the test because is takes a couple of days to run. Anyway it works fine now that I fixed my self-induced error.Code:; Walking bit memory test. Program clears the test area to $0000 and then sets; a single bit in the test word and checks that the whole of the rest of the test; area is $0000 (apart from the test location of course!).; I've left in some simpler memory tests too... These were useful to find simple; errors quickly.; D Waine 24/05/24; Program assumes that there are a number of 64K blocks to check (8 in my case); and then the program repeats a number of times (2 typically); The source has 4 simple memory tests which are currently jumped over to get to; the walking bit test more quickly.codeStart equ $1000 ; Start of this routineramStart equ $40000blocks equ $7 ; Number of 64K blocks to testrepeats equ 2org codeStartmove.l #$2000,A7 ; Init the stack; Output welcome stringmove.l #testStart,A5move.l #testEnd,A6trap #15dc.w 2; Set up a counter for number of repeats of all tests to domove.l #repeats,D1sub.l #1,D1 ; Make loop count correct for dbramove.l #0,D0 ; Counts number of times done all testsbigLoop; Simple all zeros memory write/read test using byte accessmove.l #ramStart,A0move.l #blocks,D3sub.l #1,D3 ; Make loop count correct for dbrablock00BLoopmove.l #$ffff,D2test00BLoop move.b #$00,(A0) ; write 00move.b (A0)+,D4 ; Read test bytecmpi.b #$00,D4 ; test for non zerobne errIn00BTest ; If byte read back in <>0, we have an errordbra D2,test00BLoopdbra D3,block00BLoop; Simple all bits set memory write/read test using byte accessmove.l #ramStart,A0move.l #blocks,D3sub.l #1,D3 ; Make loop count correct for dbrablockFFBLoopmove.l #$ffff,D2testFFBLoop move.b #$ff,(A0) ; write FFmove.b (A0)+,D4 ; Read test bytecmpi.b #$ff,D4 ; test for non matchbne errInFFBTest ; If byte read back in <>0, we have an errordbra D2,testFFBLoopdbra D3,blockFFBLoop; Simple all zeros memory write/read test using word accessmove.l #ramStart,A0move.l #blocks,D3sub.l #1,D3 ; Make loop count correct for dbrablock00WLoopmove.l #$7fff,D2 ; Becausee we're working on words, we only need $8000 loopstest00WLoop move.w #$0000,(A0) ; write 00move.w (A0)+,D4 ; Read test bytecmpi.w #$0000,D4 ; test for non zerobne errIn00WTest ; If word read back in <>0, we have an errordbra D2,test00WLoopdbra D3,block00WLoop; Simple all bits set memory write/read test using word accessmove.l #ramStart,A0move.l #blocks,D3sub.l #1,D3 ; Make loop count correct for dbrablockFFWLoopmove.l #$7fff,D2 ; Becausee we're working on words, we only need $8000 loopstestFFWLoop move.w #$ffff,(A0) ; write FFFFmove.w (A0)+,D4 ; Read test bytecmpi.w #$ffff,D4 ; test for non zerobne errInFFWTest ; If word read back in <>0, we have an errordbra D2,testFFWLoopdbra D3,blockFFWLoopjmp missOutTests; Use word access to write one bit set at a time in the whole of the memory under test and check that only; that one bit is actually set. Test happens in a number of steps as follows;; 1 - Set every word in memory to 0000; 2 - Initialise a "one bit in one word" loop; 3 - Set the bit in the word under test; 4 - Read the whole of memory checking that everything is zero apart from the word under test and that should; have exacty the correct bit set; 5 - Bump the bit under test; 6 - When we've done all 16 bits, step to the next word; 7 - Repeat until all words under test have been checked; The program loops so that this test can be repeated as required; Initalise every word in the memory under test to 0000start move.l #ramStart,A0move.l #blocks,D3sub.l #1,D3 ; Make loop count correct for dbrainitBlockLoopmove.l #$7fff,D2 ; Because we're working on words, we only need $8000 loopsinitWordLoopmove.w #$0000,(A0)+ ; write 00 and bump the pointer by 2 (it's a word remember!)dbra D2,initWordLoopdbra D3,initBlockLoop; Init the "one bit set" loop which requires just the same loop as above but we also set up the one-bit-set; and the address under testmove.l #ramStart,A0move.l #blocks,D3sub.l #1,D3 ; Make loop count correct for dbratestBlockLoopmove.l #$7fff,D2 ; Because we're working on words, we only need $8000 loopschkNxtAddr move.l #$0001,D1 ; D1 holds the bit we're testing currently. If you make this; $8000 then the test does not try all 16 bits... Just the MSB; which speed it up by 16*; Set the test bit and then read the whole of memory checking that it's zero unless we're on the; test word, in which case, test that it's the correct bit (i.e. The contents of D1)testBitWordLoopmove.w D1,(A0) ; place the current test word in memorymove.l #ramStart,A1 ; Read the whole of memory under testmove.l #blocks,D4sub.l #1,D4chkBlockLoopmove.l #$7fff,D5chkMemLoopmove.w (A1),D6 ; Get the contents of the memory location we're testingcmp.l A0,A1 ; Are we at the test memory location?beq.s testLocationcmpi.w #0000,D6 ; Not at test location so check memory is zerobeq nextBitPatternbsr errInBitTestNonZeronextBitPatternadd.l #2,A1 ; point at next memory location we're checkingdbra D5,chkMemLoopdbra D4,chkBlockLoopasl.w #1,D1 ; Move to next bit patternbcs.s nextAddress ; If we've done all the bits, goto next address to testbra testBitWordLoopnextAddress move.w #0000,(A0)+ ; Repair the test word and point at next address; jmp testExitbsr printAddress ; Print the address we've just testedbra chkNxtAddrdbra D2,testBitWordLoopdbra D3,testBlockLoopbra nextRepeattestLocationcmp.w D6,D1 ; At test location so check against bit patternbeq nextBitPatternbsr errInBitTestBitPatternbra nextBitPatternmissOutTests; Bump the test repeats counter and print it outnextRepeat add.l #1,D0 ; Bump the test countermove.l #testStart,A5 ; Point at RAM buffer to print D0bsr bin2Asc32Bitsmove.l A5,A6move.l #testStart,A5trap #15dc.w 2; Go round for another repeat if we need todbra D1,bigLooptestExit; Print bye bye messagemove.l #byeStart,A5move.l #byeEnd,A6trap #15dc.w 2; Return to 110BugexitToBug trap #15dc.w 0errIn00BTestmove.l #err00BStart,A5move.l #err00BEnd,A6trap #15dc.w 2bra exitToBugerrInFFBTestmove.l #errFFBStart,A5move.l #errFFBEnd,A6trap #15dc.w 2bra exitToBugerrIn00WTestmove.l #err00WStart,A5move.l #err00WEnd,A6trap #15dc.w 2bra exitToBugerrInFFWTestmove.l #errFFWStart,A5move.l #errFFWEnd,A6trap #15dc.w 2bra exitToBugerrInBitTestNonZeromove.l #errBit1Start,A5move.l #errBit1End,A6trap #15dc.w 2bsr printRegsrtserrInBitTestBitPatternmove.l #errBit2Start,A5move.l #errBit2End,A6trap #15dc.w 2bsr printRegsrts; Print the contents of A0 which is the address being testedprintAddressmove.l D0,-(A7)move.l A0,D0move.l #testAdEnd,A5 ; Point at place to store ASCIIbsr bin2Asc32Bitsmove.l A5,A6 ; Point at end of messagemove.l #testAdStart,A5trap #15dc.w 2move.l (A7)+,D0rts; Print the important registers; D0 Repeat counter Word; A0 Address being tested Long; D1 Test pattern Word; A1 Address where anomoly found Long; D6 Data found at anomoly Word; Notice that the Bin-> Ascii converter drops the number into the correct; place in the string and then prints it outprintRegs move.l D0,-(A7) ; Save on stackmove.l D6,-(A7); Print D0move.l #repeatEnd,A5 ; Point at place to store ASCIIbsr bin2Asc16Bitsmove.l A5,A6 ; Point at end of messagemove.l #repeatStart,A5trap #15dc.w 2; Print A0move.l A0,D0move.l #testAdEnd,A5 ; Point at place to store ASCIIbsr bin2Asc32Bitsmove.l A5,A6 ; Point at end of messagemove.l #testAdStart,A5trap #15dc.w 2; Print D1move.w D1,D0move.l #testPtEnd,A5 ; Point at place to store ASCIIbsr bin2Asc16Bitsmove.l A5,A6 ; Point at end of messagemove.l #testPtStart,A5trap #15dc.w 2; Print A1move.l A1,D0move.l #erAdEnd,A5 ; Point at place to store ASCIIbsr bin2Asc32Bitsmove.l A5,A6 ; Point at end of messagemove.l #erAdStart,A5trap #15dc.w 2; Print D6move.l (A7)+,D0 ; Get D6 back into D0move.l #erDtEnd,A5 ; Point at place to store ASCIIbsr bin2Asc16Bitsmove.l A5,A6 ; Point at end of messagemove.l #erDtStart,A5trap #15dc.w 2move.l (A7)+,D0 ; Restore from stackrtsrepeatStart dc.b "Repeat counter "repeatEnd dc.b "****"dc.b $0d,$0atestAdStart dc.b "Test address "testAdEnd dc.b "********"dc.b $0d,$0atestPtStart dc.b "Test pattern "testPtEnd dc.b "****"dc.b $0d,$0aerAdStart dc.b "Error address "erAdEnd dc.b "********"dc.b $0d,$0aerDtStart dc.b "Error data "erDtEnd dc.b "****"dc.b $0d,$0aerr00BStart dc.b "Error in all zero byte access test, exiting to MVME110-BUG"err00BEnd dc.b 00dc.b $0d,$0aerrFFBStart dc.b "Error in all bits set byte access test, exiting to MVME110-BUG"errFFBEnd dc.b 00dc.b $0d,$0aerr00WStart dc.b "Error in all zero word access test, exiting to MVME110-BUG"err00WEnd dc.b 00dc.b $0d,$0aerrFFWStart dc.b "Error in all bits set word access test, exiting to MVME110-BUG"errFFWEnd dc.b 00dc.b $0d,$0aerrBit1Startdc.b "Error in bit test: location <>0"errBit1End dc.b 00dc.b $0d,$0aerrBit2Startdc.b "Error in bit test: wrong bit pattern"errBit2End dc.b 00dc.b $0d,$0atestStart dc.b 'MVME211 memory test', $0d, $0a, 'D. Waine 24/05/24 Version 0.2'testEnd dc.b 00dc.b $0d,$0abyeStart dc.b "Exiting to MVME110-BUG"byeEnd dc.b 00dc.b $0d,$0a; Series of routines to convert D0 from binary to ASCII which is stored at (A5). Bump; A5 before we return. The first routine is the core; all the others just rotate the nibble; we're currently considering into the correct placealign 2bin2Asc4Bitsmove.w D0,-(A7) ; Save D0and.l #$0F,D0 ; remove everything above the bottom 4 bitsadd.b #$30,D0 ; Turn into ASCIIcmpi.b #$3A,D0 ; Is result greater than "9"?bmi.s lessThanAadd.b #7,D0 ; hex over 9 look like a letterlessThanA move.b D0,(A5)+ ; Store result in ASCII buffermove.w (A7)+,D0 ; Restore D0rtsbin2Asc8Bitsmove.l D0,-(A7) ; Save the whole of D0ror.l #4,D0 ; Get the high part of the byte into the bottombsr bin2Asc4Bits ; And convert itmove.l (A7),D0 ; Recover the number without moving the stackbsr bin2Asc4Bitsmove.l (A7)+,D0rtsbin2Asc16Bitsmove.l D0,-(A7)ror.l #8,D0 ; Get the hi byte in the lo bytebsr bin2Asc8Bits ; Convert what was the hi byte of the wordrol.l #8,D0 ; Restore the lo byte to the correct placebsr bin2Asc8Bitsmove.l (A7)+,D0rtsbin2Asc32Bitsmove.l D0,-(A7)swap D0 ; Get the hi word in the lo part (but save the lo part)bsr bin2Asc16Bitsswap D0bsr bin2Asc16Bitsmove.l (A7)+,D0rtsend codeStartThanks again to MarcoPolo for the configuration information that unlocked the way forward.

author: marcopolo
author_rank: Regular Contributor
timestamp: May 24, 2024, 06:51:53 pm
content: It's time to test the MVME320

author: LittleFrog
author_rank: Contributor
timestamp: May 24, 2024, 07:30:54 pm
content: QuoteYes....I've got three commands to play with IOT, IOP & IOC.Some great news is that in the past (pre-off board RAM), both the IOT & IOP commands just hung up until the red "Fail" LED came on. Now all three commands respond with sensible configuration options. I'm going to go quiet now while I work out what works and what does not.I have two little utilities to help me.... They're not amazingly written but here they are for your interest...Both make use of the fact that I'm using a terminal server to talk to the MVME110 serial port. The TS has Ethernet on one end and RS232 on the other. It's actually this;https://www.amazon.co.uk/dp/B07G5P4CPR?psc=1&ref=ppx_yo2ov_dt_b_product_detailsThe programs are written in Python 3.RAM variablesIt would be useful to know what the various RAM resident variables in the memory of the MVME110 are set to. I have the source code from BitSavers and so if I could find the memory location of one of them, then I could work out the memory location of the rest of them from that hint. Knowing that, I could populate a list of their current state by doing a MD of the correct memory range....I worked out that a variable called "ENDON" is stored at memory location $00060E. This is actually fairly easy to do by looking at some source that uses that variable and doing a search of the EPROM for the correct byte sequence. You get the byte sequence by quickly assembling that part of the source.Anyway you put the hint in the first line of a text file then follow that with the source that describes memory allocation like this;Code:ENDON 60EIPCNUM DS.W 1 CONTROLLER NUMBERDRVNUM EQU IPCNUM+1 DEVICE NUMBERDS.W 0 ..force word boundryCMD DS.B 10 IMAGE OF 6 to 10 BYTE COMMANDBPS DS.W 1 BYTES PER SECTORSECNUMW DS.W 1 .W NUMBER OF SECTORS TO TRANSFERBD1SEC DS.L 1 BOOT-DUMP DISK 1ST BLOCK ADDRESSBDNSEC DS.W 1 BOOT-DUMP DISK NUMBER OF BLOCKSBO1SEC DS.L 1 BOOT-LOAD DISK 1ST BLOCK ADDRESSBONSEC DS.W 1 BOOT-LOAD DISK NUMBER OF BLOCKSBOMEMA DS.L 1 BOOT-LOAD MEMORY ADDRESSTEMP_L DS.L 1 Temporary storage where Vol ID is savedUSERREGS DS.L 2 Temp. storage for reg. A5 & A6USERTEXT DS.B 26 Reserve space for boot messageBHIND DS.B 1 BOOT AND HALT FLAGCTYPE DS.B 1 CONTROLLER TYPE INDICATORDS.W 0 ...FORCE WORD BOUNDRYDISKSIZ DS.L 1 DISK SPACE REMAINING AFTER DUMPCTYPENOW DS.L 1 CONTROLLER TYPE PRESENTLY IN USECTYPE4 DS.L 1 CTYPE*4 USED AS ADDR. OFFSET IN JMP TABLCONTYPE DS.L 1 IPCNUM 0 CONTROLLER TYPE DEFAULTDS.L 1 IPCNUM 1 CONTROLLER TYPE DEFAULTDS.L 1 IPCNUM 2 CONTROLLER TYPE DEFAULTDS.L 1 IPCNUM 3 CONTROLLER TYPE DEFAULTDS.L 1 IPCNUM 4 CONTROLLER TYPE DEFAULTSCRATCH DS.L 1 OFFBOARD RAM $1K SCRATCH PADCONFDAT DS.L 1 POINTER TO CONFIGURATION DATAENDON DS.L 1 END OF ON BOARD MEMORYENDOFF DS.L 1 END OF OFF BOARD MEMORYHARDWRE DS.L 1 POINTER TO HARDWARE FROM 'GETVOLID'OFFBLKS DS.L 1 NUMBER OF BLOCKS OF OFFBOARD RAMOFFDUMP DS.L 1 NUMBER OF OFFBOARD RAM DUMPEDONBLKS DS.L 1 NUMBER OF BLOCKS OF ONBOARD RAMONDUMP DS.L 1 NUMBER OF ONBOARD RAM DUMPEDSTARTOFF DS.L 1 START OF OFF BOARD MEMORY (0 IF NONE)GAPBLKS DS.L 1 SIZE OF THE GAP BET. ONBD & OFFBD RAMMSIZED DS.B 1 "MEMORY SIZED" INDICATOR SWITCH***************************************************** - PARAMETERS USED IN 'IOP' COMMAND - *****************************************************ZAPRW DS.W 1 READ OR WRITE SWITCH (INITIALIZED SETS TO "R")ZAPARGS DS.L 0 PARMS USED TO LOAD VARIABLES TO PACKET IN "ZAP" COMMAND.ZAPSCNT DS.L 1 NUMBER OF BLOCKS TO BE OR READ/WRITTEN.ZAPMEM DS.L 1 I/O ADDRESS.ZAP1STS DS.L 1 STARTING BLOCK NUMBER.Then run this program;Code:# Program uses extracts from assembler source listings and a hint address to load up current# contents memory/symbol contents from an MVME110. Here's an example;# ENDON 60E# IPCNUM DS.W 1 CONTROLLER NUMBER# DRVNUM DS.W 10 DEVICE NUMBER# ENDON DS.L 1 Holds end of local memory# The source listing typically contains assembler directives like DC.B or DS.W etc.# The hint address can refer to any of the symbols in the source and should be the first record in# the source listing and is of the form "symbol <SPACE> address"# The program then works out what memory to read to get all the current values from the MVME110# Comms with the MVME110 re via a Terminal Server# D. Waine 13/05/24# In order that this program function, you need to have the TS and the MVME110 switched on (!)# then start a terminal session using puTTY to the TS. Run this program, providing the filename# of the source file (without extension) on the command line. This program assumes that the file# extension is .map. For example;# mvme110RAMRead 320 (to read the file called "320.map")import sysimport osimport socketfrom time import sleep# Col widths for fancy output of finished tablecolWidth = [4, 6, 10, 6, 10, 20, 50]colTitles = ['Len', 'Adrs', 'Label', 'Op', 'Reps', 'Value', 'Comment']def ascii2Int (value):return (int (value, 16))def fancyRow (r):# Make the supplied list, which contains a row in the output table, nice for printingoutputString = ''for ptr in range (0, len (r)):if ptr == 0:# Format the length fieldcontents = str (r )elif ptr == 1:# Format the address field allowing for a title or actual numberif isinstance (r , str) == True:contents = r else:contents = '{:04X}'.format(r )elif ptr == 2:# Format the Label fieldcontents = r elif ptr == 3:# Format the Operation fieldcontents = r elif ptr == 4:# format the Repeats fieldcontents = str (r )elif ptr == 5:# Format the value fieldcontents = r elif ptr == 6:# Format the comment fieldcontents = r else:print ('Error in fancy formatting')# Add trailing spaces to all fieldsfieldWidth = colWidth contents = contents + ' ' * (fieldWidth - len (contents))# Stop the contents being too longif len (contents) > (fieldWidth - 1):contents = contents [0:fieldWidth - 1] + ' 'outputString = outputString + contentsreturn (outputString)# Location of the TS on the LANterminalAddress = '192.168.0.3'terminalPort = 8234if len (sys.argv) != 2:print ('Incorrect number of arguments. You need to supply the input .map file without an extension')sys.exit (1)filename = sys.argv fileExtension = '.map'inputFile = filename + fileExtensionlineCounter = 0outputCounter = 0# Open the input fileprint ('Opening source symbol file: ', inputFile)with open(inputFile) as inFile:print ('Opening output stream to MVME110')sock = socket.socket()sock.connect((terminalAddress, terminalPort))print ('Connected')sleep (0.1)# Read the address hint, this contains two fields; the symbol and the address# Strip off Spaces and CR/LF. Split the string into two at the space# Turn the address into an INT (from ASCII hex)# Do some error checking (bit crude!)hint = inFile.readline ()hint = hint.strip ()hint = hint.split ()hintSymbol = hinthintAddress = hint hintAddress = ascii2Int (hintAddress)if len (hintSymbol) < 3:print ('hint Symbol too short (it needs to be longer than 3 chars')exit (1)if hintAddress == 0:print ("hint address can't be zero")exit (1)print ('Using hint symbol: ', hintSymbol)print ('Using hint address: ', hex(hintAddress))# Read the symbols and their definition a record at a time and load up a list of valid symbolstable = [] # Valid records stored in herefoundHint = False # Gets set to true when we've found the hint symbolfor inputLine in inFile:# Trim the input line of CR/LF but don't strip SPACEinputLine = inputLine.strip ('\n')inputLine = inputLine.strip ('\r')# Throw away lines that are not symbols but save valid lines to the tableif len (inputLine.strip ()) == 0:pass# print ('Blank line')elif inputLine  == '*':pass# print ('Comment')elif inputLine  == ' ':pass# print ("First char can't be space")else:# Split the record into fieldsvalidRecord = inputLine.split ()# Depending on the assembler command, indicate how many bytes the record# will require.if validRecord  == 'DS.B':size = (1 * int (validRecord ))elif validRecord  == 'DS.W':size = (2 * int (validRecord ))elif validRecord  == 'DS.L':size = (4 * int (validRecord ))else:print ("Warning, can't update offset: ", inputLine)size = 0# Spot the hint symbol (or not!)if validRecord  == hintSymbol:# If we've already found the hint symbol, then we've found another one# so errorif foundHint == True:print ('Found multiple hint symbols which this program cannot handle')exit (1)# Append a new entry to the table to include the size and hint addresstable = table + [ +  + validRecord]foundHint = Trueelse:# This record we're appending ot the list does not have the hint address# so just make it zero for now (it gets loaded later)table = table + [ +  + validRecord]# Little bit of error checking; the rest of the program will only work if we've# got a valid hint addressif foundHint == False:print ("Didn't find match between hint symbol and symbol table entry")exit (1)# So the table is basically built, now we have to load up the correct addresses# Go DOWN the table working out actual addresses at offsets AFTER the hintfoundAddress = Falsefor row in range (0, len (table)):# Search for the row containing the hint addressif table  != 0:foundAddress = True# As long as we've found the hint address and we're not at the first row of the table# or the row containing the hint, we can work out what the address should beif (foundAddress == True) and (row > 0) and (table  == 0):# It's the address of the previous row plus the previous row's lengthtable  = table [row - 1] + table[row - 1]# More error checking; this should not happen!if foundAddress == False:print ('Could not find hint address in table')exit (1)# Do the reverse; working from the end of the table back UP the table to row zerofoundEmpty = False# The loop counter goes up but...for pointer in range (0, len (table)):# The row we want to look at is from the end back over. The -1 is required because# len() returns the number of rows NOT the index of the last rowrow = len (table) - pointer - 1if table   == 0:foundEmpty = True# Update the address if we've already found an empty address and we're not looking# at the last row in the tableif (foundEmpty == True) and (row < (len(table) - 1)):# The address is the next entry address minus the next entry lengthtable  = table [row + 1] - table # We can now work out the addressses we need and get them in a form useful for forming# the MVME110 command [2:] removes the leading 0x supplied by hex()startAddress = hex(table ) [2:]endAddress = hex (table [len (table) - 1]) [2:]# The number of bytes is the address of the last record minus the address of the first record# I add a bit to this to make sure we get enough (needs a close look at!)length = hex ((table [len (table) - 1]) - (table ) + 16) [2:]print ('Starting address that we need: ', startAddress)print ('Ending address that we need : ', endAddress)print ('Length : ', length)# Form the command to be sent to the MVME110 and send itcmdToSend = 'MD ' + startAddress + ' ' + lengthsock.send ((cmdToSend + '\r').encode())# read in the response, building it up in charStream until we see the command prompt# Notice that we work with character strings and not byte strings. This is just to make# processing easier.charStream = ''while 'MVME110 4.1>' not in charStream:# Read the input and convert from byte to char stringchars = sock.recv (1).decode ()charStream = charStream + chars# Turn the massive string into a listresponse = charStream.split ('\r\n')# Build a massive string containing only the ASCII byte valuesbytes = ''# Loop over list, missing out the echo-ed command and a pair of post-content linesfor row in range (1, len (response) - 2):# For each row, strip off the address and trailing spaces. This is done quite# carefully to ensure that there is always a space between byte valuesbyteLine = (response )[10:59]bytes = bytes + byteLine# When we're complete, spilt the big string into a list of ASCII byte valuesbyteList = bytes.split ()# Go through the table, incrementing the pointer with the length of each field# and using that pointer to grab the correct ASCII byte valuespointer = 0for row in range (0, len (table)):# Get the field length and...fieldLength = table # Use it to point at the end of the bytes we want to grab. The start of the bytes# is the "pointer"fieldValue = (byteList [pointer:pointer + fieldLength])# Drop the new field into the list. There's a lot going on here! We re-create the content# of the current row to contain the first 5 fields (0 -> 4) and then join together the byte# values into a single string. Finally we join together any trailing comments into a single# stringtable = table [0:5] + [' '.join (fieldValue)] + [' '.join (table  [5:])]# Point at the next place in the ASCII byte value listpointer = pointer + fieldLengthprintableTitle = fancyRow (colTitles)print (printableTitle)for row in table:printableRow = fancyRow (row)print (printableRow)sock.close()exit (0)So from a W10 cmd session, run it like this;Code:C:\Users\dgwai\OneDrive\Documents\68k\68K VME Boards\dgwDevelopment>mvme110ramread 320Opening source symbol file: 320.mapOpening output stream to MVME110ConnectedUsing hint symbol: ENDONUsing hint address: 0x60eWarning, can't update offset: DRVNUM EQU IPCNUM+1 DEVICE NUMBERStarting address that we need: 5aeEnding address that we need : 63dLength : 9fLen Adrs Label Op Reps Value Comment2 05AE IPCNUM DS.W 1 00 00 CONTROLLER NUMBER0 05B0 DRVNUM EQU IPCNUM+1 DEVICE NUMBER10 05B0 CMD DS.B 10 00 00 00 00 00 00 0 IMAGE OF 6 to 10 BYTE COMMAND2 05BA BPS DS.W 1 00 00 BYTES PER SECTOR2 05BC SECNUMW DS.W 1 00 00 .W NUMBER OF SECTORS TO TRANSFER4 05BE BD1SEC DS.L 1 00 00 00 00 BOOT-DUMP DISK 1ST BLOCK ADDRESS2 05C2 BDNSEC DS.W 1 00 00 BOOT-DUMP DISK NUMBER OF BLOCKS4 05C4 BO1SEC DS.L 1 00 00 00 00 BOOT-LOAD DISK 1ST BLOCK ADDRESS2 05C8 BONSEC DS.W 1 00 00 BOOT-LOAD DISK NUMBER OF BLOCKS4 05CA BOMEMA DS.L 1 00 00 00 00 BOOT-LOAD MEMORY ADDRESS4 05CE TEMP_L DS.L 1 00 00 00 00 Temporary storage where Vol ID is saved8 05D2 USERREGS DS.L 2 00 00 00 00 00 00 0 Temp. storage for reg. A5 & A626 05DA USERTEXT DS.B 26 00 00 00 00 00 00 0 Reserve space for boot message1 05F4 BHIND DS.B 1 49 BOOT AND HALT FLAG1 05F5 CTYPE DS.B 1 4E CONTROLLER TYPE INDICATOR4 05F6 DISKSIZ DS.L 1 53 41 53 35 DISK SPACE REMAINING AFTER DUMP4 05FA CTYPENOW DS.L 1 53 41 53 38 CONTROLLER TYPE PRESENTLY IN USE4 05FE CTYPE4 DS.L 1 20 33 31 35 CTYPE*4 USED AS ADDR. OFFSET IN JMP TABL4 0602 CONTYPE DS.L 1 20 33 32 30 IPCNUM 0 CONTROLLER TYPE DEFAULT4 0606 SCRATCH DS.L 1 00 0B F0 00 OFFBOARD RAM $1K SCRATCH PAD4 060A CONFDAT DS.L 1 00 00 00 00 POINTER TO CONFIGURATION DATA4 060E ENDON DS.L 1 00 00 7F FF END OF ON BOARD MEMORY4 0612 ENDOFF DS.L 1 00 0B FF FF END OF OFF BOARD MEMORY4 0616 HARDWRE DS.L 1 00 FF B0 00 POINTER TO HARDWARE FROM 'GETVOLID'4 061A OFFBLKS DS.L 1 00 00 08 00 NUMBER OF BLOCKS OF OFFBOARD RAM4 061E OFFDUMP DS.L 1 00 00 00 00 NUMBER OF OFFBOARD RAM DUMPED4 0622 ONBLKS DS.L 1 00 00 00 80 NUMBER OF BLOCKS OF ONBOARD RAM4 0626 ONDUMP DS.L 1 00 00 00 00 NUMBER OF ONBOARD RAM DUMPED4 062A STARTOFF DS.L 1 00 04 00 00 START OF OFF BOARD MEMORY (0 IF NONE)4 062E GAPBLKS DS.L 1 00 00 03 80 SIZE OF THE GAP BET. ONBD & OFFBD RAM1 0632 MSIZED DS.B 1 01 "MEMORY SIZED" INDICATOR SWITCH2 0633 ZAPRW DS.W 1 00 57 READ OR WRITE SWITCH (INITIALIZED SETS TO "R")0 0635 ZAPARGS DS.L 0 PARMS USED TO LOAD VARIABLES TO PACKET IN "ZAP" C4 0635 ZAPSCNT DS.L 1 00 00 00 00 NUMBER OF BLOCKS TO BE OR READ/WRITTEN.4 0639 ZAPMEM DS.L 1 01 00 04 00 I/O ADDRESS.4 063D ZAP1STS DS.L 1 00 00 00 00 STARTING BLOCK NUMBER.C:\Users\dgwai\OneDrive\Documents\68k\68K VME Boards\dgwDevelopment>Under the hood, the program has worked out the memory range it needs to read and then sent the MD command to the MVME110... It then parses the response and pokes the correct response into the correct row of the output table. What is exciting about this is that you can see "STARTOFF" & "ENDOFF"... They have the correct values for the RAM board. Exciting.I'm going to use this program to check that when I do an IOT, I get the correct numbers poked into the variables you see near the bottom...IOC command formatterThe IOC command needs a row of bytes assembling into RAM that represent the command to be sent to the MVME320. You only attempt to key them in once and then you realise it's impossible to do without error. So I have a configuration file like this which I built up by reading various documents on the ECA.Code:# Start loading the ECA as per MVME320 User Manual pg C-9# Commands described pg 6-1# Zap the buffer back to 00bf BFF110 BFF170 00# Zap the data bufferbf B00000 b01000 00# Fill the ECAm BFF110# Command code (Recalibrate to track 0) $0001# Main status $0100# Extended status (Should be 00 before command) $020000# Max retries $040a# Actual retries $0500# DMA type $0600# Command options $0700# Buffer address (Hi word) $0800B0# Buffer address (Lo word)0000# Buffer length requested $0C0001# Buffer length transferred $0E0000# Cylinder number $100000# Head number00# Sector number01# Current cylinder position $140002# Reserved $1600000000000000000000# Pre index gap $2000# Post index gap00# Sync byte count00# Post ID gap00# Post data gap36# Address mark count03# Sector length code01# Fill bytee5# Reserved $28000000000000# Drive type - Soft sectored, Hard disk with CRC $2E03# Number of surfaces $2F04# Sectors per track $300a# Stepping rate $3106# Head settling time $3246# Head load time $3346# Seek type $3402# Reserved (Phase count) $3500# Low write current $360050# Precomp cylinder $380050# ECC remainder $3A000000000000# Appended EEC remainder $40000000000000# Reserved $4600000000# MVME320 working area $4A000000000000000000000000# Reserved for the controller $560ff0# Get out of data entry mode.# Present a dump of the commandmd BFF110 5f# Actually send the command to the MVME320# ioc 0,4Then this program squirts the keystrokes down to the MVME110 Terminal Server and on to the board itself...Code:# Send a series of keystrokes to an MVME110 serial port via a terminal server connected# on the LAN. The keystrokes are held in a file, e.g. keystrokes.key# D. Waine 27/04/24# In order that this program function, you need to have the TS and the MVME110 switched on (!)# then start a terminal session using puTTY to the TS. Run this program and the# key strokes will load.import sysimport osimport socketfrom time import sleep# Location of the TS on the LANterminalAddress = '192.168.0.3'terminalPort = 8234if len (sys.argv) != 2:print ('Incorrect number of arguments. You need to supply the input .key file without an extension')sys.exit (2)filename = sys.argv fileExtension = '.key'inputFile = filename + fileExtensionlineCounter = 0outputCounter = 0# Open the input fileprint ('Openning input file: ', inputFile)with open(inputFile) as inFile:print ('Openning output stream to MVME110')sock = socket.socket()sock.connect((terminalAddress, terminalPort))print ('Connected')sleep (0.1)# Read it a record at a timefor line in inFile:# Trim the input line of CR/LF & any spacesinputLine = line.strip()# Bump the correct input record type counterlineCounter += 1if inputLine  != '#':sock.send ((inputLine + '\r').encode())sleep (0.1)outputCounter += 1sock.close()print ('Input records: ', lineCounter)print ('Sent records: ', outputCounter)The only bit that I've not explained is how to find the location in memory that IOC uses to form the ECA. This is easy because the first time you run it, it shows you a hex dump of MOST (but not all!) the bytes in the ECA like this;Code:MVME110 4.1> iocDRIVE number=.......$00 ?CONTROLLER number=.......$04 ?Packet(s) at $000BF110 0602 0010 0A00 0000 0004 0000 0100 0000000BF120 0000 0001 FF77 0000 0000 0000 0000 0000000BF130 0000 0000 0F01 01E5 0000 0000 0000 0204000BF140 2014 0000 0200 0032 0032 8889 8888 0000ARE YOU SURE, (Y/N) ?So I set up the keystrokes to start loadinf RAM at $00BF110.Anyway, I'll report back with some notes on progress (assuming there is some!).

author: LittleFrog
author_rank: Contributor
timestamp: May 28, 2024, 10:05:00 am
content: HiThere has been progress so here's an update... I've really written all this down to arrange my own thinking, but if you have any ideas, I'd be most grateful to hear from you.Review of the set upMy host processor is an MVME110 (i.e. 68000) with 32K bytes of static RAM on board. The board is running MVME-Bug 4.1 which contains an early version of three commands that let you interact with the HD controller (IOT, IOP & IOC) as well as a number of boot commands which I've not tried because I don't have an image to load.Then I have two MVME211 static RAM boards, each with 512K bytes of static RAM on board. I think these work reliably now as I can do block test like this without error;Code:bt 40000 13fffeI've got a MVME320-1 Disk Controller and I'm trying to use it with a single Hard Disk. The HD is actually an MFM HD Emulator and although there's no documentation on the file format of the image it stores within itself, I can SSH onto it and see/guess what's going on in real-time which is quite exciting. Documentation for the HD Controller is here;http://www.bitsavers.org/pdf/motorola/VME/MVME320/MVME320B_D1_Disk_Controller_Users_Manual_Jan88.pdfAnd for the emulator here;https://www.pdp8online.com/mfm/mfm.shtmlProgressI re-read the HD Controller manual (!) and found that I'd misunderstood the HD connection details. Cutting a long story short, I'm using a 34 way ribbon cable to connect between the MVME320-1 and the HD Emulator. I am connecting this to the first 34 pins of J3 (not J4!). This is achieved using a 50 way IDC header on the end of the 34 way cable.With this set up in play, and with some working off-board RAM, all three IO commands now do something (rather than hang/timeout).I can set up the drive dimensions using IOT and then either attempt to issue an IOC or IOP command.There is a command that does not require any data I/O and this works without error; it's called "Recalibrate to track 0". I can see the emulator receiving pulses to step off TK0 and then back onto TK0 which is very exciting. Constructing this command in the ECA and then issuing an "IOC" is quite fiddly and I use that keystroke generator I described previously.I can issue an IOP to read HD sectors into RAM but this fails (reason below). The exciting thing is that if I ask for a block which I know (because of the disk geometry) is on cylinder 32, then I can see the head stepping out to cylinder 32.Any command that requires data flow always fails with a major status of $01 = "Non-recoverable error" and a minor bit map error of $0008 = "No identifier found". I guess this is due to the HD not being low-level formatted.As I said, I've not been able to find documentation on the file format used by the emulator, but it's visible as a disk file and I can SCP it to another computer in "real-time". The file format seems to be that there's a 102 byte header containing a mix of binary and ASCII. Then there's basically EVERYTHING that a real HD would read/write on a track by track basis.... So you've got TK0 H0 -> TK0 H1 -> TK0 H2 and so on in a linear, fixed length block. Each block is separated from the next with a header that I guess is used internally. Then you can see that the emulator has initialised the fill bytes to some value ($AA).Getting to the point, I'm not going to get anywhere until I can low-level format the emulator. I think there are two ways of doing this; issue IOC command type $7 "Format with implied seek" or figure out the format of the emulator file and make one programmatically using some python or similar. I can't do the second method yet because I don't know how to build an image of a track.So right now, I'm trying to make the format command work. This requires constructing an ECA in memory at the correct place expected by the IOC command and then issuing the actual IOC command.Using "sensible" defaults, I can format part of TK0 and I can see the bytes laid out in the emulator image of the contents of the HD in a sort-of sensible way.I'm confident that the whole track is being written which is very exciting.There are some troubles however;I always get 512 byte sectors even though I think I've set up for 256 byte sectorsI only get 3 sectors. The rest of the track is filled with fill bytes ($A924)So to me, I'm not constructing the ECA correctly. Although I mentioned the ECA, the format command also requires a kind of "format map" which tells the controller what order to lay down the sectors and actually what track to lay them out on. This is how the controller implements interleaving.So there are three issues at play;1- Am I constructing the ECA correctly?2- Am I constructing the format map correctly?3- Am I pointing the ECA at the format map correctly?To me, I've got issue one and two sorted. I'm suspicious that I don't know how to point the ECA at the format map. The manual is really vague on this which is very frustrating.What I'm going to do next is gain confidence in my understanding of the ECA by fiddling with the byte level format parameters (e.g. Sync field, Post-ID gap etc...) and then look at the image produced and make sure it's correct. These are described on pg.8-2 if you're interested. You put them in the ECA as described on pg. 5-1.The the format map is so simple, I can't see how I can have it wrong. It's described on pg. 6-4.So the final thing is am I pointing the ECA at the format map correctly?I'm loading the map into memory locations usually used as the data I/O buffer in the ECA; the start and end are seen as "Buffer address" and "Buffer length" but I might have this totally wrong.Like I said, any comments/thoughts most welcome.In case you're interested, I've attached a screenshot of part of the emulator disk file; I took this after issuing a format command. I think you can see the track/sector header and then the actual contents of the freshly low level formatted sector.

author: marcopolo
author_rank: Regular Contributor
timestamp: May 28, 2024, 06:38:50 pm
content: Hello Dave,I think it would be simpler to run a versaDOS installation which will take care of formatting the disk, this is possible with a GOTEK.Kind Regards,Marc

author: LittleFrog
author_rank: Contributor
timestamp: May 28, 2024, 06:52:47 pm
content: Hi,Thanks for the pointer. I've searched for versaDOS emulators/installers but found nothing (maybe that was a tip from you in the past actually). If you could point me at something to get me started, I'd be grateful.I think you might have proposed two ways forward... Run some kind of versaDOS emulator (on a Linux box?) or run versaDOS on my 68K hardware with a Gotek emulating a floppy disk. Have I got that right?I tried to follow up the versaDOS emulator and found WJM had produced something, for example;https://groups.google.com/g/comp.sys.m68k/c/kNo3Vz5MM1sBut further googling produced a dead end.... I can't read Usenet posts, or rather I don't know how to read Usenet posts, for free anyway. So that link above to an old google mirror is all I have.Further to my recent post, I realise that I might be totally wasting my time trying to create an image of the HD to drop into the emulator because what I'm looking at in the hex dump might be an MFM bit stream; not a conventional byte stream. Anyway I'm following that up now.Thanks again for the tip.PS: Way off topic but we are planning a trip to your country, I think Mrs Waine has in mind to go to the Loire Valley and then to the Champagne region. I think you're way over to the east? Anyway we're looking forward to it. Tunnel and driving.

author: marcopolo
author_rank: Regular Contributor
timestamp: May 28, 2024, 07:39:56 pm
content: Hi,The usenet message you quote clearly says (if you read it with a good newsreader):it is availible through gaby.de... find it in the emulator section..or just google vme10 emulator....the current release is very stable but is still beta... i will be releasing a newer version in a few days, also still beta... i am running a sysgen on it right now..MOTOROLA VME10 Emulator from Wiley (WJM) :http://www.z80.info/z80emu.htm#EMU_MISCDisk images:http://www.gaby.de/edownl.htmI haven't heard from Wiley since December 2023.Usenet free provider:https://usenet.blueworldhosting.com/"I realise that I might be totally wasting my time trying to create an image of the HD to drop into the emulator"Yes, I agreeI can provide you with the images for the GOTEK and the configuration file (created by a friend of mine).About France, yes I'm in the north-east.

author: LittleFrog
author_rank: Contributor
timestamp: May 28, 2024, 08:21:55 pm
content: Hi Marco,This is going to take some reading. Thanks for the links/tips.Will be in touch when I've got somewhere with this new direction.