• Please review our updated Terms and Rules here

Program to initialize 20x4 LCD screen for 8088

So your debuglcd5 code gets through LCDinit and just repeats that over and over.

Chuck's test goes through LCDinit and gets into LCDinit2 then sometimes hangs around location 80. Other times it gets past that point and sometimes cycles back to LCDinit.

Chuck's test2 gets through LCDinit and just repeats that over and over.

It still looks like there is something happening at location 80h in the EEPROM.

Doesn't seem to be a software issue.

You said before A7 never goes high, or only when you power up.

Maybe a broken wire? I would replace all the wires in the A7 chain from the EEPROM back to the CPU.

Still seems like an EEPROM problem. I would replace it. Might as well try a faster one.

Could be noise, ground loops or weird coupling with your protoboard. You said you were going to wire wrap. That could solve it.
 
The big difference between test and test2 is that test2 doesn't use RAM at all.

I agree that there's something funny going on. You might want to investigate if you've got a couple of address lines swapped, say, A7 and A6.
 
Unfortunately, I found that pin 1 for A7 on the ROM is correctly hooked up to pin 19 of the lower 373 latch, and that 8088 pin 9 is hooked up to pin 18 on the latch. I also checked A6, and it's correctly hooked up. I have enough spare parts to make the same computer on some other breadboards. I'm going to see if I can reproduce the same problem on that one. I really wish I had a logic analyzer. I'll probably have to save up for one.

Although, I did find out that the author mistakenly labeled the A7 input on the 373 latch pin 19 instead of 18.
 
Personally, I hate NASM--it's a crippled assembler trying to invent its own syntax. TASM is at least somewhat better. MASM has bells and whistles, such as automatic forward jump optimization, TYPEDEFs and a whole bunch of other stuff.

Sorry for going off topic but I have to ask why you hate NASM?

A small DOS program written in DEBUG:
Code:
1567:0100 1F            POP     DS
1567:0101 C70672043412  MOV     WORD PTR [0472],1234
1567:0107 EA0000FFFF    JMP     FFFF:0000

As small and simple as this is I've never been able to make MASM or TASM output the same code (at least not without cheating by resorting to DB statements) but NASM can do it easily. How would you do this in MASM/TASM? If anyone can do it it's you but if you can't, I'd say that MASM and TASM is crippled. :p

That being said, there is one thing I really like about MASM and that is the previous/next labels. TASM has nothing going for it (IMO).
 
Like this?
Code:
					.model	small,c

 0000				Startseg	segment at 0ffffh
 0000				Startup		label	far
 0000				Startseg	ends
 0000					.code

 0000  C7 06 0472 1234			mov	word ptr ds:[0472h],1234h
 0006  EA ---- 0000 R			jmp	Startup

					end

Generating:

Code:
00000000  c7 06 72 04 34 12 ea 00  00 ff ff                 |..r.4......|
0000000b

I'd probably put the 472h segment as a "segment at" as well. After all, this is the BIOS data segment at 40h. The idea is that you're trying to keep as much content as possible symbolic. In general, absolute numeric addresses with no symbolic hint are the bane of good programming.

There are certainly other ways to do this.
 
Like this?

No, that emits a 524 byte exe file (using MASM 6.11d and LINK Version 5.31.009). I want it as a 12 byte com file since this is literally the entire program, not just a snippet of code taken out of context (I guess I was too vague in my description, sorry about that). I also tried it with TASM 3.2, didn't work at all.

Also,
Code:
mov	word ptr ds:[0472h],1234h
why do I have to use a segment override to make MASM accept this? Obviously, there is no segment override in the actual binary but I have no way of knowing that without looking at the list file or the binary in a debugger etc. This is the kind of ambiguity that I hate about MASM and TASM.

BTW, you didn't say what's wrong with NASM?

