0

I am a freshman studying computer science. In computer engineering we are working on a Zilog Z80 8-bit microprocessor (1MHz) and a set of components that need to be manually connected using a breadboard and cables.

The connecting part doesn't worry me, but I do have questions concerning the Assembly program that I need to write to get my programm working (LED running light, with manual input for behaviour and frequency).

I have already read the manual and know the set of instructions that can be used (only the bare necessities). For a start, I am not trying to get the cleanest, best looking code possible; but not to worry, I'll prettify it later on, since I like clean and efficient code.

For the moment, the program seems to run well in the simulator, so the syntax seems to be alright. Still, I am not sure how to progress with certain logical problems.

The exercise has the following specifications:

  1. Start address RAM: E000h
  2. Input port 1: 03h
  3. Output port 1: 05h
  4. I/O-mapping for ports
  5. Circuits are automatically open (1) so LED are LOW-active (0)
  6. Input 2,3,4 changes LED movement behaviour
  7. Input 5,6 changes LED blinking frequency

I have set the start address using ORG E000h and initialized the stackpointer using MOV SP,FFFFh. For the input (three different types of blinking/running, as well as two different frequencies, equaling five buttons in total) I have created different labels.

My problem at the moment is that I am not quite sure how to get my physical input right - IIRC, I'd need to specify a bit pattern by using XOR with everything being 1 but the desired input, so that I can use the information in my program.

But even though I do know the way it is supposed to work (at least I think that I know), I cannot quite wrap my head around the software implementation. Also, I have problems with conditions: Pressing one switch changes blinking frequency to 1/4 Hz, while pressing the other changes it to 4Hz. In higher-level languages I'd just use IF/ELSE here, but I don't know how to do it in this case - sadly, the manual only includes basic operations so I am at a loss.

Therefore, I thought I'd try my luck and ask the community for help.

For those interested, I'll post my code. It is very basic, as I already mentioned, but I just need it to get the job done for the time being. Since I am not a fan of huge chunks of clumsily formatted code, I have posted the file here. The file is a *.txt hosted through GoogleDrive.

Thank you for your time and have a nice day!

[EDIT] added specific code in post, according to input by user Ruud Helderman

[EDIT] updated code in *.txt-file - now simpler and more efficient

[EDIT] used HTML-formatting to highlight directives in post

Specific code snippet:

blink:       ;function: all LED blinking, activated via input[2]
MOV A,FFh
OUT 05h,A     ;all LED out
CALL pause1   ;frequency 1/4Hz, activated via input[5]
MOV A,00h
OUT 05h,A     ;all LED on
CALL pause1
JP blink      ;jump back to begin of function

The above function changes LED behavior (in this case: blinking) and also frequency using different specific physical switches on an input switch board with eight switches total (1 to 8, inactivate state = 1; switches 2 to 6 are used). I know that getting input should be a piece of cake - it should just be a matter of using XOR with bit patterns of 0 and exactly one 1.

While trying to find a solution for my problem I came upon different approaches online, such as using TEST to check for bits on specific locations. Still, my instruction manual has no mention of any such directive and the assignment itself doesn't mention it either.

I am quite aware that this is probably a trivial question, and maybe I am just stuck in a mental loop that I created myself by overthinking, but at the moment I don't know how to get to were I need to be (even though I can see the castle on the horizon - thanks Kafka!).

Any help is greatly appreciated.

Tsahi Asher
  • 1,767
  • 15
  • 28
MichaelP
  • 41
  • 10
  • 3
    I can understand you would like somebody to help you get along, but in stackoverflow it is important to ask one question at a time. It seems your main concern is with bitwise tests; you may want to edit your question and focus on that. As for if-then-else constructs, that shouldn't be too hard for you, since you already used JP and JPNZ before. Looking at your code, I'd say you're doing fine so far. Just don't reset SP at the start of every subroutine; you are killing your return address. – Ruud Helderman Nov 03 '18 at 22:03
  • 1
    Thank you very much for your input. I will edit my question accordingly. Concerning the unneccessary extra-initialization of SP, I must admit that I don't know how it ended up there. Thanks for the heads-up in any case; my instructor wouldn't have been to fond of that! – MichaelP Nov 03 '18 at 22:18
  • 2
    Please note that it is preferred to include (the relevant part of) your source code in the question, rather than link to an external source. – Ruud Helderman Nov 04 '18 at 09:57
  • 1
    Once again thank you. I will edit my post accordingly and will directly include specific code snippets in future posts. I just need to get an understanding of all the different formatting options since I don't want my code to look clunky. Well, guess I'll have to spend some more time in the according section! – MichaelP Nov 04 '18 at 14:33

