• Please review our updated Terms and Rules here

Can't get this 20x4 LCD to work

Well, you're still working in the vagaries of CS=FFFF. Really, if you want to be clear, you can even code a jump like this at the restart location:

Code:
    DB  0EAh
    DW   0000h,0FF80h

Which would jump to the start of the last 2K in memory.

So where do you set SS:SP for your stack? You CALLed routines aren't going to return anywhere meaningful if the stack's not in RAM.
 
Last edited:
So far, I haven't been directly using the stack. I've just been using the registers. The program that keeps looping the hex value to the 7-segment LEDs works fine. I'm still learning, but so far I haven't found a need to use the stack. I've actually been studying and learning about the stack, though.
 
Good point--I'd assumed that the OP had already debugged basic operation, although I'd wondered a bit about the code.

On 8086/88 CS=FFFF, IP=0000. Other registers are undefined at reset. So a CALL may work, but also may not without setting SS:SP.

===================
Take it from an old guy--when you're working with a new hardware setup, the first thing you should get going is a serial monitor. I've done it for years--and very often on embedded devices, you'll even see pads that the original developers included for a serial hookup.

There's nothing quite like a "Hello world" message to raise your confidence.

In your case, a UART, such as a 8250-type chip or even a 2650-type is easy to interface to. Add a MAX232 to shift your signals to RS232 levels and you've got it. I've done it so much that I have a MAX232 as a dongle that I can attach to any board needing comms.

If you need code for a simple serial console, I'd be happy to pass some along.

Cool, I'll probably do that. I've never tried it before. By serial monitor, do you mean an actual CRT monitor hooked up via a serial port? Or do you mean connecting another computer to the serial port and sending data that way? And I'd definitely like to see your code.

Thanks again :D
 
Last edited:
So far, I haven't been directly using the stack. I've just been using the registers. The program that keeps looping the hex value to the 7-segment LEDs works fine. I'm still learning, but so far I haven't found a need to use the stack. I've actually been studying and learning about the stack, though.

Um, I see "call" instructions in your code--they use the stack by pushing the return address onto it, then popping it into IP when a return is made. Is the code you posted your real code or not?

Color me confused. :sigh:
 
A far jump is for a range extending past a 64 KB segment. The jump is a short, since it's only within 2 KB. Since the EEPROM is only 2 KB, it sees the first 11 address lines. The A19 only selects the chip. The EEPROM doesn't know or care where it is in the memory map. Yes, I should probably use the segment directives, but I'm still relatively new to assembly. I should probably include them in my next programming session.

You may want to review how the Segment Registers in the 8088 work. If at startup CS=FFFF, your code segment goes from FFFF0 through FFFFF and then wraps around to 00000 through 0FFEF, so your short jump should put you in the bottom half of the address space, with A19=0.

I have a youtube video demonstrating a functioning short jump in a looping assembly program, and it can be viewed here http://www.youtube.com/watch?v=hmTlIxZ5NeY
In the video description is a link to download the assembly/hex file.

I looked at your assembler output, and yes you're doing a short jump, and yes it works!:eek:

Are you sure you're connected to A19, pin 35? If you trace from pin 35 through your latch and you're connected to the right latch output pin?

OK, I'm confused. I guess if it ain't broke don't fix it. But you might use a far jump just to be safe.

I was going to point out that CALL uses the stack, but I see Chuck already did. After reset, SS is initialized to zero, but SP is not initialized. So your stack pointer will be somewhere in RAM, but you don't know where. Since you're only using 2 bytes of RAM for your program, I guess odds are you won't have a problem. But again I'd probably initialize SP at least to be safe.
 
Last edited:
Yeah Chuck, it's my code. Thanks for making it seem like I'm taking credit for someone else's code. I did start out with the source code in the 8088 Project Book, but I modified most of it to fit my program. He did initialize the stack pointers, but when I wrote the program, I didn't know how to convert directives from masm to nasm. Like I said, I'm NEW to assembly. Yeah, A19 is connected to the EEPROM and latch. I already posted schematics for the computer on flickr. You can look at them if you want.
 
Last edited:
Yeah Chuck, it's my code. Thanks for making it seem like I'm taking credit for someone else's code. I did start out with the source code in the 8088 Project Book, but I modified most of it to fit my program. He did initialize the stack pointers, but when I wrote the program, I didn't know how to convert directives from masm to nasm. Like I said, I'm NEW to assembly. Yeah, A19 is connected to the EEPROM and latch. I already posted schematics for the computer on flickr. You can look at them if you want.

That wasn't my point--it's just that I found that "I'm not using the stack" and the presence of CALL instructions were contradictory statements. That left me with the conclusion that (a) you didn't know how the CALL instruction worked or (b) knew, but didn't think that initializing the stack pointer mattered or (c) didn't write the code.