I'd probably put the 472h segment as a "segment at" as well. After all, this is the BIOS data segment at 40h. The idea is that you're trying to keep as much content as possible symbolic. In general, absolute numeric addresses with no symbolic hint are the bane of good programming.

Agreed. Words are easier to remember than numbers. About the BDA at 40h, I prefer to use segment 0h and offset 04xxh simply because 0 is a common number (a lot more common than 40h) which means less setup and more efficient code (even if you don't have a register with 0 available it's better to do XOR AX,AX instead of MOV AX,40h).
 
No, that emits a 524 byte exe file (using MASM 6.11d and LINK Version 5.31.009). I want it as a 12 byte com file since this is literally the entire program, not just a snippet of code taken out of context (I guess I was too vague in my description, sorry about that). I also tried it with TASM 3.2, didn't work at all.

MASM (and probably TASM) emits a relocatable .EXE file after linking. Use EXE2BIN (or the /T on later versions of LINK) to get to the absolute file. Did you even look at the hex dump that I posted? 12 bytes.

Also,
Code:
mov	word ptr ds:[0472h],1234h
why do I have to use a segment override to make MASM accept this? Obviously, there is no segment override in the actual binary but I have no way of knowing that without looking at the list file or the binary in a debugger etc. This is the kind of ambiguity that I hate about MASM and TASM.

There's a good reason for demanding that--you've specified an absolute, not symbolic address, but the assembler has no idea of what segment to tie that address to. If it were a symbolic address, that would be no problem; e.g.:

Code:
					.model	small,c

 0000				Startseg	segment at 0ffffh
 0000				Startup		label	far		; entry after reset
 0000				Startseg	ends

 0000				Zeroseg		segment at 0h
					org	0472h
 0472 0000			WBFlag	dw	?			; warm boot flag
 0474				Zeroseg		ends

 0000					.code
					assume	ds:Zeroseg
 0000  1F				pop	ds
 0001  C7 06 0472 R 1234		mov	WBFlag,1234h
 0007  EA ---- 0000 R			jmp	Startup

					end

And again, that's my point--a good assembler encourages a clean way of thinking. Since NASM doesn't have the ASSUME directive, you/re on your own as far as what segment was intended--and heaven save you if you decide at some point that you'd rather make all of your BIOS segment references relative to ES rather than DS. In MASM/TASM's case, you can do this easily by changing an ASSUME--everything follows. In NASM's case, you have to find every single one and change it.

Suppose you decided that instead of assuming that the BIOS data segment is at 00000, you decide to use 00400h? In the above case, you'd change whatever code loads DS (or whatever segment register you decide to use) and the value of the segment declaration "AT". That's it--everything would adjust.

A good assembler tries to make it difficult to construct semantic errors or opaque code. Suppose IBM had published the 5150 BIOS listing where all low-memory references were coded as literal locations, rather than symbolic. Think of the joy trying to read that!

Now, as to why I dislike NASM--it's syntax is sui generis. TASM and MASM closely follow Intel's syntax. NASM's macro facility, such as it is, is very weak. In a sense, it's very similar to the first DRI x86 assemblers that invented their own mnemonics and had almost nothing in the way of macro facilities. If you code, you want to code in such a manner that the prevailing dialect--the lingua franca as it were--is used. You want to code such that anyone familiar with the language can pick up your code and see what you're doing without a bottle of antacids. You want to code so that you'll be able to pick up your code 20 years from now and still make sense of it.

None of this stuff was invented by Intel. Go to bitsavers and take a look at the 1960's System/360 assembly language. Observe the extensive macro facility. Note that, like x86, the S/360 assembler of 20 years earlier had a USING pseudo-op (Intel has ASSUME) to specify what base register is being used to access data.

There's a reason for all of this stuff--it's not just creeping featuritis.

Please forgive my evangelism, but I've seen too much code that was agony to read.
 
MASM (and probably TASM) emits a relocatable .EXE file after linking. Use EXE2BIN (or the /T on later versions of LINK) to get to the absolute file.

