• Please review our updated Terms and Rules here

Program to initialize 20x4 LCD screen for 8088

otacon14112

Experienced Member
Joined
Apr 19, 2012
Messages
115
Location
Iowa, United States
Hi guys, I've been trying to figure out what's wrong for over a year. I can get this screen to work P E R F E C T L Y on a PIC microcontroller, PIC16F690 to be exact. I'm trying to use an 8255 Programmable Peripheral Interface and output the data through its ports. I've tried 4-bit mode on port 1, 4-bit mode on port 2, and 8-bit mode with D0-D7 on port 1, and RS and EN as D4 and D0, respectively, on port 2. I have a 74LS373 latch on both ports.

I can't believe I can't get this ****ed thing to work on the 8088. There is this to consider: most of the time when I power on the breadboard, there are random LEDs that light up (when I have LEDs plugged into the 16 output pins on the 2 8-bit ports). I have to hit the reset button several times, and if that doesn't work, I have to turn the power supply off and back on a few times until the program in ROM runs.

I've been following Robert Grossblatt's 8088 Project Book very closely.

Here is my code in 8-bit:
Code:
;              ************************************************
;              *                                              *
;              *  A PROGRAM TO EXPERIMENT WITH MY LCD DISPLAY *
;              *                                              *
;              ************************************************

;*****************************************************************************
;									     *
;                          USING 8-BIT MODE				     *
;									     *
; Lines used for control signals on port 2:                                  *
; B4  B0			        				     *
; RS  EN    Hex  Operation                                                   *
; **  **    ***  *********                                                   *
; 0   1     01h  IR write as an internal operation (display clear, etc.)     *
; 1   1     11h  Write data to DDRAM or CGRAM (DR to DDRAM or CGRAM)         *
;                                                                            *
;*****************************************************************************

USE16
section		.text

start:		xor	ax,ax
		mov	ss,ax
		mov	sp,2048
		mov	al,90h			;This sets the 8255 to operate
                                                ;in Mode 0 (basic input output)
                                                ;with port 0 as an input and 
                out	03h,al                  ;ports 1 and 2 as outputs.  

		call	LongDelay

		mov	al,0x00			; Clear the pins on ports 1 and 2
		out	01h,al
		out	02h,al

		call	LCDinit 

		mov	al,0x01
		out	02h,al
;		call	Delay100us
		call	Delay2ms
		mov	al,0x80			;Set DDRAM address to 00h
		out	01h,al			
		mov	al,0x00
		out	02h,al
;		call	Delay100us 
		call	Delay2ms

		mov	al,11h
		out	02h,al
;		call	Delay100us
		call	Delay2ms
		mov	al,0x48
		out	01h,al			;Output "H"
		mov	al,10h
		out	02h,al
;		call	Delay100us
		call	Delay2ms

		mov	al,11h
		out	02h,al
;		call	Delay100us
		call	Delay2ms
		mov	al,0x69
		out	01h,al			;Output "i"
		mov	al,10h
		out	02h,al
;		call	Delay100us
		call	Delay2ms

		jmp	$

LongDelay:	mov	word [bx],0x2FFF	; Reset the countdown timer.
Delaya:		dec	word [bx]		; Decrement it by 1 each time.

		cmp	word [bx],00h		
		jnz	Delaya			; If the counter hasn't counted
						; down to 00h yet, keep going.
		ret


Delay100us:	mov	word [bx],0x0009	; Reset the countdown timer.
us100:		dec	word [bx]		; Decrement it by 1 each time.

		cmp	word [bx],00h	
		jnz	us100			; If the counter hasn't counted
						; down to 00h yet, keep going.
		ret



Delay2ms:	mov	word [bx],0x008f	; Reset the countdown timer.
ms2:		dec	word [bx]		; Decrement it by 1 each time.

		cmp	word [bx],00h
		jnz	ms2			; If the counter hasn't counted
						; down to 00h yet, keep going.
		ret