2 Answers2

2

First things first: if you're using MOV then you're probably using 8080 syntax rather than Z80 syntax. For historical legal reasons the Z80 doesn't just extend the 8080's assembly language, it also renames all of the existing mnemonics (MOV to LD, for example). If you're searching for Z80 code and finding instructions you don't recognise, that's likely to be part of it.

The usual way to implement if/else-type conditionals is:

  1. perform any operation that sets a status flag in an appropriate manner; and
  2. use one of the conditional jumps to either skip some code or not, depending on the status flag.

In your case you want to do something or not do something depending on whether a bit is set, so one way to do that is ANI (z80: AND). That computes the logical and of the accumulator and an operand, storing it in the accumulator but amongst other things it also sets the zero flag. So you can use JNZ (/JP NZ) and JZ (/JP Z) to do something or not based on whether a bit is set. E.g.

; upon entry, A has an unknown value, loaded from somewhere.

ANI 08h    ; Set a = a & 8; so either bit 2 was originally set and a now
           ; has the value 8, or bit 2 wasn't set and a now has the value 0.

           ; Also: the zero flag is now set if a is zero, reset otherwise.

           ; So you've loaded NOT (a.bit2) into the zero flag.

           ; You've also lost the rest of the accumulator, but such is life.
           ; Keep a copy somewhere, or grab it again via IN as required.

JZ bitnotset

; code here will be performed only if bit 2 was originally unset.

bitnotset:

; this code will happen regardless of whether bit 2 was set.

I'm not aware of a TEST in either 8080- or Z80-style syntax.

You might also see a less destructive and slightly more efficient solution for testing multiple bits in a byte in sequence by shifting them into the carry bit. That's another option, but not necessarily worth worrying about unless your course notes strongly imply it's the direction you should be looking in.

Tommy
  • 99,986
  • 12
  • 185
  • 204
  • Thank you very much for your answer! As you said, the syntax seemed a little "off" even to me. `LD` was mentioned online but our manual uses `MOV`. Still, there is no `ANI`, only `AND`. Regarding `TEST`: this was just me being desperate. But there is `CP`, so I think I'll find a way to correctly compare bit patterns and set flags accordingly. By the way, the reason for this "syntax-medley" could be that we are using a very old circuit board (the "MC8") for practicing architecture and understanding the very basics of Assembly. But I digress - thank you again, your answer has been very helpful! – MichaelP Nov 05 '18 at 20:21
  • @OingoBoingo as far as I am aware, `AND` is the z80 syntax for anding either a register or an immediate value, whereas the 8080 syntax differentiates between `ANA` (for anding with another register) and `ANI` (and and immediate). So possibly your assembler will accept either. `CP` does a subtract rather than a bitwise and, so it depends on whether you're looking for a bit pattern or for magnitude versus another value. Whatever works. – Tommy Nov 05 '18 at 20:25
  • Why would you ever need a shift to test something about multiple bits? `ani 0f0h` sets ZF if all 4 high bits were clear, otherwise clears ZF. Or did you mean if one bit is cleared while another is set? You should be able to XOR to flip some bits, and then AND to create an all-zero or not-all-zero condition. If there are more than 2 branch targets (not just a binary decision), then yeah you could shift bits in a decision tree for a `switch`. – Peter Cordes Nov 06 '18 at 05:21
  • @PeterCordes if you have a byte where each bit is an independent input then a series of shifts and jumping upon carry clear allows you to deal with each in turn without having to reload the byte from anywhere. Given the resources the internet focuses on, most likely it'd be something like a joystick input — test bit 0 to see whether the player is pushing left, then test bit 1 to see whether they are pushing up, etc. – Tommy Nov 06 '18 at 12:32
  • 1
    @Tommy: oh right, you said "in sequence", so yeah that matches what I was saying in my last sentence where you aren't just testing the value of one multi-bit field. – Peter Cordes Nov 06 '18 at 12:42
  • Maybe I can help by providing further detail. The program's goal is to check if certain buttons are pressed. Frequency (buttons 5, 6) goes from 00 = 0.5Hz to 01 = 1Hz to 10 = 2Hz to 11 = 4Hz. I have been trying to check for that input by inverting bit patterns with `XOR FFh` and then using `AND` to get specific patterns, e.g. all LED blinking with 2Hz delay. But I am not 100% sure on how to implement this. As contributor Ruud Helderman already said, it should be done with jumps and certain flags. But I am still a little stuck in getting my brain to "compile" my ideas into working code. – MichaelP Nov 06 '18 at 14:53
  • @OingoBoingo the 00, 01, is AFTER or BEFORE `xor FF`? Anyway, there's clear patter in that, every next step is halving previous delay. So you can us those 00, 01, ... as counter for recursion depth, i.e. `call_delay(freq) { if (freq < 3) { call_delay(freq+1); call_delay(freq+1); } else do_4Hz_delay(); }` - this will wait 2000/1000/500/250ms depending on that "freq" value being 0, 1, 2 or 3. (of course with proper 4Hz delay routine, and ignoring the time spend in the control code, so the real delay will be few more cycles). But this will not read/react to inputs during delay, UX will suffer. – Ped7g Nov 09 '18 at 15:27
  • @OingoBoingo so you actually may want to read inputs as part of delay code itself and decide when to react to input changes... maybe even restart code with new change. Also `ld sp,0ffffh` is a bit weird, as Z80 has 16 bit `push/pop` instructions, so I would rather go with `ld sp,0` to have stack "aligned" (you may do first `push` something to have some canary value at top of the stack and `sp=FFFE`, but sp=0 should work well too). – Ped7g Nov 09 '18 at 15:31
  • Ped7g Thank you for your help! It also caught my eye that the values where consecutively halving, as you mentioned. I thought about using that in my code but didn't in the end, due to time constraints and the more serious problem that I still didn't know how to check my input. But in the end, the exam went well - there were some deductions for being clumsy and not getting the program to work 100%, but I did find out how to check my input, at the very last minute during the exam. I will post my answer so that others can use it. Once again, thank you all for your input, you helped me a lot! – MichaelP Nov 10 '18 at 17:36