Thank you! /LINK /T works fine. Back when I originally wrestled with this I tried using '.model tiny' with 'segment at' but MASM complained. I also tried using MASM with the /AT switch but again, MASM refused so I eventually gave up, feeling very frustrated by not being able to do something so simple despite hours of trial and error and searching for info on google. I then tried it with NASM and it just worked, no questions asked. In my opinion there are too many buttons to push, knobs to twist and levers to pull to get MASM going. And to think that throughout this discussion we've been using the "simplified" directives...

Hell, MASM has all the info it needs to create a .COM program. Why do I even have to use LINK? Or worse, LINK+EXE2BIN?

Did you even look at the hex dump that I posted? 12 bytes.
Yes I did.

There's a good reason for demanding that--you've specified an absolute, not symbolic address, but the assembler has no idea of what segment to tie that address to.

I'm not buying this. Why would the assembler need to tie a segment to the address? Especially when I can do this;
Code:
		mov	bx,472h
		mov	word ptr [bx],1234h
without using any segment overrides, in fact without ever mentioning DS at all in the source file.

But if I do this;
Code:
		mov	word ptr [472h],1234h
then MASM gives error A2001: immediate operand not allowed. The explanation for A2001 is "A constant or memory offset was given to an instruction that cannot take an immediate operand."

Not only is the error message wrong, it goes away by simply adding a (nonfunctional) segment override. If I as the programmer can't be trusted to know what I'm doing in the latter example, why should the former work? Both examples implies use of DS as the segment register.

Here's a fun little piece of inefficient code;
Code:
7C5D 68C007      * PUSH    07C0         ; New segment ref. to be used for
7C60 1F            POP     DS           ;   both Data (DS = Data Segment)
7C61 1E            PUSH    DS           ;   . . .
7C62 686600      * PUSH    0066         ;   and Code segements . . .
7C65 CB            RETF                 ;   after this RETF instruction.
taken from a page showing a disassembly of the Windows 7 Volume Boot Record. This clearly shows that someone at Microsoft felt the need to use a workaround for the "clean way of thinking" behind the design of MASM. I know it's not exacly proof but I'm willing to bet at least one of two precious body parts that this is the case.

Since NASM doesn't have the ASSUME directive, you/re on your own as far as what segment was intended--and heaven save you if you decide at some point that you'd rather make all of your BIOS segment references relative to ES rather than DS. In MASM/TASM's case, you can do this easily by changing an ASSUME--everything follows. In NASM's case, you have to find every single one and change it.

Suppose you decided that instead of assuming that the BIOS data segment is at 00000, you decide to use 00400h? In the above case, you'd change whatever code loads DS (or whatever segment register you decide to use) and the value of the segment declaration "AT". That's it--everything would adjust.

I ASSUME ;) that you've read section 2.2 'Quick Start for MASM Users' in the NASM manual? They didn't do a very good job of explaining why ASSUME is not supported (I guess poor documentation is one thing both NASM and MASM have in common) but if you read that and then read Art of Assembly: Chapter Eight from 8.8.10 'Controlling Segments with the ASSUME Directive' all the way down to the end of 8.8.11 'Combining Segments: The GROUP Directive' it should be very obvious why ASSUME is so bad.

A good assembler tries to make it difficult to construct semantic errors or opaque code. Suppose IBM had published the 5150 BIOS listing where all low-memory references were coded as literal locations, rather than symbolic. Think of the joy trying to read that!
I thought we had covered this already? I agreed with you, remember? That said, there are situations where 'symbols' just gets in the way and adds a pointless layer of abstraction.

Now, as to why I dislike NASM--it's syntax is sui generis. TASM and MASM closely follow Intel's syntax. NASM's macro facility, such as it is, is very weak.
Believe it or not but I actually agree with you on this as well. NASM is not nearly as powerful as MASM in that respect. Accessing elements of a structure on the stack is also a bit more involved with NASM.