LCDinit:	
		mov	al,01h			
		out	02h,al
		call	Delay2ms
		call	Delay2ms
		mov	al,00111000b		; 8-bit, 2 lines, 5x8 characters
		out	01h,al
		mov	al,00h
		out	02h,al
		call	Delay2ms
		call	Delay2ms
		
		mov	al,0x00			; Clear the pins on ports 1 and 2
		out	01h,al
		out	02h,al
		
		mov	al,01h
		out	02h,al
		call	Delay2ms
		call	Delay2ms
		mov	al,00001100b		; Turn display on, cursor off, and do not blink the character at cursor
		out	01h,al
		mov	al,00h
		out	02h,al
		call	Delay2ms
		call	Delay2ms
		
		mov	al,0x00			; Clear the pins on ports 1 and 2
		out	01h,al
		out	02h,al

		mov	al,01h
		out	02h,al
		call	Delay2ms
		call	Delay2ms
		mov	al,00000001b		; Clear the screen
		out	01h,al
		mov	al,00h
		out	02h,al
		call	Delay2ms
		call	Delay2ms

		mov	al,0x00			; Clear the pins on ports 1 and 2
		out	01h,al
		out	02h,al

		mov	al,01h
		out	02h,al
		call	Delay2ms
		call	Delay2ms
		mov	al,00010100b		; Set cursor to move and display to shift to the right
		out	01h,al
		mov	al,00h
		out	02h,al
		call	Delay2ms
		call	Delay2ms

		mov	al,0x00			; Clear the pins on ports 1 and 2
		out	01h,al
		out	02h,al

		mov	al,01h
		out	02h,al
		call	Delay2ms
		call	Delay2ms
		mov	al,00000110b		; Increment, and no display shift
		out	01h,al
		mov	al,00h
		out	02h,al
		call	Delay2ms
		call	Delay2ms

		out	01h,al			; Clear the pins on ports 1 and 2
		out	02h,al

		mov	al,01h
		out	02h,al
		call	Delay2ms
		call	Delay2ms
		mov	al,00000010b		; Return the cursor to home
		out	01h,al
		mov	al,00h
		out	02h,al
		call	Delay2ms 
		call	Delay2ms
		mov	al,0x00
		out	01h,al
		out	02h,al
		ret

		times	2032- ($-$$) db 0
                DB      0EAh
                DW      0000h,0FF80h
                times   10 db 0

I've done pretty much everything I can think of to the board, changing capacitor values, swapping chips, etc. There is actually a program I can run almost all the time on the first try if I hold in the reset switch while I turn on the power supply, so at this point, I'm thinking it's software-related. It's quite possible I'm doing something completely wrong. If so, please show me what I'm doing wrong. I would prefer to figure it out on my own, but I'm completely out of ideas, and it works effortlessly with my PIC, so I KNOW it should be able to work.

If it helps, here are some pictures I took of the system and my oscilloscope:
8284 pin 8 CLK when the system is not working: http://www.flickr.com/photos/9114793@N03/sets/72157634880600574/
8088 pin 25 ALE when the system is not working: http://www.flickr.com/photos/9114793@N03/sets/72157634874156595/
8088 pin 25 ALE when the system is working: http://www.flickr.com/photos/9114793@N03/sets/72157634874292649/

Schematics: http://www.flickr.com/photos/9114793@N03/sets/72157634881520510/

I've kind of posted a duplicate thread from another board, but after more investigation, I feel it's software-related, so I decided to put it here.

Well, if anyone can help me, that would be awesome. Thanks :)

Oh, and I'm using NASM as my assembler, if that helps.
 
Last edited:
If I were you, I'd take the code you have, turn it into a small program that runs on DOS (replacing the I/O ports with a dummy write to, for instance, the UART scratch register), and see if/where the program is getting stuck using DEBUG or some other debugger of your choice...

But first, change your 'jmp $' to halt, and see if CPU activity ceases on the oscilloscope.
 
I usually start with a HALT ROM if you are decoding status enough to detect a HLT, or a tight JMP loop if not. Then usually some simple byte I/O to either LEDs on a latch or a DL2416 ASCII display. Then input from a buffer and output to the simple output device. Then delegate input and output to subroutines and demonstrate the stack/RAM is working.

The schematic you posted shows a 74LS373 being used as a byte-wide output to LEDs on pages 98-99 -- that's probably the best thing to start with until you're certain that you have memory and I/O decode down.

If you aren't able to get into your code reliably, check the operation of your reset circuit and make sure that your memory devices are fast enough. I tend to use modern memories for this reason.
 
One thing I notice in your code:
Code:
LongDelay:	mov	word [bx],0x2FFF	; Reset the countdown timer.
Delaya:		dec	word [bx]		; Decrement it by 1 each time.

		cmp	word [bx],00h		
		jnz	Delaya			; If the counter hasn't counted
						; down to 00h yet, keep going.
		ret

You are moving a value to the memory location pointed to by bx. But I don't see where you are initializing bx. So you are moving something to some random spot in memory. You are only using RAM for the stack, so I guess the odds are low that you would hit it, but it might cause a problem.
 