0

After some days of pondering and racking my brain, and with the help of the great input provided by all of you, I finally found a solution to my problem. The biggest concern for me was, at the end, that I didn't know how to correctly check for input.

The problem was, as I had assumed, owed to my being stuck on a misconception, which fortunately was corrected by my lab partner. So, after all, we were able to get our program to work - last minute and not at 100%, but it worked and met the requirements.

So what was my misconception? Funny enough, I knew where we had to go, and the idea was correct. The problem was that I had skipped a major part of the logic operation - I had already calculated it in my head, and then combined the actual working solution with an obsolete AND which destroyed functionality.

All in all, the correct combination of XOR and AND was as following:

programloop:
MOV A,40h       ;state of button 2, inverted (XOR FFh)
MOV B,A         ;save state to register B
IN A,03h        ;input at port-address 03h
AND B           ;find out if button is pressed
JPNZ blink      ;if yes, jump to blink

MOV A,20h       ;state of button 3, inverted (XOR FFh)
MOV B,A         ;save state to register B
IN A,03h        ;input at port-address 03h
AND B           ;find out if button is pressed
JPNZ goright    ;if yes, jump to goright

MOV A,10h       ;state of button 4, inverted (XOR FFh)
MOV B,A         ;save state to register B
IN A,03h        ;input at port-address 03h
AND B           ;find out if button is pressed
JPNZ goleft     ;if yes, jump to goleft
JP programloop  ;go back to beginning (input has to be checked constantly)

This did the trick concerning the three buttons that changed LED-behavior.

Concerning frequency, we had to reduce complexity down to only two states, due to heavy time constraints (we misread the assignment and erroneously started with the bonus question, which cost us about 50% of our development time - yay us. Lesson learned: always start reading at the top and read carefully.)

But since the changing of frequency worked, it turned out OK.

MOV A,03h       ;state of button five being pressed (inverted)
MOV B,A         ;saved state into register B for later use
IN A,03h        ;physical input over button
AND B           ;find out if button is pressed
JPNZ freq025Hz  ;if yes, jump to freq025Hz
JPZ freq4Hz     ;if no, jump to freq4Hz

So this is it!

Once again, thank you all for your help.

If any questions remain, feel free to ask!

MichaelP
  • 41
  • 10