Please forgive my evangelism, but I've seen too much code that was agony to read.
I think the coding style has more to do with this than the choice of assembler. And coding styles evolve with experience.

I think LOOP got even slower on modern processors as a result of a Win95 bug.

No, actually I think it was a bug in OS/2.
 
Chacun à son goût. <sigh>

Use whatever you want--I have an old copy of DRI's RASM for DOS; how about that? It's even better than NASM because it uses different mnemonics, so you don't have to use stupid things such as BYTE PTR...

If you want to use an assembler that almost no one else uses, be my guest. It's your project.

It's strange--writers of assemblers always seem to think that they have a better idea. ANSI/IEEE tried to put forth a standard for assembly language (694-1985), which put forth a universal scheme for expressing machine operations symbolically. It was remarkable in that it was almost universally ignored. I am aware of no extant x86 assembler (but for some well-meaning C compilers that support inline assembly) that supports the standard. It was withdrawn around 2000 mostly because it was ignored--and the IEEE didn't have the nerve to charge $72 for a copy of a standard nobody cared about.

Back in the SIMTEL DOS days of x86, I think there were several attemps at alternate assembly languages for the platform; some were pretty bizarre.

Plus ça change, plus c'est la même chose, I guess.
 
Last edited:
What's with all the latin or French stuff? Reminds me of legal speak. And as an agnostic, I am particularly entertained by holy wars. I use nasm not because of any particular feature. I use it because I favor open-source software over proprietary. That's pretty much it. The only winblows installation under my roof (which includes several computers) is on my laptop because back when I got my BASIC Stamp and my USB eeprom programmer, they were written for winblows. Proprietary aside, I have nothing against MASM. I might have even eventually favored it if I had used it a long time ago, long before I switched to Linux. I still haven't ruled out learning it. In addition to the bad-tasting proprietary factor, my laptop is a pain to use because its battery is going out, the keyboard has bad keys, and it only has 2 USB slots. I ONLY use it to program eeproms. I have a USB keyboard, but I always have to unplug either the keyboard or USB programmer when I plug my flash drive in.

Despite all that, if I am encouraged enough, I might be persuaded to learn MASM. I'll just have to use a crappy computer when I program in assembly though (I have no real need to throw down a few hundred bucks for a new laptop when my old one does what I need. I'm a hardcore geek, but I've never really felt the need to upgrade just because something has newer technology.).
 
If you're a hardcore geek, then program the thing in machine language. Bootstrap your own tools.

FWIW, the examples I posted were done on a system running AMD64 Xubuntu.
 
If you're a hardcore geek, then program the thing in machine language. Bootstrap your own tools.

FWIW, the examples I posted were done on a system running AMD64 Xubuntu.

Alright. Thanks for the sincere advice. I guess I could use a vm. Also, I said geek, not hacker. But I'm always reading and learning, and I intend to get to the level to implement your suggestions. Sounds like the "Right Thing" to do. Btw, even the most casual observer could notice sarcasm in your post. And I have to wonder what tripped your trigger. I'm pretty laid back, so I wasn't offended, but I could see someone else interpreting it otherwise. Anyone with a hobby is a geek in their particular area of interest. I consider people who are obsessed with learning more and more and improving their skills in their chosen hobby a hardcore geek. FWIW, I would regard almost everyone here as a hardcore geek. I'll bet there are even some people here who are considered hackers by their peers. I'm sure there are hackers who do reinvent the wheel. By doing that, at least they know exactly how the wheel works. You seem like a nice guy, so I'm surprised by your remark, but no offense taken.
 
Last edited:
Hi Otacon, I'd like to thank you for posting your code.
I tried the code in your starting post. It works. I am using a 82C55 PPI, a 8088 microprocessor, and a FDCC1602B-FLYYBW-51LR LCD. The LCD is connected to the PPI.
 
Back
Top