Alright, thanks guys. I've modified the code so many times before I posted this code. I'll definitely try what you guys have suggested. One thing that's kept nagging at the back of my mind is: at the start-up locatotion FFFF0h, should I initialize and set cs to the beginning of my program? xprt has suggested in the past that a "jmp start" would wrap around in memory. So what if I set cs to the beginning of my 2KB ROM? I have several x86 assembly books, and none of them talk about manually setting cs and ip. In fact, when I just tried to assemble the program last time, it said symbol 'ip' undefined. I'm doing my best to learn assembly, but I can't learn what I can't read in my books :(
 
You are setting CS and IP by doing a far jump at FFFF0h. If you disassemble your code:

Code:
...
000FFFEC  0000              add [bx+si],al
000FFFEE  0000              add [bx+si],al
000FFFF0  EA000080FF        jmp word 0xff80:0x0
000FFFF5  0000              add [bx+si],al
000FFFF7  0000              add [bx+si],al
000FFFF9  0000              add [bx+si],al
000FFFFB  0000              add [bx+si],al
000FFFFD  0000              add [bx+si],al

Opcode EA is an intersegment jump. This sets CS to FF80h and IP to 0000h.
Before you were using a near jump, opcode E9. This is a jump within the current code segment which would have the wrap-around problem.

I believe Chuck suggested you could use:
Code:
               DB      0EAh
               DW      0000h,0FF80h
to make sure that you ended up with opcode EA.

So what you have now looks correct.:)
 
Ok, thanks xprt. I really appreciate your experience. I took off the LCD screen and inserted several extra time delays, so I could catch the signals with leds. I even took the latches off, and I noticed that after the 4th initialization command, it jumps back to the beginning of the routine, and keeps looping. I inserted a hlt instruction after the 4th command, but it ignores it and keeps looping. I'll post my code whrn I get home. Thanks again.
 
I decided to simplify my code to try to track down where the program is going wrong. I decided to go through the init routine and give it enough of a delay so that I could view the binary values on 16 LEDs. I wanted to see if it ran correctly, and it did, up until the fourth init command. I pinpointed exactly where it starts going berserk, and it is right after the "mov al,00h" line. I tried putting a "hlt" opcode in various places to see where it started going wrong, and it was precisely at that spot. It never gets to the "out 02h,al" line. I discovered this by putting a "hlt" opcode there. I swapped the CPU, 8284A, the ROM and the RAM, and nothing changed. The program always goes screwy right at that spot every time.


This is what the program does:
It outputs the binary value of the command, and it flashes the bits on port 2 correctly just like it should, until it gets to the "mov al,00h", and then it restarts the LCDinit routine over and over and over again in an infinite loop. It never makes it to the "out 02h,al" line to put out the 4th LED of port 2. I noticed, however, that right after the last line of program code that it executes, for a short instant, all the LEDs are off, then the program starts all over again, or at least the LCDinit routine restarts. I quadruple-checked every wire and capacitor, etc., and everything is snug and firm in the breadboard.

I put my oscilloscope probe on the RESET line to see if maybe something was causing it to go high, and nothing did. Has anyone ever encountered anything like this before? What am I doing wrong? Here is my current, up-to-date debug code:

Code:
;              ************************************************
;              *                                              *
;              *  A PROGRAM TO EXPERIMENT WITH MY LCD DISPLAY *
;              *                                              *
;              ************************************************

;*****************************************************************************
;									     *
;                          USING 8-BIT MODE				     *
;									     *
; Lines used for control signals on port 2:                                  *
; B4  B0			        				     *
; RS  EN    Hex  Operation                                                   *
; **  **    ***  *********                                                   *
; 0   1     01h  IR write as an internal operation (display clear, etc.)     *
; 1   1     11h  Write data to DDRAM or CGRAM (DR to DDRAM or CGRAM)         *
;                                                                            *
;*****************************************************************************

USE16
section		.text
org		0
		
		xor	ax,ax
		mov	ss,ax
		mov	sp,2046
		mov	al,90h			;This sets the 8255 to operate
                                                ;in Mode 0 (basic input output)
                                                ;with port 0 as an input and 
                out	03h,al                  ;ports 1 and 2 as outputs.  
		xor	bx,bx

		call	LongDelay
		call	LCDinit


LongDelay:	mov	bx,0x3FFF		; Reset the countdown timer.
		dec	bx			; Decrement it by 1 each time.  
		cmp	bx,00h		
		jnz	$-4			; If the counter hasn't counted
						; down to 00h yet, keep going.
		ret

