mirror of
https://github.com/irmen/prog8.git
synced 2025-01-26 19:30:59 +00:00
got the number guessing example fully working on c64 asm
This commit is contained in:
parent
a499ac6def
commit
be819ba8a7
@ -2,15 +2,21 @@
|
||||
%import c64lib
|
||||
%import c64utils
|
||||
|
||||
|
||||
; The classic number guessing game.
|
||||
; This version uses more low-level subroutines (calls directly into the C64's ROM routines)
|
||||
; and instead of a loop (with the added behind the scenes processing), uses absolute jumps.
|
||||
; It's less readable I think, but produces a smaller program.
|
||||
|
||||
|
||||
~ main {
|
||||
sub start() {
|
||||
str name = "????????????????????????????????????????"
|
||||
str guessstr = "??????????"
|
||||
str input = "??????????"
|
||||
ubyte guess
|
||||
ubyte secretnumber = 0
|
||||
ubyte attempts_left = 10
|
||||
memory uword freadstr_arg = $22 ; argument for FREADSTR
|
||||
uword testword
|
||||
memory uword freadstr_arg = $22 ; argument for FREADSTR ($22/$23)
|
||||
|
||||
; greeting
|
||||
c64.VMCSB |= 2 ; switch lowercase chars
|
||||
@ -23,7 +29,7 @@
|
||||
c64.STROUT(".\nLet's play a number guessing game.\nI am thinking of a number from 1 to 100!You'll have to guess it!\n")
|
||||
|
||||
; create a secret random number from 1-100
|
||||
c64.RND() ; fac = random number
|
||||
c64.RND() ; fac = random number between 0 and 1
|
||||
c64.MUL10() ; fac *= 10
|
||||
c64.MUL10() ; .. and now *100
|
||||
c64.FADDH() ; add 0.5..
|
||||
@ -35,30 +41,28 @@ ask_guess:
|
||||
c64.STROUT("\nYou have ")
|
||||
c64scr.print_byte_decimal(attempts_left)
|
||||
c64.STROUT(" guess")
|
||||
if(attempts_left>0) c64.STROUT("es")
|
||||
if(attempts_left>1)
|
||||
c64.STROUT("es")
|
||||
|
||||
c64.STROUT(" left.\nWhat is your next guess? ")
|
||||
Y=c64scr.input_chars(guessstr)
|
||||
Y=c64scr.input_chars(input)
|
||||
c64.CHROUT('\n')
|
||||
freadstr_arg = guessstr
|
||||
freadstr_arg = input
|
||||
c64.FREADSTR(Y)
|
||||
A, Y = c64flt.GETADRAY()
|
||||
guess=A
|
||||
c64.EXTCOL=guess ; @debug
|
||||
c64.BGCOL0=secretnumber ;@debug
|
||||
if(guess==secretnumber) {
|
||||
c64.STROUT("\nThat's my number, impressive!\n")
|
||||
goto goodbye
|
||||
}
|
||||
c64.STROUT("\nThat is too ")
|
||||
if(guess > secretnumber)
|
||||
if(guess < secretnumber)
|
||||
c64.STROUT("low!\n")
|
||||
else
|
||||
c64.STROUT("high!\n")
|
||||
|
||||
attempts_left--
|
||||
if(attempts_left>0) goto ask_guess
|
||||
; more efficient: if_nz goto ask_guess
|
||||
if_nz goto ask_guess
|
||||
|
||||
; game over.
|
||||
c64.STROUT("\nToo bad! It was: ")
|
||||
|
@ -1,12 +1,16 @@
|
||||
%import c64utils
|
||||
%import mathlib
|
||||
|
||||
; The classic number guessing game.
|
||||
; This version uses mostly high level subroutine calls and loops.
|
||||
; It's more readable than the low-level version, but produces a slightly larger program.
|
||||
|
||||
~ main {
|
||||
|
||||
sub start() {
|
||||
str name = "????????????????????????????????????????"
|
||||
str guess = "??????????"
|
||||
ubyte secretnumber = rnd() % 100
|
||||
str input = "??????????"
|
||||
ubyte secretnumber = rnd() % 99 + 1 ; random number 1..100
|
||||
|
||||
c64.VMCSB |= 2 ; switch lowercase chars
|
||||
c64scr.print_string("Please introduce yourself: ")
|
||||
@ -17,50 +21,67 @@
|
||||
|
||||
for ubyte attempts_left in 10 to 1 step -1 {
|
||||
|
||||
|
||||
c64scr.print_byte_decimal(X)
|
||||
c64.CHROUT('\n')
|
||||
; stackptr debugging
|
||||
; c64scr.print_byte_decimal(X)
|
||||
; c64.CHROUT('\n')
|
||||
|
||||
c64scr.print_string("\nYou have ")
|
||||
c64scr.print_byte_decimal(attempts_left)
|
||||
c64scr.print_string(" guess")
|
||||
if attempts_left>1 c64scr.print_string("es")
|
||||
if attempts_left>1
|
||||
c64scr.print_string("es")
|
||||
c64scr.print_string(" left.\nWhat is your next guess? ")
|
||||
c64scr.input_chars(guess)
|
||||
ubyte guessednumber = str2ubyte(guess)
|
||||
c64scr.input_chars(input)
|
||||
ubyte guess = str2ubyte(input)
|
||||
|
||||
; debug info
|
||||
c64scr.print_string(" > attempts left=")
|
||||
c64scr.print_byte_decimal(attempts_left)
|
||||
c64scr.print_string("\n > secretnumber=")
|
||||
c64scr.print_byte_decimal(secretnumber)
|
||||
c64scr.print_string("\n > guess=")
|
||||
c64scr.print_string(guess)
|
||||
c64scr.print_string("\n > guessednumber=")
|
||||
c64scr.print_byte_decimal(guessednumber)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_byte_decimal(X)
|
||||
c64.CHROUT('\n')
|
||||
; debug info
|
||||
; c64scr.print_string(" > attempts left=")
|
||||
; c64scr.print_byte_decimal(attempts_left)
|
||||
; c64scr.print_string("\n > secretnumber=")
|
||||
; c64scr.print_byte_decimal(secretnumber)
|
||||
; c64scr.print_string("\n > input=")
|
||||
; c64scr.print_string(input)
|
||||
; c64scr.print_string("\n > guess=")
|
||||
; c64scr.print_byte_decimal(guess)
|
||||
; c64.CHROUT('\n')
|
||||
; c64scr.print_byte_decimal(X) ; stackptr debugging
|
||||
; c64.CHROUT('\n')
|
||||
|
||||
|
||||
if guessednumber==secretnumber {
|
||||
c64scr.print_string("\n\nYou guessed it, impressive!\n")
|
||||
c64scr.print_string("Thanks for playing, ")
|
||||
c64scr.print_string(name)
|
||||
c64scr.print_string(".\n")
|
||||
return
|
||||
if guess==secretnumber {
|
||||
ending(true)
|
||||
return ; @todo make return ending(true) actually work as well
|
||||
} else {
|
||||
c64scr.print_string("\n\nThat is too ")
|
||||
if guessednumber<secretnumber
|
||||
if guess<secretnumber
|
||||
c64scr.print_string("low!\n")
|
||||
else
|
||||
c64scr.print_string("high!\n")
|
||||
}
|
||||
}
|
||||
|
||||
c64scr.print_string("\nToo bad! My number was: ")
|
||||
c64scr.print_byte_decimal(secretnumber)
|
||||
c64scr.print_string(".\n")
|
||||
return
|
||||
; return 99 ;@todo error message (no return values)
|
||||
; return 99,44 ;@todo error message (no return values)
|
||||
; return ending(false) ; @todo fix this, actuall needs to CALL ending even though no value is returned
|
||||
|
||||
ending(false)
|
||||
return ; @todo make return ending(false) actually work as well
|
||||
|
||||
|
||||
sub ending(success: ubyte) {
|
||||
if success
|
||||
c64scr.print_string("\n\nYou guessed it, impressive!\n")
|
||||
else {
|
||||
c64scr.print_string("\nToo bad! My number was: ")
|
||||
c64scr.print_byte_decimal(secretnumber)
|
||||
c64scr.print_string(".\n")
|
||||
}
|
||||
c64scr.print_string("Thanks for playing, ")
|
||||
c64scr.print_string(name)
|
||||
c64scr.print_string(".\n")
|
||||
|
||||
; return 99 ; @todo error message (no return values)
|
||||
; return 99,44 ; @todo error message (no return values)
|
||||
; return 99,44 ; @todo should check number of return values!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -260,7 +260,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
DataType.FLOAT -> Opcode.PUSH_VAR_FLOAT
|
||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS,
|
||||
DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F,
|
||||
DataType.ARRAY_B, DataType.ARRAY_W -> Opcode.PUSH_VAR_WORD
|
||||
DataType.ARRAY_B, DataType.ARRAY_W -> Opcode.PUSH_ADDR_HEAPVAR
|
||||
}
|
||||
}
|
||||
|
||||
@ -565,7 +565,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
|
||||
if(lv.heapId==null)
|
||||
throw CompilerException("string should have been moved into heap ${lv.position}")
|
||||
prog.instr(Opcode.PUSH_WORD, Value(lv.type, lv.heapId))
|
||||
prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = "@todo-string-varname?") // XXX push address of string
|
||||
}
|
||||
DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F,
|
||||
DataType.ARRAY_B, DataType.ARRAY_W -> {
|
||||
|
@ -41,7 +41,9 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
|
||||
optimizeDataConversionAndUselessDiscards()
|
||||
optimizeVariableCopying()
|
||||
optimizeMultipleSequentialLineInstrs()
|
||||
// todo optimize stackvm code more
|
||||
// todo: optimize stackvm code more
|
||||
// todo: stackvm replace rrestorex+rsavex combo by only rrestorex (note: can have label/comment inbetween)
|
||||
// todo: stackvm replace call X + return (without values) combo by a jump X
|
||||
|
||||
// remove nops (that are not a label)
|
||||
for (blk in blocks) {
|
||||
|
@ -487,6 +487,9 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
Opcode.PUSH_REGAY_WORD -> {
|
||||
" sta ${ESTACK_LO.toHex()},x | tya | sta ${ESTACK_HI.toHex()},x | dex "
|
||||
}
|
||||
Opcode.PUSH_ADDR_HEAPVAR -> {
|
||||
" lda #<${ins.callLabel} | sta ${ESTACK_LO.toHex()},x | lda #>${ins.callLabel} | sta ${ESTACK_HI.toHex()},x | dex"
|
||||
}
|
||||
Opcode.POP_REGAX_WORD -> throw AssemblyError("cannot load X register from stack because it's used as the stack pointer itself")
|
||||
Opcode.POP_REGXY_WORD -> throw AssemblyError("cannot load X register from stack because it's used as the stack pointer itself")
|
||||
Opcode.POP_REGAY_WORD -> {
|
||||
|
@ -186,7 +186,7 @@ Values will usually be part of an expression or assignment statement::
|
||||
|
||||
12345 ; integer number
|
||||
$aa43 ; hex integer number
|
||||
%100101 ; binary integer number
|
||||
%100101 ; binary integer number (% is also remainder operator so be careful)
|
||||
"Hi, I am a string" ; text string
|
||||
'a' ; petscii value (byte) for the letter a
|
||||
-33.456e52 ; floating point number
|
||||
|
@ -258,6 +258,8 @@ type identifier type storage size example var declara
|
||||
**hexadecimal numbers:** you can use a dollar prefix to write hexadecimal numbers: ``$20ac``
|
||||
|
||||
**binary numbers:** you can use a percent prefix to write binary numbers: ``%10010011``
|
||||
Note that ``%`` is also the remainder operator so be careful: if you want to take the remainder
|
||||
of something with an operand starting with 1 or 0, you'll have to add a space in between.
|
||||
|
||||
**character values:** you can use a single character in quotes like this ``'a'`` for the Petscii byte value of that character.
|
||||
|
||||
@ -342,7 +344,7 @@ arithmetic: ``+`` ``-`` ``*`` ``/`` ``//`` ``**`` ``%``
|
||||
``+``, ``-``, ``*``, ``/`` are the familiar arithmetic operations.
|
||||
``//`` is the floor-divide, the division resulting in a whole number rounded towards minus infinity.
|
||||
``**`` is the power operator: ``3 ** 5`` is equal to 3*3*3*3*3 and is 243.
|
||||
``%`` is the remainder operator: ``25 % 7`` is 4.
|
||||
``%`` is the remainder operator: ``25 % 7`` is 4. Be careful: without a space, %10 will be parsed as the binary number 2
|
||||
|
||||
|
||||
bitwise arithmetic: ``&`` ``|`` ``^`` ``~``
|
||||
|
@ -17,8 +17,8 @@ asmsub init_system () -> clobbers(A,X,Y) -> () {
|
||||
; ---- initializes the machine to a sane starting state
|
||||
; This means that the BASIC, KERNAL and CHARGEN ROMs are banked in,
|
||||
; the VIC, SID and CIA chips are reset, screen is cleared, and the default IRQ is set.
|
||||
; Also a different color scheme is chosen to identify ourselves a little.
|
||||
; All three registers set to 0, status flags cleared.
|
||||
; Also a different color scheme is chosen to identify ourselves a little.
|
||||
; Uppercase charset is activated, and all three registers set to 0, status flags cleared.
|
||||
%asm {{
|
||||
sei
|
||||
cld
|
||||
|
@ -250,8 +250,16 @@ remainder_b .proc
|
||||
.pend
|
||||
|
||||
remainder_ub .proc
|
||||
inx
|
||||
lda ESTACK_LO,x ; right operand
|
||||
sta SCRATCH_ZPB1
|
||||
lda ESTACK_LO+1,x ; left operand
|
||||
- cmp SCRATCH_ZPB1
|
||||
bcc +
|
||||
sbc SCRATCH_ZPB1
|
||||
jmp -
|
||||
+ sta ESTACK_LO+1,x
|
||||
rts
|
||||
.warn "not implemented"
|
||||
.pend
|
||||
|
||||
remainder_w .proc
|
||||
@ -742,18 +750,57 @@ func_str2byte .proc
|
||||
.warn "not implemented"
|
||||
.pend
|
||||
|
||||
func_str2ubyte .proc
|
||||
;-- convert string (address on stack) to ubyte number
|
||||
; @todo load address into $22/$23
|
||||
; @todo length of string in A
|
||||
;jsr c64.FREADSTR ; string to fac1
|
||||
;jsr c64.GETADR ; fac1 to unsigned word in Y/A
|
||||
inx
|
||||
lda #99 ; @todo
|
||||
sta ESTACK_LO,x
|
||||
dex
|
||||
; @todo python code for a str-to-ubyte function that doesn't use the basic rom:
|
||||
;def str2ubyte(s, slen):
|
||||
; hundreds_map = {
|
||||
; 0: 0,
|
||||
; 1: 100,
|
||||
; 2: 200
|
||||
; }
|
||||
; digitvalue = 0
|
||||
; result = 0
|
||||
; if slen==0:
|
||||
; return digitvalue
|
||||
; digitvalue = ord(s[slen-1])-48
|
||||
; slen -= 1
|
||||
; if slen==0:
|
||||
; return digitvalue
|
||||
; result = digitvalue
|
||||
; digitvalue = 10 * (ord(s[slen-1])-48)
|
||||
; result += digitvalue
|
||||
; slen -= 1
|
||||
; if slen==0:
|
||||
; return result
|
||||
; digitvalue = hundreds_map[ord(s[slen-1])-48]
|
||||
; result += digitvalue
|
||||
; return result
|
||||
|
||||
func_str2ubyte jmp func_str2uword
|
||||
|
||||
func_str2uword .proc
|
||||
;-- convert string (address on stack) to uword number
|
||||
lda ESTACK_LO+1,x
|
||||
sta $22
|
||||
lda ESTACK_HI+1,x
|
||||
sta $23
|
||||
jsr _strlen2233
|
||||
tya
|
||||
stx SCRATCH_ZPREG
|
||||
jsr c64.FREADSTR ; string to fac1
|
||||
jsr c64.GETADR ; fac1 to unsigned word in Y/A
|
||||
ldx SCRATCH_ZPREG
|
||||
sta ESTACK_HI+1,x
|
||||
tya
|
||||
sta ESTACK_LO+1,x
|
||||
rts
|
||||
.warn "not implemented"
|
||||
_strlen2233
|
||||
;-- return the length of the (zero-terminated) string at $22/$23, in Y
|
||||
ldy #0
|
||||
- lda ($22),y
|
||||
beq +
|
||||
iny
|
||||
bne -
|
||||
+ rts
|
||||
.pend
|
||||
|
||||
func_str2word .proc
|
||||
@ -761,11 +808,6 @@ func_str2word .proc
|
||||
.warn "not implemented"
|
||||
.pend
|
||||
|
||||
func_str2uword .proc
|
||||
rts
|
||||
.warn "not implemented"
|
||||
.pend
|
||||
|
||||
func_str2float .proc
|
||||
rts
|
||||
.warn "not implemented"
|
||||
|
Loading…
x
Reference in New Issue
Block a user