No malice meant here, ever. Just doing what I've been doing all my life--trying to understand.

Let's initialize the stack to the top of the lowest 2K of memory (00000-007ff), which I'm assuming is RAM:

Code:
    xor     ax,ax
    mov     ss,ax
    mov     sp,2048

The first CALL will decrement SP by two, then store the return address at SS:SP.
 
That wasn't my point--it's just that I found that "I'm not using the stack" and the presence of CALL instructions were contradictory statements. That left me with the conclusion that (a) you didn't know how the CALL instruction worked or (b) knew, but didn't think that initializing the stack pointer mattered or (c) didn't write the code.

No malice meant here, ever. Just doing what I've been doing all my life--trying to understand.

Let's initialize the stack to the top of the lowest 2K of memory (00000-007ff), which I'm assuming is RAM:

Code:
    xor     ax,ax
    mov     ss,ax
    mov     sp,2048

The first CALL will decrement SP by two, then store the return address at SS:SP.

Ok, are you using exclusive or to determine if there is a value in ax? Thanks
 
No, I'm using XOR AX,AX to set AX to 0000. Consider:

MOV AX,0 encodes to B8 00 00 (3 bytes)

but

XOR AX,AX encodes to 31 C0 (2 bytes)

You could just as easily use:

SUB AX,AX encodes to 29 C0 (2 bytes)