LCDinit:	mov	al,00111000b		; 8-bit, 2 lines, 5x8 characters
		out	01h,al
		mov	al,01h			
		out	02h,al
		call	LongDelay
		call	LongDelay
		mov	al,00h
		out	02h,al
		call	LongDelay
		call	LongDelay
		call	LongDelay
		
		mov	al,00001100b		; Turn display on, cursor off, and do not blink the character at cursor
		out	01h,al
		mov	al,01h
		out	02h,al
		call	LongDelay
		call	LongDelay
		mov	al,00h
		out	02h,al
		call	LongDelay
		call	LongDelay
		call	LongDelay
		
		mov	al,00000001b		; Clear the screen
		out	01h,al
		mov	al,01h
		out	02h,al
		call	LongDelay
		call	LongDelay
		mov	al,00h
		out	02h,al
		call	LongDelay
		call	LongDelay
		call	LongDelay

		mov	al,00010100b		; Set cursor to move and display to shift to the right
		out	01h,al
		mov	al,01h
		out	02h,al
		call	LongDelay
		call	LongDelay
		mov	al,00h
		out	02h,al
		call	LongDelay
		call	LongDelay
		call	LongDelay

		mov	al,00000110b		; Increment, and no display shift
		out	01h,al
		mov	al,01h
		out	02h,al
		call	LongDelay
		call	LongDelay
		mov	al,00h
		out	02h,al
		call	LongDelay
		call	LongDelay
		call	LongDelay

		mov	al,00000010b		; Return the cursor to home
		out	01h,al
		mov	al,00h
		out	02h,al
		call	LongDelay
		call	LongDelay
		out	02h,al
		call	LongDelay
		call	LongDelay
		call	LongDelay
		hlt

;		jmp	$
		ret

		times	2032- ($-$$) db 0
		DB      0EAh
		DW      0000h,0FF80h
		times   10 db 0

The only reason "hlt" is at the end of the LCDinit subroutine is because after I changed chips, I wanted to see if anything improved, and nothing did. I just didn't move it back to the place where it starts going crazy. When the "hlt" opcode is after the "mov", and before the "out", the program actually does halt. I moved it down one line, after the "out" line, and it never made it to the "hlt" line.

I can't believe it's doing this even after changing the CPU, clock chip, ROM, and RAM chips. I thought something must be defective. I doubt a faulty latch would cause it to do this, and I doubt a faulty 8255 would cause an infinite loop. I don't have an extra 8255 to swap, anyway.

This is so frustrating and insane. At this point, I'm thinking about just putting a jump to get past that address in code. The first thing that came to my mind was that maybe the ROM chip was malfunctioning at that address, but that doesn't seem to be the problem. I'm going to keep messing around to see if I can solve it, but I'm really curious what you guys make of all of this. I think it should be able to run a simple little program like this.

The CPU that's in there now is the 8088-1 10Mhz. I read that the closer the system clock gets to the max CPU clock speed, the better the digital waveform has to be, so I thought the higher speed CPU might help. Despite all this, I think it is running better from a different aspect. I don't have to hit the reset button as many times now for it to run the program. If it doesn't run on power-on, it usually runs after I hit reset once.

I'm going to look into the memory speeds, and see if they're fast enough for the CPU's timing requirements and clock periods. I'm pretty sure this 6116 should be fine, though, because that's what the author uses in his 8088 Project Book, and he seems extremely experienced. I have a 32KB SRAM chip, but I haven't put it in because I wanted to get it working his way first, and then I was going to modify it my way. I wonder if it's possible that the CPU isn't getting the address it put on the stack during the calls back in time. Just a hypothesis.

I forgot to add that I removed the 2 373 latches, because in "The 80x86 IBM PC and Compatible Computers (Volumes I & II), 4th Edition", they didn't show a latch between their LCD and the 8255, and they were doing it the same way I am, with a second port for the control signals.
 
Last edited:
It certainly sounds strange. I'm not exactly sure the point in your program you are talking about, but it might be the point where your address goes to 0080h in your ROM. That is on line 88 of your code, right after the out 02h, al. If the ROM for some reason is not responding fast enough to the change in the A7 address bit, it could be cycling back to address 0000h in ROM.

You might look at A7 with your scope and see if it is changing as the program cycles back and seems to reset. It could be a wiring problem or a problem with the latch or the EPROM.

