what would this be equivilent to in assembler?
what would this be equivilent to in assembler?
if inkeys$="m" then x=x+1
i understand the keyboard matrix is read differently in assembler/m code
i understand the keyboard matrix is read differently in assembler/m code
i started programming the spectrum when i was 8 :-
1 plot rnd*255,rnd*175
2 goto 1
http://zxspeccy.great-site.net/
1 plot rnd*255,rnd*175
2 goto 1
http://zxspeccy.great-site.net/
Re: what would this be equivilent to in assembler?
There's lots of ways depending on whether X is in a register or memory location, whether it can go larger than 255, whether you want it to wrap or stop at a certain max value, whether you want to do something with X after increasing it, etc. But something like this is a good start:
It's worth pointing out that here, the and sets the flags, including the zero flag. Then the ld loads a new value into a, which doesn't change the flags. Then the jr nz is doing the conditional jump based on the flags from the old value of a.
Code: Select all
Key.SpSsMNB equ $7ffe ; Keyboard half row for Space, Sybmbol Shift, M, N, and B
Xcoordinate: db 0 ; Location to store X (0..255)
; ...
IncreaseX: ld bc, Key.SpSsMNB ; Bit 0 = Space, bit 4 = B
in a, (c) ; Read kb half row with Space, Sybmbol Shift, M, N, and B
and %00100 ; Clear all bits except for M
ld a, (Xcoordinate) ; Read existing value of X
jr nz, .notM ; A will only be zero if X key is pressed
inc a ; X = X + 1
ld (Xcoordinate), a ; Save new value of X back for next time
.notM: ; X is now in A register so we can do something with it next
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel • NXTP • ESP Update • ESP Reset • CSpect Plugins
SevenFFF / Threetwosevensixseven / colonel32
NXtel • NXTP • ESP Update • ESP Reset • CSpect Plugins
- ParadigmShifter
- Manic Miner
- Posts: 779
- Joined: Sat Sep 09, 2023 4:55 am
Re: what would this be equivilent to in assembler?
I separate my keyboard reading from the game logic since I may not always want to move right when right is pressed, there may be a wall in the way etc.
I set BC as follows (controls here are WASD but comment in the function shows which port to read and what bit gets cleared when pressed) so it's not hard to change the controls. Note I only read the port once to handle all of ASD since they are on the same half-row.
Also makes it easier to redefine keys, use a joystick port read instead, etc.
EDIT: That's for arcade style control anyway. I also have a routine which scans all keys pressed into an 8 byte buffer which is easier to use for text input (high score entry, text parsing etc.).
Anyway point is you are better separating your keyboard logic from game logic. Also means you only need to read the keyboard once per frame in one place and not scattered throughout the game loop.
EDIT2: My routine also does the sensible thing if you are pressing left and right at the same time (sets C to 0). The non-sensible thing would be to have 1 key taking priority over another e.g. pressing right and left means right wins (not really a good way of handling that).
EDIT3: I also clear D and E because I also read IJKL which is same as WASD but sets DE instead of BC. But that is not shown.
EDIT4: And the to increment a variable if the "right" key is pressed I just call read_keyboard (once per frame) then process what is returned straight after.
I set BC as follows (controls here are WASD but comment in the function shows which port to read and what bit gets cleared when pressed) so it's not hard to change the controls. Note I only read the port once to handle all of ASD since they are on the same half-row.
Code: Select all
; at exit, B contains 1 if we pressed down and -1 if we pressed up
; at exit, C contains 1 if we pressed right and -1 if we pressed left
read_keyboard:
; Read these ports to scan keyboard
; bit N (0-based) is clear if the key is being pressed
; #FE - SHIFT, Z, X, C, & V
; #FD - A, S, D, F, & G
; #FB - Q, W, E, R, & T
; #F7 - 1, 2, 3, 4, & 5
; #EF - 0, 9, 8, 7, & 6
; #DF - P, O, I, U, & Y
; #BF - ENTER, L, K, J, & H
; #7F - SPACE, FULL-STOP, M, N, & B
; ld a, port
; in a, (#FE)
; to do the read of the port
ld bc, 0
ld d, b
ld e, b
; are we pressing W?
ld a, #FB
in a, (#FE)
bit 1, a
jr nz, .notpressingW
dec b
.notpressingW
; are we pressing A, S or D?
ld a, #FD
in a, (#FE)
bit 1, a
jr nz, .notpressingS
inc b
.notpressingS
; are we pressing A?
bit 0, a
jr nz, .notpressingA
dec c
.notpressingA
; are we pressing D?
bit 2, a
jr nz, .notpressingD
inc c
.notpressingD
ret
EDIT: That's for arcade style control anyway. I also have a routine which scans all keys pressed into an 8 byte buffer which is easier to use for text input (high score entry, text parsing etc.).
Anyway point is you are better separating your keyboard logic from game logic. Also means you only need to read the keyboard once per frame in one place and not scattered throughout the game loop.
EDIT2: My routine also does the sensible thing if you are pressing left and right at the same time (sets C to 0). The non-sensible thing would be to have 1 key taking priority over another e.g. pressing right and left means right wins (not really a good way of handling that).
EDIT3: I also clear D and E because I also read IJKL which is same as WASD but sets DE instead of BC. But that is not shown.
EDIT4: And the to increment a variable if the "right" key is pressed I just call read_keyboard (once per frame) then process what is returned straight after.
Code: Select all
call read_keyboard
ld a, c
cp 1 ; right is pressed
jr nz,.rightisnotpressed
;increment some variable here
Re: what would this be equivilent to in assembler?
cool, got that working. so how do i tell the program not to not let 'a' register go passed say 31?Seven.FFF wrote: ↑Sun Mar 17, 2024 3:20 pm There's lots of ways depending on whether X is in a register or memory location, whether it can go larger than 255, whether you want it to wrap or stop at a certain max value, whether you want to do something with X after increasing it, etc. But something like this is a good start:
It's worth pointing out that here, the and sets the flags, including the zero flag. Then the ld loads a new value into a, which doesn't change the flags. Then the jr nz is doing the conditional jump based on the flags from the old value of a.Code: Select all
Key.SpSsMNB equ $7ffe ; Keyboard half row for Space, Sybmbol Shift, M, N, and B Xcoordinate: db 0 ; Location to store X (0..255) ; ... IncreaseX: ld bc, Key.SpSsMNB ; Bit 0 = Space, bit 4 = B in a, (c) ; Read kb half row with Space, Sybmbol Shift, M, N, and B and %00100 ; Clear all bits except for M ld a, (Xcoordinate) ; Read existing value of X jr nz, .notM ; A will only be zero if X key is pressed inc a ; X = X + 1 ld (Xcoordinate), a ; Save new value of X back for next time .notM: ; X is now in A register so we can do something with it next
i started programming the spectrum when i was 8 :-
1 plot rnd*255,rnd*175
2 goto 1
http://zxspeccy.great-site.net/
1 plot rnd*255,rnd*175
2 goto 1
http://zxspeccy.great-site.net/
- ParadigmShifter
- Manic Miner
- Posts: 779
- Joined: Sat Sep 09, 2023 4:55 am
Re: what would this be equivilent to in assembler?
If your x position is 31, and you press right, ignore it (easier again if you separate keyboard reading from logic). If you can move more than 1 x value at a time, add some amount to the value stored in the x coordinate, see if it is 32 or over (cp 31 ; clears carry flag if A >= 32), if it is set it to 31.
So you calculate what the next position would be and if it is not valid you either ignore it (do not write the value back), or write back to the x coord variable what it should be.
To wrap around 0-31 you can use AND 31.
So to use that code as an example
cp 32 sets the C flag (and the Z/S flags). C flag is the chibi flag (chibi = smaller) so since you want to do something when you are not chibi (greater or equal) compared to 32, so you check the NC flag.
It's not really called the chibi flag of course but that is a good way of remembering it
So you calculate what the next position would be and if it is not valid you either ignore it (do not write the value back), or write back to the x coord variable what it should be.
To wrap around 0-31 you can use AND 31.
So to use that code as an example
Code: Select all
IncreaseX: ld bc, Key.SpSsMNB ; Bit 0 = Space, bit 4 = B
in a, (c) ; Read kb half row with Space, Sybmbol Shift, M, N, and B
and %00100 ; Clear all bits except for M
ld a, (Xcoordinate) ; Read existing value of X
jr nz, .notM ; A will only be zero if X key is pressed
inc a ; X = X + 1
cp 32 ; A < 32?
jr nc, .notM ; A >= 32 : do not write back new value of Xcoordinate
ld (Xcoordinate), a ; Save new value of X back for next time
.notM
It's not really called the chibi flag of course but that is a good way of remembering it
Last edited by ParadigmShifter on Sun Mar 17, 2024 5:51 pm, edited 3 times in total.
Re: what would this be equivilent to in assembler?
If you wanted to wrap X around so it goes 0,1,..,30,31,0,1 you can use a neat trick with and 31, because 32 is a power of two:
Code: Select all
Key.SpSsMNB equ $7ffe ; Half row for Space, Sybmbol Shift, M, N, and B
Xcoordinate: db 0 ; Location to store X (0..255)
ld bc, Key.SpSsMNB ; Bit 0 = Space, bit 4 = B
in a, (c) ; Read half row with Space, Sybmbol Shift, M, N, and B
and %00100 ; Clear all bits except for M
ld a, (Xcoordinate) ; Read existing value of X
jr nz, .notM ; A will only be zero if X key is pressed
inc a ; X = X + 1
and 31 ; Constrain between 0..31 so X wraps round
ld (Xcoordinate), a ; Save new value of X back for next time
.notM: ; X is now in A register so we can do something with it next
Code: Select all
Key.SpSsMNB equ $7ffe ; Half row for Space, Sybmbol Shift, M, N, and B
Xcoordinate: db 0 ; Location to store X (0..255)
ld bc, Key.SpSsMNB ; Bit 0 = Space, bit 4 = B
in a, (c) ; Read half row with Space, Sybmbol Shift, M, N, and B
and %00100 ; Clear all bits except for M
ld a, (Xcoordinate) ; Read existing value of X
jr nz, .notM ; A will only be zero if X key is pressed
cp 31 ; 31 is the max value X can have
jr nc, .skipXincrease ; If X >= 31 then we're in range, so skip increasing X
inc a ; X = X + 1
.skipXincrease: ld (Xcoordinate), a ; Save new value of X back for next time
.notM: ; X is now in A register so we can do something with it next
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel • NXTP • ESP Update • ESP Reset • CSpect Plugins
SevenFFF / Threetwosevensixseven / colonel32
NXtel • NXTP • ESP Update • ESP Reset • CSpect Plugins
- ParadigmShifter
- Manic Miner
- Posts: 779
- Joined: Sat Sep 09, 2023 4:55 am
Re: what would this be equivilent to in assembler?
Reason for me using 32 and poster above using 31 is because I check the chibi flag after the inc a. Seven.FFF is also writing back the value regardless of whether it changes, that's not really necessary can just skip to the .notM case and not bother writing the value back (since it has not changed).
That code might be a little confusing for beginners though since you have to remember that ld a, (Xcoordinate) does not set any flags so the following jr nz is testing the result of the AND with the keycode mask, it is not checking whether the Xcoordinate is non-zero.
I still think you would be better separating the keyboard read from the logic though.
That code might be a little confusing for beginners though since you have to remember that ld a, (Xcoordinate) does not set any flags so the following jr nz is testing the result of the AND with the keycode mask, it is not checking whether the Xcoordinate is non-zero.
I still think you would be better separating the keyboard read from the logic though.
Re: what would this be equivilent to in assembler?
Some more options without bit operations:
- read out LASTK by:
ld hl LASTK
ld a (hl)
ld (hl) 0
LASTK is at 23560 - LASTK is also the same as iy-50, so you might use:
ld a (iy-50)
ld (iy-50) 0 - call $2BF yourself to get the key code in A
POKE 23614,10: STOP 1..0 hold, SS/m/n colors, b/spc toggle
- ParadigmShifter
- Manic Miner
- Posts: 779
- Joined: Sat Sep 09, 2023 4:55 am
Re: what would this be equivilent to in assembler?
LASTK has the same problems that INKEY$ has though (you can only press one key at a time and it cares about shift keys as well I think?). You also need to use the standard interrupt routine (probably not a problem for beginners of course) or call the scan routine/default interrupt service routine from your own interrupt service routine.
Here is some simple code which sets 40 bytes (1 for each key) with 1 for "pressed" and 0 for "not pressed". In my proper code I pack that into 8 bytes not 40 though, but this is useful for detecting multiple keys from BASIC. The key pressed/not pressed table is address 65000 - 65039 with that ORG address. If you were doing it all in ASM you can put the table anywhere you like and just call read_keys to fill it (which you would do once per frame).
You can easily turn that into a redefine keys routine or extend it so it prints the keys pressed etc.
If you want to map the entry in the table to the actual key so you can print it, this will be useful
Both shift keys map to 0 in that table though. You have to test multiple values if you want to support pressing shift giving different tokens.
Here is some simple code which sets 40 bytes (1 for each key) with 1 for "pressed" and 0 for "not pressed". In my proper code I pack that into 8 bytes not 40 though, but this is useful for detecting multiple keys from BASIC. The key pressed/not pressed table is address 65000 - 65039 with that ORG address. If you were doing it all in ASM you can put the table anywhere you like and just call read_keys to fill it (which you would do once per frame).
Code: Select all
ORG 65000
kbdbuff BLOCK 40
read_keys:
ld hl, kbdbuff
ld bc, #fefe
.nexthalfline
in a, (c)
cpl
and #1f
ld e, a
ld d, 5
.loop
xor a ; clear carry
rr e ; shift lowest bit from half row out into the carry
adc a ; set A to 1 if the key was pressed, 0 otherwise
ld (hl), a
inc hl
dec d
jr nz, .loop
rlc b
jr c, .nexthalfline
ret
If you want to map the entry in the table to the actual key so you can print it, this will be useful
Code: Select all
CR EQU #0D ; enter key code
key_tbl db 0, "zxcvasdfgqwert1234509876poiuy", CR, "lkjh ", 0, "mnb"
Last edited by ParadigmShifter on Sun Mar 17, 2024 6:42 pm, edited 1 time in total.
Re: what would this be equivilent to in assembler?
The ROM routines (that set LASTK) are great for quick routines when you're typing words and sentences on one key at a time. In most games you're moving up/down and left/right at the same time, or left/right and jump and the same time, or moving and firing at the same time, or rotating and thrusting and firing at the same time. Users don't want to be pecking those keys one at a time to suit the code reading the keys, so it makes more sense to read simultaneous keyspresses directly with IN $xxFE.
You can press and detect up to three keys simultaneously, the way the matrix keyboard is implemented in hardware. Any more and you start getting ghost keys or phantom keypresses. But simultaneous presses is great for the scenarios above.
You can press and detect up to three keys simultaneously, the way the matrix keyboard is implemented in hardware. Any more and you start getting ghost keys or phantom keypresses. But simultaneous presses is great for the scenarios above.
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel • NXTP • ESP Update • ESP Reset • CSpect Plugins
SevenFFF / Threetwosevensixseven / colonel32
NXtel • NXTP • ESP Update • ESP Reset • CSpect Plugins
- ParadigmShifter
- Manic Miner
- Posts: 779
- Joined: Sat Sep 09, 2023 4:55 am
Re: what would this be equivilent to in assembler?
I'm definitely able to detect at least 4 keys at once (it also depends on the PC keyboard when emulating though I guess, some keyboards have limits to which keys can be pressed simultaneously). I had a robotron style thing working ages ago which used WASD to move and IJKL to fire... you were able to move and shoot in 8 independent directions no problems.Seven.FFF wrote: ↑Sun Mar 17, 2024 6:42 pm The ROM routines (that set LASTK) are great for quick routines when you're typing words and sentences on one key at a time. In most games you're moving up/down and left/right at the same time, or left/right and jump and the same time, or moving and firing at the same time. Users don't want to be pecking those keys one at a time, so it makes more sense to read simultaneous keyspresses directly with IN $xxFE.
You can press and detect up to three keys sumultaneously, the way the matrix keyboard is implemented in hardware. Any more and you start getting ghost keys or phantom keypresses. But simultaneous presses is great for the scenarios above.
The ROM LASTK routine bails out if more than 3 keys are pressed IIRC which is why it does not work for LASTK. It also only returns 1 key of course (I think it takes into account shifts though). I think it also uses the key repeat rate and stuff? Or maybe that is another SYSVAR, I don't use the ROM for keyboard handling so I'm not very familiar with it.
My routine which I use instead of LASTK only decodes a keypress if you are pressing max 1 key and 1 shift key at the same time, it's a lot more complicated than the other routine I posted above though. I use that for text entry only.
Re: what would this be equivilent to in assembler?
Using emulators to derive the hardware characteristics of real spectrums is a bit... counterproductive. It's true that most emulator authors don't bother emulating the Spectrum keyboard accurately, but this does lead to people using them as the primary reference source, to write games that won't work properly on any real 80s models of Spectrum.ParadigmShifter wrote: ↑Sun Mar 17, 2024 6:47 pm I'm definitely able to detect at least 4 keys at once (it also depends on the PC keyboard when emulating though I guess, some keyboards have limits to which keys can be pressed simultaneously).
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel • NXTP • ESP Update • ESP Reset • CSpect Plugins
SevenFFF / Threetwosevensixseven / colonel32
NXtel • NXTP • ESP Update • ESP Reset • CSpect Plugins
- ParadigmShifter
- Manic Miner
- Posts: 779
- Joined: Sat Sep 09, 2023 4:55 am
Re: what would this be equivilent to in assembler?
Can someone try my routine on a real speccy then and tell us the results? Since my Spectrum broke when I was 17, 35 years ago. I use Spin and occasionally additional testing on Fuse. EDIT: 35 years ago not 3 lol
I would like to know if it is possible to detect more than 3 keys on real hardware.
I had a basic listing which printed out the keyboard array (and poked the values for the ASM in to memory at address 65040) but it was a postimage image and it seems to not be working now :/
Basic listings were here
viewtopic.php?t=10794
but I just see postimage not available
I see 1 image (the poke the routine from BASIC) but not the others, I guess postimage is just having a busy server brainfart or something.
I would like to know if it is possible to detect more than 3 keys on real hardware.
I had a basic listing which printed out the keyboard array (and poked the values for the ASM in to memory at address 65040) but it was a postimage image and it seems to not be working now :/
Basic listings were here
viewtopic.php?t=10794
but I just see postimage not available
I see 1 image (the poke the routine from BASIC) but not the others, I guess postimage is just having a busy server brainfart or something.
Re: what would this be equivilent to in assembler?
You can detect multiple keys, but you can’t detect that other keys weren’t pressed, because of the way this kind of matrix shorts out a row to a column. And the more extra keys are pressed, the more phantom keypresses register.
With carefully crafted fixed defined keys you can get away with a certain amount, but that’s no good if you allow all your keys to be user defined. Sometimes it’s good enough for your needs though.
I always recommend having real hardware to test projects on. Emulation, even 25+ years into the era of accurate emulators. is a little bit of a circle jerk where people will often make their emulators work the same as other emulators and use that as the proof of accuracy.
You can read more about phantom keys in the Chris Smith book, to save buying a Spectrum just to test this.
With carefully crafted fixed defined keys you can get away with a certain amount, but that’s no good if you allow all your keys to be user defined. Sometimes it’s good enough for your needs though.
I always recommend having real hardware to test projects on. Emulation, even 25+ years into the era of accurate emulators. is a little bit of a circle jerk where people will often make their emulators work the same as other emulators and use that as the proof of accuracy.
You can read more about phantom keys in the Chris Smith book, to save buying a Spectrum just to test this.
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel • NXTP • ESP Update • ESP Reset • CSpect Plugins
SevenFFF / Threetwosevensixseven / colonel32
NXtel • NXTP • ESP Update • ESP Reset • CSpect Plugins
- ParadigmShifter
- Manic Miner
- Posts: 779
- Joined: Sat Sep 09, 2023 4:55 am
Re: what would this be equivilent to in assembler?
I found this which suggests ASZ pressed produces CAPS SHIFT but I haven't got time to think about it all yet Also looks a bit "hardware"-y for me as well lol.
https://retrocomputing.stackexchange.co ... y-ghosting
That suggests to me pressing WAS (= Q) at the same time or WSD (= E) at the same time may go wrong, but that involves pressing up and down at the same time I guess.
I do allow users to redefine the controls so that would enable them to pick something else if there are problems anyway, so I think that is fine and not really a (serious) problem?
SJOE has 8 keys and W is flip not move up though... you aren't likely to be flipping and dropping pieces at the same time. IJKL by default is rotate/shuffle but you can't shuffle clockwise and anticlockwise at the same time so you not likely to press I and K at the same time I guess.
There's also a pause key for 9 keys total.
EDIT: I'm not going to buy a Spectrum no, but I would appreciate people testing SJOE on real hardware of course, which I can't do. Especially the much improved new version I have planned (still in progress developing the MULLET physics engine).
https://retrocomputing.stackexchange.co ... y-ghosting
That suggests to me pressing WAS (= Q) at the same time or WSD (= E) at the same time may go wrong, but that involves pressing up and down at the same time I guess.
I do allow users to redefine the controls so that would enable them to pick something else if there are problems anyway, so I think that is fine and not really a (serious) problem?
SJOE has 8 keys and W is flip not move up though... you aren't likely to be flipping and dropping pieces at the same time. IJKL by default is rotate/shuffle but you can't shuffle clockwise and anticlockwise at the same time so you not likely to press I and K at the same time I guess.
There's also a pause key for 9 keys total.
EDIT: I'm not going to buy a Spectrum no, but I would appreciate people testing SJOE on real hardware of course, which I can't do. Especially the much improved new version I have planned (still in progress developing the MULLET physics engine).
- ParadigmShifter
- Manic Miner
- Posts: 779
- Joined: Sat Sep 09, 2023 4:55 am
Re: what would this be equivilent to in assembler?
Ok I think I will add a key test to my games then if there is enough space left in the frontend (which flashes keys pressed) so you would be able to test whether combinations of redefinable keys were suitable in the frontend before or after redefining keys.
I think WASD was used for FPS games on PC to reduce the amount of ghost keypresses anyway so having those as default on the speccy seems to make sense (you don't press up and down at same time usually).
I will see if Spin does ghost the keys in a bit as well I think. It doesn't in BASIC cos it only supports 1 keypress at a time for input so pressing WAS at the same time does nothing... can modify my basic test code of my 40 keypress array routine though to check.
EDIT: That WoS thread has good info as well, cheers for posting that. So any 3 keys corner of a rectangle, so that would be WSIK keys affected with my default keyboard layout problems can occur if you press WS same time as any other key, or IK same time as any other key I think... probably not a serious issue. Of course you can redefine the keys to be able to cause more issues if you want...
I think WASD was used for FPS games on PC to reduce the amount of ghost keypresses anyway so having those as default on the speccy seems to make sense (you don't press up and down at same time usually).
I will see if Spin does ghost the keys in a bit as well I think. It doesn't in BASIC cos it only supports 1 keypress at a time for input so pressing WAS at the same time does nothing... can modify my basic test code of my 40 keypress array routine though to check.
EDIT: That WoS thread has good info as well, cheers for posting that. So any 3 keys corner of a rectangle, so that would be WSIK keys affected with my default keyboard layout problems can occur if you press WS same time as any other key, or IK same time as any other key I think... probably not a serious issue. Of course you can redefine the keys to be able to cause more issues if you want...
Re: what would this be equivilent to in assembler?
If I wrote it in M4 Forth it would look like
Spoiler
Code: Select all
dworkin@dw-A15:~/Programovani/ZX/Forth/M4$ ../check_word.sh 'PUSH(__TESTKEY_M) TESTKEY IF _1ADD THEN'
ld A, 0x7F ; 2:7 0x7F04 testkey if ( -- ) if press "M"
in A,[0xFE] ; 2:11 0x7F04 testkey if
and 0x04 ; 2:7 0x7F04 testkey if
jp nz, else101 ; 3:10 0x7F04 testkey if
inc HL ; 1:6 1+
else101 EQU $ ; then = endif
endif101: ; then
; seconds: 0 ;[10:41]
Spoiler
Code: Select all
dworkin@dw-A15:~/Programovani/ZX/Forth/M4$ ../check_word.sh 'VARIABLE(x) PUSH(__TESTKEY_M) TESTKEY IF PUSH(1) PUSH(x) ADDSTORE THEN'
ld A, 0x7F ; 2:7 0x7F04 testkey if ( -- ) if press "M"
in A,[0xFE] ; 2:11 0x7F04 testkey if
and 0x04 ; 2:7 0x7F04 testkey if
jp nz, else101 ; 3:10 0x7F04 testkey if
;[9:46] 1 x +! ( -- ) [x] += 1
ld BC,[x] ; 4:20 1 x +!
inc BC ; 1:6 1 x +!
ld [x], BC ; 4:20 1 x +!
else101 EQU $ ; then = endif
endif101: ; then
VARIABLE_SECTION:
x: ; variable x
dw 0 ; variable x
;# ============================================================================
if ($<0x0100)
.error Overflow 64k! over 0..255 bytes
endif
if ($<0x0200)
.error Overflow 64k! over 256..511 bytes
endif
if ($<0x0400)
.error Overflow 64k! over 512..1023 bytes
endif
if ($<0x0800)
.error Overflow 64k! over 1024..2047 bytes
endif
if ($<0x1000)
.error Overflow 64k! over 2048..4095 bytes
endif
if ($<0x2000)
.error Overflow 64k! over 4096..8191 bytes
endif
if ($<0x3000)
.error Overflow 64k! over 8192..12287 bytes
endif
if ($<0x4000)
.error Overflow 64k! over 12288..16383 bytes
endif
if ($>0xFF00)
.warning Data ends at 0xFF00+ address!
endif
; seconds: 1 ;[18:81]
If I wanted to use reading from the buffer:
Spoiler
Code: Select all
dworkin@dw-A15:~/Programovani/ZX/Forth/M4$ ../check_word.sh 'KEY PUSH(m) CEQ IF _1ADD THEN'
call READKEY ; 3:17 key
;[8:35] m c= if ( char1 -- ) bool: lo(char1) = lo(m)
ld A, L ; 1:4 m c= if
xor low m ; 2:7 m c= if L ^ lo(m)
ex DE, HL ; 1:4 m c= if
pop DE ; 1:10 m c= if
jp nz, else101 ; 3:10 m c= if variant: variable
inc HL ; 1:6 1+
else101 EQU $ ; then = endif
endif101: ; then
;#==============================================================================
;# Read key from keyboard
;# In:
;# Out: push stack, TOP = HL = key
READKEY:
ex DE, HL ; 1:4 readkey ( ret . old _DE old_HL -- old_DE ret . old_HL key )
ex [SP],HL ; 1:19 readkey
push HL ; 1:11 readkey
ld A,[0x5C08] ; 3:13 readkey read new value of LAST K
or A ; 1:4 readkey is it still zero?
jr z, $-4 ; 2:7/12 readkey
ld L, A ; 1:4 readkey
ld H, 0x00 ; 2:7 readkey
;# ...fall down to clearbuff{}dnl
;#==============================================================================
;# Clear key buffer
;# In:
;# Out: {(LAST_K)} = 0
CLEARBUFF:
push AF ; 1:11 clearbuff
xor A ; 1:4 clearbuff ZX Spectrum LAST K system variable
ld [0x5C08],A ; 3:13 clearbuff
pop AF ; 1:10 clearbuff
ret ; 1:10 clearbuff
; seconds: 1 ;[31:175]
Spoiler
Code: Select all
dworkin@dw-A15:~/Programovani/ZX/Forth/M4$ ../check_word.sh 'VARIABLE(x) KEY PUSH(m) CEQ IF PUSH(1) PUSH(x) ADDSTORE THEN'
call READKEY ; 3:17 key
;[8:35] m c= if ( char1 -- ) bool: lo(char1) = lo(m)
ld A, L ; 1:4 m c= if
xor low m ; 2:7 m c= if L ^ lo(m)
ex DE, HL ; 1:4 m c= if
pop DE ; 1:10 m c= if
jp nz, else101 ; 3:10 m c= if variant: variable
;[9:46] 1 x +! ( -- ) [x] += 1
ld BC,[x] ; 4:20 1 x +!
inc BC ; 1:6 1 x +!
ld [x], BC ; 4:20 1 x +!
else101 EQU $ ; then = endif
endif101: ; then
;#==============================================================================
;# Read key from keyboard
;# In:
;# Out: push stack, TOP = HL = key
READKEY:
ex DE, HL ; 1:4 readkey ( ret . old _DE old_HL -- old_DE ret . old_HL key )
ex [SP],HL ; 1:19 readkey
push HL ; 1:11 readkey
ld A,[0x5C08] ; 3:13 readkey read new value of LAST K
or A ; 1:4 readkey is it still zero?
jr z, $-4 ; 2:7/12 readkey
ld L, A ; 1:4 readkey
ld H, 0x00 ; 2:7 readkey
;# ...fall down to clearbuff{}dnl
;#==============================================================================
;# Clear key buffer
;# In:
;# Out: {(LAST_K)} = 0
CLEARBUFF:
push AF ; 1:11 clearbuff
xor A ; 1:4 clearbuff ZX Spectrum LAST K system variable
ld [0x5C08],A ; 3:13 clearbuff
pop AF ; 1:10 clearbuff
ret ; 1:10 clearbuff
VARIABLE_SECTION:
x: ; variable x
dw 0 ; variable x
;# ============================================================================
if ($<0x0100)
.error Overflow 64k! over 0..255 bytes
endif
if ($<0x0200)
.error Overflow 64k! over 256..511 bytes
endif
if ($<0x0400)
.error Overflow 64k! over 512..1023 bytes
endif
if ($<0x0800)
.error Overflow 64k! over 1024..2047 bytes
endif
if ($<0x1000)
.error Overflow 64k! over 2048..4095 bytes
endif
if ($<0x2000)
.error Overflow 64k! over 4096..8191 bytes
endif
if ($<0x3000)
.error Overflow 64k! over 8192..12287 bytes
endif
if ($<0x4000)
.error Overflow 64k! over 12288..16383 bytes
endif
if ($>0xFF00)
.warning Data ends at 0xFF00+ address!
endif
; seconds: 1 ;[39:215]
Another way is to look at the compiler explorer for C and Z88dk. How the parameter must be written "+zx -zorg=32768".
https://godbolt.org/
but in this case it doesn't help much because it shows that it calls the _getchar built-in function
Spoiler
Code: Select all
void main(){
int x;
if ( getchar() == 'm') x++;
}
._main
push bc
call _getchar
ld de,109
and a
sbc hl,de
jp nz,i_2 ;
add hl,sp
inc (hl)
ld a,(hl)
inc hl
jr nz,ASMPC+3
inc (hl)
ld h,(hl)
ld l,a
dec hl
.i_2
pop bc
ret
It would be possible to compile the source with the switch -S to look for files in /snap/z88dk/current/share/z88dk/libsrc/_DEVELOPMENT/stdio/ or use the dissasembler for the binary. But in this particular case it would be very difficult to cut through many layers.
If I use the BorielBasic compiler
Code: Select all
dim x as integer
if inkey$="m" then x=x+1
This way we get all human-readable code in one file. We can easily find it there
Spoiler
Code: Select all
push namespace core
INKEY:
PROC
LOCAL __EMPTY_INKEY
LOCAL KEY_SCAN
LOCAL KEY_TEST
LOCAL KEY_CODE
ld bc, 3 ; 1 char length string
call __MEM_ALLOC
ld a, h
or l
ret z ; Return if NULL (No memory)
push hl ; Saves memory pointer
call KEY_SCAN
jp nz, __EMPTY_INKEY
call KEY_TEST
jp nc, __EMPTY_INKEY
dec d ; D is expected to be FLAGS so set bit 3 $FF
; 'L' Mode so no keywords.
ld e, a ; main key to A
; C is MODE 0 'KLC' from above still.
call KEY_CODE ; routine K-DECODE
pop hl
ld (hl), 1
inc hl
ld (hl), 0
inc hl
ld (hl), a
dec hl
dec hl ; HL Points to string result
ret
__EMPTY_INKEY:
pop hl
xor a
ld (hl), a
inc hl
ld (hl), a
dec hl
ret
KEY_SCAN EQU 028Eh
KEY_TEST EQU 031Eh
KEY_CODE EQU 0333h
ENDP
pop namespace
028E: THE 'KEYBOARD SCANNING' SUBROUTINE https://skoolkid.github.io/rom/asm/028E.html
031E: THE 'K-TEST' SUBROUTINE https://skoolkid.github.io/rom/asm/031E.html
0333: THE 'KEYBOARD DECODING' SUBROUTINE https://skoolkid.github.io/rom/asm/0333.html
It is relatively easy to obtain asm code from a higher language.
Z80 Forth compiler (ZX Spectrum 48kb): https://codeberg.org/DW0RKiN/M4_FORTH
- ParadigmShifter
- Manic Miner
- Posts: 779
- Joined: Sat Sep 09, 2023 4:55 am
Re: what would this be equivilent to in assembler?
I apologize if I missed the point or if it got lost in the automatic translation.ParadigmShifter wrote: ↑Mon Mar 18, 2024 12:01 am As long as it is compiled... interpreted BASIC is a lot harder and more pointless to reverse engineer like that.
My point I wanted to make was:
The code that comes from BorielBasic is relatively easy to read and understand for humans. All the code is in one file.
"Relatively" means that it is not as good as the code that comes from M4 FORTH. You can easily translate very small pieces of code there, and you don't have to deal with things around.
"Relatively" means that it is much better than what comes from C. To readability and finding where the code is hidden. GETCHAR is only an apparently simple function, but thanks to the fact that it depends on the platform, and Z88DK is multi-platform and has alternative libraries for each, so one can quickly get lost in it.
From what I understand, you write that if the basic was interpreted, it is more complicated. This doesn't make much sense to me considering what the BorielBasic compiler does.
I think I still answered within the scope of the original assignment.
Yes, the easiest way is to get an answer from someone who knows how to program in assembler. I showed other ways.
Z80 Forth compiler (ZX Spectrum 48kb): https://codeberg.org/DW0RKiN/M4_FORTH
- ParadigmShifter
- Manic Miner
- Posts: 779
- Joined: Sat Sep 09, 2023 4:55 am
Re: what would this be equivilent to in assembler?
I just meant it's harder to work out what the BASIC interpreter is doing because it has to do all the interpreting stuff inbetween the actual code which does what you asked it to do in the first place. Compiled code obviously just does the machine code translation of what you ask for and doesn't have to parse tokens, numbers, etc. Obviously (in ZX Basic when interpreted) it all boils down to ROM calls in the end but working out what those are is not easy unless you already know machine code and can read the ROM disassembly of what the interpreter does when it actually executes your basic instructions.
Re: what would this be equivilent to in assembler?
Ah, well, as I understood the question and out of all the answers, no one asked about how the Basic interpreter works. On the one hand, it can be more complex than the C compiler, on the other hand, it is easier, because you don't have to solve any optimizations, but it divides the code into small parts. Both want to know some other things. If I could choose, I would rather write a Basic interpreter than a C compiler, because it would be easier for me. Increased requirements are placed on the interpreter, regarding the fact that it will not be a cross compiler and it must be able to do as many things as possible with the fewest requirements in order to get into the ROM. The C translation is expected to take the code from a human, wonder what it is doing and write new, efficient code suitable for the processor. He is expected to throw out unnecessary parts, etc. All difficult or, in principle, unsolvable tasks under all circumstances. On the one hand, we enter "GO TO THE STORE" on the other hand, we expect "AND CLIMB THIS FENCE (BECAUSE IT'S A FASTER WAY)" but we have no word for "CLIMB" nor can we see the "FENCE" even though we know it's there. So then we try to give the command "LOCK THE DOOR AND THEN GO TO THE SHOP" and hope that instead of unlocking it, he will climb over the fence. The compiler won't be too happy about it either.ParadigmShifter wrote: ↑Mon Mar 18, 2024 12:42 am I just meant it's harder to work out what the BASIC interpreter is doing because it has to do all the interpreting stuff inbetween the actual code which does what you asked it to do in the first place. Compiled code obviously just does the machine code translation of what you ask for and doesn't have to parse tokens, numbers, etc. Obviously (in ZX Basic when interpreted) it all boils down to ROM calls in the end but working out what those are is not easy unless you already know machine code and can read the ROM disassembly of what the interpreter does when it actually executes your basic instructions.
But I'm way off topic.
Z80 Forth compiler (ZX Spectrum 48kb): https://codeberg.org/DW0RKiN/M4_FORTH
Re: what would this be equivilent to in assembler?
well u know what im going to ask u now dont u?
how do i get it to move the character the other way?
heres what i have so far but it doesnt work. i tried to adapt it.
im using the x key
i did try swapping the key and key2 labels around and reads the key fine...
how do i get it to move the character the other way?
heres what i have so far but it doesnt work. i tried to adapt it.
im using the x key
i did try swapping the key and key2 labels around and reads the key fine...
Code: Select all
;
; ROM routine addresses
;
ROM_CLS EQU 0x0DAF ; Clears the screen and opens channel 2
ROM_OPEN_CHANNEL EQU 0x1601 ; Open a channel
ROM_PRINT EQU 0x203C ; Print a string
;
; PRINT control codes - work with ROM_PRINT and RST 0x10
;
INK EQU 0x10
PAPER EQU 0x11
FLASH EQU 0x12
BRIGHT EQU 0x13
INVERSE EQU 0x14
OVER EQU 0x15
AT1 EQU 0x16
TAB EQU 0x17
CR EQU 0x0C
;
; Or this...
;
org 30000
Key equ $7ffe ; Keyboard half row for Space, Sybmbol Shift, M, N, and B
Key2 equ $fefe ; Keyboard half row
Xcoordinate db 0 ; Location to store X (0..255)
loop
IncreaseX ld bc, Key ; Bit 0 = Space, bit 4 = B
in a, (c) ; Read kb half row with Space, Sybmbol Shift, M, N, and B
and %00100 ; Clear all bits except X
ld a, (Xcoordinate) ; Read existing value of X
jr nz, notM ; A will only be zero if X key is pressed
inc a
cp 31 ; A < 32?
jr nc, notM ; A >= 32 : do not write back new value of Xcoordinate ; X = X + 1
ld (Xcoordinate), a ; Save new value of X back for next time
DecreaseX ld bc, Key2 ; Bit 0 = Space, bit 4 = B
in a, (c) ; Read kb half row
and %00100 ; Clear all bits except for M
ld a, (Xcoordinate) ; Read existing value of X
jr nz, notM ; A will only be zero if X key is pressed
dec a ; X = X - 1
ld (Xcoordinate), a ; Save new value of X back for next time
notM ; X is now in A register so we can do something with it next
ld (30050),a
; You can either do this...
;
CALL ROM_CLS ; Clear screen and open Channel 2 (Screen
; Print a single character at screen position (15, 1)
;
LD A, AT1 ; AT control character
RST 0x10
LD A, 10 ; Y
RST 0x10
LD A, 15 ; X
RST 0x10
LD A, INK ; Ink colour
RST 0x10
LD A, 0 ; Red
RST 0x10
LD A, 65 ; Character to print
RST 0x10
jp loop
i started programming the spectrum when i was 8 :-
1 plot rnd*255,rnd*175
2 goto 1
http://zxspeccy.great-site.net/
1 plot rnd*255,rnd*175
2 goto 1
http://zxspeccy.great-site.net/
- ParadigmShifter
- Manic Miner
- Posts: 779
- Joined: Sat Sep 09, 2023 4:55 am
Re: what would this be equivilent to in assembler?
Easiest way is to check the Xcoordinate before you decrement it and if it is 0 skip writing it back.
You can use
ld a, (Xcoordinate)
or a ; same as cp 0 but faster and smaller
jr z, .skipmoveleft
dec a
ld (Xcoordinate), a
.skipmoveleft
for that
EDIT: So with your code
But you've done the "wrong thing" there in that checking for moving right trumps moving left... consider my first post as an alternative... that makes you not move if you are pressing right and left at the same time.
You can use
ld a, (Xcoordinate)
or a ; same as cp 0 but faster and smaller
jr z, .skipmoveleft
dec a
ld (Xcoordinate), a
.skipmoveleft
for that
EDIT: So with your code
Code: Select all
DecreaseX ld bc, Key2 ; Bit 0 = Space, bit 4 = B
in a, (c) ; Read kb half row
and %00100 ; Clear all bits except for M
ld a, (Xcoordinate) ; Read existing value of X
jr nz, notM ; A will only be zero if X key is pressed
or a
jr z, notM
dec a ; X = X - 1
ld (Xcoordinate), a ; Save new value of X back for next time
Re: what would this be equivilent to in assembler?
thank you but now its not running at all...ParadigmShifter wrote: ↑Mon Mar 18, 2024 2:56 am Easiest way is to check the Xcoordinate before you decrement it and if it is 0 skip writing it back.
You can use
ld a, (Xcoordinate)
or a ; same as cp 0 but faster and smaller
jr z, .skipmoveleft
dec a
ld (Xcoordinate), a
.skipmoveleft
for that
EDIT: So with your code
But you've done the "wrong thing" there in that checking for moving right trumps moving left... consider my first post as an alternative... that makes you not move if you are pressing right and left at the same time.Code: Select all
DecreaseX ld bc, Key2 ; Bit 0 = Space, bit 4 = B in a, (c) ; Read kb half row and %00100 ; Clear all bits except for M ld a, (Xcoordinate) ; Read existing value of X jr nz, notM ; A will only be zero if X key is pressed or a jr z, notM dec a ; X = X - 1 ld (Xcoordinate), a ; Save new value of X back for next time
Code: Select all
;
; ROM routine addresses
;
ROM_CLS EQU 0x0DAF ; Clears the screen and opens channel 2
ROM_OPEN_CHANNEL EQU 0x1601 ; Open a channel
ROM_PRINT EQU 0x203C ; Print a string
;
; PRINT control codes - work with ROM_PRINT and RST 0x10
;
INK EQU 0x10
PAPER EQU 0x11
FLASH EQU 0x12
BRIGHT EQU 0x13
INVERSE EQU 0x14
OVER EQU 0x15
AT1 EQU 0x16
TAB EQU 0x17
CR EQU 0x0C
;
; Or this...
;
org 30000
Key equ $7ffe ; Keyboard half row for Space, Sybmbol Shift, M, N, and B
Key2 equ $fefe ; Keyboard half row
Xcoordinate db 0 ; Location to store X (0..255)
loop
IncreaseX ld bc, Key ; Bit 0 = Space, bit 4 = B
in a, (c) ; Read kb half row with Space, Sybmbol Shift, M, N, and B
and %00100 ; Clear all bits except for M
ld a, (Xcoordinate) ; Read existing value of X
jr nz, notM ; A will only be zero if X key is pressed
inc a
cp 31 ; A < 32?
jr nc, notM ; A >= 32 : do not write back new value of Xcoordinate ; X = X + 1
ld (Xcoordinate), a ; Save new value of X back for next time
DecreaseX ld bc, Key2 ; Bit 0 = Space, bit 4 = B
in a, (c) ; Read kb half row
and %00100 ; Clear all bits except for M
ld a, (Xcoordinate) ; Read existing value of X
jr nz, notM ; A will only be zero if X key is pressed
or a
jr z, notM
dec a ; X = X - 1
ld (Xcoordinate), a ; Save new value of X back for next time
notM ; X is now in A register so we can do something with it next
ld (30069),a
; You can either do this...
;
CALL ROM_CLS ; Clear screen and open Channel 2 (Screen
; Print a single character at screen position (15, 1)
;DecreaseX ld bc, Key2 ; Bit 0 = Space, bit 4 = B
in a, (c) ; Read kb half row
and %00100 ; Clear all bits except for M
ld a, (Xcoordinate) ; Read existing value of X
jr nz, notM ; A will only be zero if X key is pressed
or a
jr z, notM
dec a ; X = X - 1
ld (Xcoordinate), a ; Save new value of X back for next time
LD A, AT1 ; AT control character
RST 0x10
LD A, 10 ; Y
RST 0x10
LD A, 15 ; X
RST 0x10
LD A, INK ; Ink colour
RST 0x10
LD A, 0 ; Red
RST 0x10
LD A, 65 ; Character to print
RST 0x10
jp loop
i started programming the spectrum when i was 8 :-
1 plot rnd*255,rnd*175
2 goto 1
http://zxspeccy.great-site.net/
1 plot rnd*255,rnd*175
2 goto 1
http://zxspeccy.great-site.net/
- ParadigmShifter
- Manic Miner
- Posts: 779
- Joined: Sat Sep 09, 2023 4:55 am
Re: what would this be equivilent to in assembler?
Not running isn't very helpful Say how it does not work. Did it compile?
If you are using an emulator use the debugger to put breakpoints on the code paths and step through.
My code looks ok to me but it is way past beer o'clock so disclaimers apply.
EDIT: This looks a bit dodgy
ld (30069),a
where did you get 30069 from? Use a label instead of a hardcoded address.
If you are using an emulator use the debugger to put breakpoints on the code paths and step through.
My code looks ok to me but it is way past beer o'clock so disclaimers apply.
EDIT: This looks a bit dodgy
ld (30069),a
where did you get 30069 from? Use a label instead of a hardcoded address.