DEBUG (available on most Windows (but for 64 bit) versions under a command prompt can be your friend. It has a rudimentary assembler, shows register contents and allows you to single-step through code and discover some cute shortcuts.

For example, suppose you have a problem that requires you to interrogate the carry flag and set AX to 0 if it's clear or FFFF if it's set. You could code it this way:

mov ax,0 (MOV doesn't affect the flags)
jnc nocarry
not ax (set ax to FFFF)
nocarry:

Alternatively, you could use:

sbb ax,ax

which will do the same thing.
 
That's cool. I know the truth table to the XOR TTL, but wasn't sure about that one. I like stuff like that how it can optimize and reduce the size of code and stuff. I've only been into assembler seriously for like a month or less. It's so fun. Unfortunately, all of the tutorials, even the books I've bought, teach how to program in assembly for DOS. So far, I haven't found anything that teaches how to approach assembly from the ground up. Even my 8088/86 books that talk about the support chips and all that stuff talk about how to write DOS assembly programs. I use Linux, for one, and at this point in time, I don't want to write DOS-compatible programs. I only use XP on my laptop that's not connected to the internet for 3 reasons: 1. My EEPROM programmer works on it. 2. My BASIC Stamp, and its software works on it. 3. My PicKit 2 Microchip PIC16F690 software works on it. Otherwise, I wouldn't have a Windows installation, so I probably won't be using DEBUG.

So far, I like nasm a lot. The only slight problems are translating how to do masm stuff on it. Also, I would really love to see your code for the serial communicitations. I would love to find a good book that doesn't assume you're operating in a DOS environment, or any OS for that matter, and to show how to write stand-alone assembler code. A lot of the code I've studied in my books have worth to me, but not the DOS stuff. It's especially annoying when, in later chapters, they deal almost exclusively with DOS assembly programs.
 
Yeah, A19 is connected to the EEPROM and latch. I already posted schematics for the computer on flickr. You can look at them if you want.

Yes, I saw the schematic. And I saw your videos. Nice videos. It looks like a fun project.

My problem was I couldn't figure out how it was working, and I'm actually still confused:confused:

For example, here is the code you posted to count on the LEDS:

Code:
    1                                  ;              ************************************************
     2                                  ;              *                                              *
     3                                  ;              *    A TEST PROGRAM TO PRACTICE TIMING LOOPS   *
     4                                  ;              *                                              *
     5                                  ;              ************************************************
     6                                  ; 
     7                                  ;*****************************************************************************
     8                                  ;                            INITIALIZE THE 8255                             *
     9                                  ;*****************************************************************************
    10                                  
    11                                  
    12 00000000 B090                    start:		mov	al,90h			;This sets the 8255 to operate
    13                                                                                  ;in Mode 0 (basic input output)
    14                                                                                  ;with port 0 as an input and 
    15 00000002 E603                                    out	03h,al                  ;ports 1 and 2 as outputs.  
    16 00000004 BB0000                  		mov	bx,0x0000 
    17                                  
    18 00000007 E90F00                  		jmp	begin			;Jump to the main loop.
    19                                  
    20 0000000A C707FF6F                waitasec:	mov	word [bx],0x6FFF	;Reset the countdown timer.
    21 0000000E FF0F                    waitasec1:	dec	word [bx]		;Decrement it by 1 each time.
    22                                  
    23 00000010 E601                    		out	01h,al 			;Display the hex value at the 
    24                                  						;8255's port 1 on the LEDs.
    25                                  
    26 00000012 813F0000                		cmp	word [bx],00h		;If the timer has counted down
    27                                  						;all the way, return to the 
    28                                  						;'nexthex' label so that it
    29                                  						;can move on to the next hex
    30                                  						;value to display on the LEDs.
    31                                  
    32 00000016 75F6                    		jnz	waitasec1		;If the counter hasn't counted
    33                                  						;down to 00h yet, keep going.
    34                                  
    35 00000018 C3                      		ret				;Here is where the zero flag
    36                                  						;is checked. If the timer has
    37                                  						;reached 00h, go to 'nexthex'.
    38                                  
    39 00000019 B000                    begin:		mov	al,00000000b 		;Set all eight bits to 0.
    40                                  
    41 0000001B E8ECFF                  		call	waitasec		;Start the countdown timer.
    42                                  
    43 0000001E FEC0                    nexthex:	inc	al			;Go on to the next hex value.
    44                                  
    45 00000020 E8E7FF                  		call	waitasec		;Show the hex digit for a while.
    46                                  
    47 00000023 3C2F                    		cmp	al,2Fh			;We're back in 'nexthex' again, so
    48                                  						;we check to see if the second LED
    49                                  						;has reached the maximum value.
    50                                  
    51 00000025 75F7                    		jnz	nexthex			;If not, move to the next hex 
    52                                  						;digit to display.
    53                                  
    54 00000027 E9EFFF                  		jmp	begin			;If we have reached the last 
    55                                  						;possible value in the character
    56                                  						;table, start all over again.
    57                                  
    58 0000002A 00<rept>                		times	2032- ($-$$) db 0
    59 000007F0 E90DF8                  		jmp	start
    60 000007F3 00<rept>                		times	12 db 0
    61

Line 59 "jmp start" is at location 7F0h, and since the PROM is mapped to A19=1, this would get mapped to an absolute address of FFFF0h, which is the 8088 startup vector.
So far, so good.

The jmp start is coded as E9 0D F8, so it's a relative jump, back to the start location, which at the beginning of the PROM.

But if I go step by step, pretending to be the 8088 I have to deal with brain-hurting segment math:

At reset, CS = FFFFh, IP=0000h. So absolute program counter is at FFFF0h + 0000h = FFFF0h.

I fetch the instruction at FFFF0h, it's a 3 byte instruction, so I end up with CS = FFFFh, IP = 0003h, absolute address is FFFF3h.

The instruction is a relative jump with displacement of F80Dh.

So IP goes to 0003h + F80Dh = F810h. Now my absolute address is FFFF0h + F810h = 10F800h. But I only have 20 bits of absolute address, so it becomes 0F800h.

So I jump to 0F800h, which is in RAM, not PROM because A19=0 now. So it doesn't work?:eek:

But we have video proof that it works. I was grasping at straws trying to figure out why?

I thought, well what if instead of wiring up to the A19 pin (35), you wired it to the A15 pin (39) by mistake... then it would work. Very unlikely, I know. Or I thought you could have connected to the wrong output pin on the latch by mistake.. if you connected to pin 9, which leads back to pin A15, instead of pin 19 which leads back to pin A19.

That's why I was asking you to check the connection to A19. I was just trying to understand what's going on.:cool:
 
So why don't you use masm? It's free and an excellent assembler with capabilities far beyond nasm.

I'll dig around and see if I can find some sample code for you.
 
Yes, I saw the schematic. And I saw your videos. Nice videos. It looks like a fun project.

My problem was I couldn't figure out how it was working, and I'm actually still confused:confused:

For example, here is the code you posted to count on the LEDS:

Code:
    1                                  ;              ************************************************
     2                                  ;              *                                              *
     3                                  ;              *    A TEST PROGRAM TO PRACTICE TIMING LOOPS   *
     4                                  ;              *                                              *
     5                                  ;              ************************************************
     6                                  ; 
     7                                  ;*****************************************************************************
     8                                  ;                            INITIALIZE THE 8255                             *
     9                                  ;*****************************************************************************
    10                                  
    11                                  
    12 00000000 B090                    start:		mov	al,90h			;This sets the 8255 to operate
    13                                                                                  ;in Mode 0 (basic input output)
    14                                                                                  ;with port 0 as an input and 
    15 00000002 E603                                    out	03h,al                  ;ports 1 and 2 as outputs.  
    16 00000004 BB0000                  		mov	bx,0x0000 
    17                                  
    18 00000007 E90F00                  		jmp	begin			;Jump to the main loop.
    19                                  
    20 0000000A C707FF6F                waitasec:	mov	word [bx],0x6FFF	;Reset the countdown timer.
    21 0000000E FF0F                    waitasec1:	dec	word [bx]		;Decrement it by 1 each time.
    22                                  
    23 00000010 E601                    		out	01h,al 			;Display the hex value at the 
    24                                  						;8255's port 1 on the LEDs.
    25                                  
    26 00000012 813F0000                		cmp	word [bx],00h		;If the timer has counted down
    27                                  						;all the way, return to the 
    28                                  						;'nexthex' label so that it
    29                                  						;can move on to the next hex
    30                                  						;value to display on the LEDs.
    31                                  
    32 00000016 75F6                    		jnz	waitasec1		;If the counter hasn't counted
    33                                  						;down to 00h yet, keep going.
    34                                  
    35 00000018 C3                      		ret				;Here is where the zero flag
    36                                  						;is checked. If the timer has
    37                                  						;reached 00h, go to 'nexthex'.
    38                                  
    39 00000019 B000                    begin:		mov	al,00000000b 		;Set all eight bits to 0.
    40                                  
    41 0000001B E8ECFF                  		call	waitasec		;Start the countdown timer.
    42                                  
    43 0000001E FEC0                    nexthex:	inc	al			;Go on to the next hex value.
    44                                  
    45 00000020 E8E7FF                  		call	waitasec		;Show the hex digit for a while.
    46                                  
    47 00000023 3C2F                    		cmp	al,2Fh			;We're back in 'nexthex' again, so
    48                                  						;we check to see if the second LED
    49                                  						;has reached the maximum value.
    50                                  
    51 00000025 75F7                    		jnz	nexthex			;If not, move to the next hex 
    52                                  						;digit to display.
    53                                  
    54 00000027 E9EFFF                  		jmp	begin			;If we have reached the last 
    55                                  						;possible value in the character
    56                                  						;table, start all over again.
    57                                  
    58 0000002A 00<rept>                		times	2032- ($-$$) db 0
    59 000007F0 E90DF8                  		jmp	start
    60 000007F3 00<rept>                		times	12 db 0
    61

Line 59 "jmp start" is at location 7F0h, and since the PROM is mapped to A19=1, this would get mapped to an absolute address of FFFF0h, which is the 8088 startup vector.
So far, so good.

The jmp start is coded as E9 0D F8, so it's a relative jump, back to the start location, which at the beginning of the PROM.

But if I go step by step, pretending to be the 8088 I have to deal with brain-hurting segment math:

At reset, CS = FFFFh, IP=0000h. So absolute program counter is at FFFF0h + 0000h = FFFF0h.

I fetch the instruction at FFFF0h, it's a 3 byte instruction, so I end up with CS = FFFFh, IP = 0003h, absolute address is FFFF3h.

The instruction is a relative jump with displacement of F80Dh.

So IP goes to 0003h + F80Dh = F810h. Now my absolute address is FFFF0h + F810h = 10F800h. But I only have 20 bits of absolute address, so it becomes 0F800h.

So I jump to 0F800h, which is in RAM, not PROM because A19=0 now. So it doesn't work?:eek:

But we have video proof that it works. I was grasping at straws trying to figure out why?

I thought, well what if instead of wiring up to the A19 pin (35), you wired it to the A15 pin (39) by mistake... then it would work. Very unlikely, I know. Or I thought you could have connected to the wrong output pin on the latch by mistake.. if you connected to pin 9, which leads back to pin A15, instead of pin 19 which leads back to pin A19.

That's why I was asking you to check the connection to A19. I was just trying to understand what's going on.:cool:

Maybe as I newb, I just got lucky :p Or maybe it just goes through all the memory locations looking for code, or maybe it just stays in the rom. I see what you're saying, and I dunno lol.
 
That wasn't my point--it's just that I found that "I'm not using the stack" and the presence of CALL instructions were contradictory statements. That left me with the conclusion that (a) you didn't know how the CALL instruction worked or (b) knew, but didn't think that initializing the stack pointer mattered or (c) didn't write the code.

No malice meant here, ever. Just doing what I've been doing all my life--trying to understand.

Let's initialize the stack to the top of the lowest 2K of memory (00000-007ff), which I'm assuming is RAM:

Code:
    xor     ax,ax
    mov     ss,ax
    mov     sp,2048

The first CALL will decrement SP by two, then store the return address at SS:SP.

I just thought of something. Shouldn't SP be set to 2047, since a 2 KB chip only has 11 address lines, A0 - A10?
 
Well, you can set it to anything you want, but the way the call operation works is that return address(low)->SP-2, return address(high)->SP-1 (i.e. 2047). Although it doesn't matter on the 8088, it does matter on the 16-bit 8086; you should set SP to an even address to avoid unnecessary memory accesses.

But that brings up another cute trick. Suppose that you want to set a trap for the case of a stack underrun; that is, you have too many pops for your pushes. You can start off your code by pushing the trap address onto the stack, e.g.,

mov ax,offset TrapRoutine
push ax

That way, if you popped one too many words off the stack and execute a RET, your code will go to the trap routine, rather than off into nowhere.
 
Back
Top