Also you might have too slow an EPROM. It looks like from your video you are using a 28C16A-25. I figure you have less than 219nS from RD to the data being stable for a clock speed of 4.71MHz. Your chip has a 250nS access time from address to output and a 120nS time from OE to output according to one data sheet I looked at. So it may or may not be fast enough. You might try a faster one like 28C16A-20 or slow down your CPU clock speed. You can go down as low as 2MHz.
 
I'm wondering the same thing about access time. I'm studying the bus cycle and clock periods. I have a 32KB eeprom with an access time of 150nS. If my programmer supports it, I'm going to try that.

Code:
;              ************************************************
;              *                                              *
;              *  A PROGRAM TO EXPERIMENT WITH MY LCD DISPLAY *
;              *                                              *
;              ************************************************

;*****************************************************************************
;									     *
;                          USING 8-BIT MODE				     *
;									     *
; Lines used for control signals on port 2:                                  *
; B4  B0			        				     *
; RS  EN    Hex  Operation                                                   *
; **  **    ***  *********                                                   *
; 0   1     01h  IR write as an internal operation (display clear, etc.)     *
; 1   1     11h  Write data to DDRAM or CGRAM (DR to DDRAM or CGRAM)         *
;                                                                            *
;*****************************************************************************

USE16
section		.text
org		0
		
		xor	ax,ax
		mov	ss,ax
		mov	sp,2046
		mov	al,90h			;This sets the 8255 to operate
                                                ;in Mode 0 (basic input output)
                                                ;with port 0 as an input and 
                out	03h,al                  ;ports 1 and 2 as outputs.  
		xor	bx,bx

		call	LongDelay
		call	LCDinit


LongDelay:	mov	bx,0x3FFF		; Reset the countdown timer.
		dec	bx			; Decrement it by 1 each time.  
		cmp	bx,00h		
		jnz	$-4			; If the counter hasn't counted
						; down to 00h yet, keep going.
		ret

LCDinit:	mov	al,00111000b		; 8-bit, 2 lines, 5x8 characters
		out	01h,al
		mov	al,01h			
		out	02h,al
		call	LongDelay
		call	LongDelay
		mov	al,00h
		out	02h,al
		call	LongDelay
		call	LongDelay
		call	LongDelay
		
		mov	al,00001100b		; Turn display on, cursor off, and do not blink the character at cursor
		out	01h,al
		mov	al,01h
		out	02h,al
		call	LongDelay
		call	LongDelay
		mov	al,00h
		out	02h,al
		call	LongDelay
		call	LongDelay
		call	LongDelay
		
		mov	al,00000001b		; Clear the screen
		out	01h,al
		mov	al,01h
		out	02h,al
		call	LongDelay
		call	LongDelay
		mov	al,00h
		out	02h,al
		call	LongDelay
		call	LongDelay
		call	LongDelay

		mov	al,00010100b		; Set cursor to move and display to shift to the right
		out	01h,al
		mov	al,01h
		out	02h,al
		call	LongDelay
		call	LongDelay
		mov	al,00h
		hlt				; It's right here that the program seems to jump back to the beginning of the routine.
						; It can make it to this "hlt" opcode, but if I take it out, it never makes it to the "out" line below.
		out	02h,al
		call	LongDelay
		call	LongDelay
		call	LongDelay

		mov	al,00000110b		; Increment, and no display shift
		out	01h,al
		mov	al,01h
		out	02h,al
		call	LongDelay
		call	LongDelay
		mov	al,00h
		out	02h,al
		call	LongDelay
		call	LongDelay
		call	LongDelay

		mov	al,00000010b		; Return the cursor to home
		out	01h,al
		mov	al,00h
		out	02h,al
		call	LongDelay
		call	LongDelay
		out	02h,al
		call	LongDelay
		call	LongDelay
		call	LongDelay

;		jmp	$
		ret

		times	2032- ($-$$) db 0
		DB      0EAh
		DW      0000h,0FF80h
		times   10 db 0

I modified the code to see if bypassing the weird part in memory would help. It didn't. Here is my new code:
Code:
;              ************************************************
;              *                                              *
;              *  A PROGRAM TO EXPERIMENT WITH MY LCD DISPLAY *
;              *                                              *
;              ************************************************

;*****************************************************************************
;									     *
;                          USING 8-BIT MODE				     *
;									     *
; Lines used for control signals on port 2:                                  *
; B4  B0			        				     *
; RS  EN    Hex  Operation                                                   *
; **  **    ***  *********                                                   *
; 0   1     01h  IR write as an internal operation (display clear, etc.)     *
; 1   1     11h  Write data to DDRAM or CGRAM (DR to DDRAM or CGRAM)         *
;                                                                            *
;*****************************************************************************

USE16
section		.text
org		0
		
		xor	ax,ax
		mov	ss,ax
		mov	sp,2046
		mov	al,90h			;This sets the 8255 to operate
                                                ;in Mode 0 (basic input output)
                                                ;with port 0 as an input and 
                out	03h,al                  ;ports 1 and 2 as outputs.  
		xor	bx,bx

		call	LongDelay
		call	LCDinit
		call	LCDinit2


LongDelay:	mov	bx,0x3FFF		; Reset the countdown timer.
		dec	bx			; Decrement it by 1 each time.  
		cmp	bx,00h		
		jnz	$-4			; If the counter hasn't counted
						; down to 00h yet, keep going.
		ret

LCDinit:	mov	al,00111000b		; 8-bit, 2 lines, 5x8 characters
		out	01h,al
		mov	al,01h			
		out	02h,al
		call	LongDelay
		call	LongDelay
		mov	al,00h
		out	02h,al
		call	LongDelay
		call	LongDelay
		call	LongDelay
		
		mov	al,00001100b		; Turn display on, cursor off, and do not blink the character at cursor
		out	01h,al
		mov	al,01h
		out	02h,al
		call	LongDelay
		call	LongDelay
		mov	al,00h
		out	02h,al
		call	LongDelay
		call	LongDelay
		call	LongDelay
		
		mov	al,00000001b		; Clear the screen
		out	01h,al
		mov	al,01h
		out	02h,al
		call	LongDelay
		call	LongDelay
		mov	al,00h
		out	02h,al
		call	LongDelay
		call	LongDelay
		call	LongDelay

		mov	al,00010100b		; Set cursor to move and display to shift to the right
		out	01h,al
		mov	al,01h
		out	02h,al
		ret
		times	6 db 0	

LCDinit2: 	

		call	LongDelay
		call	LongDelay
		mov	al,00h
		out	02h,al
		call	LongDelay
		call	LongDelay
		call	LongDelay

		mov	al,00000110b		; Increment, and no display shift
		out	01h,al
		mov	al,01h
		out	02h,al
		call	LongDelay
		call	LongDelay
		mov	al,00h
		out	02h,al
		call	LongDelay
		call	LongDelay
		call	LongDelay

		mov	al,00000010b		; Return the cursor to home
		out	01h,al
		mov	al,00h
		out	02h,al
		call	LongDelay
		call	LongDelay
		out	02h,al
		call	LongDelay
		call	LongDelay
		call	LongDelay
		hlt
		ret

;		jmp	$

		times	2032- ($-$$) db 0
		DB      0EAh
		DW      0000h,0FF80h
		times   10 db 0

It still kept repeating in an infinite loop, and sometimes it acts like it makes it to the "hlt" opcode and stops. When I get home from work, I'll try the other eeprom.
 
Last edited:
May I offer a couple of coding hints?

Unless you're forced to, never use $-based addressing on x86 code. The reason is that the same x86 mnemonic can generate differing instruction lengths. For example, CMP BX,00 can generate either 83 FB 00 or 81 FB 00 00--both work, but one is a byte longer. Use a tag related to the name of the routine to point out jump locations.

So:

Code:
LongDelay:
             mov bx,3FFFh
LongDelay2:
             dec bx
             jnz LongDelay2
             ret

You don't need the CMP BX,0 because the DEC instruction sets ZF if the decremented result was zero.

Also: So you're using a 2716/2816 PROM to store this stuff?
 
Last edited:
You don't need the CMP BX,0 because the DEC instruction sets ZF if the decremented result was zero.
Which is why if you're going to count, use CX -- then you can use loop instead for one less opcode...

BUT -- I assumed those were inefficient on purpose to create the proper length delays... and that's where I'd be questioning things in terms of this code working -- are those time delays accurate or not. What clock speed is that 8088 running at? Generally I try to do something that I KNOW will take extra clocks outside the BUI (like read from a memory location and discard the value) so as to be sure such delays take the minimum time necessary. (since clock counting is a PITA 8088, much less newer). Or if you know you're only using one hardware target, a NOP is good as always.

... and I'm with chuck on the use labels instead of direct jumps. If you are on a good compiler it will allow for LOCAL labels using the @ symbol, meaning they only exist between normal labels allowing them to be reused.
Code:
LongDelay:
	mov  cx,4000h
@longLoop:
	nop
	loop @longLoop
	ret

After all, that's why we have NOP in the first place. First loop the NOP would take 3 clocks, on the second loop NOP would take 7 clocks since the LOOP would empty the BIU. Figuring in the 17 clocks on loop and the 5 clocks on drop-through, with the 16 clocks of the MOV, that would make a 393,233 clock cycle loop (not counting of course the far call to this routine and it's RET) once you figure in the BIU optimizations.

Dunno what you are actually aiming for clock-wise.
 
Didja know that on the 386 and up, the decrement-and-jump sequence is marginally faster than LOOP? There's a bunch of stuff like that; e.g. JCXZ is slower on the 386 than TEST, JZ...
 
May I offer a couple of coding hints?

Unless you're forced to, never use $-based addressing on x86 code. The reason is that the same x86 mnemonic can generate differing instruction lengths. For example, CMP BX,00 can generate either 83 FB 00 or 81 FB 00 00--both work, but one is a byte longer. Use a tag related to the name of the routine to point out jump locations.

So:

Code:
LongDelay:
             mov bx,3FFFh
LongDelay2:
             dec bx
             jnz LongDelay2
             ret

You don't need the CMP BX,0 because the DEC instruction sets ZF if the decremented result was zero.

Also: So you're using a 2716/2816 PROM to store this stuff?

Thanks a lot for your valuable advice. I didn't know that it could result in differing instruction lengths. I actually was using a jmp. The only reason I changed it to $-4 was to see if it would make any difference when it ran. I'm going to change it back to the jmp now that I know about that. I only know basic stuff about x86 assembly. While this is frustrating, it's fun to learning new things like that. Since I'm still a novice, I just put the cmp line there because the author had it in his timing delay. He used MASM. I wonder if it makes a difference, and I wonder if he knew about what you said, about it being unnecessary. While I'm not blaming him for anything, it may be possible that some of my problems are because of some of the things he shows in his book.

Yeah, I'm using an M28C16A-25 chip manufactured by OKI. I can't find the exact datasheet by OKI online. I can only find "compatible", or "equivalent" datasheets by other manufacturers.
 
Which is why if you're going to count, use CX -- then you can use loop instead for one less opcode...

BUT -- I assumed those were inefficient on purpose to create the proper length delays... and that's where I'd be questioning things in terms of this code working -- are those time delays accurate or not. What clock speed is that 8088 running at? Generally I try to do something that I KNOW will take extra clocks outside the BUI (like read from a memory location and discard the value) so as to be sure such delays take the minimum time necessary. (since clock counting is a PITA 8088, much less newer). Or if you know you're only using one hardware target, a NOP is good as always.

... and I'm with chuck on the use labels instead of direct jumps. If you are on a good compiler it will allow for LOCAL labels using the @ symbol, meaning they only exist between normal labels allowing them to be reused.
Code:
LongDelay:
	mov  cx,4000h
@longLoop:
	nop
	loop @longLoop
	ret

After all, that's why we have NOP in the first place. First loop the NOP would take 3 clocks, on the second loop NOP would take 7 clocks since the LOOP would empty the BIU. Figuring in the 17 clocks on loop and the 5 clocks on drop-through, with the 16 clocks of the MOV, that would make a 393,233 clock cycle loop (not counting of course the far call to this routine and it's RET) once you figure in the BIU optimizations.

Dunno what you are actually aiming for clock-wise.

That's good information. But all I was trying to do was meet or exceed the required amount of wait time for the LCD. These delay values here aren't even the ones I started with after measuring the frequency on my oscilloscope. I intentionally increased the values on the loop to create more delay time. I wanted to see if adding a longer delay would help with the LCD initialization. I would have been surprised if it had actually helped, but I wanted to try it anyway. The values I started with met or slightly exceeded the required wait times for my LCD module. I figured it couldn't hurt to try it. But after further investigation, I think that there is a more serious reason for the LCD not working, since this simple program isn't even running correctly. This is actually now a debugging program so I can see why my computer board isn't working right. Thanks for your info.
 
Just good coding practice. The problem with $-xxx is that if you get the urge to debug and insert an instruction or two, you might forget to adjust the explicit displacement in the jump. After all, what's an assembler for? If you've got to calculate your own addresses, you might as well write straight machine code.

Okay, at least I know your setup and so can make heads or tails of what you're writing.

Just curious, though why you simply didn't org the jump at F000:FFF0--you don't really have to fill the unused space, do you? So, if you're using a 2816 2K ROM, you'd org the jump at (2048-16) or (800h-10h).

Now, here's the other cool thing about an assembler. You can use symbolic constants and define everything in relation to them. So, you can say:

Code:
ROM_SIZE     equ     2048         ; size of ROM
         org     0
Startup:
        ....
        org     ROM_SIZE-16
        db      0eah                               ; far inter-segment jump
        dw      offset Startup
        dw      10000h-(ROM_SIZE/16)   ; target segment

That way, you can re-assemble for any size ROM and your code will still work. You'll also notice that I put my code labels on their own line. This allows me to use longer (read: more descriptive) names and the code still looks neat.
 
I tried to post this message, but for some reason it didn't post, so sorry if it comes up more than once.

I can't find the correct datasheet by OKI. All I see are similar datasheets from other manufacturers. The author is using a 7404 to invert the 8088's IO/M (M is active-low) line and NAND it together with the 8088's A19 line to produce the active-low ROMSEL line to enable the ROM. He says this uses 25% of the normal operating current, but it causes a longer access time when the chip is enabled. I want to study the datasheet to see if I can figure out a way to use this ROM without this timing penalty. I don't care if it uses more current. I just want to see if it helps to eliminate some problems in the system. Do you think it's ok to go off of other manufacturers' datasheets? I would prefer the actual correct one. I ordered it from Jameco, but they don't offer datasheets from the same manufacturer a lot of times. Many times, they have datasheets from other manufacturers.
 
Just good coding practice. The problem with $-xxx is that if you get the urge to debug and insert an instruction or two, you might forget to adjust the explicit displacement in the jump. After all, what's an assembler for? If you've got to calculate your own addresses, you might as well write straight machine code.

Okay, at least I know your setup and so can make heads or tails of what you're writing.

Just curious, though why you simply didn't org the jump at F000:FFF0--you don't really have to fill the unused space, do you? So, if you're using a 2816 2K ROM, you'd org the jump at (2048-16) or (800h-10h).

Now, here's the other cool thing about an assembler. You can use symbolic constants and define everything in relation to them. So, you can say:

Code:
ROM_SIZE     equ     2048         ; size of ROM
         org     0
Startup:
        ....
        org     ROM_SIZE-16
        db      0eah                               ; far inter-segment jump
        dw      offset Startup
        dw      10000h-(ROM_SIZE/16)   ; target segment

That way, you can re-assemble for any size ROM and your code will still work. You'll also notice that I put my code labels on their own line. This allows me to use longer (read: more descriptive) names and the code still looks neat.

Once again, thank you for your awesome experience and knowledge. Originally, last year LOL, I actually did start out with org 07F0h, but I just changed it to see if it made a difference. However, when I ran the command on my cli to assemble the program to a hex listing, the second org directive caused nasm to output an error saying "program origin redefined". That was the reason I didn't org it to 07F0h. Once again, (I've changed the code so many times, seeing if little changes here and there cause it to run differently) I originally orged it at 07F0h. I just took it out to see if it would make a difference. I would have been surprised if it did, but I just wanted to see.

I do have one question, though. What is your last line of code supposed to do? I took 65536-128, and it came out to 65408. I don't know what that's supposed to do. Thanks again for all your advice. I feel bad for taking up so much of you guys' time, but I do greatly appreciate as much wizardly advice as you are willing to bestow unto me. This is stuff that I don't find in books, so to me, it's treasure.
 
I'm probably not following very well--it's been a long day. What display module are you using? I'd start there first.
I'm also assuming that you have 2K of (tested) RAM at 00000, no?

When I get an unfamiliar LCD display, I'll occasionally use the parallel port on a PC to get the hang of it, then move it into my design. You may want to consider that, since you'll be able to use a full-fledged debugger on your code. A parallel port is scarcely more than a couple of latches and a receiver (for status), so it's very easy to program.

The idea is to limit the number of unknowns in your design--your frustration seems to come from having to "fly blind".

There are several places to find information about LCDs Here's one that describes working with a display that I have a pile of and what it takes to get it running on a parallel port.

What display exactly do you have?
 
I do have one question, though. What is your last line of code supposed to do? I took 65536-128, and it came out to 65408. I don't know what that's supposed to do. Thanks again for all your advice. I feel bad for taking up so much of you guys' time, but I do greatly appreciate as much wizardly advice as you are willing to bestow unto me. This is stuff that I don't find in books, so to me, it's treasure.

65408 = FF80, so that FF80:0 points to the bottom of your ROM (assuming that it's mapped into FF800-FFFFF.

Suppose you change to an 8K ROM (e.g. 2864). You'd change ROM_SIZE to 8192 and then that segment calculated above would be FE00--and everything magically adjusts. Your code is again at the "bottom" of ROM, which is now FE000.
 
Back
Top